diff --git a/config.mk b/config.mk index 7a488cf..d40a334 100644 --- a/config.mk +++ b/config.mk @@ -26,7 +26,7 @@ GLIB_MIN_VERSION = 2.50 GLIB_PKG_CONFIG_NAME = glib-2.0 # GTK GTK_VERSION_CHECK ?= 1 -GTK_MIN_VERSION = 3.6 +GTK_MIN_VERSION = 3.10 GTK_PKG_CONFIG_NAME = gtk+-3.0 # pkg-config binary diff --git a/zathura/callbacks.c b/zathura/callbacks.c index 7db22f8..c7ba706 100644 --- a/zathura/callbacks.c +++ b/zathura/callbacks.c @@ -216,6 +216,29 @@ cb_refresh_view(GtkWidget* GIRARA_UNUSED(view), gpointer data) statusbar_page_number_update(zathura); } +void +cb_scale_factor(GObject* object, GParamSpec* UNUSED(pspec), gpointer data) +{ + zathura_t* zathura = data; + if (zathura == NULL || zathura->document == NULL) { + return; + } + + int new_factor = gtk_widget_get_scale_factor(GTK_WIDGET(object)); + if (new_factor == 0) { + girara_debug("Ignoring new device scale factor = 0"); + return; + } + + zathura_device_factors_t current = zathura_document_get_device_factors(zathura->document); + if (fabs(new_factor - current.x) >= DBL_EPSILON || + fabs(new_factor - current.y) >= DBL_EPSILON) { + zathura_document_set_device_factors(zathura->document, new_factor, new_factor); + girara_debug("New device scale factor: %d", new_factor); + render_all(zathura); + } +} + void cb_page_layout_value_changed(girara_session_t* session, const char* name, girara_setting_type_t UNUSED(type), void* value, void* UNUSED(data)) { diff --git a/zathura/callbacks.h b/zathura/callbacks.h index 15e4ba1..95ee0fe 100644 --- a/zathura/callbacks.h +++ b/zathura/callbacks.h @@ -79,6 +79,18 @@ void cb_view_vadjustment_changed(GtkAdjustment *adjustment, gpointer data); */ void cb_refresh_view(GtkWidget* view, gpointer data); +/** + * This function gets called when the view widget scale factor changes (e.g. + * when moving from a regular to a HiDPI screen). + * + * It records the new value and triggers a re-rendering of the document. + * + * @param object The view widget + * @param pspec The GParamSpec for the scale-factor property + * @param gpointer The zathura instance + */ +void cb_scale_factor(GObject* object, GParamSpec* pspec, gpointer data); + /** * This function gets called when the value of the "pages-per-row" * variable changes diff --git a/zathura/document.c b/zathura/document.c index cddb00d..bf6539d 100644 --- a/zathura/document.c +++ b/zathura/document.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,7 @@ struct zathura_document_s { double cell_height; /**< height of a page cell in the document (not ransformed by scale and rotation) */ unsigned int view_width; /**< width of current viewport */ unsigned int view_height; /**< height of current viewport */ + zathura_device_factors_t device_factors; /**< x and y device scale factors (for e.g. HiDPI) */ unsigned int pages_per_row; /**< number of pages in a row */ unsigned int first_page_column; /**< column of the first page */ unsigned int page_padding; /**< padding between pages */ @@ -132,6 +134,8 @@ zathura_document_open(zathura_t* zathura, const char* path, const char* uri, document->cell_height = 0.0; document->view_height = 0; document->view_width = 0; + document->device_factors.x = 1.0; + document->device_factors.y = 1.0; document->position_x = 0.0; document->position_y = 0.0; @@ -500,6 +504,34 @@ zathura_document_get_viewport_size(zathura_document_t* document, *width = document->view_width; } +void +zathura_document_set_device_factors(zathura_document_t* document, + double x_factor, double y_factor) +{ + if (document == NULL) { + return; + } + if (fabs(x_factor) < DBL_EPSILON || fabs(y_factor) < DBL_EPSILON) { + girara_debug("Ignoring new device factors %f and %f: too small", + x_factor, y_factor); + return; + } + + document->device_factors.x = x_factor; + document->device_factors.y = y_factor; +} + +zathura_device_factors_t +zathura_document_get_device_factors(zathura_document_t* document) +{ + if (document == NULL) { + /* The function is guaranteed to not return zero values */ + return (zathura_device_factors_t){1.0, 1.0}; + } + + return document->device_factors; +} + void zathura_document_get_cell_size(zathura_document_t* document, unsigned int* height, unsigned int* width) diff --git a/zathura/document.h b/zathura/document.h index 3118f30..a993411 100644 --- a/zathura/document.h +++ b/zathura/document.h @@ -249,6 +249,24 @@ void zathura_document_get_viewport_size(zathura_document_t* document, unsigned int *height, unsigned int* width); +/** + * Set the device scale factors (e.g. for HiDPI). These are generally integers + * and equal for x and y. These scaling factors are only used when rendering to + * the screen. + * + * @param[in] x_factor,yfactor The x and y scale factors + */ +void +zathura_document_set_device_factors(zathura_document_t* document, + double x_factor, double y_factor); +/** + * Return the current device scale factors (guaranteed to be non-zero). + * + * @return The x and y device scale factors + */ +zathura_device_factors_t +zathura_document_get_device_factors(zathura_document_t* document); + /** * Return the size of a cell from the document's layout table in pixels. Assumes * that the table is homogeneous (i.e. every cell has the same dimensions). It diff --git a/zathura/page-widget.c b/zathura/page-widget.c index d5e4ad6..ebb41f5 100644 --- a/zathura/page-widget.c +++ b/zathura/page-widget.c @@ -409,6 +409,30 @@ zathura_page_widget_get_property(GObject* object, guint prop_id, GValue* value, } } +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,14,0) +static zathura_device_factors_t +get_safe_device_factors(cairo_surface_t* surface) +{ + zathura_device_factors_t factors; + cairo_surface_get_device_scale(surface, &factors.x, &factors.y); + + if (fabs(factors.x) < DBL_EPSILON) { + factors.x = 1.0; + } + if (fabs(factors.y) < DBL_EPSILON) { + factors.y = 1.0; + } + + return factors; +} +#else +static zathura_device_factors_t +get_safe_device_factors(cairo_surface_t* UNUSED(surface)) +{ + return (zathura_device_factors_t){1.0, 1.0}; +} +#endif + static gboolean zathura_page_widget_draw(GtkWidget* widget, cairo_t* cairo) { @@ -445,8 +469,13 @@ zathura_page_widget_draw(GtkWidget* widget, cairo_t* cairo) } else { const unsigned int height = cairo_image_surface_get_height(priv->thumbnail); const unsigned int width = cairo_image_surface_get_width(priv->thumbnail); - const unsigned int pheight = (rotation % 180 ? page_width : page_height); - const unsigned int pwidth = (rotation % 180 ? page_height : page_width); + unsigned int pheight = (rotation % 180 ? page_width : page_height); + unsigned int pwidth = (rotation % 180 ? page_height : page_width); + + /* note: this always returns 1 and 1 if Cairo too old for device scale API */ + zathura_device_factors_t device = get_safe_device_factors(priv->thumbnail); + pwidth *= device.x; + pheight *= device.y; cairo_scale(cairo, pwidth / (double)width, pheight / (double)height); cairo_set_source_surface(cairo, priv->thumbnail, 0, 0); @@ -625,8 +654,14 @@ draw_thumbnail_image(cairo_surface_t* surface, size_t max_size) width = width * scale; height = height * scale; + /* note: this always returns 1 and 1 if Cairo too old for device scale API */ + zathura_device_factors_t device = get_safe_device_factors(surface); + const unsigned int user_width = width / device.x; + const unsigned int user_height = height / device.y; + + /* create thumbnail surface, taking width and height as device sizes */ cairo_surface_t *thumbnail; - thumbnail = cairo_surface_create_similar(surface, CAIRO_CONTENT_COLOR, width, height); + thumbnail = cairo_surface_create_similar(surface, CAIRO_CONTENT_COLOR, user_width, user_height); if (thumbnail == NULL) { return NULL; } diff --git a/zathura/render.c b/zathura/render.c index 51967a8..7cd3f3e 100644 --- a/zathura/render.c +++ b/zathura/render.c @@ -732,20 +732,30 @@ render(render_job_t* job, ZathuraRenderRequest* request, ZathuraRenderer* render unsigned int page_width = 0; unsigned int page_height = 0; + /* page size in points */ zathura_document_t* document = zathura_page_get_document(page); const double height = zathura_page_get_height(page); const double width = zathura_page_get_width(page); + /* page size in user pixels base on document zoom: 100% results in 1 pixel per point */ const double real_scale = page_calc_height_width(document, height, width, &page_height, &page_width, false); +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,14,0) + zathura_device_factors_t device_factors = zathura_document_get_device_factors(document); + page_width *= device_factors.x; + page_height *= device_factors.y; +#endif cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, page_width, page_height); if (surface == NULL) { return false; } +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,14,0) + cairo_surface_set_device_scale(surface, device_factors.x, device_factors.y); +#endif if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { cairo_surface_destroy(surface); return false; @@ -759,11 +769,11 @@ render(render_job_t* job, ZathuraRenderRequest* request, ZathuraRenderer* render cairo_save(cairo); cairo_set_source_rgb(cairo, 1, 1, 1); - cairo_rectangle(cairo, 0, 0, page_width, page_height); - cairo_fill(cairo); + cairo_paint(cairo); cairo_restore(cairo); cairo_save(cairo); + /* apply scale (used by e.g. Poppler as pixels per point) */ if (fabs(real_scale - 1.0f) > FLT_EPSILON) { cairo_scale(cairo, real_scale, real_scale); } diff --git a/zathura/types.h b/zathura/types.h index e0028d7..0cf1f9b 100644 --- a/zathura/types.h +++ b/zathura/types.h @@ -271,4 +271,13 @@ void zathura_document_information_entry_free(zathura_document_information_entry_ */ typedef struct zathura_content_type_context_s zathura_content_type_context_t; +/** + * Device scaling structure. + */ +typedef struct zathura_device_factors_s +{ + double x; + double y; +} zathura_device_factors_t; + #endif // TYPES_H diff --git a/zathura/zathura.c b/zathura/zathura.c index 07439dc..2195871 100644 --- a/zathura/zathura.c +++ b/zathura/zathura.c @@ -153,6 +153,9 @@ init_ui(zathura_t* zathura) g_signal_connect(G_OBJECT(zathura->ui.session->gtk.view), "refresh-view", G_CALLBACK(cb_refresh_view), zathura); + g_signal_connect(G_OBJECT(zathura->ui.session->gtk.view), + "notify::scale-factor", G_CALLBACK(cb_scale_factor), zathura); + /* page view */ zathura->ui.page_widget = gtk_grid_new(); gtk_grid_set_row_homogeneous(GTK_GRID(zathura->ui.page_widget), TRUE); @@ -956,6 +959,10 @@ document_open(zathura_t* zathura, const char* path, const char* uri, const char* const unsigned int view_height = (unsigned int)floor(gtk_adjustment_get_page_size(vadjustment)); zathura_document_set_viewport_height(zathura->document, view_height); + /* get initial device scale */ + int device_factor = gtk_widget_get_scale_factor(zathura->ui.session->gtk.view); + zathura_document_set_device_factors(zathura->document, device_factor, device_factor); + /* create blank pages */ zathura->pages = calloc(number_of_pages, sizeof(GtkWidget*)); if (zathura->pages == NULL) {