diff --git a/devel.but b/devel.but index d2b9cac..9f734e1 100644 --- a/devel.but +++ b/devel.but @@ -2972,7 +2972,7 @@ when finished with by passing it to the game's own \H{midend-size} \cw{midend_size()} -\c void midend_size(midend *me, int *x, int *y, bool user_size); +\c void midend_size(midend *me, int *x, int *y, bool user_size, double device_pixel_ratio); Tells the mid-end to figure out its window size. @@ -3029,6 +3029,24 @@ to use scroll bars for large puzzles), you can pass dimensions of \cw{INT_MAX} as input to this function. You should probably not do that \e{and} set the \c{user_size} flag, though! +The \cw{device_pixel_ratio} allows the front end to specify that its +pixels are unusually large or small (or should be treated as such). +The mid-end uses this to adjust the tile size, both at startup (if the +ratio is not 1) and if the ratio changes. + +A \cw{device_pixel_ratio} of 1 indicates normal-sized pixels. +\q{Normal} is not precisely defined, but it's about 4 pixels per +millimetre on a screen designed to be viewed from a metre away, or a +size such that text 15 pixels high is comfortably readable. Some +platforms have a concept of a logical pixel that this can be mapped +onto. For instance, Cascading Style Sheets (CSS) has a unit called +\cq{px} that only matches physical pixels at a \cw{device_pixel_ratio} +of 1. + +The \cw{device_pixel_ratio} indicates the number of physical pixels in +a normal-sized pixel, so values less than 1 indicate unusually large +pixels and values greater than 1 indicate unusually small pixels. + The midend relies on the frontend calling \cw{midend_new_game()} (\k{midend-new-game}) before calling \cw{midend_size()}. diff --git a/emcc.c b/emcc.c index 651fc5a..b7af686 100644 --- a/emcc.c +++ b/emcc.c @@ -195,7 +195,7 @@ static void resize(bool initial) int w, h; double dpr; w = h = INT_MAX; - midend_size(me, &w, &h, false); + midend_size(me, &w, &h, false, 1.0); if (initial) { dpr = js_get_device_pixel_ratio(); if (dpr != 1.0) { @@ -207,7 +207,7 @@ static void resize(bool initial) */ w *= dpr; h *= dpr; - midend_size(me, &w, &h, true); + midend_size(me, &w, &h, true, 1.0); } } js_canvas_set_size(w, h); @@ -219,7 +219,7 @@ static void resize(bool initial) /* Called from JS when the device pixel ratio changes */ void rescale_puzzle(int w, int h) { - midend_size(me, &w, &h, true); + midend_size(me, &w, &h, true, 1.0); if (canvas_w != w || canvas_h != h) { js_canvas_set_size(w, h); canvas_w = w; diff --git a/gtk.c b/gtk.c index 2373e38..3ded45a 100644 --- a/gtk.c +++ b/gtk.c @@ -1674,7 +1674,7 @@ static void resize_puzzle_to_area(frontend *fe, int x, int y) fe->w = x; fe->h = y; - midend_size(fe->me, &x, &y, true); + midend_size(fe->me, &x, &y, true, 1.0); fe->pw = x; fe->ph = y; #if GTK_CHECK_VERSION(3,10,0) @@ -2210,7 +2210,7 @@ static void get_size(frontend *fe, int *px, int *py) */ x = INT_MAX; y = INT_MAX; - midend_size(fe->me, &x, &y, false); + midend_size(fe->me, &x, &y, false, 1.0); *px = x; *py = y; } diff --git a/midend.c b/midend.c index 2489803..a8f9cf3 100644 --- a/midend.c +++ b/midend.c @@ -89,7 +89,8 @@ struct midend { int pressed_mouse_button; - int preferred_tilesize, tilesize, winwidth, winheight; + int preferred_tilesize, preferred_tilesize_dpr, tilesize; + int winwidth, winheight; void (*game_id_change_notify_function)(void *); void *game_id_change_notify_ctx; @@ -130,11 +131,14 @@ static const char *midend_deserialise_internal( void midend_reset_tilesize(midend *me) { me->preferred_tilesize = me->ourgame->preferred_tilesize; + me->preferred_tilesize_dpr = 1.0; { /* * Allow an environment-based override for the default tile * size by defining a variable along the lines of * `NET_TILESIZE=15'. + * + * XXX How should this interact with DPR? */ char buf[80], *e; @@ -306,10 +310,50 @@ static void midend_size_new_drawstate(midend *me) } } -void midend_size(midend *me, int *x, int *y, bool user_size) +/* + * There is no one correct way to convert tilesizes between device + * pixel ratios, because there's only a loosely-defined relationship + * between tilesize and the actual size of a puzzle. We define this + * function as the canonical conversion function so everything in the + * midend will be consistent. + */ +static int convert_tilesize(midend *me, int old_tilesize, + double old_dpr, double new_dpr) +{ + int x, y, rx, ry, min, max, mid; + game_params *defaults = me->ourgame->default_params(); + + if (new_dpr == old_dpr) + return old_tilesize; + me->ourgame->compute_size(defaults, old_tilesize, &x, &y); + x *= new_dpr / old_dpr; + y *= new_dpr / old_dpr; + + min = max = 1; + do { + max *= 2; + me->ourgame->compute_size(defaults, max, &rx, &ry); + } while (rx <= x && ry <= y); + + while (max - min > 1) { + int mid = (max + min) / 2; + me->ourgame->compute_size(defaults, mid, &rx, &ry); + if (rx <= x && ry <= y) + min = mid; + else + max = mid; + } + + me->ourgame->free_params(defaults); + return min; +} + +void midend_size(midend *me, int *x, int *y, bool user_size, + double device_pixel_ratio) { int min, max; int rx, ry; + int preferred_tilesize; /* * We can't set the size on the same drawstate twice. So if @@ -339,7 +383,9 @@ void midend_size(midend *me, int *x, int *y, bool user_size) me->ourgame->compute_size(me->params, max, &rx, &ry); } while (rx <= *x && ry <= *y); } else - max = me->preferred_tilesize + 1; + max = convert_tilesize(me, me->preferred_tilesize, + me->preferred_tilesize_dpr, + device_pixel_ratio) + 1; min = 1; /* @@ -362,9 +408,11 @@ void midend_size(midend *me, int *x, int *y, bool user_size) */ me->tilesize = min; - if (user_size) + if (user_size) { /* If the user requested a change in size, make it permanent. */ me->preferred_tilesize = me->tilesize; + me->preferred_tilesize_dpr = device_pixel_ratio; + } midend_size_new_drawstate(me); *x = me->winwidth; *y = me->winheight; diff --git a/nestedvm.c b/nestedvm.c index 947abe0..ec633d3 100644 --- a/nestedvm.c +++ b/nestedvm.c @@ -217,7 +217,7 @@ int jcallback_resize(int width, int height) int x, y; x = width; y = height; - midend_size(fe->me, &x, &y, true); + midend_size(fe->me, &x, &y, true, 1.0); fe->ox = (width - x) / 2; fe->oy = (height - y) / 2; fe->w = x; @@ -358,7 +358,7 @@ static void resize_fe(frontend *fe) x = INT_MAX; y = INT_MAX; - midend_size(fe->me, &x, &y, false); + midend_size(fe->me, &x, &y, false, 1.0); _call_java(3, x, y, 0); } diff --git a/osx.m b/osx.m index 36e9693..646cffc 100644 --- a/osx.m +++ b/osx.m @@ -524,7 +524,7 @@ struct frontend { frame.origin.x = 0; w = h = INT_MAX; - midend_size(me, &w, &h, false); + midend_size(me, &w, &h, false, 1.0); frame.size.width = w; frame.size.height = h; fe.w = w; @@ -559,7 +559,7 @@ struct frontend { */ midend_new_game(me); w = h = INT_MAX; - midend_size(me, &w, &h, false); + midend_size(me, &w, &h, false, 1.0); rect.size.width = w; rect.size.height = h; fe.w = w; @@ -959,7 +959,7 @@ struct frontend { int w, h; w = h = INT_MAX; - midend_size(me, &w, &h, false); + midend_size(me, &w, &h, false, 1.0); size.width = w; size.height = h; fe.w = w; diff --git a/puzzles.h b/puzzles.h index f0a7e99..1d4dd33 100644 --- a/puzzles.h +++ b/puzzles.h @@ -299,7 +299,8 @@ void midend_free(midend *me); const game *midend_which_game(midend *me); void midend_set_params(midend *me, game_params *params); game_params *midend_get_params(midend *me); -void midend_size(midend *me, int *x, int *y, bool user_size); +void midend_size(midend *me, int *x, int *y, bool user_size, + double device_pixel_ratio); void midend_reset_tilesize(midend *me); void midend_new_game(midend *me); void midend_restart_game(midend *me); diff --git a/windows.c b/windows.c index aa95b62..e58a150 100644 --- a/windows.c +++ b/windows.c @@ -1285,7 +1285,7 @@ static bool check_window_resize(frontend *fe, int cx, int cy, * See if we actually got the window size we wanted, and adjust * the puzzle size if not. */ - midend_size(fe->me, &x, &y, true); + midend_size(fe->me, &x, &y, true, 1.0); if (x != cx || y != cy) { /* * Resize the window, now we know what size we _really_ @@ -1611,7 +1611,7 @@ static int fe_set_midend(frontend *fe, midend *me) fe->statusbar = NULL; get_max_puzzle_size(fe, &x, &y); - midend_size(fe->me, &x, &y, false); + midend_size(fe->me, &x, &y, false, 1.0); r.left = r.top = 0; r.right = x; @@ -2374,12 +2374,12 @@ static void new_game_size(frontend *fe, float scale) int x, y; get_max_puzzle_size(fe, &x, &y); - midend_size(fe->me, &x, &y, false); + midend_size(fe->me, &x, &y, false, 1.0); if (scale != 1.0) { x = (int)((float)x * fe->puzz_scale); y = (int)((float)y * fe->puzz_scale); - midend_size(fe->me, &x, &y, true); + midend_size(fe->me, &x, &y, true, 1.0); } fe->ymin = (fe->xmin * y) / x;