1 /*-
2  * Copyright (c) 2004-2008 os-cillation e.K.
3  *
4  * Written by Benedikt Meurer <benny@xfce.org>.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #ifdef HAVE_ERRNO_H
25 #include <errno.h>
26 #endif
27 #ifdef HAVE_LIMITS_H
28 #include <limits.h>
29 #endif
30 #include <stdlib.h>
31 #ifdef HAVE_MEMORY_H
32 #include <memory.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 
38 #include <libxfce4ui/libxfce4ui.h>
39 
40 #ifdef GDK_WINDOWING_X11
41 #include <X11/Xlib.h>
42 #include <X11/Xutil.h>
43 #endif
44 
45 #include <terminal/terminal-app.h>
46 #include <terminal/terminal-config.h>
47 #include <terminal/terminal-preferences.h>
48 #include <terminal/terminal-private.h>
49 #include <terminal/terminal-window.h>
50 #include <terminal/terminal-window-dropdown.h>
51 
52 #define ACCEL_MAP_PATH "xfce4/terminal/accels.scm"
53 #define TERMINAL_DESKTOP_FILE (DATADIR "/applications/xfce4-terminal.desktop")
54 
55 
56 
57 static void     terminal_app_finalize                 (GObject            *object);
58 static void     terminal_app_update_accels            (TerminalApp        *app);
59 static void     terminal_app_update_tab_key_accels    (gpointer            data,
60                                                        const gchar        *accel_path,
61                                                        guint               accel_key,
62                                                        GdkModifierType     accel_mods,
63                                                        gboolean            changed);
64 static void     terminal_app_update_windows_accels    (gpointer            user_data);
65 static gboolean terminal_app_accel_map_load           (gpointer            user_data);
66 static gboolean terminal_app_accel_map_save           (gpointer            user_data);
67 static gboolean terminal_app_unset_urgent_bell        (TerminalWindow     *window,
68                                                        GdkEvent           *event,
69                                                        TerminalApp        *app);
70 static void     terminal_app_new_window               (TerminalWindow     *window,
71                                                        const gchar        *working_directory,
72                                                        TerminalApp        *app);
73 static void     terminal_app_new_window_with_terminal (TerminalWindow     *existing,
74                                                        TerminalScreen     *terminal,
75                                                        gint                x,
76                                                        gint                y,
77                                                        TerminalApp        *app);
78 static void     terminal_app_window_destroyed         (GtkWidget          *window,
79                                                        TerminalApp        *app);
80 static void     terminal_app_save_yourself            (XfceSMClient       *client,
81                                                        TerminalApp        *app);
82 static void     terminal_app_open_window              (TerminalApp        *app,
83                                                        TerminalWindowAttr *attr);
84 
85 
86 
87 struct _TerminalAppClass
88 {
89   GObjectClass parent_class;
90 };
91 
92 struct _TerminalApp
93 {
94   GObject              parent_instance;
95   TerminalPreferences *preferences;
96   XfceSMClient        *session_client;
97   gchar               *initial_menu_bar_accel;
98   GSList              *windows;
99 
100   guint                accel_map_load_id;
101   guint                accel_map_save_id;
102   GtkAccelMap         *accel_map;
103   GSList              *tab_key_accels;
104 };
105 
106 
107 
108 GQuark
terminal_error_quark(void)109 terminal_error_quark (void)
110 {
111   static GQuark quark = 0;
112   if (G_UNLIKELY (quark == 0))
113     quark = g_quark_from_static_string ("terminal-error-quark");
114   return quark;
115 }
116 
117 
118 
G_DEFINE_TYPE(TerminalApp,terminal_app,G_TYPE_OBJECT)119 G_DEFINE_TYPE (TerminalApp, terminal_app, G_TYPE_OBJECT)
120 
121 
122 
123 static void
124 terminal_app_class_init (TerminalAppClass *klass)
125 {
126   GObjectClass *gobject_class;
127 
128   gobject_class = G_OBJECT_CLASS (klass);
129   gobject_class->finalize = terminal_app_finalize;
130 }
131 
132 
133 
134 static void
terminal_app_init(TerminalApp * app)135 terminal_app_init (TerminalApp *app)
136 {
137   app->preferences = terminal_preferences_get ();
138   g_signal_connect_swapped (G_OBJECT (app->preferences), "notify::shortcuts-no-menukey",
139                             G_CALLBACK (terminal_app_update_accels), app);
140   g_signal_connect_swapped (G_OBJECT (app->preferences), "notify::shortcuts-no-helpkey",
141                             G_CALLBACK (terminal_app_update_accels), app);
142 
143   /* remember the original menu bar accel */
144   g_object_get (G_OBJECT (gtk_settings_get_default ()),
145                 "gtk-menu-bar-accel", &app->initial_menu_bar_accel,
146                 NULL);
147 
148   terminal_app_update_accels (app);
149 
150   /* schedule accel map load and update windows when finished */
151   app->accel_map_load_id = gdk_threads_add_idle_full (G_PRIORITY_LOW, terminal_app_accel_map_load, app,
152                                                       terminal_app_update_windows_accels);
153 }
154 
155 
156 
157 static void
terminal_app_finalize(GObject * object)158 terminal_app_finalize (GObject *object)
159 {
160   TerminalApp *app = TERMINAL_APP (object);
161   GSList      *lp;
162 
163   /* stop accel map stuff */
164   if (G_UNLIKELY (app->accel_map_load_id != 0))
165     g_source_remove (app->accel_map_load_id);
166   if (app->accel_map != NULL)
167     g_object_unref (G_OBJECT (app->accel_map));
168   if (G_UNLIKELY (app->accel_map_save_id != 0))
169     {
170       g_source_remove (app->accel_map_save_id);
171       terminal_app_accel_map_save (app);
172     }
173 
174   for (lp = app->windows; lp != NULL; lp = lp->next)
175     {
176       g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), G_CALLBACK (terminal_app_window_destroyed), app);
177       g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), G_CALLBACK (terminal_app_new_window), app);
178       g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), G_CALLBACK (terminal_app_new_window_with_terminal), app);
179       g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), G_CALLBACK (terminal_app_unset_urgent_bell), app);
180       gtk_widget_destroy (GTK_WIDGET (lp->data));
181     }
182   g_slist_free (app->windows);
183 
184   g_signal_handlers_disconnect_by_func (G_OBJECT (app->preferences), G_CALLBACK (terminal_app_update_accels), app);
185   g_object_unref (G_OBJECT (app->preferences));
186 
187   if (app->initial_menu_bar_accel != NULL)
188     g_free (app->initial_menu_bar_accel);
189 
190   if (app->session_client != NULL)
191     g_object_unref (G_OBJECT (app->session_client));
192 
193   for (lp = app->tab_key_accels; lp != NULL; lp = lp->next)
194     g_free (((TerminalAccel*) lp->data)->path);
195   g_slist_free_full (app->tab_key_accels, g_free);
196 
197   (*G_OBJECT_CLASS (terminal_app_parent_class)->finalize) (object);
198 }
199 
200 
201 
202 static void
terminal_app_update_accels(TerminalApp * app)203 terminal_app_update_accels (TerminalApp *app)
204 {
205   gboolean        no_key;
206   GdkModifierType mod;
207   guint           key;
208 
209   g_object_get (G_OBJECT (app->preferences),
210                 "shortcuts-no-menukey", &no_key,
211                 NULL);
212   g_object_set (G_OBJECT (gtk_settings_get_default ()),
213                 "gtk-menu-bar-accel",
214                 no_key ? NULL : app->initial_menu_bar_accel,
215                 NULL);
216   gtk_accelerator_parse (app->initial_menu_bar_accel, &key, &mod);
217   gtk_accel_map_change_entry ("<Actions>/terminal-window/toggle-menubar",
218                               no_key ? 0 : key, no_key ? 0 : mod, TRUE);
219 
220   g_object_get (G_OBJECT (app->preferences),
221                 "shortcuts-no-helpkey", &no_key,
222                 NULL);
223   gtk_accel_map_change_entry ("<Actions>/terminal-window/contents",
224                               no_key ? 0 : GDK_KEY_F1, 0, TRUE);
225 }
226 
227 
228 
229 static void
terminal_app_update_tab_key_accels(gpointer data,const gchar * accel_path,guint accel_key,GdkModifierType accel_mods,gboolean changed)230 terminal_app_update_tab_key_accels (gpointer         data,
231                                     const gchar     *accel_path,
232                                     guint            accel_key,
233                                     GdkModifierType  accel_mods,
234                                     gboolean         changed)
235 {
236   if (accel_key == GDK_KEY_Tab || accel_key == GDK_KEY_ISO_Left_Tab)
237     {
238       TerminalApp *app = TERMINAL_APP (data);
239       TerminalAccel *accel = g_new0 (TerminalAccel, 1);
240 
241       accel->path = g_strdup (g_strrstr (accel_path, "/") + 1); // <Actions>/terminal-window/action
242       accel->mods = accel_mods;
243 
244       app->tab_key_accels = g_slist_prepend (app->tab_key_accels, accel);
245     }
246 }
247 
248 
249 
250 static void
terminal_app_update_windows_accels(gpointer user_data)251 terminal_app_update_windows_accels (gpointer user_data)
252 {
253   TerminalApp *app = TERMINAL_APP (user_data);
254   GSList      *lp;
255 
256   for (lp = app->windows; lp != NULL; lp = lp->next)
257     {
258       terminal_window_rebuild_tabs_menu (TERMINAL_WINDOW (lp->data));
259       terminal_window_update_tab_key_accels (TERMINAL_WINDOW (lp->data), app->tab_key_accels);
260     }
261 
262   app->accel_map_load_id = 0;
263 }
264 
265 
266 
267 static gboolean
terminal_app_accel_map_save(gpointer user_data)268 terminal_app_accel_map_save (gpointer user_data)
269 {
270   TerminalApp *app = TERMINAL_APP (user_data);
271   gchar       *path;
272 
273   app->accel_map_save_id = 0;
274 
275   /* save the current accel map */
276   path = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, ACCEL_MAP_PATH, TRUE);
277   if (G_LIKELY (path != NULL))
278     {
279       /* save the accel map */
280       gtk_accel_map_save (path);
281       g_free (path);
282     }
283 
284   return FALSE;
285 }
286 
287 
288 
289 static void
terminal_app_accel_map_changed(TerminalApp * app)290 terminal_app_accel_map_changed (TerminalApp *app)
291 {
292   /* stop pending save */
293   if (app->accel_map_save_id != 0)
294     {
295       g_source_remove (app->accel_map_save_id);
296       app->accel_map_save_id = 0;
297     }
298 
299   /* schedule new save */
300   app->accel_map_save_id = gdk_threads_add_timeout_seconds (10, terminal_app_accel_map_save, app);
301 }
302 
303 
304 
305 static gboolean
terminal_app_accel_map_load(gpointer user_data)306 terminal_app_accel_map_load (gpointer user_data)
307 {
308   TerminalApp *app = TERMINAL_APP (user_data);
309   gchar       *path;
310   gchar        name[50];
311   guint        i;
312 
313   path = xfce_resource_lookup (XFCE_RESOURCE_CONFIG, ACCEL_MAP_PATH);
314   if (G_LIKELY (path != NULL))
315     {
316       /* load the accel map */
317       gtk_accel_map_load (path);
318       g_free (path);
319     }
320 
321   /* watch for changes */
322   app->accel_map = gtk_accel_map_get ();
323   g_signal_connect_swapped (G_OBJECT (app->accel_map), "changed",
324       G_CALLBACK (terminal_app_accel_map_changed), app);
325 
326   /* check and create default Alt+N accelerators */
327   for (i = 1; i < 10; i++)
328     {
329       g_snprintf (name, sizeof (name), "<Actions>/terminal-window/goto-tab-%d", i);
330       if (!gtk_accel_map_lookup_entry (name, NULL))
331         gtk_accel_map_change_entry (name, GDK_KEY_0 + i, GDK_MOD1_MASK, FALSE);
332     }
333 
334   /* identify accelerators containing the Tab key */
335   if (app->tab_key_accels != NULL)
336     {
337       GSList *lp;
338       for (lp = app->tab_key_accels; lp != NULL; lp = lp->next)
339         g_free (((TerminalAccel*) lp->data)->path);
340       g_slist_free_full (app->tab_key_accels, g_free);
341       app->tab_key_accels = NULL;
342     }
343   gtk_accel_map_foreach (app, terminal_app_update_tab_key_accels);
344 
345   return FALSE;
346 }
347 
348 
349 
350 static gboolean
terminal_app_unset_urgent_bell(TerminalWindow * window,GdkEvent * event,TerminalApp * app)351 terminal_app_unset_urgent_bell (TerminalWindow *window,
352                                 GdkEvent       *event,
353                                 TerminalApp    *app)
354 {
355   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (window));
356   gtk_window_set_urgency_hint (GTK_WINDOW (toplevel), FALSE);
357 
358   return FALSE;
359 }
360 
361 
362 
363 static void
terminal_app_take_window(TerminalApp * app,GtkWindow * window)364 terminal_app_take_window (TerminalApp *app,
365                           GtkWindow   *window)
366 {
367   GtkWindowGroup *group;
368 
369   terminal_return_if_fail (GTK_IS_WINDOW (window));
370 
371   group = gtk_window_group_new ();
372   gtk_window_group_add_window (group, window);
373   g_object_unref (group);
374 
375   g_signal_connect (G_OBJECT (window), "destroy",
376                     G_CALLBACK (terminal_app_window_destroyed), app);
377   g_signal_connect (G_OBJECT (window), "new-window",
378                     G_CALLBACK (terminal_app_new_window), app);
379   g_signal_connect (G_OBJECT (window), "new-window-with-screen",
380                     G_CALLBACK (terminal_app_new_window_with_terminal), app);
381   g_signal_connect (G_OBJECT (window), "focus-in-event",
382                     G_CALLBACK (terminal_app_unset_urgent_bell), app);
383   g_signal_connect (G_OBJECT (window), "key-release-event",
384                     G_CALLBACK (terminal_app_unset_urgent_bell), app);
385   app->windows = g_slist_prepend (app->windows, window);
386 
387   terminal_window_update_tab_key_accels (TERMINAL_WINDOW (window), app->tab_key_accels);
388 }
389 
390 
391 
392 static GtkWidget*
terminal_app_create_window(TerminalApp * app,const gchar * role,gboolean fullscreen,TerminalVisibility menubar,TerminalVisibility borders,TerminalVisibility toolbar)393 terminal_app_create_window (TerminalApp       *app,
394                             const gchar       *role,
395                             gboolean           fullscreen,
396                             TerminalVisibility menubar,
397                             TerminalVisibility borders,
398                             TerminalVisibility toolbar)
399 {
400   GtkWidget *window;
401   gchar     *new_role = NULL;
402 
403   if (role == NULL)
404     {
405       /* create a new window role */
406       new_role = g_strdup_printf ("%s-%u-%u", PACKAGE_NAME, (guint) time (NULL), g_random_int ());
407       role = new_role;
408     }
409 
410   window = terminal_window_new (role, fullscreen, menubar, borders, toolbar);
411   g_free (new_role);
412 
413   terminal_app_take_window (app, GTK_WINDOW (window));
414 
415   return window;
416 }
417 
418 
419 
420 static GtkWidget*
terminal_app_create_drop_down(TerminalApp * app,const gchar * role,const gchar * icon,gboolean fullscreen,TerminalVisibility menubar,TerminalVisibility toolbar)421 terminal_app_create_drop_down (TerminalApp        *app,
422                                const gchar        *role,
423                                const gchar        *icon,
424                                gboolean            fullscreen,
425                                TerminalVisibility  menubar,
426                                TerminalVisibility  toolbar)
427 {
428   GtkWidget *window;
429 
430   window = terminal_window_dropdown_new (role, icon, fullscreen, menubar, toolbar);
431 
432   terminal_app_take_window (app, GTK_WINDOW (window));
433 
434   return window;
435 }
436 
437 
438 
439 static void
terminal_app_new_window(TerminalWindow * window,const gchar * working_directory,TerminalApp * app)440 terminal_app_new_window (TerminalWindow *window,
441                          const gchar    *working_directory,
442                          TerminalApp    *app)
443 {
444   TerminalWindowAttr *win_attr;
445   TerminalTabAttr    *tab_attr;
446   TerminalScreen     *terminal;
447   GdkScreen          *screen;
448   gboolean            inherit_geometry;
449   glong               w, h;
450 
451   screen = gtk_window_get_screen (GTK_WINDOW (window));
452 
453   win_attr = terminal_window_attr_new ();
454   win_attr->display = g_strdup (gdk_display_get_name (gdk_screen_get_display (screen)));
455   tab_attr = win_attr->tabs->data;
456   tab_attr->directory = g_strdup (working_directory);
457   if (terminal_window_get_font (window))
458     win_attr->font = g_strdup (terminal_window_get_font (window));
459   win_attr->zoom = terminal_window_get_zoom_level (window);
460 
461   /* check if we should try to inherit the parent geometry */
462   g_object_get (G_OBJECT (app->preferences), "misc-inherit-geometry", &inherit_geometry, NULL);
463   if (G_UNLIKELY (inherit_geometry))
464     {
465       /* determine the currently active terminal screen for the window */
466       terminal = terminal_window_get_active (window);
467       if (G_LIKELY (terminal != NULL))
468         {
469           /* let the new window inherit the geometry from its parent */
470           g_free (win_attr->geometry);
471           terminal_screen_get_size (terminal, &w, &h);
472           win_attr->geometry = g_strdup_printf ("%ldx%ld", w, h);
473         }
474     }
475 
476   terminal_app_open_window (app, win_attr);
477 
478   terminal_window_attr_free (win_attr);
479 }
480 
481 
482 
483 static void
terminal_app_new_window_with_terminal(TerminalWindow * existing,TerminalScreen * terminal,gint x,gint y,TerminalApp * app)484 terminal_app_new_window_with_terminal (TerminalWindow *existing,
485                                        TerminalScreen *terminal,
486                                        gint            x,
487                                        gint            y,
488                                        TerminalApp    *app)
489 {
490   GtkWidget *window;
491   GdkScreen *screen;
492   glong      width, height;
493 
494   terminal_return_if_fail (TERMINAL_IS_WINDOW (existing));
495   terminal_return_if_fail (TERMINAL_IS_SCREEN (terminal));
496   terminal_return_if_fail (TERMINAL_IS_APP (app));
497 
498   window = terminal_app_create_window (app, NULL, FALSE,
499                                        TERMINAL_VISIBILITY_DEFAULT,
500                                        TERMINAL_VISIBILITY_DEFAULT,
501                                        TERMINAL_VISIBILITY_DEFAULT);
502 
503   /* set new window position */
504   if (x > -1 && y > -1)
505     gtk_window_move (GTK_WINDOW (window), x, y);
506 
507   /* place the new window on the same screen as
508    * the existing window.
509    */
510   screen = gtk_window_get_screen (GTK_WINDOW (existing));
511   if (G_LIKELY (screen != NULL))
512     gtk_window_set_screen (GTK_WINDOW (window), screen);
513 
514   /* this is required to get the geometry right
515    * later in the terminal_window_add() call.
516    */
517   gtk_widget_hide (GTK_WIDGET (terminal));
518 
519   terminal_window_add (TERMINAL_WINDOW (window), terminal);
520 
521   if (G_UNLIKELY (terminal_window_is_drop_down (existing)))
522     {
523       /* resize new window to the default geometry */
524 #ifdef GDK_WINDOWING_X11
525       gchar *geo;
526       guint  w, h;
527       g_object_get (G_OBJECT (app->preferences), "misc-default-geometry", &geo, NULL);
528       if (G_LIKELY (geo != NULL))
529         {
530           XParseGeometry (geo, NULL, NULL, &w, &h);
531           g_free (geo);
532           terminal_screen_force_resize_window (terminal, GTK_WINDOW (window), w, h);
533         }
534 #endif
535     }
536   else
537     {
538       /* resize new window to the original terminal geometry */
539       terminal_screen_get_size (terminal, &width, &height);
540       terminal_screen_force_resize_window (terminal, GTK_WINDOW (window), width, height);
541     }
542 
543   gtk_widget_show (window);
544 }
545 
546 
547 
548 static void
terminal_app_window_destroyed(GtkWidget * window,TerminalApp * app)549 terminal_app_window_destroyed (GtkWidget   *window,
550                                TerminalApp *app)
551 {
552   terminal_return_if_fail (g_slist_find (app->windows, window) != NULL);
553 
554   app->windows = g_slist_remove (app->windows, window);
555 
556   if (G_UNLIKELY (app->windows == NULL))
557     gtk_main_quit ();
558 }
559 
560 
561 
562 static void
terminal_app_save_yourself(XfceSMClient * client,TerminalApp * app)563 terminal_app_save_yourself (XfceSMClient *client,
564                             TerminalApp  *app)
565 {
566   GSList               *result = NULL;
567   GSList               *lp;
568   const gchar * const  *oargv;
569   gchar               **argv;
570   gint                  argc;
571   gint                  n;
572 
573   for (lp = app->windows, n = 0; lp != NULL; lp = lp->next)
574     {
575       /* don't session save dropdown windows */
576       if (TERMINAL_IS_WINDOW_DROPDOWN (lp->data))
577         continue;
578 
579       if (n++ != 0)
580         result = g_slist_append (result, g_strdup ("--window"));
581       result = g_slist_concat (result, terminal_window_get_restart_command (lp->data));
582     }
583 
584   /* no windows were saved - this can happen if there is only a dropdown window
585      that we don't want to save */
586   if (result == NULL)
587     return;
588 
589   argc = g_slist_length (result) + 1;
590   argv = g_new (gchar*, argc + 1);
591   for (lp = result, n = 1; n < argc && lp != NULL; lp = lp->next, ++n)
592     argv[n] = lp->data;
593   argv[n] = NULL;
594 
595   oargv = xfce_sm_client_get_restart_command (client);
596   if (oargv != NULL)
597     {
598       terminal_assert (oargv[0] != NULL);
599       argv[0] = g_strdup (oargv[0]);
600     }
601   else
602     {
603       argv[0] = g_strdup (PACKAGE_NAME);
604     }
605 
606   xfce_sm_client_set_restart_command (client, argv);
607 
608   g_slist_free (result);
609   g_strfreev (argv);
610 }
611 
612 
613 
614 static GdkDisplay *
terminal_app_find_display(const gchar * display_name,gint * screen_num)615 terminal_app_find_display (const gchar *display_name,
616                            gint        *screen_num)
617 {
618   const gchar *other_name;
619   GdkDisplay  *display = NULL;
620   GSList      *displays;
621   GSList      *dp;
622   gulong       n;
623   gchar       *period;
624   gchar       *name;
625   gchar       *end;
626   gint         num = -1;
627 
628   if (display_name != NULL)
629     {
630       name = g_strdup (display_name);
631 
632       /* extract screen number from display name */
633       period = strrchr (name, '.');
634       if (period != NULL)
635         {
636           errno = 0;
637           *period++ = '\0';
638           end = period;
639           n = strtoul (period, &end, 0);
640           if (errno == 0 && period != end)
641             num = n;
642         }
643 
644       displays = gdk_display_manager_list_displays (gdk_display_manager_get ());
645       for (dp = displays; dp != NULL; dp = dp->next)
646         {
647           other_name = gdk_display_get_name (dp->data);
648           if (strncmp (other_name, name, strlen (name)) == 0)
649             {
650               display = dp->data;
651               break;
652             }
653         }
654       g_slist_free (displays);
655 
656       g_free (name);
657 
658       if (display == NULL)
659         display = gdk_display_open (display_name);
660     }
661 
662   if (display == NULL)
663     display = gdk_display_get_default ();
664 
665   if (screen_num != NULL)
666     *screen_num = num;
667 
668   return display;
669 }
670 
671 
672 
673 static GdkScreen *
terminal_app_find_screen(GdkDisplay * display,gint screen_num)674 terminal_app_find_screen (GdkDisplay *display,
675                           gint        screen_num)
676 {
677   GdkScreen *screen = NULL;
678 
679   if (display != NULL)
680     {
681       if (screen == NULL)
682         screen = gdk_display_get_default_screen (display);
683 
684       if (screen != NULL)
685         g_object_ref (G_OBJECT (screen));
686     }
687 
688   if (screen == NULL)
689     {
690       screen = gdk_screen_get_default ();
691       g_object_ref (G_OBJECT (screen));
692     }
693 
694   return screen;
695 }
696 
697 
698 
699 static GdkScreen *
terminal_app_find_screen_by_name(const gchar * display_name)700 terminal_app_find_screen_by_name (const gchar *display_name)
701 {
702   GdkDisplay *display;
703   gint        screen_num;
704 
705   display = terminal_app_find_display (display_name, &screen_num);
706   return terminal_app_find_screen (display, screen_num);
707 }
708 
709 
710 
711 static void
terminal_app_open_window(TerminalApp * app,TerminalWindowAttr * attr)712 terminal_app_open_window (TerminalApp        *app,
713                           TerminalWindowAttr *attr)
714 {
715   GtkWidget       *window;
716   TerminalScreen  *terminal;
717   GdkScreen       *screen;
718   gchar           *geometry;
719   GSList          *lp;
720   gboolean         reuse_window = FALSE;
721   GdkDisplay      *attr_display;
722   gint             attr_screen_num;
723   gint             active_tab = -1, i;
724 #ifdef GDK_WINDOWING_X11
725   GdkGravity       gravity = GDK_GRAVITY_NORTH_WEST;
726   gint             mask = NoValue, x, y, new_x, new_y;
727   guint            width = 1, height = 1, new_width, new_height;
728   gint             screen_width = 0, screen_height = 0;
729   gint             window_width, window_height;
730 #endif
731 
732   terminal_return_if_fail (TERMINAL_IS_APP (app));
733   terminal_return_if_fail (attr != NULL);
734 
735   if (attr->drop_down)
736     {
737       /* look for an exising drop-down window */
738       attr_display = terminal_app_find_display (attr->display, &attr_screen_num);
739       for (lp = app->windows; lp != NULL; lp = lp->next)
740         {
741           if (TERMINAL_IS_WINDOW_DROPDOWN (lp->data))
742             {
743               /* check if the screen of the display matches (bug #9957) */
744               screen = gtk_window_get_screen (GTK_WINDOW (lp->data));
745               if (gdk_screen_get_display (screen) == attr_display)
746                 break;
747             }
748         }
749 
750       if (lp != NULL)
751         {
752           if (G_UNLIKELY (attr->reuse_last_window))
753             {
754               /* use the drop-down window to insert the tab */
755               window = lp->data;
756               reuse_window = TRUE;
757             }
758           else
759             {
760               /* toggle state of visible window */
761               terminal_window_dropdown_toggle (lp->data, attr->startup_id, FALSE);
762               return;
763             }
764         }
765       else
766         {
767           /* create new drop-down window */
768           window = terminal_app_create_drop_down (app,
769                                                   attr->role,
770                                                   attr->icon,
771                                                   attr->fullscreen,
772                                                   attr->menubar,
773                                                   attr->toolbar);
774 
775           /* drop-down window can be maximized */
776           if (attr->maximize)
777             gtk_window_maximize (GTK_WINDOW (window));
778 
779           /* put it on the correct screen/display */
780           screen = terminal_app_find_screen (attr_display, attr_screen_num);
781           gtk_window_set_screen (GTK_WINDOW (window), screen);
782         }
783     }
784   else if (attr->reuse_last_window && app->windows != NULL)
785     {
786       /* open tabs in the existing window */
787       window = app->windows->data;
788       /* try to find active window (bug #13891) */
789       for (lp = app->windows; lp != NULL; lp = lp->next)
790         {
791           if (gtk_window_has_toplevel_focus (GTK_WINDOW (lp->data)))
792             {
793               window = lp->data;
794               break;
795             }
796         }
797       reuse_window = TRUE;
798     }
799   else
800     {
801       /* create new window */
802       window = terminal_app_create_window (app,
803                                            attr->role,
804                                            attr->fullscreen,
805                                            attr->menubar,
806                                            attr->borders,
807                                            attr->toolbar);
808 
809       /* apply normal window properties */
810       if (attr->maximize)
811         gtk_window_maximize (GTK_WINDOW (window));
812       if (attr->minimize)
813         gtk_window_iconify (GTK_WINDOW (window));
814 
815       if (attr->startup_id != NULL)
816         gtk_window_set_startup_id (GTK_WINDOW (window), attr->startup_id);
817 
818       if (attr->icon != NULL)
819         {
820           if (g_path_is_absolute (attr->icon))
821             gtk_window_set_icon_from_file (GTK_WINDOW (window), attr->icon, NULL);
822           else
823             gtk_window_set_icon_name (GTK_WINDOW (window), attr->icon);
824         }
825 
826       screen = terminal_app_find_screen_by_name (attr->display);
827       if (G_LIKELY (screen != NULL))
828         {
829           gtk_window_set_screen (GTK_WINDOW (window), screen);
830           g_object_unref (G_OBJECT (screen));
831         }
832     }
833 
834   terminal_window_set_scrollbar_visibility (TERMINAL_WINDOW (window), attr->scrollbar);
835 
836   /* font and zoom for new window */
837   if (!reuse_window)
838     {
839       if (attr->font)
840         terminal_window_set_font (TERMINAL_WINDOW (window), attr->font);
841       terminal_window_set_zoom_level (TERMINAL_WINDOW (window), attr->zoom);
842     }
843 
844   if (!attr->drop_down)
845     {
846       /* try to apply the geometry to the window */
847       g_object_get (G_OBJECT (app->preferences), "misc-default-geometry", &geometry, NULL);
848 #ifdef GDK_WINDOWING_X11
849       /* defaults */
850       mask = XParseGeometry (geometry, &x, &y, &width, &height);
851 
852       /* geometry provided via command line parameter */
853       if (G_UNLIKELY (attr->geometry != NULL))
854         {
855           g_free (geometry);
856           geometry = g_strdup (attr->geometry);
857           mask = XParseGeometry (geometry, &new_x, &new_y, &new_width, &new_height);
858 
859           if (mask & WidthValue)
860             width = new_width;
861           if (mask & HeightValue)
862             height = new_height;
863           if (mask & XValue)
864             x = new_x;
865           if (mask & YValue)
866             y = new_y;
867         }
868 #endif
869     }
870 
871   /* add the tabs */
872   for (lp = attr->tabs, i = 0; lp != NULL; lp = lp->next, ++i)
873     {
874       TerminalTabAttr *tab_attr = (TerminalTabAttr *) lp->data;
875       terminal = terminal_screen_new (tab_attr, width, height);
876       terminal_window_add (TERMINAL_WINDOW (window), terminal);
877       terminal_screen_launch_child (terminal);
878 
879       /* whether the tab was set as active */
880       if (G_UNLIKELY (tab_attr->active))
881         active_tab = i;
882     }
883 
884   /* set active tab */
885   if (active_tab > -1)
886     {
887       GtkNotebook *notebook = GTK_NOTEBOOK (terminal_window_get_notebook (TERMINAL_WINDOW (window)));
888       gtk_notebook_set_current_page (notebook, active_tab);
889     }
890 
891   if (!attr->drop_down)
892     {
893       /* move the window to desired position */
894 #ifdef GDK_WINDOWING_X11
895       if ((mask & XValue) || (mask & YValue))
896         {
897           screen = gtk_window_get_screen (GTK_WINDOW (window));
898           gdk_window_get_geometry (gdk_screen_get_root_window (screen), NULL, NULL,
899                                    &screen_width, &screen_height);
900           gtk_window_get_size (GTK_WINDOW (window), &window_width, &window_height);
901           if (mask & XNegative)
902             {
903               x = screen_width - window_width + x;
904               gravity = GDK_GRAVITY_NORTH_EAST;
905             }
906           if (mask & YNegative)
907             {
908               y = screen_height - window_height + y;
909               gravity = (mask & XNegative) ? GDK_GRAVITY_SOUTH_EAST : GDK_GRAVITY_SOUTH_WEST;
910             }
911           gtk_window_set_gravity (GTK_WINDOW (window), gravity);
912           gtk_window_move (GTK_WINDOW (window), x, y);
913         }
914       else if (!(mask & WidthValue) && !(mask & XValue))
915 #else
916       if (!gtk_window_parse_geometry (GTK_WINDOW (window), geometry))
917 #endif
918         g_printerr (_("Invalid geometry string \"%s\"\n"), geometry);
919 
920       /* cleanup */
921       g_free (geometry);
922     }
923 
924   /* show the window */
925   if (attr->drop_down)
926     terminal_window_dropdown_toggle (TERMINAL_WINDOW_DROPDOWN (window), attr->startup_id, reuse_window);
927   else
928     {
929       /* save window geometry to prevent overriding */
930       terminal_window_set_grid_size (TERMINAL_WINDOW (window), width, height);
931       terminal_screen_force_resize_window (terminal_window_get_active (TERMINAL_WINDOW (window)),
932                                            GTK_WINDOW (window), width, height);
933 
934       if (reuse_window)
935         gtk_window_present (GTK_WINDOW (window));
936       else
937         gtk_widget_show (window);
938     }
939 }
940 
941 
942 
943 /**
944  * terminal_app_process:
945  * @app
946  * @argv
947  * @argc
948  * @error
949  *
950  * Return value:
951  **/
952 gboolean
terminal_app_process(TerminalApp * app,gchar ** argv,gint argc,GError ** error)953 terminal_app_process (TerminalApp  *app,
954                       gchar       **argv,
955                       gint          argc,
956                       GError      **error)
957 {
958   GSList             *attrs, *lp;
959   gchar              *sm_client_id = NULL;
960   TerminalWindowAttr *attr;
961   GError             *err = NULL;
962 
963   attrs = terminal_window_attr_parse (argc, argv, app->windows != NULL, error);
964   if (G_UNLIKELY (attrs == NULL))
965     return FALSE;
966 
967   /* Connect to session manager first before starting any other windows */
968   for (lp = attrs; lp != NULL; lp = lp->next)
969     {
970       attr = lp->data;
971 
972       /* take first sm client id */
973       if (attr->sm_client_id != NULL)
974         {
975           sm_client_id = g_strdup (attr->sm_client_id);
976           break;
977         }
978     }
979 
980   if (G_LIKELY (app->session_client == NULL))
981     {
982       app->session_client = xfce_sm_client_get_full (XFCE_SM_CLIENT_RESTART_NORMAL,
983                                                      XFCE_SM_CLIENT_PRIORITY_DEFAULT,
984                                                      sm_client_id,
985                                                      xfce_get_homedir (),
986                                                      NULL,
987                                                      PACKAGE_NAME ".desktop");
988       if (xfce_sm_client_connect (app->session_client, &err))
989         {
990           xfce_sm_client_set_desktop_file (app->session_client, TERMINAL_DESKTOP_FILE);
991           g_signal_connect (G_OBJECT (app->session_client), "save-state",
992                             G_CALLBACK (terminal_app_save_yourself), app);
993           g_signal_connect (G_OBJECT (app->session_client), "quit",
994                             G_CALLBACK (gtk_main_quit), NULL);
995         }
996       else
997         {
998           g_printerr (_("Failed to connect to session manager: %s\n"), err->message);
999           g_error_free (err);
1000         }
1001     }
1002 
1003   for (lp = attrs; lp != NULL; lp = lp->next)
1004     {
1005       attr = lp->data;
1006 
1007       terminal_app_open_window (app, attr);
1008       terminal_window_attr_free (attr);
1009     }
1010 
1011   g_slist_free (attrs);
1012   g_free (sm_client_id);
1013 
1014   return TRUE;
1015 }
1016