diff --git a/src/terminal/display.c b/src/terminal/display.c index 1c803b41..d148b411 100644 --- a/src/terminal/display.c +++ b/src/terminal/display.c @@ -206,15 +206,15 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client, guac_terminal_color* foreground, guac_terminal_color* background, guac_terminal_color (*palette)[256]) { - PangoFontMap* font_map; - PangoFont* font; - PangoFontMetrics* metrics; - PangoContext* context; - /* Allocate display */ guac_terminal_display* display = malloc(sizeof(guac_terminal_display)); display->client = client; + /* Initially no font loaded */ + display->font_desc = NULL; + display->char_width = 0; + display->char_height = 0; + /* Create default surface */ display->display_layer = guac_client_alloc_layer(client); display->select_layer = guac_client_alloc_layer(client); @@ -225,42 +225,10 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client, guac_protocol_send_move(client->socket, display->select_layer, display->display_layer, 0, 0, 0); - /* Get font */ - display->font_desc = pango_font_description_new(); - pango_font_description_set_family(display->font_desc, font_name); - pango_font_description_set_weight(display->font_desc, PANGO_WEIGHT_NORMAL); - pango_font_description_set_size(display->font_desc, - font_size * PANGO_SCALE * dpi / 96); - - font_map = pango_cairo_font_map_get_default(); - context = pango_font_map_create_context(font_map); - - font = pango_font_map_load_font(font_map, context, display->font_desc); - if (font == NULL) { - guac_client_abort(display->client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to get font \"%s\"", font_name); - free(display); - return NULL; - } - - metrics = pango_font_get_metrics(font, NULL); - if (metrics == NULL) { - guac_client_abort(display->client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, - "Unable to get font metrics for font \"%s\"", font_name); - free(display); - return NULL; - } - display->default_foreground = display->glyph_foreground = *foreground; display->default_background = display->glyph_background = *background; display->default_palette = palette; - /* Calculate character dimensions */ - display->char_width = - pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE; - display->char_height = - (pango_font_metrics_get_descent(metrics) - + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; - /* Initially empty */ display->width = 0; display->height = 0; @@ -269,12 +237,23 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client, /* Initially nothing selected */ display->text_selected = false; + /* Attempt to load font */ + if (guac_terminal_display_set_font(display, font_name, font_size, dpi)) { + guac_client_abort(display->client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, + "Unable to set initial font \"%s\"", font_name); + free(display); + return NULL; + } + return display; } void guac_terminal_display_free(guac_terminal_display* display) { + /* Free font description */ + pango_font_description_free(display->font_desc); + /* Free default palette. */ free(display->default_palette); @@ -966,4 +945,78 @@ void guac_terminal_display_clear_select(guac_terminal_display* display) { } +int guac_terminal_display_set_font(guac_terminal_display* display, + const char* font_name, int font_size, int dpi) { + + PangoFontDescription* font_desc; + + /* Build off existing font description if possible */ + if (display->font_desc != NULL) + font_desc = pango_font_description_copy(display->font_desc); + + /* Create new font description if there is nothing to copy */ + else { + font_desc = pango_font_description_new(); + pango_font_description_set_weight(font_desc, PANGO_WEIGHT_NORMAL); + } + + /* Optionally update font name */ + if (font_name != NULL) + pango_font_description_set_family(font_desc, font_name); + + /* Optionally update size */ + if (font_size != -1) { + pango_font_description_set_size(font_desc, + font_size * PANGO_SCALE * dpi / 96); + } + + PangoFontMap* font_map = pango_cairo_font_map_get_default(); + PangoContext* context = pango_font_map_create_context(font_map); + + /* Load font from font map */ + PangoFont* font = pango_font_map_load_font(font_map, context, font_desc); + if (font == NULL) { + guac_client_log(display->client, GUAC_LOG_INFO, "Unable to load " + "font \"%s\"", pango_font_description_get_family(font_desc)); + pango_font_description_free(font_desc); + return 1; + } + + /* Get metrics from loaded font */ + PangoFontMetrics* metrics = pango_font_get_metrics(font, NULL); + if (metrics == NULL) { + guac_client_log(display->client, GUAC_LOG_INFO, "Unable to get font " + "metrics for font \"%s\"", + pango_font_description_get_family(font_desc)); + pango_font_description_free(font_desc); + return 1; + } + + /* Save effective size of current display */ + int pixel_width = display->width * display->char_width; + int pixel_height = display->height * display->char_height; + + /* Calculate character dimensions using metrics */ + display->char_width = + pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE; + display->char_height = + (pango_font_metrics_get_descent(metrics) + + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; + + /* Atomically replace old font description */ + PangoFontDescription* old_font_desc = display->font_desc; + display->font_desc = font_desc; + pango_font_description_free(old_font_desc); + + /* Recalculate dimensions which will fit within current surface */ + int new_width = pixel_width / display->char_width; + int new_height = pixel_height / display->char_height; + + /* Resize display if dimensions have changed */ + if (new_width != display->width || new_height != display->height) + guac_terminal_display_resize(display, new_width, new_height); + + return 0; + +} diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c index 1984185d..cf70f92d 100644 --- a/src/terminal/terminal.c +++ b/src/terminal/terminal.c @@ -360,6 +360,10 @@ guac_terminal* guac_terminal_create(guac_client* client, term->upload_path_handler = NULL; term->file_download_handler = NULL; + /* Set size of available screen area */ + term->outer_width = width; + term->outer_height = height; + /* Init modified flag and conditional */ term->modified = 0; pthread_cond_init(&(term->modified_cond), NULL); @@ -1338,6 +1342,10 @@ int guac_terminal_resize(guac_terminal* terminal, int width, int height) { /* Acquire exclusive access to terminal */ guac_terminal_lock(terminal); + /* Set size of available screen area */ + terminal->outer_width = width; + terminal->outer_height = height; + /* Calculate available display area */ int available_width = width - GUAC_TERMINAL_SCROLLBAR_WIDTH; if (available_width < 0) @@ -1968,3 +1976,27 @@ void guac_terminal_apply_color_scheme(guac_terminal* terminal, } +void guac_terminal_apply_font(guac_terminal* terminal, const char* font_name, + int font_size, int dpi) { + + guac_client* client = terminal->client; + guac_terminal_display* display = terminal->display; + + if (guac_terminal_display_set_font(display, font_name, font_size, dpi)) + return; + + /* Resize terminal to fit available region, now that font metrics may be + * different */ + guac_terminal_resize(terminal, terminal->outer_width, + terminal->outer_height); + + /* Redraw terminal text and background */ + guac_terminal_repaint_default_layer(terminal, client->socket); + __guac_terminal_redraw_rect(terminal, 0, 0, + terminal->term_height - 1, + terminal->term_width - 1); + + guac_terminal_notify(terminal); + +} + diff --git a/src/terminal/terminal/display.h b/src/terminal/terminal/display.h index b1274235..5377cb14 100644 --- a/src/terminal/terminal/display.h +++ b/src/terminal/terminal/display.h @@ -334,5 +334,35 @@ void guac_terminal_display_select(guac_terminal_display* display, */ void guac_terminal_display_clear_select(guac_terminal_display* display); +/** + * Alters the font of the terminal display. The available display area and the + * regular grid of character cells will be resized as necessary to compensate + * for any changes in font metrics. + * + * If successful, the terminal itself MUST be manually resized to take into + * account the new character dimensions, and MUST be manually redrawn. Failing + * to do so will result in graphical artifacts. + * + * @param display + * The display whose font family and/or size are being changed. + * + * @param font_name + * The name of the new font family, or NULL if the font family should + * remain unchanged. + * + * @param font_size + * The new font size, in points, or -1 if the font size should remain + * unchanged. + * + * @param dpi + * The resolution of the display in DPI. If the font size will not be + * changed (the font size given is -1), this value is ignored. + * + * @return + * Zero if the font was successfully changed, non-zero otherwise. + */ +int guac_terminal_display_set_font(guac_terminal_display* display, + const char* font_name, int font_size, int dpi); + #endif diff --git a/src/terminal/terminal/terminal.h b/src/terminal/terminal/terminal.h index 28085f67..ac1217eb 100644 --- a/src/terminal/terminal/terminal.h +++ b/src/terminal/terminal/terminal.h @@ -277,6 +277,20 @@ struct guac_terminal { */ int requested_scrollback; + /** + * The width of the space available to all components of the terminal, in + * pixels. This may include space which will not actually be used for + * character rendering. + */ + int outer_width; + + /** + * The height of the space available to all components of the terminal, in + * pixels. This may include space which will not actually be used for + * character rendering. + */ + int outer_height; + /** * The width of the terminal, in pixels. */ @@ -1086,5 +1100,29 @@ int guac_terminal_available_scroll(guac_terminal* term); void guac_terminal_apply_color_scheme(guac_terminal* terminal, const char* color_scheme); +/** + * Alters the font of the terminal. The terminal will automatically be redrawn + * and resized as necessary. If the terminal size changes, the remote side of + * the terminal session must be manually informed of that change or graphical + * artifacts may result. + * + * @param terminal + * The terminal whose font family and/or size are being changed. + * + * @param font_name + * The name of the new font family, or NULL if the font family should + * remain unchanged. + * + * @param font_size + * The new font size, in points, or -1 if the font size should remain + * unchanged. + * + * @param dpi + * The resolution of the display in DPI. If the font size will not be + * changed (the font size given is -1), this value is ignored. + */ +void guac_terminal_apply_font(guac_terminal* terminal, const char* font_name, + int font_size, int dpi); + #endif