1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include <X11/keysym.h>
4 #include <xlib/ui_xim.h> /* ui_xim_display_opened */
5 #include <gdk/gdkx.h>
6 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
7 #include <X11/extensions/XInput2.h>
8 #endif
9 
10 #if GTK_CHECK_VERSION(2, 90, 0)
11 #define gdk_x11_drawable_get_xid(window) gdk_x11_window_get_xid(window)
12 #endif
13 
14 #if 0
15 /* Forcibly enable transparency on gnome-terminal which doesn't support it. */
16 #define FORCE_TRANSPARENCY
17 #endif
18 
19 /* --- static variables --- */
20 
21 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
22 static int is_xinput2;
23 #endif
24 
25 /* --- static functions --- */
26 
toplevel_configure(gpointer data)27 static gboolean toplevel_configure(gpointer data) {
28   VteTerminal *terminal = data;
29 
30   if (PVT(terminal)->screen->window.is_transparent) {
31     XEvent ev;
32 
33     if (!XCheckTypedWindowEvent(disp.display, gdk_x11_drawable_get_xid(gtk_widget_get_window(
34                                                   gtk_widget_get_toplevel(GTK_WIDGET(terminal)))),
35                                 ConfigureNotify, &ev)) {
36       ui_window_set_transparent(&PVT(terminal)->screen->window,
37                                 ui_screen_get_picture_modifier(PVT(terminal)->screen));
38     } else {
39       XPutBackEvent(disp.display, &ev);
40     }
41   }
42 
43   return FALSE;
44 }
45 
46 static void vte_terminal_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
47 
48 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
xi2_get_state(XIModifierState * mods_state,XIButtonState * buttons_state,XIGroupState * group_state)49 static u_int xi2_get_state (XIModifierState *mods_state,
50                             XIButtonState   *buttons_state,
51                             XIGroupState    *group_state) {
52   u_int state = 0;
53 
54   if (mods_state) {
55     state = mods_state->effective;
56   }
57 
58   if (buttons_state) {
59     int count;
60 
61     /*
62      * 5 is always less than buttons_state->mask_len * 8,
63      * so skip to check if 5 <= buttons_state->mask_len * 8.
64      */
65     for (count = 1; count <= 5; count++) {
66       if (XIMaskIsSet (buttons_state->mask, count)) {
67         state |= (Button1Mask << (count - 1));
68       }
69     }
70   }
71 
72   if (group_state) {
73     state |= (group_state->effective) << 13;
74   }
75 
76   return state;
77 }
78 
xievent_to_xevent(XIDeviceEvent * xiev,XEvent * xev)79 static int xievent_to_xevent(XIDeviceEvent *xiev, XEvent *xev) {
80   switch (xiev->evtype) {
81   case XI_KeyPress:
82     xev->xkey.type = KeyPress;
83     xev->xkey.window = xiev->event;
84     xev->xkey.root = xiev->root;
85     xev->xkey.subwindow = xiev->child;
86     xev->xkey.time = xiev->time;
87     xev->xkey.x = xiev->event_x;
88     xev->xkey.y = xiev->event_y;
89     xev->xkey.x_root = xiev->root_x;
90     xev->xkey.y_root = xiev->root_y;
91     xev->xkey.state = xi2_get_state(&xiev->mods, &xiev->buttons, &xiev->group);
92     xev->xkey.keycode = xiev->detail;
93     xev->xkey.same_screen = True;
94     break;
95 
96   case XI_ButtonPress:
97     xev->xbutton.type = ButtonPress;
98     xev->xbutton.window = xiev->event;
99     xev->xbutton.root = xiev->root;
100     xev->xbutton.subwindow = xiev->child;
101     xev->xbutton.time = xiev->time;
102     xev->xbutton.x = xiev->event_x;
103     xev->xbutton.y = xiev->event_y;
104     xev->xbutton.x_root = xiev->root_x;
105     xev->xbutton.y_root = xiev->root_y;
106     xev->xbutton.state = xi2_get_state(&xiev->mods, &xiev->buttons, &xiev->group);
107     xev->xbutton.button = xiev->detail;
108     xev->xbutton.same_screen = True;
109     break;
110 
111   case XI_ButtonRelease:
112     xev->xbutton.type = ButtonRelease;
113     xev->xbutton.window = xiev->event;
114     xev->xbutton.root = xiev->root;
115     xev->xbutton.subwindow = xiev->child;
116     xev->xbutton.time = xiev->time;
117     xev->xbutton.x = xiev->event_x;
118     xev->xbutton.y = xiev->event_y;
119     xev->xbutton.x_root = xiev->root_x;
120     xev->xbutton.y_root = xiev->root_y;
121     xev->xbutton.state = xi2_get_state(&xiev->mods, &xiev->buttons, &xiev->group);
122     xev->xbutton.button = xiev->detail;
123     xev->xbutton.same_screen = True;
124     break;
125 
126   case XI_Motion:
127     xev->xmotion.type = MotionNotify;
128     xev->xmotion.window = xiev->event;
129     xev->xmotion.root = xiev->root;
130     xev->xmotion.subwindow = xiev->child;
131     xev->xmotion.time = xiev->time;
132     xev->xmotion.x = xiev->event_x;
133     xev->xmotion.y = xiev->event_y;
134     xev->xmotion.x_root = xiev->root_x;
135     xev->xmotion.y_root = xiev->root_y;
136     xev->xmotion.state = xi2_get_state(&xiev->mods, &xiev->buttons, &xiev->group);
137     xev->xmotion.is_hint = 0;
138     xev->xmotion.same_screen = True;
139     break;
140 
141 #if 0
142   case XI_FocusIn:
143     xev->xfocus.type = FocusIn;
144     xev->xfocus.window = ((XIEnterEvent*)xiev)->event;
145     xev->xfocus.mode = ((XIEnterEvent*)xiev)->mode;
146     xev->xfocus.detail = ((XIEnterEvent*)xiev)->detail;
147     break;
148 
149   case XI_FocusOut:
150     xev->xfocus.type = FocusOut;
151     xev->xfocus.window = ((XIEnterEvent*)xiev)->event;
152     xev->xfocus.mode = ((XIEnterEvent*)xiev)->mode;
153     xev->xfocus.detail = ((XIEnterEvent*)xiev)->detail;
154     break;
155 #endif
156 
157   default:
158     return 0;
159   }
160 
161 #if 0
162   bl_debug_printf("%s devid %d srcid %d window %d child %d: "
163                   "rootx %0.f rooty %0.f x %0.f y %0.f detail %d serial %d\n",
164                   xiev->evtype == XI_KeyPress ? "KeyPress" :
165                   (xiev->evtype == XI_ButtonPress ? "ButtonPress" :
166                    (xiev->evtype == XI_ButtonRelease ? "ButtonRelease" :
167                     (xiev->evtype == XI_Motion ? "Motion" : "Unknown"))),
168                   xiev->deviceid, xiev->sourceid, xiev->event, xiev->child,
169                   xiev->root_x, xiev->root_y, xiev->event_x, xiev->event_y,
170                   xiev->detail, xev->xany.serial);
171 #endif
172 
173   return 1;
174 }
175 #endif
176 
177 /*
178  * Don't call vt_close_dead_terms() before returning GDK_FILTER_CONTINUE,
179  * because vt_close_dead_terms() will destroy widget in pty_closed and
180  * destroyed widget can be touched right after this function.
181  */
vte_terminal_filter(GdkXEvent * xevent,GdkEvent * event,gpointer data)182 static GdkFilterReturn vte_terminal_filter(GdkXEvent *xevent, GdkEvent *event, /* GDK_NOTHING */
183                                            gpointer data) {
184   u_int count;
185   int is_key_event;
186 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
187   XEvent new_xev;
188   XGenericEventCookie *cookie;
189 #endif
190 
191   if (XFilterEvent((XEvent *)xevent, None)) {
192     return GDK_FILTER_REMOVE;
193   }
194 
195 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
196   if (((XEvent *)xevent)->type == GenericEvent) {
197 #if 0
198     static int xi_opcode;
199 
200     if (xi_opcode == 0) {
201       int xi_error;
202       int xi_event;
203 
204       if(!XQueryExtension(disp.display, "XInputExtension", &xi_opcode, &xi_event, &xi_error)) {
205         return GDK_FILTER_CONTINUE;
206       }
207     }
208 #endif
209 
210     cookie = &((XEvent*)xevent)->xcookie;
211 
212 #if 0
213     if (cookie->extension != xi_opcode) {
214       return GDK_FILTER_CONTINUE;
215     }
216 #endif
217 
218     memcpy(&new_xev, xevent, sizeof(XGenericEventCookie));
219     if (!xievent_to_xevent(cookie->data, &new_xev)) {
220       return GDK_FILTER_CONTINUE;
221     }
222     xevent = &new_xev;
223   }
224 #endif
225 
226   if ((((XEvent *)xevent)->type == KeyPress || ((XEvent *)xevent)->type == KeyRelease)) {
227     is_key_event = 1;
228   } else {
229     is_key_event = 0;
230   }
231 
232   for (count = 0; count < disp.num_roots; count++) {
233     VteTerminal *terminal;
234 
235     if (IS_MLTERM_SCREEN(disp.roots[count])) {
236       terminal = VTE_WIDGET((ui_screen_t *)disp.roots[count]);
237 
238       if (!PVT(terminal)->term) {
239         /* pty is already closed and new pty is not attached yet. */
240         continue;
241       }
242 
243       /*
244        * Key events are ignored if window isn't focused.
245        * This processing is added for key binding of popup menu.
246        */
247       if (is_key_event) {
248         if (((XEvent *)xevent)->xany.window == disp.roots[count]->my_window) {
249           if (PVT(terminal)->screen->copymode == NULL) {
250             vt_term_search_reset_position(PVT(terminal)->term);
251           }
252 
253           if (!disp.roots[count]->is_focused) {
254 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
255             if (xevent == &new_xev) {
256               ((XIDeviceEvent*)cookie->data)->event =
257                 gdk_x11_drawable_get_xid(gtk_widget_get_window(GTK_WIDGET(terminal)));
258             } else
259 #endif
260             {
261               ((XEvent *)xevent)->xany.window =
262                 gdk_x11_drawable_get_xid(gtk_widget_get_window(GTK_WIDGET(terminal)));
263             }
264 
265             return GDK_FILTER_CONTINUE;
266           }
267         }
268       } else if (PVT(terminal)->screen->window.is_transparent &&
269           ((XEvent *)xevent)->type == ConfigureNotify &&
270           ((XEvent *)xevent)->xconfigure.event ==
271               gdk_x11_drawable_get_xid(gtk_widget_get_window(GTK_WIDGET(terminal)))) {
272         /*
273          * If terminal position is changed by adding menu bar or tab,
274          * transparent background is reset.
275          */
276 
277         gint x;
278         gint y;
279 
280         gdk_window_get_position(gtk_widget_get_window(GTK_WIDGET(terminal)), &x, &y);
281 
282         /*
283          * XXX
284          * I don't know why but the height of menu bar has been already
285          * added to the position of terminal before first
286          * GdkConfigureEvent whose x and y is 0 is received.
287          * But (x != xconfigure.x || y != xconfigure.y) is true eventually
288          * and ui_window_set_transparent() is called expectedly.
289          */
290         if (x != ((XEvent *)xevent)->xconfigure.x || y != ((XEvent *)xevent)->xconfigure.y) {
291           ui_window_set_transparent(&PVT(terminal)->screen->window,
292                                     ui_screen_get_picture_modifier(PVT(terminal)->screen));
293         }
294 
295         return GDK_FILTER_CONTINUE;
296       }
297     } else {
298       terminal = NULL;
299     }
300 
301     if (ui_window_receive_event(disp.roots[count], (XEvent *)xevent)) {
302       static pid_t config_menu_pid = 0;
303 
304       if (!terminal || /* SCIM etc window */
305           /* XFilterEvent in ui_window_receive_event. */
306           ((XEvent *)xevent)->xany.window != disp.roots[count]->my_window) {
307         return GDK_FILTER_REMOVE;
308       }
309 
310       /* XXX Hack for waiting for config menu program exiting. */
311       if (PVT(terminal)->term->pty &&
312           config_menu_pid != PVT(terminal)->term->pty->config_menu.pid) {
313         if ((config_menu_pid = PVT(terminal)->term->pty->config_menu.pid)) {
314           vte_reaper_add_child(config_menu_pid);
315         }
316       }
317 
318       if (is_key_event || ((XEvent *)xevent)->type == ButtonPress ||
319           ((XEvent *)xevent)->type == ButtonRelease) {
320         /* Hook key and button events for popup menu. */
321 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
322         if (xevent == &new_xev) {
323           ((XIDeviceEvent*)cookie->data)->event =
324             gdk_x11_drawable_get_xid(gtk_widget_get_window(GTK_WIDGET(terminal)));
325         } else
326 #endif
327         {
328           ((XEvent *)xevent)->xany.window =
329             gdk_x11_drawable_get_xid(gtk_widget_get_window(GTK_WIDGET(terminal)));
330         }
331 
332         return GDK_FILTER_CONTINUE;
333       } else {
334         return GDK_FILTER_REMOVE;
335       }
336     }
337     /*
338      * xconfigure.window:  window whose size, position, border, and/or stacking
339      *                     order was changed.
340      *                      => processed in following.
341      * xconfigure.event:   reconfigured window or to its parent.
342      * (=XAnyEvent.window)  => processed in ui_window_receive_event()
343      */
344     else if (/* terminal && */ ((XEvent *)xevent)->type == ConfigureNotify &&
345              ((XEvent *)xevent)->xconfigure.window == disp.roots[count]->my_window) {
346 #if 0
347       /*
348        * This check causes resize problem in opening tab in
349        * gnome-terminal(2.29.6).
350        */
351       if (((XEvent *)xevent)->xconfigure.width != GTK_WIDGET(terminal)->allocation.width ||
352           ((XEvent *)xevent)->xconfigure.height != GTK_WIDGET(terminal)->allocation.height)
353 #else
354       if (CHAR_WIDTH(terminal) != ui_col_width(PVT(terminal)->screen) ||
355           CHAR_HEIGHT(terminal) != ui_line_height(PVT(terminal)->screen))
356 #endif
357       {
358         /* Window was changed due to change of font size inside mlterm. */
359         GtkAllocation alloc;
360 
361         gtk_widget_get_allocation(GTK_WIDGET(terminal), &alloc);
362         alloc.width = ((XEvent *)xevent)->xconfigure.width;
363         alloc.height = ((XEvent *)xevent)->xconfigure.height;
364 
365 #ifdef __DEBUG
366         bl_debug_printf(BL_DEBUG_TAG " child is resized\n");
367 #endif
368 
369         vte_terminal_size_allocate(GTK_WIDGET(terminal), &alloc);
370       }
371 
372       return GDK_FILTER_REMOVE;
373     }
374   }
375 
376   return GDK_FILTER_CONTINUE;
377 }
378 
379 #ifdef FORCE_TRANSPARENCY
380 #if VTE_CHECK_VERSION(0, 38, 0)
set_rgba_visual(GtkWidget * widget)381 static void set_rgba_visual(GtkWidget *widget) {
382   GdkScreen *screen;
383 
384   if ((screen = gtk_widget_get_screen(widget)) && gdk_screen_is_composited(screen)) {
385     gtk_widget_set_visual(widget, gdk_screen_get_rgba_visual(screen));
386   }
387 }
388 
toplevel_draw(GtkWidget * widget,cairo_t * cr,void * user_data)389 static gboolean toplevel_draw(GtkWidget *widget, cairo_t *cr, void *user_data) {
390   GtkWidget *child = user_data;
391 
392   gint width = gtk_widget_get_allocated_width(child);
393   gint height = gtk_widget_get_allocated_height(child);
394   gint x;
395   gint y;
396 
397   gtk_widget_translate_coordinates(child, widget, 0, 0, &x, &x);
398   cairo_rectangle(cr, x, y, width, height);
399   cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
400   cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
401   cairo_fill(cr);
402 
403   return FALSE;
404 }
405 #endif
406 #endif
407 
vte_terminal_hierarchy_changed(GtkWidget * widget,GtkWidget * old_toplevel,gpointer data)408 static void vte_terminal_hierarchy_changed(GtkWidget *widget, GtkWidget *old_toplevel,
409                                            gpointer data) {
410   GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
411 
412   if (old_toplevel) {
413     g_signal_handlers_disconnect_by_func(old_toplevel, toplevel_configure, widget);
414   }
415 
416   g_signal_connect_swapped(toplevel, "configure-event",
417                            G_CALLBACK(toplevel_configure), VTE_TERMINAL(widget));
418 
419 #ifdef FORCE_TRANSPARENCY
420 #if VTE_CHECK_VERSION(0, 38, 0)
421   /*
422    * Though vte 0.38.0 or later doesn't support rgba visual,
423    * this forcibly enables it.
424    */
425   set_rgba_visual(toplevel);
426 
427   /* roxterm calls gtk_widget_set_app_paintable(TRUE) internally. */
428   if (!gtk_widget_get_app_paintable(toplevel)) {
429     /*
430      * XXX
431      * gtk_widget_set_app_paintable(TRUE) enables transparency, but unexpectedly
432      * makes a menu bar of gnome-terminal(3.24.2) transparent, sigh...
433      */
434     gtk_widget_set_app_paintable(toplevel, TRUE);
435     g_signal_connect(toplevel, "draw", G_CALLBACK(toplevel_draw), widget);
436 
437     if (old_toplevel) {
438       g_signal_handlers_disconnect_by_func(old_toplevel, toplevel_draw, widget);
439     }
440   }
441 #endif
442 #endif
443 }
444 
show_root(ui_display_t * disp,GtkWidget * widget)445 static void show_root(ui_display_t *disp, GtkWidget *widget) {
446   VteTerminal *terminal = VTE_TERMINAL(widget);
447   XID xid = gdk_x11_drawable_get_xid(gtk_widget_get_window(widget));
448 
449   if (disp->gc->gc == DefaultGC(disp->display, disp->screen)) {
450     /*
451      * Replace visual, colormap, depth and gc with those inherited from parent
452      * xid.
453      * In some cases that those of parent xid is not DefaultVisual,
454      * DefaultColormap
455      * and so on (e.g. compiz), BadMatch error can happen.
456      */
457 
458     XWindowAttributes attr;
459     XGCValues gc_value;
460     int depth_is_changed;
461 
462     XGetWindowAttributes(disp->display, xid, &attr);
463     disp->visual = attr.visual;
464     disp->colormap = attr.colormap;
465     depth_is_changed = (disp->depth != attr.depth);
466     disp->depth = attr.depth;
467 
468     /* ui_gc_t using DefaultGC is already created in vte_terminal_class_init */
469     gc_value.foreground = disp->gc->fg_color;
470     gc_value.background = disp->gc->bg_color;
471     gc_value.graphics_exposures = True;
472     disp->gc->gc =
473         XCreateGC(disp->display, xid, GCForeground | GCBackground | GCGraphicsExposures, &gc_value);
474 
475 #ifdef __DEBUG
476     bl_debug_printf(BL_DEBUG_TAG " Visual %x Colormap %x Depth %d\n", disp->visual, disp->colormap,
477                     disp->depth);
478 #endif
479 
480     if (depth_is_changed &&
481         /* see ui_screen_new() */
482         !PVT(terminal)->screen->window.is_transparent &&
483         !PVT(terminal)->screen->pic_file_path) {
484       ui_change_true_transbg_alpha(PVT(terminal)->screen->color_man, main_config.alpha);
485       ui_color_manager_reload(PVT(terminal)->screen->color_man);
486 
487 /* No colors are cached for now. */
488 #if 0
489       ui_color_cache_unload_all();
490 #endif
491     }
492   }
493 
494 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
495   if (is_xinput2) {
496     ui_window_remove_event_mask(&PVT(terminal)->screen->window,
497                                 ButtonPressMask | ButtonMotionMask | ButtonReleaseMask |
498                                 KeyPressMask);
499   }
500 #endif
501 
502   ui_display_show_root(disp, &PVT(terminal)->screen->window, 0, 0, 0, "mlterm", xid);
503 
504 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
505   if (is_xinput2) {
506     XIEventMask mask;
507 
508     /* If XIAllDevices is set and 'a' key is pressed, 'aa' can be output. */
509     mask.deviceid = XIAllMasterDevices;
510     mask.mask_len = XIMaskLen(XI_LASTEVENT);
511     mask.mask = calloc(mask.mask_len, 1);
512     XISetMask(mask.mask, XI_Motion);
513     XISetMask(mask.mask, XI_ButtonPress);
514     XISetMask(mask.mask, XI_ButtonRelease);
515     XISetMask(mask.mask, XI_KeyPress);
516 #if 0
517     XISetMask(mask.mask, XI_KeyRelease);
518     XISetMask(mask.mask, XI_FocusIn);
519     XISetMask(mask.mask, XI_FocusOut);
520 #endif
521     XISelectEvents(disp->display, PVT(terminal)->screen->window.my_window, &mask, 1);
522     XSync(disp->display, False);
523     free(mask.mask);
524   }
525 #endif
526 }
527 
init_display(ui_display_t * disp,VteTerminalClass * vclass)528 static void init_display(ui_display_t *disp, VteTerminalClass *vclass) {
529   GdkDisplay *gdkdisp = gdk_display_get_default();
530   const char *name = gdk_display_get_name(gdkdisp);
531 #if GTK_CHECK_VERSION(2, 90, 0) && defined(GDK_TYPE_X11_DEVICE_MANAGER_XI2)
532   GdkDeviceManager *devman = gdk_display_get_device_manager(gdkdisp);
533 
534   if (G_OBJECT_TYPE(devman) == GDK_TYPE_X11_DEVICE_MANAGER_XI2) {
535     is_xinput2 = 1;
536   }
537 #endif
538 
539   if (name && strstr(name, "wayland")) {
540     GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
541                                                GTK_BUTTONS_CLOSE,
542                                                "%s display is not supported on xlib", name);
543     gtk_dialog_run(GTK_DIALOG(dialog));
544     gtk_widget_destroy(dialog);
545     exit(1);
546   }
547 
548   disp->display = gdk_x11_display_get_xdisplay(gdkdisp);
549   disp->screen = DefaultScreen(disp->display);
550   disp->my_window = DefaultRootWindow(disp->display);
551   disp->visual = DefaultVisual(disp->display, disp->screen);
552   disp->colormap = DefaultColormap(disp->display, disp->screen);
553   disp->depth = DefaultDepth(disp->display, disp->screen);
554   disp->gc = ui_gc_new(disp->display, None);
555   disp->width = DisplayWidth(disp->display, disp->screen);
556   disp->height = DisplayHeight(disp->display, disp->screen);
557   disp->modmap.serial = 0;
558   disp->modmap.map = XGetModifierMapping(disp->display);
559 
560   ui_xim_display_opened(disp->display);
561 
562   gdk_window_add_filter(NULL, vte_terminal_filter, NULL);
563 }
564