1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ 26
+ 27
+ 28
+ 29
+ 30
+ 31
+ 32
+ 33
+ 34
+ 35
+ 36
+ 37
+ 38
+ 39
+ 40
+ 41
+ 42
+ 43
+ 44
+ 45
+ 46
+ 47
+ 48
+ 49
+ 50
+ 51
+ 52
+ 53
+ 54
+ 55
+ 56
+ 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548 | /*
+ * Copyright (C) 2008 Canonical Ltd
+ * Copyright (C) 2012-2021 MATE Developers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by Neil Jagdish Patel <neil.patel@canonical.com>
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gio/gio.h>
+
+#include "maximus-app.h"
+#include "maximus-bind.h"
+#include "xutils.h"
+
+/* GSettings schemas and keys */
+#define APP_SCHEMA "org.mate.maximus"
+#define APP_EXCLUDE_CLASS "exclude-class"
+#define APP_UNDECORATE "undecorate"
+#define APP_NO_MAXIMIZE "no-maximize"
+
+/* A set of default exceptions */
+static gchar *default_exclude_classes[] =
+{
+ "Apport-gtk",
+ "Bluetooth-properties",
+ "Bluetooth-wizard",
+ "Download", /* Firefox Download Window */
+ "Ekiga",
+ "Extension", /* Firefox Add-Ons/Extension Window */
+ "Gcalctool",
+ "Gimp",
+ "Global", /* Firefox Error Console Window */
+ "Mate-dictionary",
+ "Mate-language-selector",
+ "Mate-nettool",
+ "Mate-volume-control",
+ "Kiten",
+ "Kmplot",
+ "Nm-editor",
+ "Pidgin",
+ "Polkit-mate-authorization",
+ "Update-manager",
+ "Skype",
+ "Toplevel", /* Firefox "Clear Private Data" Window */
+ "Transmission"
+};
+
+struct _MaximusAppPrivate
+{
+ MaximusBind *bind;
+ WnckScreen *screen;
+ GSettings *settings;
+
+ gchar **exclude_class_list;
+ gboolean undecorate;
+ gboolean no_maximize;
+};
+
+static GQuark was_decorated = 0;
+
+/* <TAKEN FROM GDK> */
+typedef struct {
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+ long input_mode;
+ unsigned long status;
+} MotifWmHints, MwmHints;
+
+#define MWM_HINTS_FUNCTIONS (1L << 0)
+#define MWM_HINTS_DECORATIONS (1L << 1)
+#define _XA_MOTIF_WM_HINTS "_MOTIF_WM_HINTS"
+
+G_DEFINE_TYPE_WITH_PRIVATE (MaximusApp, maximus_app, G_TYPE_OBJECT);
+
+static gboolean
+wnck_window_is_decorated (WnckWindow *window)
+{
+ GdkDisplay *display = gdk_display_get_default();
+ Atom hints_atom = None;
+ guchar *data = NULL;
+ MotifWmHints *hints = NULL;
+ Atom type = None;
+ gint format;
+ gulong nitems;
+ gulong bytes_after;
+ gboolean retval;
+
+ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
+
+ hints_atom = gdk_x11_get_xatom_by_name_for_display (display,
+ _XA_MOTIF_WM_HINTS);
+
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ wnck_window_get_xid (window),
+ hints_atom, 0, sizeof (MotifWmHints)/sizeof (long),
+ False, AnyPropertyType, &type, &format, &nitems,
+ &bytes_after, &data);
+
+ if (type == None || !data) return TRUE;<--- Assuming that condition '!data' is not redundant
+
+ hints = (MotifWmHints *)data;
+
+ retval = hints->decorations;
+
+ if (data)<--- Condition 'data' is always true
+ XFree (data);
+
+ return retval;
+}
+
+static void
+gdk_window_set_mwm_hints (WnckWindow *window,
+ MotifWmHints *new_hints)
+{
+ GdkDisplay *display = gdk_display_get_default();
+ Atom hints_atom = None;
+ guchar *data = NULL;
+ MotifWmHints *hints = NULL;
+ Atom type = None;
+ gint format;
+ gulong nitems;
+ gulong bytes_after;
+
+ g_return_if_fail (WNCK_IS_WINDOW (window));
+ g_return_if_fail (GDK_IS_DISPLAY (display));
+
+ hints_atom = gdk_x11_get_xatom_by_name_for_display (display,
+ _XA_MOTIF_WM_HINTS);
+
+ gdk_x11_display_error_trap_push (display);
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ wnck_window_get_xid (window),
+ hints_atom, 0, sizeof (MotifWmHints)/sizeof (long),
+ False, AnyPropertyType, &type, &format, &nitems,
+ &bytes_after, &data);
+ if (gdk_x11_display_error_trap_pop (display))
+ return;
+
+ if (type != hints_atom || !data)
+ hints = new_hints;
+ else
+ {
+ hints = (MotifWmHints *)data;
+
+ if (new_hints->flags & MWM_HINTS_FUNCTIONS)
+ {
+ hints->flags |= MWM_HINTS_FUNCTIONS;
+ hints->functions = new_hints->functions;
+ }
+ if (new_hints->flags & MWM_HINTS_DECORATIONS)
+ {
+ hints->flags |= MWM_HINTS_DECORATIONS;
+ hints->decorations = new_hints->decorations;
+ }
+ }
+
+ _wnck_error_trap_push ();
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ wnck_window_get_xid (window),
+ hints_atom, hints_atom, 32, PropModeReplace,
+ (guchar *)hints, sizeof (MotifWmHints)/sizeof (long));
+ gdk_display_flush (display);
+ _wnck_error_trap_pop ();
+
+ if (data)
+ XFree (data);
+}
+
+static void
+_window_set_decorations (WnckWindow *window,
+ GdkWMDecoration decorations)
+{
+ MotifWmHints *hints;
+
+ g_return_if_fail (WNCK_IS_WINDOW (window));
+
+ /* initialize to zero to avoid writing uninitialized data to socket */
+ hints = g_slice_new0 (MotifWmHints);
+ hints->flags = MWM_HINTS_DECORATIONS;
+ hints->decorations = decorations;
+
+ gdk_window_set_mwm_hints (window, hints);
+
+ g_slice_free (MotifWmHints, hints);
+}
+
+/* </TAKEN FROM GDK> */
+
+gboolean
+window_is_too_large_for_screen (WnckWindow *window)
+{
+ static GdkScreen *screen = NULL;
+ gint x=0, y=0, w=0, h=0;
+
+ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
+
+ if (screen == NULL)
+ screen = gdk_screen_get_default ();
+
+ wnck_window_get_geometry (window, &x, &y, &w, &h);
+
+ /* some wiggle room */
+ return (screen &&
+ (w > (WidthOfScreen (gdk_x11_screen_get_xscreen (screen)) + 20) ||
+ h > (HeightOfScreen (gdk_x11_screen_get_xscreen (screen)) + 20)));
+}
+
+static gboolean
+on_window_maximised_changed (WnckWindow *window)
+{
+ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
+
+ if (window_is_too_large_for_screen (window))
+ {
+ _window_set_decorations (window, 1);
+ wnck_window_unmaximize (window);
+ }
+ else
+ {
+ _window_set_decorations (window, 0);
+ }
+ return FALSE;
+}
+
+static gboolean
+enable_window_decorations (WnckWindow *window)
+{
+ g_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE);
+
+ _window_set_decorations (window, 1);
+ return FALSE;
+}
+
+static void
+on_window_state_changed (WnckWindow *window,
+ WnckWindowState change_mask,
+ WnckWindowState new_state,
+ MaximusApp *app)
+{
+ g_return_if_fail (WNCK_IS_WINDOW (window));
+
+ if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "exclude"))==1)
+ return;
+
+ if (change_mask & WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY
+ || change_mask & WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY)
+ {
+ if (wnck_window_is_maximized (window) && app->priv->undecorate)
+ {
+ g_idle_add ((GSourceFunc)on_window_maximised_changed, window);
+ }
+ else
+ {
+ g_idle_add ((GSourceFunc)enable_window_decorations, window);
+ }
+ }
+}
+
+static gboolean
+is_excluded (MaximusApp *app, WnckWindow *window)
+{
+ MaximusAppPrivate *priv;
+ WnckWindowType type;
+ WnckWindowActions actions;
+ gchar *res_name;
+ gchar *class_name;
+ gint i;
+
+ g_return_val_if_fail (MAXIMUS_IS_APP (app), TRUE);
+ g_return_val_if_fail (WNCK_IS_WINDOW (window), TRUE);
+ priv = app->priv;
+
+ type = wnck_window_get_window_type (window);
+ if (type != WNCK_WINDOW_NORMAL)
+ return TRUE;
+
+ if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "exclude"))==1)
+ return TRUE;
+
+ /* Ignore if the window is already fullscreen */
+ if (wnck_window_is_fullscreen (window))
+ {
+ g_debug ("Excluding (is fullscreen): %s\n",wnck_window_get_name (window));
+ return TRUE;
+ }
+
+ /* Make sure the window supports maximising */
+ actions = wnck_window_get_actions (window);
+ if (actions & WNCK_WINDOW_ACTION_RESIZE
+ && actions & WNCK_WINDOW_ACTION_MAXIMIZE_HORIZONTALLY
+ && actions & WNCK_WINDOW_ACTION_MAXIMIZE_VERTICALLY
+ && actions & WNCK_WINDOW_ACTION_MAXIMIZE)
+ ; /* Is good to maximise */
+ else
+ return TRUE;
+
+ _wnck_get_wmclass (wnck_window_get_xid (window), &res_name, &class_name);
+
+ g_debug ("Window opened: res_name=%s -- class_name=%s", res_name, class_name);
+
+ /* Check internal list of class_ids */
+ for (i = 0; i < G_N_ELEMENTS (default_exclude_classes); i++)
+ {
+ if ((class_name && default_exclude_classes[i]
+ && strstr (class_name, default_exclude_classes[i]))
+ || (res_name && default_exclude_classes[i] && strstr (res_name,
+ default_exclude_classes[i])))
+ {
+ g_debug ("Excluding: %s\n", wnck_window_get_name (window));
+ return TRUE;
+ }
+ }
+
+ /* Check user list */
+ for (i = 0; priv->exclude_class_list[i] != NULL; i++)
+ {
+ if ((class_name && strstr (class_name, priv->exclude_class_list[i]))
+ || (res_name && strstr (res_name, priv->exclude_class_list[i]) ))
+ {
+ g_debug ("Excluding: %s\n", wnck_window_get_name (window));
+ return TRUE;
+ }
+ }
+
+ g_free (res_name);
+ g_free (class_name);
+ return FALSE;
+}
+
+extern gboolean no_maximize;
+
+static void
+on_window_opened (WnckScreen *screen,
+ WnckWindow *window,
+ MaximusApp *app)
+{
+ MaximusAppPrivate *priv;
+ WnckWindowType type;
+ gint exclude = 0;
+ GdkDisplay *gdk_display = gdk_display_get_default ();
+
+ g_return_if_fail (MAXIMUS_IS_APP (app));
+ g_return_if_fail (WNCK_IS_WINDOW (window));
+ priv = app->priv;
+
+ type = wnck_window_get_window_type (window);
+ if (type != WNCK_WINDOW_NORMAL)
+ return;
+
+ /* Ignore undecorated windows */
+ gdk_x11_display_error_trap_push (gdk_display);
+ exclude = wnck_window_is_decorated (window) ? 0 : 1;
+ if (gdk_x11_display_error_trap_pop (gdk_display))
+ return;
+
+ if (wnck_window_is_maximized (window))
+ exclude = 0;
+ g_object_set_data (G_OBJECT (window), "exclude", GINT_TO_POINTER (exclude));
+
+ if (is_excluded (app, window))
+ {
+ g_signal_connect (window, "state-changed",
+ G_CALLBACK (on_window_state_changed), app);
+ return;
+ }
+
+ if (no_maximize || priv->no_maximize)
+ {
+ if (wnck_window_is_maximized(window) && priv->undecorate)
+ {
+ _window_set_decorations (window, 0);
+ gdk_display_flush (gdk_display);
+ }
+ g_signal_connect (window, "state-changed",
+ G_CALLBACK (on_window_state_changed), app);
+ return;
+ }
+
+ if (priv->undecorate)
+ {
+ /* Only undecorate right now if the window is smaller than the screen */
+ if (!window_is_too_large_for_screen (window))
+ {
+ _window_set_decorations (window, 0);
+ gdk_display_flush (gdk_display);
+ }
+ }
+
+ wnck_window_maximize (window);
+
+ g_signal_connect (window, "state-changed",
+ G_CALLBACK (on_window_state_changed), app);
+}
+
+/* GSettings Callbacks */
+static void
+on_app_no_maximize_changed (GSettings *settings,
+ gchar *key,
+ MaximusApp *app)
+{
+ MaximusAppPrivate *priv;
+
+ g_return_if_fail (MAXIMUS_IS_APP (app));
+ priv = app->priv;
+ priv->no_maximize = g_settings_get_boolean (settings, key);
+}
+
+static void
+on_exclude_class_changed (GSettings *settings,
+ gchar *key,
+ MaximusApp *app)
+{
+ MaximusAppPrivate *priv;
+
+ g_return_if_fail (MAXIMUS_IS_APP (app));
+ priv = app->priv;
+
+ if (priv->exclude_class_list)
+ g_strfreev (priv->exclude_class_list);
+
+ priv->exclude_class_list= g_settings_get_strv (settings,
+ APP_EXCLUDE_CLASS);
+}
+
+static gboolean
+show_desktop (WnckScreen *screen)
+{
+ g_return_val_if_fail (WNCK_IS_SCREEN (screen), FALSE);
+
+ wnck_screen_toggle_showing_desktop (screen, TRUE);
+ return FALSE;
+}
+
+static void
+on_app_undecorate_changed (GSettings *settings,
+ gchar *key,
+ MaximusApp *app)
+{
+ MaximusAppPrivate *priv;
+ GList *windows, *w;
+
+ g_return_if_fail (MAXIMUS_IS_APP (app));
+ priv = app->priv;
+ g_return_if_fail (WNCK_IS_SCREEN (priv->screen));
+
+ priv->undecorate = g_settings_get_boolean (settings, APP_UNDECORATE);
+ g_debug ("%s\n", priv->undecorate ? "Undecorating" : "Decorating");
+
+ windows = wnck_screen_get_windows (priv->screen);
+ for (w = windows; w; w = w->next)
+ {
+ WnckWindow *window = w->data;
+
+ if (!WNCK_IS_WINDOW (window))
+ continue;
+
+ if (no_maximize || priv->no_maximize)
+ {
+ if (!wnck_window_is_maximized(window))
+ continue;
+ }
+
+ if (!is_excluded (app, window))
+ {
+ GdkDisplay *gdk_display = gdk_display_get_default ();
+
+ gdk_x11_display_error_trap_push (gdk_display);
+ _window_set_decorations (window, priv->undecorate ? 0 : 1);
+ wnck_window_unmaximize (window);
+ wnck_window_maximize (window);
+ gdk_display_flush (gdk_display);
+ gdk_x11_display_error_trap_pop_ignored (gdk_display);
+
+ sleep (1);
+ }
+ }
+ /* We want the user to be left on the launcher/desktop after switching modes*/
+ g_timeout_add_seconds (1, (GSourceFunc)show_desktop, priv->screen);
+}
+
+/* GObject stuff */
+static void
+maximus_app_class_init (MaximusAppClass *klass)
+{
+}
+
+static void
+maximus_app_init (MaximusApp *app)
+{
+ MaximusAppPrivate *priv;
+ WnckScreen *screen;
+
+ priv = app->priv = maximus_app_get_instance_private (app);
+
+ priv->bind = maximus_bind_get_default ();
+
+ was_decorated = g_quark_from_static_string ("was-decorated");
+
+ priv->settings = g_settings_new (APP_SCHEMA);
+
+ g_signal_connect (priv->settings, "changed::" APP_EXCLUDE_CLASS,
+ G_CALLBACK (on_exclude_class_changed), app);
+ g_signal_connect (priv->settings, "changed::" APP_UNDECORATE,
+ G_CALLBACK (on_app_undecorate_changed), app);
+ g_signal_connect (priv->settings, "changed::" APP_NO_MAXIMIZE,
+ G_CALLBACK (on_app_no_maximize_changed), app);
+
+ priv->exclude_class_list = g_settings_get_strv (priv->settings, APP_EXCLUDE_CLASS);
+ priv->undecorate = g_settings_get_boolean (priv->settings, APP_UNDECORATE);
+ priv->no_maximize = g_settings_get_boolean (priv->settings, APP_NO_MAXIMIZE);
+ g_print ("no maximize: %s\n", priv->no_maximize ? "true" : "false");
+
+ priv->screen = screen = wnck_screen_get_default ();
+ g_signal_connect (screen, "window-opened",
+ G_CALLBACK (on_window_opened), app);
+}
+
+MaximusApp *
+maximus_app_get_default (void)
+
+{
+ static MaximusApp *app = NULL;
+
+ if (!app)
+ app = g_object_new (MAXIMUS_TYPE_APP,
+ NULL);
+
+ return app;
+}
+
+ |