I had left one mention of the new GTK3-only variable
'awaiting_resize_ack' unprotected by #ifdefs. Also, the GTK2 version
of message_box() was missing some consts in its prototype, presumably
because when I had that #ifdeffed out the compiler didn't warn me
about those ones.
I went through all the char * parameters and return values I could see
in puzzles.h by eye and spotted ones that surely ought to have been
const all along.
This allows me to use different types for the mutable, dynamically
allocated string value in a C_STRING control and the fixed constant
list of option names in a C_CHOICES.
While working on the Net scalability today I noticed that changing
preset from (say) 13x11 to 5x5 in GTK3 Net while the window is
maximised does not have the desired effect (that being that, since the
maximised window does not change size, the new puzzle size is instead
scaled to fit neatly in the existing window).
A git bisect suggests that this was a side effect of commit 8dfe5cec3;
it looks as if there was a useful side effect of setting fe->area as
the 'geometry widget' for fe->window, namely, that any attempt to
resize the window thereafter (even if it had no effect on the window
size) would trigger a configure event on the geometry widget, so we'd
get a notification of our new size even if it was the same as our old
size.
But that 'geometry widget' feature is deprecated, so I have to work
around it another way. Fortunately, I've found a fallback event that
still does occur, namely "size_allocate" on fe->window. So I'm
trapping that as well and using it as an indication that a configure
event won't be forthcoming.
In commit a7dc17c42 I apparently introduced two bugs in
changed_preset(). Firstly, the Custom menu option was being written
into the 'found' variable in nearly all cases, because it has a NULL
user-data pointer which caused it to take the wrong branch of an if
statement due to an erroneous complex condition. Secondly, having
written _something_ into 'found', I had set it to inactive rather than
active due to forgetting to change a FALSE into a TRUE.
Now when I start up Net with my usual nonstandard default parameters
(I like the 13x11 wrapping, so I set NET_DEFAULT=13x11w in my
environment), the right menu entry comes up ticked.
This is in addition to the existing keystrokes r, ^R and ^Y. I've
become used to Ctrl-Shift-Z in other GUI games, and my fingers keep
getting confused when my own puzzles don't handle it the same way.
This fixes an amusing UI bug that I think can currently only come up
in the unpublished puzzle 'Group', but there's no reason why other
puzzles _couldn't_ do the thing that triggers the bug, if they wanted
to.
Group has unusual keyboard handling, in that sometimes (when a cell is
selected for input and the key in question is valid for the current
puzzle size) the game's interpret_move function will eat keystrokes
like 'n' and 'u' that would otherwise trigger special UI events like
New Game or Undo.
The bug is that fake keypress events generated from the GUI menus
looked enough like those keystrokes that interpret_move would eat
those too. So if you start, say, a 16x16 Group puzzle, select an empty
cell, and then choose 'new game' from the menu, Group will enter 'n'
into the cell instead of starting a new game!
I've fixed this by inventing a new set of special keystroke values
called things like UI_NEWGAME and UI_UNDO, and having the GUI menus in
all my front ends generate those in place of 'n' and 'u'. So now the
midend can tell the difference between 'n' on the keyboard and New
Game from the menu, and so Group can treat them differently too. In
fact, out of sheer overcaution, midend.c will spot keystrokes in this
range and not even _pass_ them to the game back end, so Group
shouldn't be able to override these special events even by mistake.
One fiddly consequence is that in gtk.c I've had to rethink the menu
accelerator system. I was adding visible menu accelerators to a few
menu items, so that (for example) 'U' and 'R' showed up to the right
of Undo and Redo in the menu. Of course this had the side effect of
making them real functioning accelerators from GTK's point of view,
which activate the menu item in the same way as usual, causing it to
send whatever keystroke the menu item generates. In other words,
whenever I entered 'n' into a cell in a large Group game, this was the
route followed by even a normal 'n' originated from a real keystroke -
it activated the New Game menu item by mistake, which would then send
'n' by mistake instead of starting a new game!
Those mistakes cancelled each other out, but now I've fixed the
latter, I've had to fix the former too or else the GTK front end would
now undo all of this good work, by _always_ translating 'n' on the
keyboard to UI_NEWGAME, even if the puzzle would have wanted to treat
a real press of 'n' differently. So I've fixed _that_ in turn by
putting those menu accelerators in a GtkAccelGroup that is never
actually enabled on the main window, so the accelerator keys will be
displayed in the menu but not processed by GTK's keyboard handling.
(Also, while I was redoing this code, I've removed the logic in
add_menu_item_with_key that reverse-engineered an ASCII value into
Control and Shift modifiers plus a base key, because the only
arguments to that function were fixed at compile time anyway so it's
easier to just write the results of that conversion directly into the
call sites; and I've added the GTK_ACCEL_LOCKED flag, in recognition
of the fact that _because_ these accelerators are processed by a weird
mechanism, they cannot be dynamically reconfigured by users and
actually work afterwards.)
To do this, I've completely replaced the API between mid-end and front
end, so any downstream front end maintainers will have to do some
rewriting of their own (sorry). I've done the necessary work in all
five of the front ends I keep in-tree here - Windows, GTK, OS X,
Javascript/Emscripten, and Java/NestedVM - and I've done it in various
different styles (as each front end found most convenient), so that
should provide a variety of sample code to show downstreams how, if
they should need it.
I've left in the old puzzle back-end API function to return a flat
list of presets, so for the moment, all the puzzle backends are
unchanged apart from an extra null pointer appearing in their
top-level game structure. In a future commit I'll actually use the new
feature in a puzzle; perhaps in the further future it might make sense
to migrate all the puzzles to the new API and stop providing back ends
with two alternative ways of doing things, but this seemed like enough
upheaval for one day.
gdk_window_set_background_rgba is deprecated as of GTK 3.22, because
apparently you can't just _say_ any more 'here is what I want my
window's background colour to be in places where a widget isn't'.
Instead you have to provide a GtkStyleProvider which can be slotted
into a wobbly tower of other providers with associated priorities, so
that the user can override your choices if they really want to.
And the easiest way to constructc a GtkStyleProvider in turn is to
write *actual CSS* and get GTK to parse it, so I end up converting my
nice numeric RGB values into a complicated text format for another
part of _the same process_ to parse back into numbers. Sigh.
I've just noticed that the GTK game window was not being redrawn when
changing between puzzle modes that don't involve a window resize - by
selecting a preset from the Type menu (e.g. changing between different
12x12 settings in Flood) or via the Custom menu.
It looks as if the bug was introduced in commit 8dfe5cec3, which
suggests that it was a side effect of the switch from
gtk_window_resize_to_geometry to plain gtk_window_resize. My guess is
that the implementation of the former function inside GTK might have
happened to trigger an unconditional window resize, while the latter
took the shortcut of doing nothing if the window was already the right
size; hence, resize_fe() would have been reliably generating a redraw
event without me having to ask for one, but now it doesn't, so I have
to trigger one myself any time I've just called resize_fe.
Now we work out for ourselves how the drawing-area size relates to the
overall window size, by adding on the height of fe->menubar and/or
fe->statusbar.
gtk_misc_set_alignment was deprecated in GTK 3.14. But my replacement
code using gtk_label_set_{x,y}align doesn't work there, because that
function wasn't introduced until GTK 3.16, so there are two minor
versions in the middle where a third strategy is needed.
(That middle strategy doesn't permit arbitrary float alignments, but
that's OK, bceause we only actually use multiples of 0.5.)
Jonas Kölker points out that commit a800ff16b (which fixed a bug in
the previous attempt) left in another bug: if the puzzle size was
changed while the window was maximised, the system would fail to
recompute the tile size and would redraw completely wrongly.
So it's not optional after all to run midend_size(), even if the
drawing area size hasn't changed. I've reverted that code to be
unconditional, and now only the refresh of the Cairo-side backing
store system is conditionalised - and that's conditionalised on
changes to either the size of the actual window _or_ the size of the
contained pixmap. (The latter should defend against redraw failure in
the case where the puzzle aspect ratio changes, so that neither the
window size nor the tile size changes but a recentre is still needed.)
I _think_ this now fixes all the cases of resizing: this time I've
tested making an unmaximised puzzle window bigger or smaller, and
within a maximised window, forcing the puzzle to scale up, scale down,
or change its aspect ratio without changing its tile size. All work,
on GTK2 and GTK3, and yet we still don't get the visible flicker on
status line updates that was the reason I started fiddling with this
code in the first place.
(We _do_ still call configure_area on every update of the status line,
at least under GTK3; that's going to cause a forced full redraw on
every timer tick in Mines, which is wasteful of CPU, so it would still
be nice to find a better way of identifying the cases in which no
resizing at all was necessary and we could avoid renewing the game
drawstate. But the current code at least doesn't have any display
_errors_ that I know of, which is an improvement on its previous
state.)
- The file selector for loading and saving gets a g_free().
- The handling of saving (menu_save_event) gets an sfree().
- It's also slightly restructured to prevent future errors.
- menu_load_event was already structured to prevent this error.
- The OLD_FILESEL code seems to not need a g_free().
These button codes are generated by the back/forward button pair on
the sides of some mice, and web browsers treat these as the back and
forward actions in the page history.
It's becoming annoying to keep working within the increasing
restrictions on GtkDialog, in particular the fact that not only do we
have to let it have complete control of the button area, but also it's
not clear whether we can intercept a press of the 'OK' button and
display an error message rather than ending the dialog.
So, as I did in PuTTY, I'm resorting to using an ordinary GtkWindow
with controls I laid out myself.
This is a lot easier than faffing about setting up a dialog box
ourself, and also avoids direct access to GtkDialog's action area
(deprecated in GTK 3.16).
Commit 8b491946e had a bug: configure_area stopped doing most of its
work if the new size already matched fe->w and fe->h, but in fact the
GTK2 resize_fe() _already_ set up fe->w and fe->h for the new size. I
managed not to notice, because I checked it all worked on GTK 3 but
only tested resizing to a _smaller_ puzzle on GTK 2. Ahem.
Now we don't change fe->w and fe->h at all until configure_area is
called. Also, we initialise them to dummy values at setup time, so
that configure_area won't compare the new size with uninitialised
data.
I think I did this in GTK2 on the basis that our server-side cache
pixmap was double-buffering enough for us - any puzzle which erased a
big area with a background rectangle and then redrew over the top of
it would do so only on the off-screen pixmap, and the updates would
only be copied to the visible window after it was all done.
In GTK3, I don't think there's any need - this is all the usual way
things are done anyway, as far as I can see. So I've turned this call
back off, at least until I hear of a reason why I need it again.
In GTK 2, we had a big pile of horrible code to deal with the fact
that it's very hard to open a window in such a way as to make it easy
to resize smaller as well as bigger.
Essentially, we'd open the window with the drawing area's minimum size
request set to the desired _initial_ window size; then we'd wait until
GTK had finished creating other window components (menu bar, status
line) and the window's size allocation had settled down, and finally
reduce the size request to the real minimum size so that now the
window was resizable in both directions. This also involved some
deliberate checking of the Ubuntu Unity GTK extension which steals the
menu bar and put it elsewhere (see commit 8f8333a35), to avoid us
waiting forever for a menu bar that wasn't going to show up.
But in GTK3, this has all become actually sensible! All we now have to
do is to set the window's geometry hints to indicate the drawing area
itself as the base widget (probably a good plan anyway), and then we
can set the initial size using gtk_window_set_default_geometry() and
resize it later using gtk_window_resize_to_geometry(). So now we can
completely condition out all of the previous horrors, and consider
them to be legacy GTK2 compatibility code only. Phew.
This is what GTK 3 uses in place of 'expose_event'. Also I've arranged
here for my internal USE_CAIRO_WITHOUT_PIXMAP setting to be enabled in
GTK3, as well as in GTK2 with deprecated functions disabled.
This also involves setting some "hexpand" properties on the widgets
contained in the GtkGrid, to achieve effects which GtkTable did by
setting flags in gtk_table_attach.
When GDK_DISABLE_DEPRECATED is defined, we now don't have fe->pixmap;
instead we just maintain our client-side window contents in fe->image,
and draw from there directly to the window in the expose handler.
We were packing the GtkTable into the dialog's content area using
gtk_box_pack_end, which had the slightly silly effect that resizing
the config box vertically would keep all the controls aligned to the
_bottom_ rather than the top.
Sometimes, we can get a "configure_area" event telling us that the
drawing area has changed size to the same size it already was. This
can happen when we change puzzle presets in a way that doesn't change
the size, and also sometimes seems to happen as a side effect of
changing the text in the status line.
In that situation, it's a waste of effort - and can cause visible
on-screen flicker - to throw away the window's backing image and
pixmap and regenerate them from scratch. So now we detect a non-resize
and avoid doing all that.
The only thing we retain unconditionally in configure_area is the
midend_force_redraw, because that's the place where a puzzle redraw is
forced when changing presets or loading a new game.
Unity hijacks the menu bar and prevents it from appearing in the main
puzzle window. And we wait for the menu bar to appear before reducing
the puzzle drawing area's size request. These two behaviours go
together badly.
Fixed by detecting the extra GTK property that the Unity patches
invented, and using that to know whether to expect the menu bar to
show up at all.
the GTK front end, plus a 'make test' target in the GTK makefile which
uses them to automatically generate 100 puzzles for each game at each
preset configuration, test-run them back through the solver without
their aux_info to ensure that can cope, and produce an HTML box plot
of game generation times for each preset.
As part of this work I've removed the TESTSOLVE mechanism from r9549,
since the new --test-solve option does the same thing better (in that
when something goes wrong it prints the random seed that caused the
problem).
[originally from svn r9825]
[r9549 == 5a095b8a08fa9f087b93c86aea0fa027138b028d]
bombing out due to an option that we don't recognise but GTK will.
Somehow my basically workable plan had been completely nullified by
putting the error check in the wrong place.
[originally from svn r9733]
gtk_widget_get_allocation conditional on GTK being new enough to have
that function.
I'm assuming until someone proves otherwise that if it isn't that new,
then it also isn't one of the versions of GTK which exhibit the bug
which that call was working around (since gtk_widget_get_allocation
came in 2.18, and the problem seems to have arisen since 2.20).
[originally from svn r9712]
Ubuntu 12.04 machine. What seems to happen is that we set up a window
containing a menu bar, a drawing area and a status bar, and set the
size of the drawing area; then the window is displayed _without_ the
menu bar; then we reduce the drawing area's size request to (1,1) to
let the user resize the window smaller; and now GTK gets round to
constructing the menu bar, and the drawing area helpfully shrinks a
bit to make room for it.
My fix is to set a 'shrink pending' flag instead of shrinking the
drawing area's size request, and defer the actual shrink operation
until the menu bar and status bar are both present.
[originally from svn r9711]