1 /* Copyright (C) 2016 by Jeremy Tan */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
14  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
16  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
17  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
18  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
19  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
21  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 
25 #include <fontforge-config.h>
26 
27 /**
28 *  \file  ggdkdraw.c
29 *  \brief GDK drawing backend.
30 */
31 
32 #include "ggdkdrawP.h"
33 
34 #ifdef FONTFORGE_CAN_USE_GDK
35 
36 #include "gkeysym.h"
37 #include "gresource.h"
38 #include "ustring.h"
39 
40 #include <assert.h>
41 #include <math.h>
42 
43 // HACK HACK HACK
44 #ifdef GDK_WINDOWING_WIN32
45 #  define GDK_COMPILATION
46 #  include <gdk/gdkwin32.h>
47 #  undef GDK_COMPILATION
48 #endif
49 
50 // Forward declarations
51 static void GGDKDrawCancelTimer(GTimer *timer);
52 static void GGDKDrawDestroyWindow(GWindow w);
53 static void GGDKDrawPostEvent(GEvent *e);
54 static void GGDKDrawProcessPendingEvents(GDisplay *gdisp);
55 static void GGDKDrawSetCursor(GWindow w, GCursor gcursor);
56 static void GGDKDrawSetTransientFor(GWindow transient, GWindow owner);
57 static void GGDKDrawSetWindowBackground(GWindow w, Color gcol);
58 
59 // Private member functions (file-level)
60 static void _GGDKDraw_InitiateWindowDestroy(GGDKWindow gw);
61 
_GGDKDraw_NewGGC(void)62 static GGC *_GGDKDraw_NewGGC(void) {
63     GGC *ggc = calloc(1, sizeof(GGC));
64     if (ggc == NULL) {
65         Log(LOGDEBUG, "GGC: Memory allocation failed!");
66         return NULL;
67     }
68 
69     ggc->clip.width = ggc->clip.height = 0x7fff;
70     ggc->fg = 0;
71     ggc->bg = 0xffffff;
72     return ggc;
73 }
74 
_GGDKDraw_SetOpaqueRegion(GGDKWindow gw)75 static void _GGDKDraw_SetOpaqueRegion(GGDKWindow gw) {
76     cairo_rectangle_int_t r = {
77         gw->pos.x, gw->pos.y, gw->pos.width, gw->pos.height
78     };
79     cairo_region_t *cr = cairo_region_create_rectangle(&r);
80     if (cairo_region_status(cr) == CAIRO_STATUS_SUCCESS) {
81         gdk_window_set_opaque_region(gw->w, cr);
82         cairo_region_destroy(cr);
83     }
84 }
85 
_GGDKDraw_ClearSelData(GGDKDisplay * gdisp,enum selnames sn)86 static void _GGDKDraw_ClearSelData(GGDKDisplay *gdisp, enum selnames sn) {
87     GList_Glib *ptr = gdisp->selinfo[sn].datalist;
88     while (ptr != NULL) {
89         GGDKSelectionData *data = (GGDKSelectionData *)ptr->data;
90         if (data->data) {
91             if (data->freedata) {
92                 (data->freedata)(data->data);
93             } else {
94                 free(data->data);
95             }
96         }
97         free(data);
98         ptr = g_list_delete_link(ptr, ptr);
99     }
100     gdisp->selinfo[sn].datalist = NULL;
101     gdisp->selinfo[sn].owner = NULL;
102 }
103 
_GGDKDraw_TransmitSelection(GGDKDisplay * gdisp,GdkEventSelection * e)104 static bool _GGDKDraw_TransmitSelection(GGDKDisplay *gdisp, GdkEventSelection *e) {
105     // Default location to store data if none specified
106     if (e->property == GDK_NONE) {
107         e->property = e->target;
108     }
109 
110     // Check that we own the selection request
111     enum selnames sn;
112     for (sn = 0; sn < sn_max; sn++) {
113         if (e->selection == gdisp->selinfo[sn].sel_atom) {
114             break;
115         }
116     }
117     if (sn == sn_max) {
118         return false;
119     }
120 
121     GGDKSelectionInfo *sel = &gdisp->selinfo[sn];
122     GdkWindow *requestor = (GdkWindow *)g_object_ref(e->requestor);
123 
124     if (e->target == gdk_atom_intern_static_string("TARGETS")) {
125         guint i = 0, dlen = g_list_length(sel->datalist);
126         GdkAtom *targets = calloc(2 + dlen, sizeof(GdkAtom));
127         GList_Glib *ptr = sel->datalist;
128 
129         targets[i++] = gdk_atom_intern_static_string("TIMESTAMP");
130         targets[i++] = gdk_atom_intern_static_string("TARGETS");
131 
132         while (ptr != NULL) {
133             targets[i++] = ((GGDKSelectionData *)ptr->data)->type_atom;
134             ptr = ptr->next;
135         }
136 
137 #ifdef GDK_WINDOWING_WIN32
138         gdk_win32_selection_clear_targets(gdk_window_get_display(e->window), e->selection);
139         gdk_win32_selection_add_targets(e->window, e->selection, i, targets);
140 #endif
141 
142         gdk_property_change(requestor, e->property, gdk_atom_intern_static_string("ATOM"),
143                             32, GDK_PROP_MODE_REPLACE, (const guchar *)targets, i);
144     } else if (e->target == gdk_atom_intern_static_string("TIMESTAMP")) {
145         gdk_property_change(requestor, e->property, gdk_atom_intern_static_string("INTEGER"),
146                             32, GDK_PROP_MODE_REPLACE, (const guchar *)&sel->timestamp, 1);
147     } else {
148         GList_Glib *ptr = sel->datalist;
149         GGDKSelectionData *data = NULL;
150         while (ptr != NULL) {
151             if (((GGDKSelectionData *)ptr->data)->type_atom == e->target) {
152                 data = (GGDKSelectionData *)ptr->data;
153                 break;
154             }
155             ptr = ptr->next;
156         }
157         if (data == NULL) { // Unknown selection
158             g_object_unref(requestor);
159             return false;
160         }
161 
162         void *tmp = data->data;
163         int len = data->cnt;
164         if (data->gendata) {
165             tmp = data->gendata(data->data, &len);
166             if (tmp == NULL) {
167                 g_object_unref(requestor);
168                 return false;
169             }
170         }
171 
172         gdk_property_change(requestor, e->property, e->target,
173                             data->unit_size * 8, GDK_PROP_MODE_REPLACE, (const guchar *)tmp, len);
174         if (data->gendata) {
175             free(tmp);
176         }
177     }
178 
179     g_object_unref(requestor);
180     return true;
181 }
182 
_GGDKDraw_OnWindowDestroyed(gpointer data)183 static gboolean _GGDKDraw_OnWindowDestroyed(gpointer data) {
184     GGDKWindow gw = (GGDKWindow)data;
185     Log(LOGDEBUG, "Window: %p", gw);
186     if (gw->is_cleaning_up) {
187         return false;
188     }
189     gw->is_cleaning_up = true; // We're in the process of destroying it.
190 
191     if (gw->cc != NULL) {
192         cairo_destroy(gw->cc);
193         gw->cc = NULL;
194     }
195 
196     if (gw->resize_timeout != 0) {
197         g_source_remove(gw->resize_timeout);
198         gw->resize_timeout = 0;
199     }
200 
201     if (!gw->is_pixmap) {
202         if (gw != gw->display->groot) {
203             if (!gdk_window_is_destroyed(gw->w)) {
204                 gdk_window_destroy(gw->w);
205                 // Wait for it to die
206                 while (!gw->display->is_dying && !gdk_window_is_destroyed(gw->w)) {
207                     GGDKDrawProcessPendingEvents((GDisplay *)gw->display);
208                 }
209             }
210         }
211 
212         // Signal that it has been destroyed - only if we're not cleaning up the display
213         if (!gw->display->is_dying) {
214             struct gevent die = {0};
215             die.w = (GWindow)gw;
216             die.native_window = gw->w;
217             die.type = et_destroy;
218             GGDKDrawPostEvent(&die);
219         }
220 
221         // Remove all relevant timers that haven't been cleaned up by the user
222         // Note: We do not free the GTimer struct as the user may then call DestroyTimer themselves...
223         GList_Glib *ent = gw->display->timers;
224         while (ent != NULL) {
225             GList_Glib *next = ent->next;
226             GGDKTimer *timer = (GGDKTimer *)ent->data;
227             if (timer->owner == (GWindow)gw) {
228                 //Since we update the timer list ourselves, don't all GGDKDrawCancelTimer.
229                 Log(LOGDEBUG, "WARNING: Unstopped timer on window destroy!!! %p -> %p", gw, timer);
230                 timer->active = false;
231                 timer->stopped = true;
232                 g_source_remove(timer->glib_timeout_id);
233                 gw->display->timers = g_list_delete_link(gw->display->timers, ent);
234             }
235             ent = next;
236         }
237 
238         // If it owns any of the selections, clear it
239         for (int i = 0; i < sn_max; i++) {
240             if (gw->display->selinfo[i].owner == gw) {
241                 _GGDKDraw_ClearSelData(gw->display, i);
242             }
243         }
244 
245         // Decrement the toplevel window count
246         if (gw->display->groot == gw->parent && !gw->is_dlg) {
247             gw->display->top_window_count--;
248         }
249 
250         assert(gw->display->dirty_window != gw);
251 
252         Log(LOGDEBUG, "Window destroyed: %p[%p][%s][toplevel:%d][pixmap:%d]",
253             gw, gw->w, gw->window_title, gw->is_toplevel, gw->is_pixmap);
254         free(gw->window_title);
255         if (gw != gw->display->groot) {
256             // Unreference our reference to the window
257             g_object_unref(G_OBJECT(gw->w));
258         }
259     }
260 
261     if (gw->pango_layout != NULL) {
262         g_object_unref(gw->pango_layout);
263     }
264 
265     if (gw->cs != NULL) {
266         cairo_surface_destroy(gw->cs);
267     }
268 
269     g_hash_table_remove(gw->display->windows, gw);
270 
271     // Remove our reference on the parent
272     if (gw->parent != NULL && gw->parent != gw->display->groot) {
273         GGDKDRAW_DECREF(gw->parent, _GGDKDraw_InitiateWindowDestroy);
274     }
275 
276     free(gw->ggc);
277     free(gw);
278     return false;
279 }
280 
281 // FF expects the destroy call to happen asynchronously to
282 // the actual GGDKDrawDestroyWindow call. So we add it to the queue...
_GGDKDraw_InitiateWindowDestroy(GGDKWindow gw)283 static void _GGDKDraw_InitiateWindowDestroy(GGDKWindow gw) {
284     if (gw->is_pixmap) {
285         _GGDKDraw_OnWindowDestroyed(gw);
286     } else if (!gw->is_cleaning_up) { // Check for nested call - if we're already being destroyed.
287         // Note: This *MUST* be a 0-length timer so it always gets picked up on the next
288         //       call to GDrawProcessPendingEvents. There are assumptions made that the destroy
289         //       event will be invoked when that function is called after calling GDrawDestroyWindow.
290         //       Ideally fix wherever the GDrawSync/GDrawProcessPendingEvents pattern is used
291         //       to have a more concrete check that it's actually gone.
292         g_timeout_add(0, _GGDKDraw_OnWindowDestroyed, gw);
293     }
294 }
295 
_GGDKDraw_OnTimerDestroyed(GGDKTimer * timer)296 static void _GGDKDraw_OnTimerDestroyed(GGDKTimer *timer) {
297     GGDKDisplay *gdisp = ((GGDKWindow)(timer->owner))->display;
298 
299     // We may stop the timer without destroying it -
300     // e.g. if the user doesn't cancel the timer before a window is destroyed.
301     if (!timer->stopped) {
302         g_source_remove(timer->glib_timeout_id);
303     }
304     gdisp->timers = g_list_remove(gdisp->timers, timer);
305     free(timer);
306 }
307 
_GGDKDraw_OnFakedConfigure(gpointer user_data)308 static gboolean _GGDKDraw_OnFakedConfigure(gpointer user_data) {
309     GGDKWindow gw = (GGDKWindow)user_data;
310     if (!gw->is_dying) {
311         GdkEventConfigure evt = {0};
312 
313         evt.type       = GDK_CONFIGURE;
314         evt.window     = gw->w;
315         evt.send_event = true;
316         evt.width      = gdk_window_get_width(gw->w);
317         evt.height     = gdk_window_get_height(gw->w);
318         gdk_window_get_position(gw->w, &evt.x, &evt.y);
319 
320         gdk_event_put((GdkEvent *)&evt);
321     }
322     gw->resize_timeout = 0;
323     return false;
324 }
325 
326 // In their infinite wisdom, GDK does not send configure events for child windows.
_GGDKDraw_FakeConfigureEvent(GGDKWindow gw)327 static void _GGDKDraw_FakeConfigureEvent(GGDKWindow gw) {
328     if (gw->resize_timeout != 0) {
329         g_source_remove(gw->resize_timeout);
330         gw->resize_timeout = 0;
331     }
332 
333     // Always fire the first faked configure event immediately
334     if (!gw->has_had_faked_configure) {
335         _GGDKDraw_OnFakedConfigure(gw);
336         gw->has_had_faked_configure = true;
337     } else {
338         gw->resize_timeout = g_timeout_add(150, _GGDKDraw_OnFakedConfigure, gw);
339     }
340 }
341 
_GGDKDraw_CallEHChecked(GGDKWindow gw,GEvent * event,int (* eh)(GWindow gw,GEvent *))342 static void _GGDKDraw_CallEHChecked(GGDKWindow gw, GEvent *event, int (*eh)(GWindow gw, GEvent *)) {
343     if (eh) {
344         // Increment reference counter
345         GGDKDRAW_ADDREF(gw);
346         (eh)((GWindow)gw, event);
347 
348         // Cleanup after our user...
349         _GGDKDraw_CleanupAutoPaint(gw->display);
350         // Decrement reference counter
351         GGDKDRAW_DECREF(gw, _GGDKDraw_InitiateWindowDestroy);
352     }
353 }
354 
_GGDKDraw_GetPointer(GGDKDisplay * gdisp)355 static GdkDevice *_GGDKDraw_GetPointer(GGDKDisplay *gdisp) {
356 #ifdef GGDKDRAW_GDK_3_20
357     GdkSeat *seat = gdk_display_get_default_seat(gdisp->display);
358     if (seat == NULL) {
359         return NULL;
360     }
361 
362     return gdk_seat_get_pointer(seat);
363 #else
364     GdkDeviceManager *manager = gdk_display_get_device_manager(gdisp->display);
365     if (manager == NULL) {
366         return NULL;
367     }
368 
369     return gdk_device_manager_get_client_pointer(manager);
370 #endif
371 }
372 
_GGDKDraw_CenterWindowOnScreen(GGDKWindow gw)373 static void _GGDKDraw_CenterWindowOnScreen(GGDKWindow gw) {
374     GGDKDisplay *gdisp = gw->display;
375     GdkRectangle work_area, window_size;
376     int x, y;
377 
378 #ifdef GGDKDRAW_GDK_3_22
379     GdkMonitor *monitor;
380 
381     gdk_device_get_position(_GGDKDraw_GetPointer(gdisp), NULL, &x, &y);
382     monitor = gdk_display_get_monitor_at_point(gdisp->display, x, y);
383     gdk_monitor_get_workarea(monitor, &work_area);
384 #else
385     GdkScreen *pointer_screen;
386     int monitor = 0;
387 
388     gdk_device_get_position(_GGDKDraw_GetPointer(gdisp), &pointer_screen, &x, &y);
389 
390     if (pointer_screen == gdisp->screen) { // Ensure it's on the same screen
391         monitor = gdk_screen_get_monitor_at_point(gdisp->screen, x, y);
392     }
393 
394     gdk_screen_get_monitor_workarea(pointer_screen, monitor, &work_area);
395 #endif // GGDKDRAW_GDK_3_22
396 
397     gdk_window_get_frame_extents(gw->w, &window_size);
398     gw->pos.x = (work_area.width - window_size.width) / 2 + work_area.x;
399     gw->pos.y = (work_area.height - window_size.height) / 2 + work_area.y;
400 
401     if (gw->pos.x < work_area.x) {
402         gw->pos.x = work_area.x;
403     }
404 
405     if (gw->pos.y < work_area.y) {
406         gw->pos.y = work_area.y;
407     }
408     gdk_window_move(gw->w, gw->pos.x, gw->pos.y);
409 }
410 
_GGDKDraw_CreateWindow(GGDKDisplay * gdisp,GGDKWindow gw,GRect * pos,int (* eh)(GWindow,GEvent *),void * user_data,GWindowAttrs * wattrs)411 static GWindow _GGDKDraw_CreateWindow(GGDKDisplay *gdisp, GGDKWindow gw, GRect *pos,
412                                       int (*eh)(GWindow, GEvent *), void *user_data, GWindowAttrs *wattrs) {
413 
414     GWindowAttrs temp = GWINDOWATTRS_EMPTY;
415     GdkWindowAttr attribs = {0};
416     gint attribs_mask = 0;
417     GGDKWindow nw = (GGDKWindow)calloc(1, sizeof(struct ggdkwindow));
418 
419     if (nw == NULL) {
420         Log(LOGDEBUG, "_GGDKDraw_CreateWindow: GGDKWindow calloc failed.");
421         return NULL;
422     }
423     if (wattrs == NULL) {
424         wattrs = &temp;
425     }
426 
427     if (gw == NULL) { // Creating a top-level window. Set parent as default root.
428         gw = gdisp->groot;
429         attribs.window_type = GDK_WINDOW_TOPLEVEL;
430     } else {
431         attribs.window_type = GDK_WINDOW_CHILD;
432     }
433 
434     // Now check window type
435     if ((wattrs->mask & wam_nodecor) && wattrs->nodecoration) {
436         // Is a modeless dialogue
437         nw->is_popup = true;
438         nw->is_dlg = true;
439         nw->not_restricted = true;
440         attribs.window_type = GDK_WINDOW_TEMP;
441     } else if ((wattrs->mask & wam_isdlg) && wattrs->is_dlg) {
442         nw->is_dlg = true;
443     }
444     if ((wattrs->mask & wam_notrestricted) && wattrs->not_restricted) {
445         nw->not_restricted = true;
446     }
447     nw->is_toplevel = attribs.window_type != GDK_WINDOW_CHILD;
448 
449     // Drawing context
450     nw->ggc = _GGDKDraw_NewGGC();
451     if (nw->ggc == NULL) {
452         Log(LOGDEBUG, "_GGDKDraw_CreateWindow: _GGDKDraw_NewGGC returned NULL");
453         free(nw);
454         return NULL;
455     }
456 
457     // Base fields
458     nw->display = gdisp;
459     nw->eh = eh;
460     nw->parent = gw;
461     nw->pos = *pos;
462     nw->user_data = user_data;
463 
464     // Window title, hints and event mask
465     attribs.event_mask = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK;
466     if (nw->is_toplevel) {
467         // Default event mask for toplevel windows
468         attribs.event_mask |= GDK_FOCUS_CHANGE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
469 
470         // Icon titles are ignored.
471         if ((wattrs->mask & wam_utf8_wtitle) && (wattrs->utf8_window_title != NULL)) {
472             nw->window_title = copy(wattrs->utf8_window_title);
473         } else if ((wattrs->mask & wam_wtitle) && (wattrs->window_title != NULL)) {
474             nw->window_title = u2utf8_copy(wattrs->window_title);
475         }
476 
477         attribs.title = nw->window_title;
478         attribs.type_hint = (nw->is_popup || (wattrs->mask & wam_palette)) ?
479                             GDK_WINDOW_TYPE_HINT_UTILITY : GDK_WINDOW_TYPE_HINT_NORMAL;
480 
481         if (attribs.title != NULL) {
482             attribs_mask |= GDK_WA_TITLE;
483         }
484         attribs_mask |= GDK_WA_TYPE_HINT;
485     }
486 
487     // Further event mask flags
488     if (wattrs->mask & wam_events) {
489         if (wattrs->event_masks & (1 << et_char)) {
490             attribs.event_mask |= GDK_KEY_PRESS_MASK;
491         }
492         if (wattrs->event_masks & (1 << et_charup)) {
493             attribs.event_mask |= GDK_KEY_RELEASE_MASK;
494         }
495         if (wattrs->event_masks & (1 << et_mousemove)) {
496             attribs.event_mask |= GDK_POINTER_MOTION_MASK;
497         }
498         if (wattrs->event_masks & (1 << et_mousedown)) {
499             attribs.event_mask |= GDK_BUTTON_PRESS_MASK | GDK_SCROLL_MASK;
500         }
501         if (wattrs->event_masks & (1 << et_mouseup)) {
502             attribs.event_mask |= GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK;
503         }
504         if (wattrs->event_masks & (1 << et_visibility)) {
505             attribs.event_mask |= GDK_VISIBILITY_NOTIFY_MASK;
506         }
507     }
508 
509     if (wattrs->mask & wam_restrict) {
510         nw->restrict_input_to_me = wattrs->restrict_input_to_me;
511     }
512     if (wattrs->mask & wam_redirect) {
513         nw->redirect_chars_to_me = wattrs->redirect_chars_to_me;
514         nw->redirect_from = wattrs->redirect_from;
515     }
516 
517     attribs.width = pos->width;
518     attribs.height = pos->height;
519     attribs.wclass = GDK_INPUT_OUTPUT;
520     // GDK docs say to not use this. But that's because it's done by GTK...
521     attribs.wmclass_name = GResourceProgramName;
522     attribs.wmclass_class = GResourceProgramName;
523     attribs_mask |= GDK_WA_WMCLASS;
524 
525     nw->w = gdk_window_new(gw->w, &attribs, attribs_mask);
526     if (nw->w == NULL) {
527         Log(LOGDEBUG, "GGDKDraw: Failed to create window!");
528         free(nw->window_title);
529         free(nw->ggc);
530         free(nw);
531         return NULL;
532     }
533 
534     // We center windows here because we need to know the window size+decor
535     // There is a bug on Windows (all versions < 3.21.1, <= 2.24.30) so don't use GDK_WA_X/GDK_WA_Y
536     // https://bugzilla.gnome.org/show_bug.cgi?id=764996
537     if (nw->is_toplevel && (!(wattrs->mask & wam_positioned) || (wattrs->mask & wam_centered))) {
538         nw->is_centered = true;
539         _GGDKDraw_CenterWindowOnScreen(nw);
540     } else {
541         gdk_window_move(nw->w, nw->pos.x, nw->pos.y);
542     }
543 
544     // Set background
545     if (!(wattrs->mask & wam_backcol) || wattrs->background_color == COLOR_DEFAULT) {
546         wattrs->background_color = gdisp->def_background;
547     }
548     nw->ggc->bg = wattrs->background_color;
549     GGDKDrawSetWindowBackground((GWindow)nw, wattrs->background_color);
550 
551     if (nw->is_toplevel) {
552         // Set opaque region
553         _GGDKDraw_SetOpaqueRegion(nw);
554 
555         // Set icon
556         GGDKWindow icon = gdisp->default_icon;
557         if (((wattrs->mask & wam_icon) && wattrs->icon != NULL) && ((GGDKWindow)wattrs->icon)->is_pixmap) {
558             icon = (GGDKWindow) wattrs->icon;
559         }
560         if (icon != NULL) {
561             GdkPixbuf *pb = gdk_pixbuf_get_from_surface(icon->cs, 0, 0, icon->pos.width, icon->pos.height);
562             if (pb != NULL) {
563                 GList_Glib ent = {.data = pb};
564                 gdk_window_set_icon_list(nw->w, &ent);
565                 g_object_unref(pb);
566             }
567         } else {
568             gdk_window_set_decorations(nw->w, GDK_DECOR_ALL | GDK_DECOR_MENU);
569         }
570 
571         GdkGeometry geom = {0};
572         GdkWindowHints hints = 0;
573         if (wattrs->mask & wam_palette) {
574             hints |= GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE;
575         }
576         if ((wattrs->mask & wam_noresize) && wattrs->noresize) {
577             hints |= GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_BASE_SIZE;
578         }
579 
580         // Hmm does this seem right?
581         geom.base_width = geom.min_width = geom.max_width = pos->width;
582         geom.base_height = geom.min_height = geom.max_height = pos->height;
583 
584         hints |= GDK_HINT_POS;
585         nw->was_positioned = true;
586 
587         gdk_window_set_geometry_hints(nw->w, &geom, hints);
588 
589         if ((wattrs->mask & wam_transient) && wattrs->transient != NULL) {
590             GGDKDrawSetTransientFor((GWindow)nw, wattrs->transient);
591             nw->is_dlg = true;
592         } else if (!nw->is_dlg) {
593             ++gdisp->top_window_count;
594         } else if (nw->restrict_input_to_me && gdisp->mru_windows->length > 0) {
595             GGDKDrawSetTransientFor((GWindow)nw, (GWindow) - 1);
596         }
597         nw->isverytransient = (wattrs->mask & wam_verytransient) ? 1 : 0;
598 
599         // Don't allow popups or 'very transient' windows from entering this list
600         if (!nw->is_popup && !nw->isverytransient) {
601             // This creates a single link with the data being the window, and next/prev NULL
602             nw->mru_link = g_list_append(NULL, (gpointer)nw);
603             if (nw->mru_link == NULL) {
604                 Log(LOGDEBUG, "Failed to create mru_link");
605                 gdk_window_destroy(nw->w);
606                 free(nw->window_title);
607                 free(nw->ggc);
608                 free(nw);
609                 return NULL;
610             }
611         }
612     }
613 
614     // Establish Pango/Cairo context
615     if (!_GGDKDraw_InitPangoCairo(nw)) {
616         gdk_window_destroy(nw->w);
617         free(nw->window_title);
618         free(nw->ggc);
619         free(nw->mru_link);
620         free(nw);
621         return NULL;
622     }
623 
624     if ((wattrs->mask & wam_cursor) && wattrs->cursor != ct_default) {
625         GGDKDrawSetCursor((GWindow)nw, wattrs->cursor);
626     }
627 
628     // Add a reference to our own structure.
629     GGDKDRAW_ADDREF(nw);
630 
631     // Add a reference on the parent window
632     if (nw->parent != gdisp->groot) {
633         GGDKDRAW_ADDREF(nw->parent);
634     }
635 
636     if (nw->mru_link) {
637         // Add it into the mru list
638         Log(LOGDEBUG, "Adding %p(%s) to the mru list", nw, nw->window_title);
639         g_queue_push_tail_link(gdisp->mru_windows, nw->mru_link);
640     }
641 
642     // Event handler
643     if (eh != NULL) {
644         GEvent e = {0};
645         e.type = et_create;
646         e.w = (GWindow) nw;
647         e.native_window = nw->w;
648         _GGDKDraw_CallEHChecked(nw, &e, eh);
649     }
650 
651     // Set the user data to the GWindow
652     // Although there is gdk_window_set_user_data, if non-NULL,
653     // GTK assumes it's a GTKWindow.
654     g_object_set_data(G_OBJECT(nw->w), "GGDKWindow", nw);
655     // Add our reference to the window
656     // This will be unreferenced when the window is destroyed.
657     g_object_ref(G_OBJECT(nw->w));
658     // Add our window to the display's hashmap
659     g_hash_table_add(gdisp->windows, nw);
660     //gdk_window_move_resize(nw->w, nw->pos.x, nw->pos.y, nw->pos.width, nw->pos.height);
661 
662     Log(LOGDEBUG, "Window created: %p[%p][%s][toplevel:%d]", nw, nw->w, nw->window_title, nw->is_toplevel);
663     return (GWindow)nw;
664 }
665 
_GGDKDraw_NewPixmap(GDisplay * disp,GWindow similar,uint16 width,uint16 height,bool is_bitmap,unsigned char * data)666 static GWindow _GGDKDraw_NewPixmap(GDisplay *disp, GWindow similar, uint16 width, uint16 height, bool is_bitmap,
667                                    unsigned char *data) {
668     GGDKDisplay *gdisp = (GGDKDisplay *)disp;
669     GGDKWindow gw = (GGDKWindow)calloc(1, sizeof(struct ggdkwindow));
670     if (gw == NULL) {
671         Log(LOGDEBUG, "GGDKDRAW: GGDKWindow calloc failed!");
672         return NULL;
673     }
674 
675     gw->ggc = _GGDKDraw_NewGGC();
676     if (gw->ggc == NULL) {
677         Log(LOGDEBUG, "GGDKDRAW: GGC alloc failed!");
678         free(gw);
679         return NULL;
680     }
681     gw->ggc->bg = gdisp->def_background;
682     width &= 0x7fff; // We're always using a cairo surface...
683 
684     gw->display = gdisp;
685     gw->is_pixmap = 1;
686     gw->parent = NULL;
687     gw->pos.x = gw->pos.y = 0;
688     gw->pos.width = width;
689     gw->pos.height = height;
690 
691     if (data == NULL) {
692         if (similar == NULL) {
693             gw->cs = cairo_image_surface_create(is_bitmap ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_ARGB32, width, height);
694         } else {
695             gw->cs = gdk_window_create_similar_surface(((GGDKWindow)similar)->w, CAIRO_CONTENT_COLOR, width, height);
696         }
697     } else {
698         cairo_format_t format = is_bitmap ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_ARGB32;
699         gw->cs = cairo_image_surface_create_for_data(data, format, width, height, cairo_format_stride_for_width(format, width));
700     }
701 
702     if (gw->cs == NULL) {
703         Log(LOGDEBUG, "GGDKDRAW: Cairo image surface creation failed!");
704         free(gw->ggc);
705         free(gw);
706         return NULL;
707     }
708 
709     if (!_GGDKDraw_InitPangoCairo(gw)) {
710         cairo_surface_destroy(gw->cs);
711         free(gw->ggc);
712         free(gw);
713         return NULL;
714     }
715     // Add a reference to ourselves
716     GGDKDRAW_ADDREF(gw);
717     return (GWindow)gw;
718 }
719 
_GGDKDraw_GdkModifierToKsm(GdkModifierType mask)720 static int16 _GGDKDraw_GdkModifierToKsm(GdkModifierType mask) {
721     int16 state = 0;
722     //Translate from mask to X11 state
723     if (mask & GDK_SHIFT_MASK) {
724         state |= ksm_shift;
725     }
726     if (mask & GDK_LOCK_MASK) {
727         state |= ksm_capslock;
728     }
729     if (mask & GDK_CONTROL_MASK) {
730         state |= ksm_control;
731     }
732     if (mask & GDK_MOD1_MASK) { //Guess
733         state |= ksm_meta;
734     }
735     if (mask & GDK_MOD2_MASK) { //Guess
736         state |= ksm_cmdmacosx;
737     }
738     if (mask & GDK_SUPER_MASK) {
739         state |= ksm_super;
740     }
741     if (mask & GDK_HYPER_MASK) {
742         state |= ksm_hyper;
743     }
744     //ksm_option?
745     if (mask & GDK_BUTTON1_MASK) {
746         state |= ksm_button1;
747     }
748     if (mask & GDK_BUTTON2_MASK) {
749         state |= ksm_button2;
750     }
751     if (mask & GDK_BUTTON3_MASK) {
752         state |= ksm_button3;
753     }
754     if (mask & GDK_BUTTON4_MASK) {
755         state |= ksm_button4;
756     }
757     if (mask & GDK_BUTTON5_MASK) {
758         state |= ksm_button5;
759     }
760 
761     return state;
762 }
763 
_GGDKDraw_GdkVisibilityStateToVS(GdkVisibilityState state)764 static VisibilityState _GGDKDraw_GdkVisibilityStateToVS(GdkVisibilityState state) {
765     switch (state) {
766         case GDK_VISIBILITY_FULLY_OBSCURED:
767             return vs_obscured;
768             break;
769         case GDK_VISIBILITY_PARTIAL:
770             return vs_partially;
771             break;
772         case GDK_VISIBILITY_UNOBSCURED:
773         default:
774             return vs_unobscured;
775             break;
776     }
777 }
778 
_GGDKDraw_WindowOrParentsDying(GGDKWindow gw)779 static int _GGDKDraw_WindowOrParentsDying(GGDKWindow gw) {
780     while (gw != NULL) {
781         if (gw->is_dying) {
782             return true;
783         }
784         if (gw->is_toplevel) {
785             return false;
786         }
787         gw = gw->parent;
788     }
789     return false;
790 }
791 
_GGDKDraw_FilterByModal(GdkEvent * event,GGDKWindow gw)792 static bool _GGDKDraw_FilterByModal(GdkEvent *event, GGDKWindow gw) {
793     switch (event->type) {
794         case GDK_KEY_PRESS:
795         case GDK_KEY_RELEASE:
796         case GDK_BUTTON_PRESS:
797         case GDK_BUTTON_RELEASE:
798         case GDK_SCROLL:
799         case GDK_MOTION_NOTIFY:
800         case GDK_DELETE:
801         case GDK_FOCUS_CHANGE:
802             break;
803         default:
804             return false;
805             break;
806     }
807 
808     GGDKWindow gww = gw;
809     GPtrArray *stack = gw->display->transient_stack;
810 
811     if (gww && gw->display->restrict_count == 0) {
812         return false;
813     }
814 
815     GGDKWindow last_modal = NULL;
816     while (gww != NULL) {
817         if (gww->is_toplevel) {
818             for (int i = ((int)stack->len) - 1; i >= 0; --i) {
819                 GGDKWindow ow = (GGDKWindow)stack->pdata[i];
820                 if (ow == gww || ow->restrict_input_to_me) { // fudged
821                     last_modal = ow;
822                     break;
823                 }
824             }
825 
826             if (last_modal == NULL || last_modal == gww) {
827                 return false;
828             }
829         }
830 
831         gww = gww->parent;
832     }
833 
834     if (event->type != GDK_MOTION_NOTIFY && event->type != GDK_BUTTON_RELEASE && event->type != GDK_FOCUS_CHANGE) {
835         gdk_window_beep(gw->w);
836         if (last_modal != NULL) {
837             GDrawRaise((GWindow)last_modal);
838         }
839     }
840 
841     return true;
842 }
843 
_GGDKDraw_ProcessTimerEvent(gpointer user_data)844 static gboolean _GGDKDraw_ProcessTimerEvent(gpointer user_data) {
845     GGDKTimer *timer = (GGDKTimer *)user_data;
846     GEvent e = {0};
847     bool ret = true;
848 
849     if (!timer->active || _GGDKDraw_WindowOrParentsDying((GGDKWindow)timer->owner)) {
850         timer->active = false;
851         timer->stopped = true;
852         return false;
853     }
854 
855     e.type = et_timer;
856     e.w = timer->owner;
857     e.native_window = timer->owner->native_window;
858     e.u.timer.timer = (GTimer *)timer;
859     e.u.timer.userdata = timer->userdata;
860 
861     GGDKDRAW_ADDREF(timer);
862     _GGDKDraw_CallEHChecked((GGDKWindow)timer->owner, &e, timer->owner->eh);
863     if (timer->active) {
864         if (timer->repeat_time == 0) {
865             timer->stopped = true; // Since we return false, this timer is no longer valid.
866             GGDKDrawCancelTimer((GTimer *)timer);
867             ret = false;
868         } else if (timer->has_differing_repeat_time) {
869             timer->has_differing_repeat_time = false;
870             timer->glib_timeout_id = g_timeout_add(timer->repeat_time, _GGDKDraw_ProcessTimerEvent, timer);
871             ret = false;
872         }
873     }
874 
875     GGDKDRAW_DECREF(timer, _GGDKDraw_OnTimerDestroyed);
876     return ret;
877 }
878 
_GGDKDraw_DispatchEvent(GdkEvent * event,gpointer data)879 static void _GGDKDraw_DispatchEvent(GdkEvent *event, gpointer data) {
880     //static int request_id = 0;
881     struct gevent gevent = {0};
882     GGDKDisplay *gdisp = (GGDKDisplay *)data;
883     GdkWindow *w = ((GdkEventAny *)event)->window;
884     GGDKWindow gw;
885     guint32 event_time = gdk_event_get_time(event);
886     if (event_time != GDK_CURRENT_TIME) {
887         gdisp->last_event_time = event_time;
888     }
889 
890     //Log(LOGDEBUG, "[%d] Received event %d(%s) %p", request_id++, event->type, GdkEventName(event->type), w);
891     //fflush(stderr);
892 
893     if (w == NULL) {
894         return;
895     } else if ((gw = g_object_get_data(G_OBJECT(w), "GGDKWindow")) == NULL) {
896         //Log(LOGDEBUG, "MISSING GW!");
897         return;
898     } else if (_GGDKDraw_WindowOrParentsDying(gw) || gdk_window_is_destroyed(w)) {
899         Log(LOGDEBUG, "DYING! %p", w);
900         return;
901     } else if (_GGDKDraw_FilterByModal(event, gw)) {
902         Log(LOGDEBUG, "Discarding event - has modals!");
903         return;
904     }
905 
906     gevent.w = (GWindow)gw;
907     gevent.native_window = (void *)gw->w;
908     gevent.type = et_noevent;
909 
910     switch (event->type) {
911         case GDK_KEY_PRESS:
912         case GDK_KEY_RELEASE: {
913             GdkEventKey *key = (GdkEventKey *)event;
914             gevent.type = event->type == GDK_KEY_PRESS ? et_char : et_charup;
915             gevent.u.chr.state = _GGDKDraw_GdkModifierToKsm(((GdkEventKey *)event)->state);
916 
917 #ifdef GDK_WINDOWING_QUARTZ
918             // On Mac, the Alt/Option key is used for alternate input.
919             // We want accelerators, so translate ourselves, forcing the group to 0.
920             if ((gevent.u.chr.state & ksm_meta) && key->group != 0) {
921                 GdkKeymap *km = gdk_keymap_get_for_display(gdisp->display);
922                 guint keyval;
923 
924                 gdk_keymap_translate_keyboard_state(km, key->hardware_keycode,
925                     key->state, 0, &keyval, NULL, NULL, NULL);
926 
927                 //Log(LOGDEBUG, "Fixed keyval from 0x%x(%s) -> 0x%x(%s)",
928                 //	key->keyval, gdk_keyval_name(key->keyval),
929                 //	keyval, gdk_keyval_name(keyval));
930 
931                 key->keyval = keyval;
932             }
933 #endif
934 
935             gevent.u.chr.autorepeat =
936                 event->type    == GDK_KEY_PRESS &&
937                 gdisp->ks.type == GDK_KEY_PRESS &&
938                 key->keyval    == gdisp->ks.keyval &&
939                 key->state     == gdisp->ks.state;
940             // Mumble mumble Mac
941             //if ((event->xkey.state & ksm_option) && gdisp->macosx_cmd) {
942             //    gevent.u.chr.state |= ksm_meta;
943             //}
944 
945             if (gevent.u.chr.autorepeat && GKeysymIsModifier(key->keyval)) {
946                 gevent.type = et_noevent;
947             } else {
948                 if (key->keyval == GDK_KEY_space) {
949                     gw->display->is_space_pressed = event->type == GDK_KEY_PRESS;
950                 }
951 
952                 gevent.u.chr.keysym = key->keyval;
953                 gevent.u.chr.chars[0] = gdk_keyval_to_unicode(key->keyval);
954             }
955 
956             gdisp->ks.type   = key->type;
957             gdisp->ks.keyval = key->keyval;
958             gdisp->ks.state  = key->state;
959         }
960         break;
961         case GDK_MOTION_NOTIFY: {
962             GdkEventMotion *evt = (GdkEventMotion *)event;
963             gevent.type = et_mousemove;
964             gevent.u.mouse.state = _GGDKDraw_GdkModifierToKsm(evt->state);
965             gevent.u.mouse.x = evt->x;
966             gevent.u.mouse.y = evt->y;
967             //Log(LOGDEBUG, "Motion: [%f %f]", evt->x, evt->y);
968         }
969         break;
970         case GDK_SCROLL: { //Synthesize a button press
971             GdkEventScroll *evt = (GdkEventScroll *)event;
972             gevent.u.mouse.state = _GGDKDraw_GdkModifierToKsm(evt->state);
973             gevent.u.mouse.x = evt->x;
974             gevent.u.mouse.y = evt->y;
975             gevent.u.mouse.clicks = gdisp->bs.cur_click = 1;
976             gevent.type = et_mousedown;
977 
978             switch (evt->direction) {
979                 case GDK_SCROLL_UP:
980                     gevent.u.mouse.button = 4;
981                     break;
982                 case GDK_SCROLL_DOWN:
983                     gevent.u.mouse.button = 5;
984                     break;
985                 case GDK_SCROLL_LEFT:
986                     gevent.u.mouse.button = 6;
987                     break;
988                 case GDK_SCROLL_RIGHT:
989                     gevent.u.mouse.button = 7;
990                     break;
991                 default: // Ignore GDK_SCROLL_SMOOTH
992                     gevent.type = et_noevent;
993             }
994             // We need to simulate two events... I think.
995             if (gevent.type != et_noevent) {
996                 GGDKDrawPostEvent(&gevent);
997                 gevent.type = et_mouseup;
998             }
999         }
1000         break;
1001         case GDK_BUTTON_PRESS:
1002         case GDK_BUTTON_RELEASE: {
1003             GdkEventButton *evt = (GdkEventButton *)event;
1004             gevent.u.mouse.state = _GGDKDraw_GdkModifierToKsm(evt->state);
1005             gevent.u.mouse.x = evt->x;
1006             gevent.u.mouse.y = evt->y;
1007             gevent.u.mouse.button = evt->button;
1008             gevent.u.mouse.time = evt->time;
1009 
1010             Log(LOGDEBUG, "Button %7s: [%f %f]", evt->type == GDK_BUTTON_PRESS ? "press" : "release", evt->x, evt->y);
1011 
1012 #ifdef GDK_WINDOWING_QUARTZ
1013             // Quartz backend fails to give a button press event
1014             // https://bugzilla.gnome.org/show_bug.cgi?id=769961
1015             if (!evt->send_event && evt->type == GDK_BUTTON_RELEASE &&
1016                     gdisp->bs.release_w == gw &&
1017                     (evt->x < 0 || evt->x > gw->pos.width ||
1018                      evt->y < 0 || evt->y > gw->pos.height)) {
1019                 evt->send_event = true;
1020                 gdk_event_put(event);
1021                 event->type = GDK_BUTTON_PRESS;
1022             }
1023 #endif
1024 
1025             if (event->type == GDK_BUTTON_PRESS) {
1026                 int xdiff, ydiff;
1027                 gevent.type = et_mousedown;
1028 
1029                 xdiff = abs(((int)evt->x) - gdisp->bs.release_x);
1030                 ydiff = abs(((int)evt->y) - gdisp->bs.release_y);
1031 
1032                 if (xdiff + ydiff < gdisp->bs.double_wiggle &&
1033                         gw == gdisp->bs.release_w &&
1034                         gevent.u.mouse.button == gdisp->bs.release_button &&
1035                         (int32_t)(gevent.u.mouse.time - gdisp->bs.last_press_time) < gdisp->bs.double_time &&
1036                         gevent.u.mouse.time >= gdisp->bs.last_press_time) {  // Time can wrap
1037 
1038                     gdisp->bs.cur_click++;
1039                 } else {
1040                     gdisp->bs.cur_click = 1;
1041                 }
1042                 gdisp->bs.last_press_time = gevent.u.mouse.time;
1043             } else {
1044                 gevent.type = et_mouseup;
1045                 gdisp->bs.release_w = gw;
1046                 gdisp->bs.release_x = evt->x;
1047                 gdisp->bs.release_y = evt->y;
1048                 gdisp->bs.release_button = evt->button;
1049             }
1050             gevent.u.mouse.clicks = gdisp->bs.cur_click;
1051         }
1052         break;
1053         case GDK_EXPOSE: {
1054             GdkEventExpose *expose = (GdkEventExpose *)event;
1055             // Okay. So on GDK3, we must exclude child window regions to prevent flicker.
1056             // However, we cannot modify the expose event's region directly, as this is
1057             // used by GDK to determine what child windows to invalidate. Hence, we must
1058             // make a copy of the region, sans any child window areas.
1059             cairo_rectangle_int_t extents;
1060             cairo_region_t *reg = _GGDKDraw_ExcludeChildRegions(gw, expose->region, true);
1061             cairo_region_get_extents(reg, &extents);
1062 
1063             gevent.type = et_expose;
1064             gevent.u.expose.rect.x = extents.x;
1065             gevent.u.expose.rect.y = extents.y;
1066             gevent.u.expose.rect.width = extents.width;
1067             gevent.u.expose.rect.height = extents.height;
1068 
1069             // This can happen if say gdk_window_raise is called, which then runs the
1070             // event loop itself, which is outside of our checked event handler
1071             // I've added autopaint cleanups before such calls, so this should
1072             // never happen any more.
1073             assert(gw->cc == NULL);
1074 
1075 #ifdef GGDKDRAW_GDK_3_22
1076             gw->drawing_ctx = gdk_window_begin_draw_frame(w, reg);
1077 #else
1078             gdk_window_begin_paint_region(w, reg);
1079 #endif
1080             gw->is_in_paint = true;
1081             gdisp->dirty_window = gw;
1082 
1083             // But wait, there's more! On GDK3, if the window isn't native,
1084             // gdk_window_begin_paint_region does nothing.
1085             // So we have to (a) ensure we draw to an offscreen surface,
1086             // and (b) clip the region appropriately. But on Windows
1087             // (and maybe Quartz), it seems to double buffer anyway.
1088             if (!gdk_window_has_native(gw->w)) {
1089 #if !defined(GDK_WINDOWING_WIN32) && !defined(GDK_WINDOWING_QUARTZ)
1090                 double sx = 1, sy = 1;
1091 
1092                 gw->cs = gdk_window_create_similar_surface(gw->w, CAIRO_CONTENT_COLOR, extents.width, extents.height);
1093 #if (CAIRO_VERSION_MAJOR >= 2) || (CAIRO_VERSION_MAJOR >= 1 && CAIRO_VERSION_MINOR >= 14)
1094                 cairo_surface_get_device_scale(gw->cs, &sx, &sy);
1095 #endif
1096                 cairo_surface_set_device_offset(gw->cs, -extents.x * sx, -extents.y * sy);
1097                 gw->cc = cairo_create(gw->cs);
1098                 gw->expose_region = cairo_region_reference(reg);
1099 #else
1100 #ifdef GGDKDRAW_GDK_3_22
1101                 gw->cc = cairo_reference(gdk_drawing_context_get_cairo_context(gw->drawing_ctx));
1102 #else
1103                 gw->cc = gdk_cairo_create(gw->w);
1104 #endif
1105 #endif
1106                 gdk_cairo_region(gw->cc, reg);
1107                 cairo_clip(gw->cc);
1108             }
1109             cairo_region_destroy(reg);
1110         }
1111         break;
1112         case GDK_VISIBILITY_NOTIFY:
1113             gevent.type = et_visibility;
1114             gevent.u.visibility.state = _GGDKDraw_GdkVisibilityStateToVS(((GdkEventVisibility *)event)->state);
1115             break;
1116         case GDK_FOCUS_CHANGE:
1117             gevent.type = et_focus;
1118             gevent.u.focus.gained_focus = ((GdkEventFocus *)event)->in;
1119             gevent.u.focus.mnemonic_focus = false;
1120 
1121             Log(LOGDEBUG, "Focus change %s %p(%s %d %d)",
1122                 gevent.u.focus.gained_focus ? "IN" : "OUT",
1123                 gw, gw->window_title, gw->is_toplevel, gw->istransient);
1124 
1125             if (gw->mru_link && gevent.u.focus.gained_focus) {
1126                 GGDKWindow cur_toplevel = (GGDKWindow)g_queue_peek_head(gdisp->mru_windows);
1127                 if (cur_toplevel != gw) {
1128                     Log(LOGDEBUG, "Last active toplevel updated: %p(%s)", gw, gw->window_title);
1129                     g_queue_unlink(gdisp->mru_windows, gw->mru_link);
1130                     g_queue_push_head_link(gdisp->mru_windows, gw->mru_link);
1131                 }
1132             }
1133 
1134             break;
1135         case GDK_ENTER_NOTIFY:
1136         case GDK_LEAVE_NOTIFY: { // Should only get this on top level
1137             GdkEventCrossing *crossing = (GdkEventCrossing *)event;
1138             if (crossing->focus) { //Focus or inferior
1139                 break;
1140             }
1141             if (gdisp->focusfollowsmouse && gw != NULL && gw->eh != NULL) {
1142                 gevent.type = et_focus;
1143                 gevent.u.focus.gained_focus = crossing->type == GDK_ENTER_NOTIFY;
1144                 gevent.u.focus.mnemonic_focus = false;
1145                 _GGDKDraw_CallEHChecked(gw, &gevent, gw->eh);
1146             }
1147             gevent.type = et_crossing;
1148             gevent.u.crossing.x = crossing->x;
1149             gevent.u.crossing.y = crossing->y;
1150             gevent.u.crossing.state = _GGDKDraw_GdkModifierToKsm(crossing->state);
1151             gevent.u.crossing.entered = crossing->type == GDK_ENTER_NOTIFY;
1152             gevent.u.crossing.device = NULL;
1153             gevent.u.crossing.time = crossing->time;
1154 
1155             // Always set to false when crossing boundary...
1156             gw->display->is_space_pressed = false;
1157         }
1158         break;
1159         case GDK_CONFIGURE: {
1160             GdkEventConfigure *configure = (GdkEventConfigure *)event;
1161             gevent.type = et_resize;
1162             gevent.u.resize.size.x      = configure->x;
1163             gevent.u.resize.size.y      = configure->y;
1164             gevent.u.resize.size.width  = configure->width;
1165             gevent.u.resize.size.height = configure->height;
1166             gevent.u.resize.dx          = configure->x - gw->pos.x;
1167             gevent.u.resize.dy          = configure->y - gw->pos.y;
1168             gevent.u.resize.dwidth      = configure->width - gw->pos.width;
1169             gevent.u.resize.dheight     = configure->height - gw->pos.height;
1170             gevent.u.resize.moved       = gevent.u.resize.sized = false;
1171             if (gevent.u.resize.dx != 0 || gevent.u.resize.dy != 0) {
1172                 gevent.u.resize.moved = true;
1173                 gw->is_centered = false;
1174             }
1175             if (gevent.u.resize.dwidth != 0 || gevent.u.resize.dheight != 0) {
1176                 gevent.u.resize.sized = true;
1177             }
1178 
1179             // I could make this Windows specific... But it doesn't seem necessary on other platforms too.
1180             // On Windows, repeated configure messages are sent if we move the window around.
1181             // This causes CPU usage to go up because mouse handlers of this message just redraw the whole window.
1182             if (gw->is_toplevel && !gevent.u.resize.sized && gevent.u.resize.moved) {
1183                 gevent.type = et_noevent;
1184                 Log(LOGDEBUG, "Configure DISCARDED: %p:%s, %d %d %d %d", gw, gw->window_title, gw->pos.x, gw->pos.y, gw->pos.width, gw->pos.height);
1185             } else {
1186                 Log(LOGDEBUG, "CONFIGURED: %p:%s, %d %d %d %d", gw, gw->window_title, gw->pos.x, gw->pos.y, gw->pos.width, gw->pos.height);
1187             }
1188             gw->pos = gevent.u.resize.size;
1189 
1190             // Update the opaque region (we're always completely opaque)
1191             // Although I don't actually know if this is completely necessary
1192             if (gw->is_toplevel && gevent.u.resize.sized) {
1193                 _GGDKDraw_SetOpaqueRegion(gw);
1194             }
1195         }
1196         break;
1197         case GDK_MAP:
1198             gevent.type = et_map;
1199             gevent.u.map.is_visible = true;
1200             gw->is_visible = true;
1201             break;
1202         case GDK_UNMAP:
1203             gevent.type = et_map;
1204             gevent.u.map.is_visible = false;
1205             gw->is_visible = false;
1206             break;
1207         case GDK_DESTROY:
1208             GGDKDrawDestroyWindow((GWindow)gw); //Note: If we get here, something's probably wrong.
1209             break;
1210         case GDK_DELETE:
1211             gw->is_waiting_for_selection = false;
1212             gevent.type = et_close;
1213             break;
1214         case GDK_SELECTION_CLEAR: {
1215             gevent.type = et_selclear;
1216             gevent.u.selclear.sel = sn_primary;
1217             for (int i = 0; i < sn_max; i++) {
1218                 GdkEventSelection *se = (GdkEventSelection *)event;
1219                 if (se->selection == gdisp->selinfo[i].sel_atom) {
1220                     gevent.u.selclear.sel = i;
1221                     break;
1222                 }
1223             }
1224             _GGDKDraw_ClearSelData(gdisp, gevent.u.selclear.sel);
1225         }
1226         break;
1227         case GDK_SELECTION_REQUEST: {
1228             GdkEventSelection *e = (GdkEventSelection *)event;
1229             gdk_selection_send_notify(
1230                 e->requestor, e->selection, e->target,
1231                 _GGDKDraw_TransmitSelection(gdisp, e) ? e->property : GDK_NONE,
1232                 e->time);
1233         }
1234         break;
1235         case GDK_SELECTION_NOTIFY: // paste
1236             if (gw->is_waiting_for_selection) {
1237                 gw->is_waiting_for_selection = false;
1238                 gw->is_notified_of_selection = ((GdkEventSelection *)event)->property != GDK_NONE;
1239             }
1240             break;
1241         case GDK_PROPERTY_NOTIFY:
1242             break;
1243         default:
1244             Log(LOGDEBUG, "UNPROCESSED GDK EVENT %d %s", event->type, GdkEventName(event->type));
1245             break;
1246     }
1247 
1248     if (gevent.type != et_noevent && gw != NULL && gw->eh != NULL) {
1249         _GGDKDraw_CallEHChecked(gw, &gevent, gw->eh);
1250     }
1251     //Log(LOGDEBUG, "[%d] Finished processing %d(%s)", request_id++, event->type, GdkEventName(event->type));
1252 }
1253 
GGDKDrawInit(GDisplay * gdisp)1254 static void GGDKDrawInit(GDisplay *gdisp) {
1255     FState *fs = calloc(1, sizeof(FState));
1256     if (fs == NULL) {
1257         Log(LOGDEBUG, "GGDKDraw: FState alloc failed!");
1258         assert(false);
1259     }
1260 
1261     // In inches, because that's how fonts are measured
1262     gdisp->fontstate = fs;
1263     fs->res = gdisp->res;
1264 }
1265 
GGDKDrawSetDefaultIcon(GWindow icon)1266 static void GGDKDrawSetDefaultIcon(GWindow icon) {
1267     GGDKWindow gicon = (GGDKWindow)icon;
1268     if (gicon->is_pixmap) {
1269         gicon->display->default_icon = gicon;
1270     }
1271 }
1272 
GGDKDrawCreateTopWindow(GDisplay * gdisp,GRect * pos,int (* eh)(GWindow gw,GEvent *),void * user_data,GWindowAttrs * gattrs)1273 static GWindow GGDKDrawCreateTopWindow(GDisplay *gdisp, GRect *pos, int (*eh)(GWindow gw, GEvent *), void *user_data,
1274                                        GWindowAttrs *gattrs) {
1275     Log(LOGDEBUG, " ");
1276     return _GGDKDraw_CreateWindow((GGDKDisplay *) gdisp, NULL, pos, eh, user_data, gattrs);
1277 }
1278 
GGDKDrawCreateSubWindow(GWindow gw,GRect * pos,int (* eh)(GWindow gw,GEvent *),void * user_data,GWindowAttrs * gattrs)1279 static GWindow GGDKDrawCreateSubWindow(GWindow gw, GRect *pos, int (*eh)(GWindow gw, GEvent *), void *user_data,
1280                                        GWindowAttrs *gattrs) {
1281     Log(LOGDEBUG, " ");
1282     return _GGDKDraw_CreateWindow(((GGDKWindow) gw)->display, (GGDKWindow) gw, pos, eh, user_data, gattrs);
1283 }
1284 
GGDKDrawCreatePixmap(GDisplay * gdisp,GWindow similar,uint16 width,uint16 height)1285 static GWindow GGDKDrawCreatePixmap(GDisplay *gdisp, GWindow similar, uint16 width, uint16 height) {
1286     Log(LOGDEBUG, " ");
1287 
1288     //TODO: Check format?
1289     return _GGDKDraw_NewPixmap(gdisp, similar, width, height, false, NULL);
1290 }
1291 
GGDKDrawCreateBitmap(GDisplay * gdisp,uint16 width,uint16 height,uint8 * data)1292 static GWindow GGDKDrawCreateBitmap(GDisplay *gdisp, uint16 width, uint16 height, uint8 *data) {
1293     Log(LOGDEBUG, " ");
1294     int stride = cairo_format_stride_for_width(CAIRO_FORMAT_A1, width);
1295     int actual = (width & 0x7fff) / 8;
1296 
1297     if (actual != stride) {
1298         GWindow ret = _GGDKDraw_NewPixmap(gdisp, NULL, width, height, true, NULL);
1299         if (ret == NULL) {
1300             return NULL;
1301         }
1302 
1303         cairo_surface_flush(((GGDKWindow)ret)->cs);
1304         uint8 *buf = cairo_image_surface_get_data(((GGDKWindow)ret)->cs);
1305         for (int j = 0; j < height; j++) {
1306             memcpy(buf + stride * j, data + actual * j, actual);
1307         }
1308         cairo_surface_mark_dirty(((GGDKWindow)ret)->cs);
1309         return ret;
1310     }
1311 
1312     return _GGDKDraw_NewPixmap(gdisp, NULL, width, height, true, data);
1313 }
1314 
GGDKDrawCreateCursor(GWindow src,GWindow mask,Color fg,Color bg,int16 x,int16 y)1315 static GCursor GGDKDrawCreateCursor(GWindow src, GWindow mask, Color fg, Color bg, int16 x, int16 y) {
1316     Log(LOGDEBUG, " ");
1317 
1318     GGDKDisplay *gdisp = (GGDKDisplay *)(src->display);
1319     GdkCursor *cursor = NULL;
1320     if (mask == NULL) { // Use src directly
1321         assert(src != NULL);
1322         assert(src->is_pixmap);
1323         cursor = gdk_cursor_new_from_surface(gdisp->display, ((GGDKWindow)src)->cs, x, y);
1324     } else { // Assume it's an X11-style cursor
1325         cairo_surface_t *cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, src->pos.width, src->pos.height);
1326         cairo_t *cc = cairo_create(cs);
1327 
1328         // Masking
1329         //Background
1330         cairo_set_source_rgb(cc, COLOR_RED(bg) / 255., COLOR_GREEN(bg) / 255., COLOR_BLUE(bg) / 255.);
1331         cairo_mask_surface(cc, ((GGDKWindow)mask)->cs, 0, 0);
1332         //Foreground
1333         cairo_set_source_rgb(cc, COLOR_RED(fg) / 255., COLOR_GREEN(fg) / 255., COLOR_BLUE(fg) / 255.);
1334         cairo_mask_surface(cc, ((GGDKWindow)src)->cs, 0, 0);
1335 
1336         cursor = gdk_cursor_new_from_surface(gdisp->display, cs, x, y);
1337 
1338         cairo_destroy(cc);
1339         cairo_surface_destroy(cs);
1340     }
1341 
1342     g_ptr_array_add(gdisp->cursors, cursor);
1343     return ct_user + (gdisp->cursors->len - 1);
1344 }
1345 
GGDKDrawDestroyCursor(GDisplay * disp,GCursor gcursor)1346 static void GGDKDrawDestroyCursor(GDisplay *disp, GCursor gcursor) {
1347     Log(LOGDEBUG, " ");
1348 
1349     GGDKDisplay *gdisp = (GGDKDisplay *)disp;
1350     gcursor -= ct_user;
1351     if ((int)gcursor >= 0 && gcursor < gdisp->cursors->len) {
1352         g_object_unref(gdisp->cursors->pdata[gcursor]);
1353         gdisp->cursors->pdata[gcursor] = NULL;
1354     }
1355 }
1356 
GGDKDrawDestroyWindow(GWindow w)1357 static void GGDKDrawDestroyWindow(GWindow w) {
1358     GGDKWindow gw = (GGDKWindow) w;
1359     Log(LOGDEBUG, "%p[%p][%s][toplevel:%d][pixmap:%d]",
1360         gw, gw->w, gw->window_title, gw->is_toplevel, gw->is_pixmap);
1361 
1362     if (gw->is_dying) {
1363         return;
1364     }
1365 
1366     gw->is_dying = true;
1367     gw->is_waiting_for_selection = false;
1368     gw->is_notified_of_selection = false;
1369 
1370     if (!gw->is_pixmap && gw != gw->display->groot) {
1371         GList_Glib *list = gdk_window_get_children(gw->w);
1372         GList_Glib *child = list;
1373         while (child != NULL) {
1374             GWindow c = g_object_get_data(child->data, "GGDKWindow");
1375             assert(c != NULL);
1376             GGDKDrawDestroyWindow(c);
1377             child = child->next;
1378         }
1379         g_list_free(list);
1380 
1381         if (gw->is_toplevel) {
1382             // Remove itself from the transient list, if present
1383             GGDKDrawSetTransientFor((GWindow)gw, NULL);
1384 
1385             // Remove it from the mru window list
1386             if (gw->mru_link != NULL) {
1387                 Log(LOGDEBUG, "Removing %p(%s) from mru window list", gw, gw->window_title);
1388                 g_queue_delete_link(gw->display->mru_windows, gw->mru_link);
1389                 gw->mru_link = NULL;
1390             }
1391             // This is so that e.g. popup menus get hidden immediately
1392             // On Quartz, this is also necessary for toplevel windows,
1393             // as focus change events are not received when a window
1394             // is destroyed, but they *are* when it's first hidden...
1395             GDrawSetVisible((GWindow)gw, false);
1396 
1397             // HACK! Reparent all windows transient to this
1398             // If they were truly transient in the normal sense, they would just be
1399             // destroyed when this window goes away
1400             for (int i = ((int)gw->display->transient_stack->len) - 1; i >= 0; --i) {
1401                 GGDKWindow tw = (GGDKWindow)gw->display->transient_stack->pdata[i];
1402                 if (tw->transient_owner == gw) {
1403                     Log(LOGWARN, "Resetting transient owner on %p(%s) as %p(%s) is dying",
1404                         tw, tw->window_title, gw, gw->window_title);
1405                     GGDKDrawSetTransientFor((GWindow)tw, (GWindow)-1);
1406                 }
1407             }
1408         }
1409 
1410         if (gw->display->last_dd.w == gw) {
1411             gw->display->last_dd.w = NULL;
1412         }
1413         // Ensure an invalid value is returned if someone tries to get this value again.
1414         g_object_set_data(G_OBJECT(gw->w), "GGDKWindow", NULL);
1415     }
1416     GGDKDRAW_DECREF(gw, _GGDKDraw_InitiateWindowDestroy);
1417 }
1418 
GGDKDrawNativeWindowExists(GDisplay * UNUSED (gdisp),void * native_window)1419 static int GGDKDrawNativeWindowExists(GDisplay *UNUSED(gdisp), void *native_window) {
1420     //Log(LOGDEBUG, " ");
1421     GdkWindow *w = (GdkWindow *)native_window;
1422 
1423     if (w != NULL) {
1424         GGDKWindow gw = g_object_get_data(G_OBJECT(w), "GGDKWindow");
1425         // So if the window is dying, the gdk window is already gone.
1426         // But gcontainer.c expects this to return true on et_destroy...
1427         if (gw == NULL || gw->is_dying) {
1428             return true;
1429         } else {
1430             return !gdk_window_is_destroyed(w);
1431         }
1432     }
1433     return false;
1434 }
1435 
GGDKDrawSetZoom(GWindow UNUSED (gw),GRect * UNUSED (size),enum gzoom_flags UNUSED (flags))1436 static void GGDKDrawSetZoom(GWindow UNUSED(gw), GRect *UNUSED(size), enum gzoom_flags UNUSED(flags)) {
1437     //Log(LOGDEBUG, " ");
1438     // Not implemented.
1439 }
1440 
GGDKDrawSetWindowBackground(GWindow w,Color gcol)1441 static void GGDKDrawSetWindowBackground(GWindow w, Color gcol) {
1442     Log(LOGDEBUG, " ");
1443     GGDKWindow gw = (GGDKWindow)w;
1444     GdkRGBA col = {
1445         .red = COLOR_RED(gcol) / 255.,
1446         .green = COLOR_GREEN(gcol) / 255.,
1447         .blue = COLOR_BLUE(gcol) / 255.,
1448         .alpha = 1.
1449     };
1450 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1451     gdk_window_set_background_rgba(gw->w, &col);
1452 G_GNUC_END_IGNORE_DEPRECATIONS
1453 }
1454 
GGDKDrawSetDither(GDisplay * UNUSED (gdisp),int UNUSED (set))1455 static int GGDKDrawSetDither(GDisplay *UNUSED(gdisp), int UNUSED(set)) {
1456     // Not implemented; does nothing.
1457     return false;
1458 }
1459 
GGDKDrawSetVisible(GWindow w,int show)1460 static void GGDKDrawSetVisible(GWindow w, int show) {
1461     Log(LOGDEBUG, "0x%p %d", w, show);
1462     GGDKWindow gw = (GGDKWindow)w;
1463     _GGDKDraw_CleanupAutoPaint(gw->display);
1464     if (show) {
1465         if (gdk_window_is_visible(gw->w)) {
1466             Log(LOGDEBUG, "0x%p %d DISCARDED", w, show);
1467             return;
1468         }
1469 #ifdef GDK_WINDOWING_QUARTZ
1470         // Quartz backend fails to send a configure event after showing a window
1471         // But FF expects one.
1472         _GGDKDraw_OnFakedConfigure(gw);
1473 #endif
1474         gdk_window_show(gw->w);
1475         if (gw->restrict_input_to_me && gw->transient_owner == NULL && gw->display->mru_windows->length > 0) {
1476             GGDKDrawSetTransientFor((GWindow)gw, (GWindow) - 1);
1477         }
1478     } else {
1479         GGDKDrawSetTransientFor((GWindow)gw, NULL);
1480         gdk_window_hide(gw->w);
1481     }
1482 }
1483 
GGDKDrawMove(GWindow gw,int32 x,int32 y)1484 static void GGDKDrawMove(GWindow gw, int32 x, int32 y) {
1485     Log(LOGDEBUG, "%p:%s, %d %d", gw, ((GGDKWindow)gw)->window_title, x, y);
1486     _GGDKDraw_CleanupAutoPaint(((GGDKWindow)gw)->display);
1487     gdk_window_move(((GGDKWindow)gw)->w, x, y);
1488     ((GGDKWindow)gw)->is_centered = false;
1489     if (!gw->is_toplevel) {
1490         _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw);
1491     }
1492 }
1493 
GGDKDrawTrueMove(GWindow w,int32 x,int32 y)1494 static void GGDKDrawTrueMove(GWindow w, int32 x, int32 y) {
1495     Log(LOGDEBUG, " ");
1496     GGDKDrawMove(w, x, y);
1497 }
1498 
GGDKDrawResize(GWindow gw,int32 w,int32 h)1499 static void GGDKDrawResize(GWindow gw, int32 w, int32 h) {
1500     Log(LOGDEBUG, "%p:%s, %d %d", gw, ((GGDKWindow)gw)->window_title, w, h);
1501     _GGDKDraw_CleanupAutoPaint(((GGDKWindow)gw)->display);
1502     gdk_window_resize(((GGDKWindow)gw)->w, w, h);
1503     if (gw->is_toplevel && ((GGDKWindow)gw)->is_centered) {
1504         _GGDKDraw_CenterWindowOnScreen((GGDKWindow)gw);
1505     }
1506     if (!gw->is_toplevel) {
1507         _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw);
1508     }
1509 }
1510 
GGDKDrawMoveResize(GWindow gw,int32 x,int32 y,int32 w,int32 h)1511 static void GGDKDrawMoveResize(GWindow gw, int32 x, int32 y, int32 w, int32 h) {
1512     Log(LOGDEBUG, "%p:%s, %d %d %d %d", gw, ((GGDKWindow)gw)->window_title, x, y, w, h);
1513     _GGDKDraw_CleanupAutoPaint(((GGDKWindow)gw)->display);
1514     gdk_window_move_resize(((GGDKWindow)gw)->w, x, y, w, h);
1515     if (!gw->is_toplevel) {
1516         _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw);
1517     }
1518 }
1519 
GGDKDrawRaise(GWindow w)1520 static void GGDKDrawRaise(GWindow w) {
1521     GGDKWindow gw = (GGDKWindow) w;
1522     Log(LOGDEBUG, "%p[%p][%s]", gw, gw->w, gw->window_title);
1523     if (!gw->is_visible) {
1524         Log(LOGINFO, "Discarding raise on hidden window: %p[%p][%s]",
1525             gw, gw->w, gw->window_title);
1526         return;
1527     }
1528 
1529     _GGDKDraw_CleanupAutoPaint(gw->display);
1530     gdk_window_raise(gw->w);
1531     if (!w->is_toplevel) {
1532         _GGDKDraw_FakeConfigureEvent(gw);
1533     }
1534 }
1535 
GGDKDrawRaiseAbove(GWindow gw1,GWindow gw2)1536 static void GGDKDrawRaiseAbove(GWindow gw1, GWindow gw2) {
1537     Log(LOGDEBUG, " ");
1538     _GGDKDraw_CleanupAutoPaint(((GGDKWindow)gw1)->display);
1539     gdk_window_restack(((GGDKWindow)gw1)->w, ((GGDKWindow)gw2)->w, true);
1540     if (!gw1->is_toplevel) {
1541         _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw1);
1542     }
1543     if (!gw2->is_toplevel) {
1544         _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw2);
1545     }
1546 }
1547 
1548 // Only used once in gcontainer - force it to call GDrawRaiseAbove
GGDKDrawIsAbove(GWindow UNUSED (gw1),GWindow UNUSED (gw2))1549 static int GGDKDrawIsAbove(GWindow UNUSED(gw1), GWindow UNUSED(gw2)) {
1550     Log(LOGDEBUG, " ");
1551     return false;
1552 }
1553 
GGDKDrawLower(GWindow gw)1554 static void GGDKDrawLower(GWindow gw) {
1555     Log(LOGDEBUG, " ");
1556     _GGDKDraw_CleanupAutoPaint(((GGDKWindow)gw)->display);
1557     gdk_window_lower(((GGDKWindow)gw)->w);
1558     if (!gw->is_toplevel) {
1559         _GGDKDraw_FakeConfigureEvent((GGDKWindow)gw);
1560     }
1561 }
1562 
1563 // Icon title is ignored.
GGDKDrawSetWindowTitles8(GWindow w,const char * title,const char * UNUSED (icontitle))1564 static void GGDKDrawSetWindowTitles8(GWindow w, const char *title, const char *UNUSED(icontitle)) {
1565     Log(LOGDEBUG, " ");// assert(false);
1566     GGDKWindow gw = (GGDKWindow)w;
1567     free(gw->window_title);
1568     gw->window_title = copy(title);
1569 
1570     if (title != NULL && gw->is_toplevel) {
1571         gdk_window_set_title(gw->w, title);
1572     }
1573 }
1574 
GGDKDrawSetWindowTitles(GWindow gw,const unichar_t * title,const unichar_t * UNUSED (icontitle))1575 static void GGDKDrawSetWindowTitles(GWindow gw, const unichar_t *title, const unichar_t *UNUSED(icontitle)) {
1576     Log(LOGDEBUG, " ");
1577     char *str = u2utf8_copy(title);
1578     if (str != NULL) {
1579         GGDKDrawSetWindowTitles8(gw, str, NULL);
1580         free(str);
1581     }
1582 }
1583 
1584 // Sigh. GDK doesn't provide a way to get the window title...
GGDKDrawGetWindowTitle(GWindow gw)1585 static unichar_t *GGDKDrawGetWindowTitle(GWindow gw) {
1586     Log(LOGDEBUG, " "); // assert(false);
1587     return utf82u_copy(((GGDKWindow)gw)->window_title);
1588 }
1589 
GGDKDrawGetWindowTitle8(GWindow gw)1590 static char *GGDKDrawGetWindowTitle8(GWindow gw) {
1591     Log(LOGDEBUG, " ");
1592     return copy(((GGDKWindow)gw)->window_title);
1593 }
1594 
GGDKDrawSetTransientFor(GWindow transient,GWindow owner)1595 static void GGDKDrawSetTransientFor(GWindow transient, GWindow owner) {
1596     Log(LOGDEBUG, "transient=%p, owner=%p", transient, owner);
1597     GGDKWindow gw = (GGDKWindow) transient, ow = NULL;
1598     GGDKDisplay *gdisp = gw->display;
1599     assert(owner == NULL || gw->is_toplevel);
1600 
1601     if (!gw->is_toplevel) {
1602         return; // Transient child windows?!
1603     }
1604 
1605     if (owner == (GWindow) - 1) {
1606         for (GList_Glib *pw = gdisp->mru_windows->head; pw != NULL; pw = pw->next) {
1607             GGDKWindow tw = (GGDKWindow)pw->data;
1608             if (tw != gw && tw->is_visible) {
1609                 ow = tw;
1610                 break;
1611             }
1612         }
1613     } else if (owner == NULL) {
1614         ow = NULL; // Does this work with GDK?
1615     } else {
1616         ow = (GGDKWindow)owner;
1617     }
1618 
1619     if (gw->transient_owner != NULL) {
1620         for (int i = ((int)gdisp->transient_stack->len) - 1; i >= 0; --i) {
1621             if (gw == (GGDKWindow)gdisp->transient_stack->pdata[i]) {
1622                 g_ptr_array_remove_index(gw->display->transient_stack, i);
1623                 if (gw->restrict_input_to_me) {
1624                     gdisp->restrict_count--;
1625                 }
1626                 break;
1627             }
1628         }
1629     }
1630 
1631     if (ow != NULL) {
1632         gdk_window_set_transient_for(gw->w, ow->w);
1633         gdk_window_set_modal_hint(gw->w, true);
1634         gw->istransient = true;
1635         g_ptr_array_add(gdisp->transient_stack, gw);
1636         if (gw->restrict_input_to_me) {
1637             gdisp->restrict_count++;
1638         }
1639     } else {
1640         gdk_window_set_modal_hint(gw->w, false);
1641         gdk_window_set_transient_for(gw->w, gw->display->groot->w);
1642         gw->istransient = false;
1643     }
1644 
1645     gw->transient_owner = ow;
1646 }
1647 
GGDKDrawGetPointerPosition(GWindow w,GEvent * ret)1648 static void GGDKDrawGetPointerPosition(GWindow w, GEvent *ret) {
1649     Log(LOGDEBUG, " ");
1650     GGDKWindow gw = (GGDKWindow)w;
1651     GdkModifierType mask;
1652     int x, y;
1653 
1654     GdkDevice *pointer = _GGDKDraw_GetPointer(gw->display);
1655     if (pointer == NULL) {
1656         ret->u.mouse.x = 0;
1657         ret->u.mouse.y = 0;
1658         ret->u.mouse.state = 0;
1659         return;
1660     }
1661 
1662     gdk_window_get_device_position(gw->w, pointer, &x, &y, &mask);
1663     ret->u.mouse.x = x;
1664     ret->u.mouse.y = y;
1665     ret->u.mouse.state = _GGDKDraw_GdkModifierToKsm(mask);
1666 }
1667 
GGDKDrawGetPointerWindow(GWindow gw)1668 static GWindow GGDKDrawGetPointerWindow(GWindow gw) {
1669     Log(LOGDEBUG, " ");
1670     GdkDevice *pointer = _GGDKDraw_GetPointer(((GGDKWindow)gw)->display);
1671     GdkWindow *window = gdk_device_get_window_at_position(pointer, NULL, NULL);
1672 
1673     if (window != NULL) {
1674         return (GWindow)g_object_get_data(G_OBJECT(window), "GGDKWindow");
1675     }
1676     return NULL;
1677 }
1678 
GGDKDrawSetCursor(GWindow w,GCursor gcursor)1679 static void GGDKDrawSetCursor(GWindow w, GCursor gcursor) {
1680     Log(LOGDEBUG, " ");
1681     GGDKWindow gw = (GGDKWindow)w;
1682     GdkCursor *cursor = NULL;
1683 
1684     switch (gcursor) {
1685         case ct_default:
1686         case ct_backpointer:
1687         case ct_pointer:
1688             cursor = gdk_cursor_new_from_name(gw->display->display, "default");
1689             break;
1690         case ct_hand:
1691             cursor = gdk_cursor_new_from_name(gw->display->display, "hand");
1692             break;
1693         case ct_question:
1694             cursor = gdk_cursor_new_from_name(gw->display->display, "help");
1695             break;
1696         case ct_cross:
1697             cursor = gdk_cursor_new_from_name(gw->display->display, "crosshair");
1698             break;
1699         case ct_4way:
1700             cursor = gdk_cursor_new_from_name(gw->display->display, "move");
1701             break;
1702         case ct_text:
1703             cursor = gdk_cursor_new_from_name(gw->display->display, "text");
1704             break;
1705         case ct_watch:
1706             cursor = gdk_cursor_new_from_name(gw->display->display, "wait");
1707             break;
1708         case ct_draganddrop:
1709             cursor = gdk_cursor_new_from_name(gw->display->display, "pointer");
1710             break;
1711         case ct_invisible:
1712             return; // There is no *good* reason to make the cursor invisible
1713             break;
1714         default:
1715             Log(LOGDEBUG, "CUSTOM CURSOR! %d", gcursor);
1716     }
1717 
1718     if (gcursor >= ct_user) {
1719         GGDKDisplay *gdisp = gw->display;
1720         gcursor -= ct_user;
1721         if (gcursor < gdisp->cursors->len && gdisp->cursors->pdata[gcursor] != NULL) {
1722             gdk_window_set_cursor(gw->w, (GdkCursor *)gdisp->cursors->pdata[gcursor]);
1723             gw->current_cursor = gcursor;
1724         } else {
1725             Log(LOGWARN, "Invalid cursor value passed: %d", gcursor);
1726         }
1727     } else {
1728         gdk_window_set_cursor(gw->w, cursor);
1729         gw->current_cursor = gcursor;
1730         if (cursor != NULL) {
1731             g_object_unref(cursor);
1732         }
1733     }
1734 }
1735 
GGDKDrawGetCursor(GWindow gw)1736 static GCursor GGDKDrawGetCursor(GWindow gw) {
1737     Log(LOGDEBUG, " ");
1738     return ((GGDKWindow)gw)->current_cursor;
1739 }
1740 
GGDKDrawGetRedirectWindow(GDisplay * UNUSED (gdisp))1741 static GWindow GGDKDrawGetRedirectWindow(GDisplay *UNUSED(gdisp)) {
1742     //Log(LOGDEBUG, " ");
1743     // Not implemented.
1744     return NULL;
1745 }
1746 
GGDKDrawTranslateCoordinates(GWindow from,GWindow to,GPoint * pt)1747 static void GGDKDrawTranslateCoordinates(GWindow from, GWindow to, GPoint *pt) {
1748     //Log(LOGDEBUG, " ");
1749     GGDKWindow gfrom = (GGDKWindow)from, gto = (GGDKWindow)to;
1750     GGDKDisplay *gdisp = gfrom->display;
1751 
1752 
1753     if (gto == gdisp->groot) {
1754         // The actual meaning of this command...
1755         int x, y;
1756         gdk_window_get_root_coords(gfrom->w, pt->x, pt->y, &x, &y);
1757         pt->x = x;
1758         pt->y = y;
1759     } else {
1760         GdkPoint from_root, to_root;
1761         gdk_window_get_origin(gfrom->w, &from_root.x, &from_root.y);
1762         gdk_window_get_origin(gto->w, &to_root.x, &to_root.y);
1763         pt->x = from_root.x - to_root.x  + pt->x;
1764         pt->y = from_root.y - to_root.y + pt->y;
1765     }
1766 }
1767 
1768 
GGDKDrawBeep(GDisplay * gdisp)1769 static void GGDKDrawBeep(GDisplay *gdisp) {
1770     //Log(LOGDEBUG, " ");
1771     gdk_display_beep(((GGDKDisplay *)gdisp)->display);
1772 }
1773 
GGDKDrawScroll(GWindow w,GRect * rect,int32 hor,int32 vert)1774 static void GGDKDrawScroll(GWindow w, GRect *rect, int32 hor, int32 vert) {
1775     //Log(LOGDEBUG, " ");
1776     GGDKWindow gw = (GGDKWindow) w;
1777     GRect temp;
1778 
1779     vert = -vert;
1780     if (rect == NULL) {
1781         temp.x = temp.y = 0;
1782         temp.width = gw->pos.width;
1783         temp.height = gw->pos.height;
1784         rect = &temp;
1785     }
1786 
1787     GDrawRequestExpose(w, rect, false);
1788 }
1789 
1790 
GGDKDrawCreateInputContext(GWindow UNUSED (gw),enum gic_style UNUSED (style))1791 static GIC *GGDKDrawCreateInputContext(GWindow UNUSED(gw), enum gic_style UNUSED(style)) {
1792     Log(LOGDEBUG, " ");
1793     return NULL;
1794 }
1795 
GGDKDrawSetGIC(GWindow UNUSED (gw),GIC * UNUSED (gic),int UNUSED (x),int UNUSED (y))1796 static void GGDKDrawSetGIC(GWindow UNUSED(gw), GIC *UNUSED(gic), int UNUSED(x), int UNUSED(y)) {
1797     Log(LOGDEBUG, " ");
1798 }
1799 
GGDKDrawKeyState(GWindow w,int keysym)1800 static int GGDKDrawKeyState(GWindow w, int keysym) {
1801     //Log(LOGDEBUG, " ");
1802     if (keysym != ' ') {
1803         Log(LOGWARN, "Cannot check state of unsupported character!");
1804         return 0;
1805     }
1806     // Since this function is only used to check the state of the space button
1807     // Dont't bother with a full implementation...
1808     return ((GGDKWindow)w)->display->is_space_pressed;
1809 }
1810 
GGDKDrawGrabSelection(GWindow w,enum selnames sn)1811 static void GGDKDrawGrabSelection(GWindow w, enum selnames sn) {
1812     Log(LOGDEBUG, " ");
1813 
1814     if ((int)sn < 0 || sn >= sn_max) {
1815         return;
1816     }
1817 
1818     GGDKWindow gw = (GGDKWindow)w;
1819     GGDKDisplay *gdisp = gw->display;
1820     GGDKSelectionInfo *sel = &gdisp->selinfo[sn];
1821 
1822     // Send a SelectionClear event to the current owner of the selection
1823     if (sel->owner != NULL && sel->datalist != NULL) {
1824         GEvent e = {0};
1825         e.w = (GWindow)sel->owner;
1826         e.type = et_selclear;
1827         e.u.selclear.sel = sn;
1828         e.native_window = sel->owner->w;
1829         if (sel->owner->eh != NULL) {
1830             _GGDKDraw_CallEHChecked(sel->owner, &e, sel->owner->eh);
1831         }
1832     }
1833     _GGDKDraw_ClearSelData(gdisp, sn);
1834 
1835     gdk_selection_owner_set(gw->w, sel->sel_atom, gdisp->last_event_time, false);
1836     sel->owner = gw;
1837     sel->timestamp = gdisp->last_event_time;
1838 }
1839 
GGDKDrawAddSelectionType(GWindow w,enum selnames sel,char * type,void * data,int32 cnt,int32 unitsize,void * gendata (void *,int32 * len),void freedata (void *))1840 static void GGDKDrawAddSelectionType(GWindow w, enum selnames sel, char *type, void *data, int32 cnt, int32 unitsize,
1841                                      void *gendata(void *, int32 *len), void freedata(void *)) {
1842     Log(LOGDEBUG, " ");
1843 
1844     GGDKWindow gw = (GGDKWindow)w;
1845     GGDKDisplay *gdisp = gw->display;
1846     GdkAtom type_atom = gdk_atom_intern(type, false);
1847     GGDKSelectionData *sd = NULL;
1848     GList_Glib *dl = gdisp->selinfo[sel].datalist;
1849 
1850     if (unitsize != 1 && unitsize != 2 && unitsize != 4) {
1851         GDrawIError("Bad unitsize to GGDKDrawAddSelectionType");
1852         return;
1853     } else if ((int)sel < 0 || sel >= sn_max) {
1854         GDrawIError("Bad selname value");
1855         return;
1856     }
1857 
1858     while (dl != NULL && (sd = ((GGDKSelectionData *)dl->data))->type_atom != type_atom) {
1859         dl = dl->next;
1860     }
1861 
1862     if (dl == NULL) {
1863         sd = calloc(1, sizeof(GGDKSelectionData));
1864         sd->type_atom = type_atom;
1865         gdisp->selinfo[sel].datalist = g_list_append(gdisp->selinfo[sel].datalist, sd);
1866     }
1867 
1868     sd->cnt = cnt;
1869     sd->data = data;
1870     sd->unit_size = unitsize;
1871     sd->gendata = gendata;
1872     sd->freedata = freedata;
1873 
1874 #ifdef GDK_WINDOWING_QUARTZ
1875     if (sel == sn_clipboard && type_atom == gdk_atom_intern_static_string("UTF8_STRING")) {
1876         char *data = sd->data;
1877         if (sd->gendata) {
1878             int len;
1879             data = sd->gendata(sd->data, &len);
1880         }
1881         _GGDKDrawCocoa_SetClipboardText(data);
1882         if (sd->gendata) {
1883             free(data);
1884         }
1885     }
1886 #endif
1887 }
1888 
GGDKDrawRequestSelection(GWindow w,enum selnames sn,char * typename,int32 * len)1889 static void *GGDKDrawRequestSelection(GWindow w, enum selnames sn, char *typename, int32 *len) {
1890     GGDKWindow gw = (GGDKWindow)w;
1891     GGDKDisplay *gdisp = gw->display;
1892     GdkAtom type_atom = gdk_atom_intern(typename, false);
1893     void *ret = NULL;
1894 
1895     if (len != NULL) {
1896         *len = 0;
1897     }
1898 
1899     if ((int)sn < 0 || sn >= sn_max || gw->is_waiting_for_selection || gw->is_dying) {
1900         return NULL;
1901     }
1902 
1903 #ifdef GDK_WINDOWING_QUARTZ
1904     if (sn == sn_clipboard && type_atom == gdk_atom_intern_static_string("UTF8_STRING")) {
1905         ret = _GGDKDrawCocoa_GetClipboardText();
1906         if (ret && len) {
1907             *len = strlen(ret);
1908         }
1909         return ret;
1910     }
1911 #endif
1912 
1913     // If we own the selection, get the data ourselves...
1914     if (gdisp->selinfo[sn].owner != NULL) {
1915         GList_Glib *ptr = gdisp->selinfo[sn].datalist;
1916         while (ptr != NULL) {
1917             GGDKSelectionData *sd = (GGDKSelectionData *)ptr->data;
1918             if (sd->type_atom == type_atom) {
1919                 if (sd->gendata != NULL) {
1920                     ret = (sd->gendata)(sd->data, len);
1921                     if (len != NULL) {
1922                         *len *= sd->unit_size;
1923                     }
1924                 } else {
1925                     int sz = sd->unit_size * sd->cnt;
1926                     ret = calloc(sz + 4, 1);
1927                     if (ret) {
1928                         memcpy(ret, sd->data, sz);
1929                         if (len != NULL) {
1930                             *len = sz;
1931                         }
1932                     }
1933                 }
1934                 return ret;
1935             }
1936             ptr = ptr->next;
1937         }
1938     }
1939 
1940 #ifndef GDK_WINDOWING_QUARTZ
1941     // Otherwise we have to ask the owner for the data.
1942     gdk_selection_convert(gw->w, gdisp->selinfo[sn].sel_atom, type_atom, gdisp->last_event_time);
1943 
1944     // On Windows, gdk_selection_convert is synchronous. Avoid the fluff of waiting
1945     // which also can cause segfault if a window gets destroyed in the meantime...
1946 #ifdef GDK_WINDOWING_X11
1947     gw->is_waiting_for_selection = true;
1948     gw->is_notified_of_selection = false;
1949 
1950     GTimer_GTK *timer = g_timer_new();
1951     g_timer_start(timer);
1952 
1953     while (gw->is_waiting_for_selection) {
1954         if (g_timer_elapsed(timer, NULL) >= (double)gdisp->sel_notify_timeout) {
1955             break;
1956         }
1957         GGDKDrawProcessPendingEvents((GDisplay *)gdisp);
1958     }
1959     g_timer_destroy(timer);
1960 
1961     if (gw->is_notified_of_selection) {
1962 #endif
1963         guchar *data;
1964         GdkAtom received_type;
1965         gint received_format;
1966         gint rlen = gdk_selection_property_get(gw->w, &data, &received_type, &received_format);
1967         if (data != NULL) {
1968             ret = calloc(rlen + 4, 1);
1969             if (ret) {
1970                 memcpy(ret, data, rlen);
1971                 if (len) {
1972                     *len = rlen;
1973                 }
1974             }
1975             g_free(data);
1976         }
1977 #ifdef GDK_WINDOWING_X11
1978     }
1979 
1980     gw->is_waiting_for_selection = false;
1981     gw->is_notified_of_selection = false;
1982 #endif // GDK_WINDOWING_X11
1983 #endif // GDK_WINDOWING_QUARTZ
1984 
1985     return ret;
1986 }
1987 
GGDKDrawSelectionHasType(GWindow w,enum selnames sn,char * typename)1988 static int GGDKDrawSelectionHasType(GWindow w, enum selnames sn, char *typename) {
1989     Log(LOGDEBUG, " ");
1990 
1991     GGDKWindow gw = (GGDKWindow)w;
1992     if (gw->is_dying || gw->is_waiting_for_selection || (int)sn < 0 || sn >= sn_max) {
1993         return false;
1994     }
1995 
1996     GGDKDisplay *gdisp = gw->display;
1997     GdkAtom sel_type = gdk_atom_intern(typename, false);
1998 
1999     // Check if we own it
2000     if (gdisp->selinfo[sn].owner != NULL) {
2001         GList_Glib *ptr = gdisp->selinfo[sn].datalist;
2002         while (ptr != NULL) {
2003             if (((GGDKSelectionData *)ptr->data)->type_atom == sel_type) {
2004                 return true;
2005             }
2006             ptr = ptr->next;
2007         }
2008         return false;
2009     }
2010 
2011 #ifndef GDK_WINDOWING_QUARTZ
2012     // Else query
2013     if (gdisp->seltypes.timestamp != gdisp->last_event_time || gdisp->seltypes.sel_atom != gdisp->selinfo[sn].sel_atom) {
2014         free(gdisp->seltypes.types);
2015         gdisp->seltypes.types = NULL;
2016         gdisp->seltypes.sel_atom = gdisp->selinfo[sn].sel_atom;
2017         gdisp->seltypes.types = (GdkAtom *) GGDKDrawRequestSelection(w, sn, "TARGETS", &gdisp->seltypes.len);
2018         gdisp->seltypes.len /= sizeof(GdkAtom *);
2019     }
2020 
2021     if (gdisp->seltypes.types != NULL) {
2022         for (int i = 0; i < gdisp->seltypes.len; i++) {
2023             if (gdisp->seltypes.types[i] == sel_type) {
2024                 return true;
2025             }
2026         }
2027     }
2028     return false;
2029 #else
2030     return sel_type == gdk_atom_intern_static_string("UTF8_STRING");
2031 #endif
2032 }
2033 
GGDKDrawBindSelection(GDisplay * disp,enum selnames sn,char * atomname)2034 static void GGDKDrawBindSelection(GDisplay *disp, enum selnames sn, char *atomname) {
2035     Log(LOGDEBUG, " ");
2036     GGDKDisplay *gdisp = (GGDKDisplay *) disp;
2037     if ((int)sn >= 0 && sn < sn_max) {
2038         gdisp->selinfo[sn].sel_atom = gdk_atom_intern(atomname, false);
2039     }
2040 }
2041 
GGDKDrawSelectionHasOwner(GDisplay * disp,enum selnames sn)2042 static int GGDKDrawSelectionHasOwner(GDisplay *disp, enum selnames sn) {
2043     Log(LOGDEBUG, " ");
2044 
2045     if ((int)sn < 0 || sn >= sn_max) {
2046         return false;
2047     }
2048 
2049     GGDKDisplay *gdisp = (GGDKDisplay *)disp;
2050     // Check if we own it
2051     if (gdisp->selinfo[sn].owner != NULL) {
2052         return true;
2053     }
2054 
2055     // Else query. Note, we cannot use gdk_selection_owner_get, as this only works for
2056     // windows that GDK created. This code is untested.
2057     void *data = GGDKDrawRequestSelection((GWindow)gdisp->groot, sn, "TIMESTAMP", NULL);
2058     if (data != NULL) {
2059         free(data);
2060         return true;
2061     }
2062     return false;
2063 }
2064 
GGDKDrawPointerUngrab(GDisplay * gdisp)2065 static void GGDKDrawPointerUngrab(GDisplay *gdisp) {
2066     Log(LOGDEBUG, " ");
2067 #ifndef GGDKDRAW_GDK_3_20
2068     GdkDevice *pointer = _GGDKDraw_GetPointer((GGDKDisplay *)gdisp);
2069     if (pointer == NULL) {
2070         return;
2071     }
2072 
2073     gdk_device_ungrab(pointer, GDK_CURRENT_TIME);
2074 #else
2075     GdkSeat *seat = gdk_display_get_default_seat(((GGDKDisplay *)gdisp)->display);
2076     if (seat == NULL) {
2077         return;
2078     }
2079 
2080     gdk_seat_ungrab(seat);
2081 #endif
2082 }
2083 
GGDKDrawPointerGrab(GWindow w)2084 static void GGDKDrawPointerGrab(GWindow w) {
2085     Log(LOGDEBUG, " ");
2086     GGDKWindow gw = (GGDKWindow)w;
2087 #ifndef GGDKDRAW_GDK_3_20
2088     GdkDevice *pointer = _GGDKDraw_GetPointer(gw->display);
2089     if (pointer == NULL) {
2090         return;
2091     }
2092 
2093     gdk_device_grab(pointer, gw->w,
2094                     GDK_OWNERSHIP_NONE,
2095                     false,
2096                     GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK,
2097                     NULL, GDK_CURRENT_TIME);
2098 #else
2099     GdkSeat *seat = gdk_display_get_default_seat(gw->display->display);
2100     if (seat == NULL) {
2101         return;
2102     }
2103 
2104     gdk_seat_grab(seat, gw->w,
2105                   GDK_SEAT_CAPABILITY_ALL_POINTING,
2106                   false, NULL, NULL, NULL, NULL);
2107 #endif
2108 }
2109 
GGDKDrawRequestExpose(GWindow w,GRect * rect,int UNUSED (doclear))2110 static void GGDKDrawRequestExpose(GWindow w, GRect *rect, int UNUSED(doclear)) {
2111     Log(LOGDEBUG, "%p [%s]", w, ((GGDKWindow)w)->window_title);
2112 
2113     GGDKWindow gw = (GGDKWindow) w;
2114     GdkRectangle clip;
2115 
2116     if (!gw->is_visible || _GGDKDraw_WindowOrParentsDying(gw)) {
2117         return;
2118     }
2119     if (rect == NULL) {
2120         clip.x = clip.y = 0;
2121         clip.width = gw->pos.width;
2122         clip.height = gw->pos.height;
2123     } else {
2124         clip.x = rect->x;
2125         clip.y = rect->y;
2126         clip.width = rect->width;
2127         clip.height = rect->height;
2128 
2129         if (rect->x < 0 || rect->y < 0 || rect->x + rect->width > gw->pos.width ||
2130                 rect->y + rect->height > gw->pos.height) {
2131 
2132             if (clip.x < 0) {
2133                 clip.width += clip.x;
2134                 clip.x = 0;
2135             }
2136             if (clip.y < 0) {
2137                 clip.height += clip.y;
2138                 clip.y = 0;
2139             }
2140             if (clip.x + clip.width > gw->pos.width) {
2141                 clip.width = gw->pos.width - clip.x;
2142             }
2143             if (clip.y + clip.height > gw->pos.height) {
2144                 clip.height = gw->pos.height - clip.y;
2145             }
2146             if (clip.height <= 0 || clip.width <= 0) {
2147                 return;
2148             }
2149         }
2150     }
2151 
2152     gdk_window_invalidate_rect(gw->w, &clip, false);
2153 }
2154 
GGDKDrawForceUpdate(GWindow gw)2155 static void GGDKDrawForceUpdate(GWindow gw) {
2156     //Log(LOGDEBUG, " ");
2157 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2158     gdk_window_process_updates(((GGDKWindow)gw)->w, true);
2159 G_GNUC_END_IGNORE_DEPRECATIONS
2160 }
2161 
GGDKDrawSync(GDisplay * gdisp)2162 static void GGDKDrawSync(GDisplay *gdisp) {
2163     //Log(LOGDEBUG, " ");
2164     gdk_display_sync(((GGDKDisplay *)gdisp)->display);
2165 }
2166 
GGDKDrawSkipMouseMoveEvents(GWindow UNUSED (gw),GEvent * UNUSED (gevent))2167 static void GGDKDrawSkipMouseMoveEvents(GWindow UNUSED(gw), GEvent *UNUSED(gevent)) {
2168     //Log(LOGDEBUG, " ");
2169     // Not implemented, not needed.
2170 }
2171 
GGDKDrawProcessPendingEvents(GDisplay * gdisp)2172 static void GGDKDrawProcessPendingEvents(GDisplay *gdisp) {
2173     //Log(LOGDEBUG, " ");
2174     GMainContext *ctx = g_main_loop_get_context(((GGDKDisplay *)gdisp)->main_loop);
2175     if (ctx != NULL) {
2176         _GGDKDraw_CleanupAutoPaint((GGDKDisplay *)gdisp);
2177         while (g_main_context_iteration(ctx, false));
2178     }
2179 }
2180 
GGDKDrawProcessWindowEvents(GWindow w)2181 static void GGDKDrawProcessWindowEvents(GWindow w) {
2182     Log(LOGWARN, "This function SHOULD NOT BE CALLED! Will likely not do as expected! Window: %p", w);
2183 
2184     if (w != NULL)  {
2185         GGDKDrawProcessPendingEvents(w->display);
2186     }
2187 }
2188 
GGDKDrawProcessOneEvent(GDisplay * gdisp)2189 static void GGDKDrawProcessOneEvent(GDisplay *gdisp) {
2190     //Log(LOGDEBUG, " ");
2191     GMainContext *ctx = g_main_loop_get_context(((GGDKDisplay *)gdisp)->main_loop);
2192     if (ctx != NULL) {
2193         _GGDKDraw_CleanupAutoPaint((GGDKDisplay *)gdisp);
2194         g_main_context_iteration(ctx, true);
2195     }
2196 }
2197 
GGDKDrawEventLoop(GDisplay * gdisp)2198 static void GGDKDrawEventLoop(GDisplay *gdisp) {
2199     Log(LOGDEBUG, " ");
2200     GMainContext *ctx = g_main_loop_get_context(((GGDKDisplay *)gdisp)->main_loop);
2201     if (ctx != NULL) {
2202         _GGDKDraw_CleanupAutoPaint((GGDKDisplay *)gdisp);
2203         do {
2204             while (((GGDKDisplay *)gdisp)->top_window_count > 0) {
2205                 g_main_context_iteration(ctx, true);
2206                 if ((gdisp->err_flag) && (gdisp->err_report)) {
2207                     GDrawIErrorRun("%s", gdisp->err_report);
2208                 }
2209                 if (gdisp->err_report) {
2210                     free(gdisp->err_report);
2211                     gdisp->err_report = NULL;
2212                 }
2213             }
2214             GGDKDrawSync(gdisp);
2215             GGDKDrawProcessPendingEvents(gdisp);
2216             GGDKDrawSync(gdisp);
2217         } while (((GGDKDisplay *)gdisp)->top_window_count > 0 || g_main_context_pending(ctx));
2218     }
2219 }
2220 
GGDKDrawPostEvent(GEvent * e)2221 static void GGDKDrawPostEvent(GEvent *e) {
2222     //Log(LOGDEBUG, " ");
2223     GGDKWindow gw = (GGDKWindow)(e->w);
2224     e->native_window = gw->w;
2225     _GGDKDraw_CallEHChecked(gw, e, gw->eh);
2226 }
2227 
GGDKDrawPostDragEvent(GWindow w,GEvent * mouse,enum event_type et)2228 static void GGDKDrawPostDragEvent(GWindow w, GEvent *mouse, enum event_type et) {
2229     Log(LOGDEBUG, " ");
2230     GGDKWindow gw = (GGDKWindow)w, cw;
2231     GGDKDisplay *gdisp = gw->display;
2232     int x, y;
2233 
2234     // If the cursor hasn't moved much, don't bother to send a drag event
2235     x = abs(mouse->u.mouse.x - gdisp->last_dd.x);
2236     y = abs(mouse->u.mouse.y - gdisp->last_dd.y);
2237     if (x + y < 4 && et == et_drag) {
2238         return;
2239     }
2240 
2241     cw = (GGDKWindow)GGDKDrawGetPointerWindow(w);
2242 
2243     if (gdisp->last_dd.w != cw) {
2244         if (gdisp->last_dd.w != NULL) {
2245             GEvent e = {0};
2246             e.type = et_dragout;
2247             e.u.drag_drop.x = gdisp->last_dd.rx;
2248             e.u.drag_drop.y = gdisp->last_dd.ry;
2249             e.w = (GWindow)gdisp->last_dd.w;
2250             e.native_window = e.w->native_window;
2251 
2252             _GGDKDraw_CallEHChecked(gdisp->last_dd.w, &e, gdisp->last_dd.w->eh);
2253         } else {
2254             /* Send foreign dragout message */
2255         }
2256         gdisp->last_dd.w = NULL;
2257     }
2258 
2259     GEvent e = {0};
2260     // Are we still within the original window?
2261     if (cw == gw) {
2262         e.type = et;
2263         e.w = w;
2264         e.native_window = w->native_window;
2265         x = e.u.drag_drop.x = mouse->u.mouse.x;
2266         y = e.u.drag_drop.y = mouse->u.mouse.y;
2267         _GGDKDraw_CallEHChecked(gw, &e, gw->eh);
2268     } else if (cw != NULL) {
2269         GPoint pt = {.x = mouse->u.mouse.x, .y = mouse->u.mouse.y};
2270         GGDKDrawTranslateCoordinates(w, (GWindow)cw, &pt);
2271         x = pt.x;
2272         y = pt.y;
2273 
2274         e.type = et;
2275         e.u.drag_drop.x = x;
2276         e.u.drag_drop.y = y;
2277         e.w = (GWindow)cw;
2278         e.native_window = cw->w;
2279         _GGDKDraw_CallEHChecked(cw, &e, cw->eh);
2280     } else {
2281         // Foreign window
2282     }
2283 
2284     if (et != et_drop) {
2285         gdisp->last_dd.w = cw;
2286         gdisp->last_dd.x = mouse->u.mouse.x;
2287         gdisp->last_dd.y = mouse->u.mouse.y;
2288         gdisp->last_dd.rx = x;
2289         gdisp->last_dd.ry = y;
2290     } else {
2291         gdisp->last_dd.w = NULL;
2292     }
2293 }
2294 
GGDKDrawRequestDeviceEvents(GWindow w,int devcnt,struct gdeveventmask * de)2295 static int GGDKDrawRequestDeviceEvents(GWindow w, int devcnt, struct gdeveventmask *de) {
2296     Log(LOGDEBUG, " ");
2297     return 0; //Not sure how to handle... For tablets...
2298 }
2299 
GGDKDrawRequestTimer(GWindow w,int32 time_from_now,int32 frequency,void * userdata)2300 static GTimer *GGDKDrawRequestTimer(GWindow w, int32 time_from_now, int32 frequency, void *userdata) {
2301     //Log(LOGDEBUG, " ");
2302     GGDKTimer *timer = calloc(1, sizeof(GGDKTimer));
2303     GGDKWindow gw = (GGDKWindow)w;
2304     if (timer == NULL)  {
2305         return NULL;
2306     }
2307 
2308     gw->display->timers = g_list_append(gw->display->timers, timer);
2309 
2310     timer->owner = w;
2311     timer->repeat_time = frequency;
2312     timer->userdata = userdata;
2313     timer->active = true;
2314     timer->has_differing_repeat_time = (time_from_now != frequency);
2315     timer->glib_timeout_id = g_timeout_add(time_from_now, _GGDKDraw_ProcessTimerEvent, timer);
2316 
2317     GGDKDRAW_ADDREF(timer);
2318     return (GTimer *)timer;
2319 }
2320 
GGDKDrawCancelTimer(GTimer * timer)2321 static void GGDKDrawCancelTimer(GTimer *timer) {
2322     //Log(LOGDEBUG, " ");
2323     GGDKTimer *gtimer = (GGDKTimer *)timer;
2324     gtimer->active = false;
2325     GGDKDRAW_DECREF(gtimer, _GGDKDraw_OnTimerDestroyed);
2326 }
2327 
2328 // Our function VTable
2329 static struct displayfuncs gdkfuncs = {
2330     GGDKDrawInit,
2331 
2332     GGDKDrawSetDefaultIcon,
2333 
2334     GGDKDrawCreateTopWindow,
2335     GGDKDrawCreateSubWindow,
2336     GGDKDrawCreatePixmap,
2337     GGDKDrawCreateBitmap,
2338     GGDKDrawCreateCursor,
2339     GGDKDrawDestroyWindow,
2340     GGDKDrawDestroyCursor,
2341     GGDKDrawNativeWindowExists, //Not sure what this is meant to do...
2342     GGDKDrawSetZoom,
2343     GGDKDrawSetWindowBackground,
2344     GGDKDrawSetDither,
2345 
2346     GGDKDrawSetVisible,
2347     GGDKDrawMove,
2348     GGDKDrawTrueMove,
2349     GGDKDrawResize,
2350     GGDKDrawMoveResize,
2351     GGDKDrawRaise,
2352     GGDKDrawRaiseAbove,
2353     GGDKDrawIsAbove,
2354     GGDKDrawLower,
2355     GGDKDrawSetWindowTitles,
2356     GGDKDrawSetWindowTitles8,
2357     GGDKDrawGetWindowTitle,
2358     GGDKDrawGetWindowTitle8,
2359     GGDKDrawSetTransientFor,
2360     GGDKDrawGetPointerPosition,
2361     GGDKDrawGetPointerWindow,
2362     GGDKDrawSetCursor,
2363     GGDKDrawGetCursor,
2364     GGDKDrawGetRedirectWindow,
2365     GGDKDrawTranslateCoordinates,
2366 
2367     GGDKDrawBeep,
2368 
2369     GGDKDrawPushClip,
2370     GGDKDrawPopClip,
2371 
2372     GGDKDrawSetDifferenceMode,
2373 
2374     GGDKDrawClear,
2375     GGDKDrawDrawLine,
2376     GGDKDrawDrawArrow,
2377     GGDKDrawDrawRect,
2378     GGDKDrawFillRect,
2379     GGDKDrawFillRoundRect,
2380     GGDKDrawDrawEllipse,
2381     GGDKDrawFillEllipse,
2382     GGDKDrawDrawArc,
2383     GGDKDrawDrawPoly,
2384     GGDKDrawFillPoly,
2385     GGDKDrawScroll,
2386 
2387     GGDKDrawDrawImage,
2388     GGDKDrawDrawGlyph,
2389     GGDKDrawDrawImageMagnified,
2390     GGDKDrawDrawPixmap,
2391 
2392     GGDKDrawCreateInputContext,
2393     GGDKDrawSetGIC,
2394     GGDKDrawKeyState,
2395 
2396     GGDKDrawGrabSelection,
2397     GGDKDrawAddSelectionType,
2398     GGDKDrawRequestSelection,
2399     GGDKDrawSelectionHasType,
2400     GGDKDrawBindSelection,
2401     GGDKDrawSelectionHasOwner,
2402 
2403     GGDKDrawPointerUngrab,
2404     GGDKDrawPointerGrab,
2405     GGDKDrawRequestExpose,
2406     GGDKDrawForceUpdate,
2407     GGDKDrawSync,
2408     GGDKDrawSkipMouseMoveEvents,
2409     GGDKDrawProcessPendingEvents,
2410     GGDKDrawProcessWindowEvents,
2411     GGDKDrawProcessOneEvent,
2412     GGDKDrawEventLoop,
2413     GGDKDrawPostEvent,
2414     GGDKDrawPostDragEvent,
2415     GGDKDrawRequestDeviceEvents,
2416 
2417     GGDKDrawRequestTimer,
2418     GGDKDrawCancelTimer,
2419 
2420     GGDKDrawGetFontMetrics,
2421 
2422     GGDKDrawHasCairo,
2423     GGDKDrawPathStartNew,
2424     GGDKDrawPathClose,
2425     GGDKDrawPathMoveTo,
2426     GGDKDrawPathLineTo,
2427     GGDKDrawPathCurveTo,
2428     GGDKDrawPathStroke,
2429     GGDKDrawPathFill,
2430     GGDKDrawPathFillAndStroke, // Currently unused
2431 
2432     GGDKDrawLayoutInit,
2433     GGDKDrawLayoutDraw,
2434     GGDKDrawLayoutIndexToPos,
2435     GGDKDrawLayoutXYToIndex,
2436     GGDKDrawLayoutExtents,
2437     GGDKDrawLayoutSetWidth,
2438     GGDKDrawLayoutLineCount,
2439     GGDKDrawLayoutLineStart,
2440     GGDKDrawStartNewSubPath,
2441     GGDKDrawFillRuleSetWinding,
2442 
2443     GGDKDrawDoText8,
2444 
2445     GGDKDrawPushClipOnly,
2446     GGDKDrawClipPreserve
2447 };
2448 
2449 // Protected member functions (package-level)
2450 
_GGDKDraw_CreateDisplay(char * displayname,char * UNUSED (programname))2451 GDisplay *_GGDKDraw_CreateDisplay(char *displayname, char *UNUSED(programname)) {
2452     GGDKDisplay *gdisp;
2453     GdkDisplay *display;
2454     GGDKWindow groot;
2455 
2456     LogInit();
2457 
2458     if (displayname == NULL) {
2459         display = gdk_display_get_default();
2460     } else {
2461         display = gdk_display_open(displayname);
2462     }
2463 
2464     if (display == NULL) {
2465         return NULL;
2466     }
2467 
2468     gdisp = (GGDKDisplay *)calloc(1, sizeof(GGDKDisplay));
2469     if (gdisp == NULL) {
2470         return NULL;
2471     }
2472 
2473     // cursors.c creates ~41.
2474     gdisp->cursors = g_ptr_array_sized_new(50);
2475     gdisp->windows = g_hash_table_new(g_direct_hash, g_direct_equal);
2476     gdisp->mru_windows = g_queue_new();
2477     if (gdisp->windows == NULL || gdisp->mru_windows == NULL) {
2478         if (gdisp->windows) {
2479             g_hash_table_destroy(gdisp->windows);
2480         }
2481         free(gdisp);
2482         return NULL;
2483     }
2484     gdisp->transient_stack = g_ptr_array_sized_new(5);
2485 
2486     gdisp->funcs = &gdkfuncs;
2487     gdisp->display = display;
2488     gdisp->screen = gdk_display_get_default_screen(display);
2489     gdisp->root = gdk_screen_get_root_window(gdisp->screen);
2490     gdisp->res = gdk_screen_get_resolution(gdisp->screen);
2491     gdisp->pangoc_context = gdk_pango_context_get_for_screen(gdisp->screen);
2492     if (gdisp->res <= 0) {
2493         gdisp->res = 96;
2494     }
2495 #ifdef GDK_WINDOWING_QUARTZ
2496     if (gdisp->res <= 72) {
2497         gdisp->res = 96;
2498     }
2499 #endif
2500 
2501     gdisp->main_loop = g_main_loop_new(NULL, true);
2502     gdisp->bs.double_time = 200;
2503     gdisp->bs.double_wiggle = 3;
2504     gdisp->sel_notify_timeout = 2; // 2 second timeout
2505 
2506     //Initialise selection atoms
2507     gdisp->selinfo[sn_primary].sel_atom = gdk_atom_intern_static_string("PRIMARY");
2508     gdisp->selinfo[sn_clipboard].sel_atom = gdk_atom_intern_static_string("CLIPBOARD");
2509     gdisp->selinfo[sn_drag_and_drop].sel_atom = gdk_atom_intern_static_string("DRAG_AND_DROP");
2510     gdisp->selinfo[sn_user1].sel_atom = gdk_atom_intern_static_string("PRIMARY");
2511     gdisp->selinfo[sn_user2].sel_atom = gdk_atom_intern_static_string("PRIMARY");
2512 
2513     bool tbf = false, mxc = false;
2514     int user_res = 0;
2515     GResStruct res[] = {
2516         {.resname = "MultiClickTime", .type = rt_int, .val = &gdisp->bs.double_time},
2517         {.resname = "MultiClickWiggle", .type = rt_int, .val = &gdisp->bs.double_wiggle},
2518         {.resname = "SelectionNotifyTimeout", .type = rt_int, .val = &gdisp->sel_notify_timeout},
2519         {.resname = "TwoButtonFixup", .type = rt_bool, .val = &tbf},
2520         {.resname = "MacOSXCmd", .type = rt_bool, .val = &mxc},
2521         {.resname = "ScreenResolution", .type = rt_int, .val = &user_res},
2522         {.resname = NULL},
2523     };
2524     GResourceFind(res, NULL);
2525     gdisp->twobmouse_win = tbf;
2526     gdisp->macosx_cmd = mxc;
2527 
2528     // Now finalise the resolution
2529     if (user_res > 0) {
2530         gdisp->res = user_res;
2531     }
2532     pango_cairo_context_set_resolution(gdisp->pangoc_context, gdisp->res);
2533 
2534     groot = (GGDKWindow)calloc(1, sizeof(struct ggdkwindow));
2535     if (groot == NULL) {
2536         g_object_unref(gdisp->pangoc_context);
2537         g_queue_free(gdisp->mru_windows);
2538         g_hash_table_destroy(gdisp->windows);
2539         free(gdisp);
2540         return NULL;
2541     }
2542 
2543     gdisp->groot = groot;
2544     groot->ggc = _GGDKDraw_NewGGC();
2545     groot->display = gdisp;
2546     groot->w = gdisp->root;
2547 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2548     groot->pos.width = gdk_screen_get_width(gdisp->screen);
2549     groot->pos.height = gdk_screen_get_height(gdisp->screen);
2550 G_GNUC_END_IGNORE_DEPRECATIONS
2551     groot->is_toplevel = true;
2552     groot->is_visible = true;
2553     g_object_set_data(G_OBJECT(gdisp->root), "GGDKWindow", groot);
2554 
2555     gdisp->def_background = GResourceFindColor("Background", COLOR_CREATE(0xf5, 0xff, 0xfa));
2556     gdisp->def_foreground = GResourceFindColor("Foreground", COLOR_CREATE(0x00, 0x00, 0x00));
2557     if (GResourceFindBool("Synchronize", false)) {
2558         gdk_display_sync(gdisp->display);
2559     }
2560 
2561     (gdisp->funcs->init)((GDisplay *) gdisp);
2562 
2563     gdk_event_handler_set(_GGDKDraw_DispatchEvent, (gpointer)gdisp, NULL);
2564 
2565     _GDraw_InitError((GDisplay *) gdisp);
2566 
2567     //DEBUG
2568     if (getenv("GGDK_DEBUG")) {
2569 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2570         gdk_window_set_debug_updates(true);
2571 G_GNUC_END_IGNORE_DEPRECATIONS
2572     }
2573     return (GDisplay *)gdisp;
2574 }
2575 
_GGDKDraw_DestroyDisplay(GDisplay * disp)2576 void _GGDKDraw_DestroyDisplay(GDisplay *disp) {
2577     GGDKDisplay *gdisp = (GGDKDisplay *)(disp);
2578     guint window_count = g_hash_table_size(gdisp->windows);
2579 
2580     // Indicate we're dying...
2581     gdisp->is_dying = true;
2582 
2583     // Destroy remaining windows
2584     if (window_count > 0) {
2585         Log(LOGINFO, "Windows left allocated - forcibly freeing!");
2586 
2587         // Note that windows are destroyed in random order. When a window is
2588         // destroyed, it decrements its parent reference count, so we need a
2589         // large enough count to keep it around until we're ready to destroy it
2590         GHashTableIter iter;
2591         GGDKWindow gw;
2592         g_hash_table_iter_init(&iter, gdisp->windows);
2593         while (g_hash_table_iter_next(&iter, (void **)&gw, NULL)) {
2594             gw->reference_count = window_count + 2;
2595         }
2596         while (g_hash_table_size(gdisp->windows) > 0) {
2597             g_hash_table_iter_init(&iter, gdisp->windows);
2598             if (g_hash_table_iter_next(&iter, (void **)&gw, NULL)) {
2599                 Log(LOGINFO, "Forcibly destroying window (%p:%s)", gw, gw->window_title);
2600                 assert(gw->reference_count >= 2);
2601                 GGDKDrawDestroyWindow((GWindow)gw);
2602                 _GGDKDraw_OnWindowDestroyed(gw);
2603             }
2604         }
2605     }
2606 
2607     // Destroy root window
2608     _GGDKDraw_OnWindowDestroyed(gdisp->groot);
2609     gdisp->groot = NULL;
2610 
2611     // Destroy the mru window list
2612     g_queue_free(gdisp->mru_windows);
2613     gdisp->mru_windows = NULL;
2614 
2615     // Finally destroy the window table
2616     g_hash_table_destroy(gdisp->windows);
2617     gdisp->windows = NULL;
2618 
2619     g_ptr_array_free(gdisp->transient_stack, true);
2620 
2621     // Destroy held selection types, if any
2622     free(gdisp->seltypes.types);
2623     gdisp->seltypes.types = NULL;
2624 
2625     // Destroy cursors
2626     for (guint i = 0; i < gdisp->cursors->len; i++) {
2627         if (gdisp->cursors->pdata[i] != NULL) {
2628             GGDKDrawDestroyCursor((GDisplay *)gdisp, (GCursor)(ct_user + i));
2629         }
2630     }
2631     g_ptr_array_free(gdisp->cursors, true);
2632     gdisp->cursors = NULL;
2633 
2634     // Destroy any orphaned timers (???)
2635     if (gdisp->timers != NULL) {
2636         Log(LOGWARN, "Orphaned timers present - forcibly freeing!");
2637         while (gdisp->timers != NULL) {
2638             GGDKTimer *timer = (GGDKTimer *)gdisp->timers->data;
2639             timer->reference_count = 1;
2640             GGDKDrawCancelTimer((GTimer *)timer);
2641         }
2642     }
2643 
2644     // Get rid of our pango context
2645     g_object_unref(gdisp->pangoc_context);
2646     gdisp->pangoc_context = NULL;
2647 
2648     // Destroy the fontstate
2649     free(gdisp->fontstate);
2650     gdisp->fontstate = NULL;
2651 
2652     // Close the display
2653     if (gdisp->display != NULL) {
2654         gdisp->display = NULL;
2655     }
2656 
2657     // Unreference the main loop
2658     g_main_loop_unref(gdisp->main_loop);
2659     gdisp->main_loop = NULL;
2660 
2661     // Free the data structure
2662     free(gdisp);
2663     return;
2664 }
2665 
2666 #endif // FONTFORGE_CAN_USE_GDK
2667