GTK 3 PuTTY/pterm has always assumed that if it was compiled with
_support_ for talking to the raw X11 layer underneath GTK and GDK,
then it was entitled to expect that raw X11 layer to exist at all
times, i.e. that GDK_DISPLAY_XDISPLAY would return a meaningful X
display that it could do useful things with. So if you ran it over the
GDK Wayland backend, it would immediately segfault.
Modern GTK applications need to cope with multiple GDK backends at run
time. It's fine for GTK PuTTY to _contain_ the code to find and use
underlying X11 primitives like the display and the X window id, but it
should be prepared to find that it's running on Wayland (or something
else again!) so those functions don't return anything useful - in
which case it should degrade gracefully to the subset of functionality
that can be accessed through backend-independent GTK calls.
Accordingly, I've centralised the use of GDK_DISPLAY_XDISPLAY into a
support function get_x_display() in gtkmisc.c, which starts by
checking that there actually is one first. All previous direct uses of
GDK_*_XDISPLAY now go via that function, and check the result for NULL
afterwards. (To save faffing about calling that function too many
times, I'm also caching the display pointer in more places, and
passing it as an extra argument to various subfunctions, mostly in
gtkfont.c.)
Similarly, the get_windowid() function that retrieves the window id to
put in the environment of pterm's child process has to be prepared for
there not to be a window id.
This isn't a complete fix for all Wayland-related problems. The other
one I'm currently aware of is that the default font is "server:fixed",
which is a bad default now that it won't be available on all backends.
And I expect that further problems will show up with more testing. But
it's a start.
Except in GTK1 (which doesn't have the former), via a gtkcompat.h
workaround.
Up-to-date GTK3 has deprecated gdk_beep(), causing build failures due
to the default -Werror setting.
Looks as if I haven't retried the GTK1 build for a while, and recent
GTK frontend development has broken it. The selection revamp has
pointed out that GTK1 didn't have the accessor function
gtk_selection_data_get_selection(), the standard GdkAtom value
GDK_SELECTION_CLIPBOARD, or keysyms for alphabetic characters; and
also I had an initialisation of one of my own structure fields
(dp->selparams) accidentally not guarded by the same GTK-versioning
ifdef that controls whether or not it was defined.
Ahem. I _spotted_ this in code review, and forgot to make the change
before pushing!
Because it's legitimate for a C implementation to define 'NULL' so
that it expands to just 0, it follows that if you use NULL in a
variadic argument list where the callee will expect to extract a
pointer, you run the risk of putting an int-sized rather than
pointer-sized argument on the list and causing the consumer to get out
of sync. So you have to add an explicit cast.
The PuTTY GUIs (Unix and Windows) maintain an in-memory event log
for display to users as they request. This uses ints for tracking
eventlog size, which is subject to memory exhaustion and (given
enough heap space) overflow attacks by servers (via, e.g., constant
rekeying).
Also a bounded log is more user-friendly. It is rare to want more
than the initial logging and the logging from a few recent rekey
events.
The Windows fix has been tested using Dr. Memory as a valgrind
substitute. No errors corresponding to the affected code showed up.
The Dr. Memory results.txt was split into a file per-error and then
grep Error $(grep -l windlg *)|cut -d: -f3-|sort |uniq -c
was used to compare. Differences arose from different usage of the GUI,
but no error could be traced to the code modified in this commit.
The Unix fix has been tested using valgrind. We don't destroy the
eventlog_stuff eventlog arrays, so we can't be entirely sure that we
don't leak more than we did before, but from code inspection it looks
like we don't (and anyways, if we leaked as much as before, just without
the integer overflow, well, that's still an improvement).
Apparently I haven't tried a GTK2 build since the most recent set of
GTK-related code reorganisation. Some functions that were ifdef'ed out
in GTK3 builds were now unused even in GTK2 builds (and, because they
were also declared static, caused a -Werror build failure); and the
pointless stub version of gtkapp.c was missing a stub version of a
recently added function referred to from another module.
This still isn't complete: I also need to add the variable collections
of things like mid-session special commands and saved session names,
and also I need to try to grey out menu items when they're not
applicable. But it's a start.
This change requires me to break up the general cleanups in
delete_inst() into two halves: one runs when the error message box is
created, and cleans up the network connection and all the stuff
associated with it, and the other runs when the error message is
dismissed and the window can actually close.
It's an incoherent concept! There should not be any such thing as an
error box that terminates the entire program but is not modal. If it's
bad enough to terminate the whole program, i.e. _all_ currently live
connections, then there's no point in permitting progress to continue
in windows other than the affected one, because all windows are
affected anyway.
So all previous uses of fatalbox() have become modalfatalbox(), except
those which looked to me as if they shouldn't have been fatal in the
first place, e.g. lingering pieces of error handling in winnet.c which
ought to have had the severity of 'give up on this particular Socket
and close it' rather than 'give up on the ENTIRE UNIVERSE'.
I've also moved it out into gtkwin.c, because it seemed easier to do
the 'find existing instance of this dialog and raise it' dance there
than to split it across source files pointlessly.
Apart from the specific benefit of non-modality, this also makes it a
lot simpler compared to the previous code! I'm not completely sure why
I wasn't using the standard gtkdlg.c message box system all along.
This fits into a new dialog-box slot (because it might have to come up
at the same time as a network prompt), and makes use of the existing
callback system in logging.c which buffers the logging data until the
user says what they want done with it.
Now it has several 'slots', each named for a particular class of
subsidiary dialog box that a session window can have at most one of,
and register_network_prompt_dialog has a more general name and takes
an enum-typed argument identifying a slot. This lets me avoid writing
a zillion annoyingly similar function pairs and corresponding snippets
of cleanup code in delete_inst.
This follows exactly the same pattern as for verify_ssh_host_key, but
the results of the dialog box are simpler (a plain yes-no response),
so the two dialog types can share a callback.
I've switched it to using the new non-modal create_message_box, and
provided a callback function which handles the cleanup afterwards.
I had expected this to be a lot more work, because I'd imagined that
I'd have to contort the coroutines in ssh.c to give them the ability
to wait for an asynchronously delivered result from that user prompt.
But in fact that wasn't necessary, because just such a mechanism has
been sitting there unused since commit 8574822b9 in 2005, when I added
it as part of my _previous_ attempt to write an OS X front end! (The
abandoned one written in native ObjC + Cocoa.)
If a dialog box is destroyed by the program before the user has
pressed one of the result-delivering buttons - e.g. because the parent
window closes so the dialog is no longer relevant to anything anyway -
then dlgparam_destroy would never call the client code's provided
callback. That makes sense in terms of the callback wanting to _take
action_ based on the result of the dialog box, but it ignores the
possibility that the callback may simply need to free its own context
structure.
So now dlgparam_destroy always calls the client's callback, even if
the result it passes is negative (meaning 'the user never got round to
pressing any of the dialog-ending buttons'), and all the existing
client callbacks handle the negative-result case by doing nothing
except freeing any allocated memory they might have.
This does the bulk of the work previously done by message_box()
proper, but takes a pointer to a result-reporting callback function
identical to the one we pass to create_config_box().
The modal version of message_box() still exists and is a small wrapper
on this function, running its own subsidiary gtk_main() loop which the
result callback terminates. But now I can start switching over
individual uses of message_box() to the non-modal version, and when
that's done, remove the modal function completely.
Now, in place of a variadic argument list with four parameters per
button and a terminating NULL, it takes a pointer to a struct which in
turn contains an (array,length) pair of small per-button structures.
In the process I've renamed the function from messagebox() to
message_box(). Partly that was just because it gave me a convenient
way to search the source for calls I hadn't converted yet, but also
I've thought for a while that that missing underscore didn't really
match the rest of my naming.
NFCI. Partly this minor refactor has the virtue that we can reuse the
more common button layouts without having to type them in at multiple
places in the code (and, indeed, I've provided buttons_yn and
buttons_ok for easy reuse, and could easily provide other things like
yesnocancel any time I need them). But mostly it's because I'm about
to split up message_box into multiple functions, and this saves me the
hassle of deciding which ones to make variadic and which to pass an
actual va_list to - particularly since messagebox() used to go over
its variadic argument list twice, which always makes delegating it to
another function that much more annoying.
Now every call to do_config_box is replaced with a call to
create_config_box, which returns immediately having constructed the
new GTK window object, and is passed a callback function which it will
arrange to be called when the dialog terminates (whether by OK or by
Cancel). That callback is now what triggers the construction of a
session window after 'Open' is pressed in the initial config box, or
the actual mid-session reconfiguration action after 'Apply' is pressed
in a Change Settings box.
We were already prepared to ignore the re-selection of 'Change
Settings' from the context menu of a window that already had a Change
Settings box open (and not accidentally create a second config box for
the same window); but now we do slightly better, by finding the
existing config box and un-minimising and raising it, in case the user
had forgotten it was there.
That's a useful featurelet, but not the main purpose of this change.
The mani point, of course, is that now the multi-window GtkApplication
based front ends now don't do anything confusing to the nesting of
gtk_main() when config boxes are involved. Whether you're changing the
settings of one (or more than one) of your already-running sessions,
preparing to start up a new PuTTY connection, or both at once, we stay
in the same top-level instance of gtk_main() and all sessions' top-
level callbacks continue to run sensibly.
This shows the build platform (32- vs 64-bit in particular, and also
whether Unix GTK builds were compiled with or without the X11 pieces),
what compiler was used to build the binary, and any interesting build
options that might have been set on the make command line (especially,
but not limited to, the security-damaging ones like NO_SECURITY or
UNPROTECT). This will probably be useful all over the place, but in
particular it should allow the different Windows binaries to be told
apart!
Commits 21101c739 and 2eb952ca3 laid the groundwork for this, by
allowing the various About boxes to contain free text and also
ensuring they could be copied and pasted easily as part of a bug
report.
If you're connecting to a new server and it _only_ provides host key
types you've configured to be below the warning threshold, it's OK to
give the standard askalg() message. But if you've newly demoted a host
key type and now reconnect to some server for which that type was the
best key you had cached, the askalg() wording isn't really appropriate
(it's not that the key we've settled on is the first type _supported
by the server_, it's that it's the first type _cached by us_), and
also it's potentially helpful to list the better algorithms so that
the user can pick one to cross-certify.
Now all the uses of the licence text or the short copyright notice get
it from a new header "licence.h", which in turn is built by a Perl
script licence.pl invoked by mkfiles.pl, using LICENCE itself as the
source.
Hence, I can completely remove a whole section from the list of
licence locations in CHECKLST.txt :-)
I've made the licence text, the About box, and the host key dialog
into GTK selectable edit controls. (The former because it contains a
lot of text; the About box because pasting version numbers into bug
reports is obviously useful; the host key because of the fingerprint.)
When we provide an editable text box with a drop-down list of useful
preset values, such as the one full of character sets in the
Translation panel, we implement it on GTK 2.4+ as a GtkComboBox
pointing at a two-column GtkListStore, in which the second column is
the actual text (the first being a numeric id). Therefore, we need to
set the "entry-text-column" property to tell GtkComboBox which of
those columns to look in for the value corresponding to the edit-box
text.
Thanks to Robert de Bath for spotting the problem and tracing it as
far as commit 5fa22495c. That commit replaced a widget construction
call via gtk_combo_box_entry_new_with_model() with one using the newer
gtk_combo_box_new_with_model_and_entry(), overlooking the fact that
the former provided the text column number as a parameter, and the
latter didn't.
In GTK3, the line 'Continue with connection?' got wrapped (in spite of
my attempt to enforce via string_width() that it didn't - probably a
few pixels were needed on top of that for various padding and
furniture) so it looked even sillier. But it looked a bit narrow to be
sensible even in GTK2, so the simplest answer is just to widen it
considerably.
I think it only did so in GTK2 by virtue of the About box being a
GtkDialog. But in GTK3 I've abandoned GtkDialog for not being flexible
enough, so I have to process the Escape key myself.
The askalg() dialog, and several one-button things like the licence
box, have no button labelled 'cancel'. But in all cases we do want
Escape to terminate the box, with as negative an answer as is
available. So now we assign the 'iscancel' flag to any button whose
numeric value is the smallest of the ones given as input to
messagebox().
(In a one-button box, this leads to isdefault and iscancel _both_
being set for that button. That's fine; it works.)
I had put an entire piece of code into win_key_press's SHORTCUT_UCTRL
handler to carefully handle all the different kinds of list box
control and do something sensible with each one, and then I just went
and used a generic SHORTCUT_FOCUS type shortcut instead of actually
_calling_ all that carefully prepared code.
Now selecting (say) the character-classes list box in the Selection
panel using its Alt-e shortcut works; also, shortcut-selecting a popup
menu such as the ones in the Bugs panel causes the menu to pop up,
which I think is nicer than what previously happened.
I'd failed to set the widget field in its shortcut structure, leading
to an annoying GTK warning log message and no useful UI action when
Alt-G was pressed in the config box.
Fixes a behaviour which I intended all along but apparently didn't
work before on GTK: if you start PuTTY, _select_ a saved session in
the list box but don't hit Load, and then just hit Open, then it will
be implicitly loaded and run for you, as a special case to save you an
extra button-press.
This depends on noticing that the saved-sessions list box last had the
focus, for which I need my widget_focus() handler to be called for
basically all config widgets so that I can track what _did_ last have
focus. Unfortunately, I missed a couple out.
Several utility functions I've written over the last few weeks were in
rather random places because I didn't have a central gtkmisc.c to put
them in. Now I've got one, put them there!
They've now deprecated gtk_dialog_get_action_area, because they really
want a dialog box's action area to be filled with nothing but buttons
controlled by GTK which end the dialog with a response code. But we're
accustomed to putting all sorts of other things in our action area -
non-buttons, buttons that don't end the dialog, and sub-widgets that
do layout - and so I think it's no longer sensible to be trying to
coerce our use cases into GtkDialog.
Hence, I'm introducing a set of wrapper functions which equivocate
between a GtkDialog for GTK1 and GTK2, and a GtkWindow with a vbox in
it for GTK3, and I'll lay out the action area by hand.
(Not everything has sensible layout and margins in the new GTK3 system
yet, but I can sort that out later.)
Because the new functions are needed by gtkask.c, which doesn't link
against gtkdlg.c or include putty.h, I've put them in a new source
file and header file pair gtkmisc.[ch] which is common to gtkask and
the main GTK edifice.
When NULL appears in variadic argument lists, it should be cast to the
pointer type that the function will be expecting, because otherwise it
might end up as a type not even the same size as a pointer.
This is a much simpler way to display simple message-box type dialogs,
whose absence I've previously been working around by laboriously
constructing something in my usual style.
We were using it in the main config box to ensure everything expanded
on window resize, but in GTK3 that's the default anyway. And we were
using it to put padding around the edges of the font selector, which
is now done using the "margin" property.
Using GTK to run on OS X is going to require several workarounds and
behaviour tweaks to be enabled at various points in the code, and it's
already getting cumbersome to remember what they all are to put on the
command line. Here's a central #define (OSX_GTK) that enables them all
in one go, and a configure option (--with-quartz) that sets it.
As part of this commit, I've also rearranged the #include order in the
GTK source files, so that they include unix.h (which now might be
where NOT_X_WINDOWS gets defined) before they test NOT_X_WINDOWS to
decide whether to include X11 headers.
In shortcut_add(), when we add an underlined letter to a GtkLabel, we
were fetching the label's height before changing its text, and
restoring it afterwards. I've no idea why - I can see no difference
with and without the code.
That code's been there since 2003 without explanation. My best guess
is that it was working around a GTK bug of the day, but since no
difference is visible even in current GTK1, I think I'm just going to
remove it. If any problems show up later, I can put it back, with an
actual comment!
The whole of get_label_text_dimensions() should have been outside the
GTK 2 ifdef; I'd left a gtk_label_set_width_chars() unconditional; and
GDK1's gdk_window_set_background() lacks a const in its prototype.
Serves me right for not test-compiling in all three versions!
My trickery in GTK2 to start with some branches of the tree collapsed
but give the widget all the width it will need when they open later
was not working in GTK3, for the same reason I've needed several other
fixes recently: just after creation, GTK3 widgets report their
preferred size as zero.
Fixed by doing basically the same trick I was doing in GTK2, but
deferring it until the "map" event happens later on.
This was another piece of code that determined text size by
instantiating a GtkLabel and asking for its size, which I had to fix
in gtkfont.c recently because that strategy doesn't work in GTK3.
Replaced the implementation of string_width() with a call to the
function I added in gtkfont.c, and now dialog boxes which depend on
that for their width measurement (e.g. the one in reallyclose()) don't
come out in silly sizes on GTK3 any more.
In cases where two controls sit alongside one another such as a label
alongside an edit box, I've previously been arranging for them to be
vertically centred by fiddling with the size request and alignment of
what I assume will be the shorter control. But now I've written
columns_force_same_height(), that's a much easier approach, and it's
also compatible with GTK3 without using a deprecated method; so this
change switches over all vertical centring to doing it that way.
Also, while I'm here, I noticed that the edit box and button of
CTRL_FILESELECT / CTRL_FONTSELECT were not vertically centred, and
since it's now really easy to make sure they are, I've added another
use of columns_force_same_height() there.
This replaces the old GtkColorSelectionDialog, and has the convenience
advantage that the actual chooser (with all the 'set colour', 'get
colour' methods) and the containing dialog box are now the same object
implementing multiple interfaces, so I don't keep having to call 'get
me the underlying chooser for this dialog' accessors. Also you now
hook into both the OK and Cancel buttons (and all other response
codes) at the same time with a single event.