1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /*
4  * Copyright (c) 2008 Red Hat, Inc.
5  * Copyright (c) 2008 Intel Corp.
6  *
7  * Based on plugin skeleton by:
8  * Author: Tomas Frydrych <tf@linux.intel.com>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA
23  * 02110-1335, USA.
24  */
25 
26 #include "config.h"
27 
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include <clutter/clutter.h>
32 #include <clutter/x11/clutter-x11.h>
33 #if defined (__arm__)
34 #else
35 #include <GL/glx.h>
36 #include <GL/glxext.h>
37 #endif
38 #include <cjs/gjs.h>
39 #include <meta/display.h>
40 #include <meta/meta-plugin.h>
41 
42 #include "cinnamon-global-private.h"
43 #include "cinnamon-perf-log.h"
44 #include "cinnamon-wm-private.h"
45 
46 static void gnome_cinnamon_plugin_dispose     (GObject *object);
47 static void gnome_cinnamon_plugin_finalize    (GObject *object);
48 
49 static void gnome_cinnamon_plugin_start            (MetaPlugin          *plugin);
50 static void gnome_cinnamon_plugin_minimize         (MetaPlugin          *plugin,
51                                                  MetaWindowActor     *actor);
52 static void gnome_cinnamon_plugin_maximize         (MetaPlugin          *plugin,
53                                                  MetaWindowActor     *actor,
54                                                  gint                 x,
55                                                  gint                 y,
56                                                  gint                 width,
57                                                  gint                 height);
58 static void gnome_cinnamon_plugin_unmaximize       (MetaPlugin          *plugin,
59                                                  MetaWindowActor     *actor,
60                                                  gint                 x,
61                                                  gint                 y,
62                                                  gint                 width,
63                                                  gint                 height);
64 static void gnome_cinnamon_plugin_tile             (MetaPlugin          *plugin,
65                                                  MetaWindowActor     *actor,
66                                                  gint                 x,
67                                                  gint                 y,
68                                                  gint                 width,
69                                                  gint                 height);
70 static void gnome_cinnamon_plugin_map              (MetaPlugin          *plugin,
71                                                  MetaWindowActor     *actor);
72 static void gnome_cinnamon_plugin_destroy          (MetaPlugin          *plugin,
73                                                  MetaWindowActor     *actor);
74 
75 static void gnome_cinnamon_plugin_switch_workspace (MetaPlugin          *plugin,
76                                                  gint                 from,
77                                                  gint                 to,
78                                                  MetaMotionDirection  direction);
79 
80 static void gnome_cinnamon_plugin_show_tile_preview (MetaPlugin     *plugin,
81                                                      MetaWindow     *window,
82                                                      MetaRectangle  *tile_rect,
83                                                      int            tile_monitor,
84                                                      guint          snap_queued);
85 static void gnome_cinnamon_plugin_hide_tile_preview (MetaPlugin *plugin);
86 
87 static void gnome_cinnamon_plugin_show_hud_preview (MetaPlugin      *plugin,
88                                                     guint           current_proximity_zone,
89                                                     MetaRectangle   *work_area,
90                                                     guint           snap_queued);
91 
92 static void gnome_cinnamon_plugin_hide_hud_preview (MetaPlugin *plugin);
93 
94 static void gnome_cinnamon_plugin_kill_window_effects   (MetaPlugin      *plugin,
95                                                       MetaWindowActor *actor);
96 
97 static gboolean              gnome_cinnamon_plugin_xevent_filter (MetaPlugin *plugin,
98                                                                XEvent     *event);
99 static const MetaPluginInfo *gnome_cinnamon_plugin_plugin_info   (MetaPlugin *plugin);
100 
101 
102 #define GNOME_TYPE_CINNAMON_PLUGIN            (gnome_cinnamon_plugin_get_type ())
103 #define CINNAMON_PLUGIN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CINNAMON_PLUGIN, CinnamonPlugin))
104 #define CINNAMON_PLUGIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GNOME_TYPE_CINNAMON_PLUGIN, CinnamonPluginClass))
105 #define GNOME_IS_CINNAMON_PLUGIN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CINNAMON_PLUGIN_TYPE))
106 #define GNOME_IS_CINNAMON_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GNOME_TYPE_CINNAMON_PLUGIN))
107 #define CINNAMON_PLUGIN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GNOME_TYPE_CINNAMON_PLUGIN, CinnamonPluginClass))
108 
109 typedef struct _CinnamonPlugin        CinnamonPlugin;
110 typedef struct _CinnamonPluginClass   CinnamonPluginClass;
111 
112 struct _CinnamonPlugin
113 {
114   MetaPlugin parent;
115 
116   Atom panel_action;
117   Atom panel_action_run_dialog;
118   Atom panel_action_main_menu;
119 
120   int glx_error_base;
121   int glx_event_base;
122   guint have_swap_event : 1;
123 
124   CinnamonGlobal *global;
125 };
126 
127 struct _CinnamonPluginClass
128 {
129   MetaPluginClass parent_class;
130 };
131 
132 GType gnome_cinnamon_plugin_get_type (void);
133 
G_DEFINE_TYPE(CinnamonPlugin,gnome_cinnamon_plugin,META_TYPE_PLUGIN)134 G_DEFINE_TYPE (CinnamonPlugin, gnome_cinnamon_plugin, META_TYPE_PLUGIN)
135 
136 static void
137 gnome_cinnamon_plugin_class_init (CinnamonPluginClass *klass)
138 {
139   GObjectClass      *gobject_class = G_OBJECT_CLASS (klass);
140   MetaPluginClass *plugin_class  = META_PLUGIN_CLASS (klass);
141 
142   gobject_class->dispose         = gnome_cinnamon_plugin_dispose;
143   gobject_class->finalize        = gnome_cinnamon_plugin_finalize;
144 
145   plugin_class->start            = gnome_cinnamon_plugin_start;
146   plugin_class->map              = gnome_cinnamon_plugin_map;
147   plugin_class->minimize         = gnome_cinnamon_plugin_minimize;
148   plugin_class->maximize         = gnome_cinnamon_plugin_maximize;
149   plugin_class->tile             = gnome_cinnamon_plugin_tile;
150   plugin_class->unmaximize       = gnome_cinnamon_plugin_unmaximize;
151   plugin_class->destroy          = gnome_cinnamon_plugin_destroy;
152 
153   plugin_class->switch_workspace = gnome_cinnamon_plugin_switch_workspace;
154 
155   plugin_class->show_tile_preview = gnome_cinnamon_plugin_show_tile_preview;
156   plugin_class->hide_tile_preview = gnome_cinnamon_plugin_hide_tile_preview;
157 
158   plugin_class->show_hud_preview = gnome_cinnamon_plugin_show_hud_preview;
159   plugin_class->hide_hud_preview = gnome_cinnamon_plugin_hide_hud_preview;
160 
161 
162   plugin_class->kill_window_effects   = gnome_cinnamon_plugin_kill_window_effects;
163 
164   plugin_class->xevent_filter    = gnome_cinnamon_plugin_xevent_filter;
165   plugin_class->plugin_info      = gnome_cinnamon_plugin_plugin_info;
166 }
167 
168 static void
gnome_cinnamon_plugin_init(CinnamonPlugin * cinnamon_plugin)169 gnome_cinnamon_plugin_init (CinnamonPlugin *cinnamon_plugin)
170 {
171 }
172 
173 static void
gnome_cinnamon_plugin_start(MetaPlugin * plugin)174 gnome_cinnamon_plugin_start (MetaPlugin *plugin)
175 {
176   CinnamonPlugin *cinnamon_plugin = CINNAMON_PLUGIN (plugin);
177 #if defined (__arm__)
178 #else
179   MetaScreen *screen;
180   MetaDisplay *display;
181   Display *xdisplay;
182 #endif
183   GError *error = NULL;
184   int status;
185 #if defined (__arm__)
186 #else
187   const char *glx_extensions;
188 #endif
189   GjsContext *gjs_context;
190 
191 #if defined (__arm__)
192   cinnamon_plugin->have_swap_event = 0;
193 #else
194   screen = meta_plugin_get_screen (plugin);
195   display = meta_screen_get_display (screen);
196 
197   xdisplay = meta_display_get_xdisplay (display);
198 
199   glXQueryExtension (xdisplay,
200                      &cinnamon_plugin->glx_error_base,
201                      &cinnamon_plugin->glx_event_base);
202 
203   glx_extensions = glXQueryExtensionsString (xdisplay,
204                                              meta_screen_get_screen_number (screen));
205   cinnamon_plugin->have_swap_event = strstr (glx_extensions, "GLX_INTEL_swap_event") != NULL;
206 #endif
207 
208   cinnamon_perf_log_define_event (cinnamon_perf_log_get_default (),
209                                "glx.swapComplete",
210                                "GL buffer swap complete event received (with timestamp of completion)",
211                                "x");
212 
213   cinnamon_plugin->global = cinnamon_global_get ();
214   _cinnamon_global_set_plugin (cinnamon_plugin->global, META_PLUGIN (cinnamon_plugin));
215 
216   gjs_context = _cinnamon_global_get_gjs_context (cinnamon_plugin->global);
217 
218   if (!gjs_context_eval (gjs_context,
219                          "imports.ui.environment.init();"
220                          "imports.ui.main.start();",
221                          -1,
222                          "<main>",
223                          &status,
224                          &error))
225     {
226       g_message ("Execution of main.js threw exception: %s", error->message);
227       g_error_free (error);
228       /* We just exit() here, since in a development environment you'll get the
229        * error in your cinnamon output, and it's way better than a busted WM,
230        * which typically manifests as a white screen.
231        *
232        * In production, we shouldn't crash =)  But if we do, we should get
233        * restarted by the session infrastructure, which is likely going
234        * to be better than some undefined state.
235        *
236        * If there was a generic "hook into bug-buddy for non-C crashes"
237        * infrastructure, here would be the place to put it.
238        */
239       exit (1);
240     }
241 }
242 
243 static void
gnome_cinnamon_plugin_dispose(GObject * object)244 gnome_cinnamon_plugin_dispose (GObject *object)
245 {
246   G_OBJECT_CLASS(gnome_cinnamon_plugin_parent_class)->dispose (object);
247 }
248 
249 static void
gnome_cinnamon_plugin_finalize(GObject * object)250 gnome_cinnamon_plugin_finalize (GObject *object)
251 {
252   G_OBJECT_CLASS(gnome_cinnamon_plugin_parent_class)->finalize (object);
253 }
254 
255 static CinnamonWM *
get_cinnamon_wm(void)256 get_cinnamon_wm (void)
257 {
258   CinnamonWM *wm;
259 
260   g_object_get (cinnamon_global_get (),
261                 "window-manager", &wm,
262                 NULL);
263   /* drop extra ref added by g_object_get */
264   g_object_unref (wm);
265 
266   return wm;
267 }
268 
269 static void
gnome_cinnamon_plugin_minimize(MetaPlugin * plugin,MetaWindowActor * actor)270 gnome_cinnamon_plugin_minimize (MetaPlugin         *plugin,
271 			     MetaWindowActor    *actor)
272 {
273   _cinnamon_wm_minimize (get_cinnamon_wm (),
274                       actor);
275 
276 }
277 
278 static void
gnome_cinnamon_plugin_maximize(MetaPlugin * plugin,MetaWindowActor * actor,gint x,gint y,gint width,gint height)279 gnome_cinnamon_plugin_maximize (MetaPlugin         *plugin,
280                              MetaWindowActor    *actor,
281                              gint                x,
282                              gint                y,
283                              gint                width,
284                              gint                height)
285 {
286   _cinnamon_wm_maximize (get_cinnamon_wm (),
287                       actor, x, y, width, height);
288 }
289 
290 static void
gnome_cinnamon_plugin_tile(MetaPlugin * plugin,MetaWindowActor * actor,gint x,gint y,gint width,gint height)291 gnome_cinnamon_plugin_tile  (MetaPlugin         *plugin,
292                              MetaWindowActor    *actor,
293                              gint                x,
294                              gint                y,
295                              gint                width,
296                              gint                height)
297 {
298   _cinnamon_wm_tile (get_cinnamon_wm (),
299                      actor, x, y, width, height);
300 }
301 
302 static void
gnome_cinnamon_plugin_unmaximize(MetaPlugin * plugin,MetaWindowActor * actor,gint x,gint y,gint width,gint height)303 gnome_cinnamon_plugin_unmaximize (MetaPlugin         *plugin,
304                                MetaWindowActor    *actor,
305                                gint                x,
306                                gint                y,
307                                gint                width,
308                                gint                height)
309 {
310   _cinnamon_wm_unmaximize (get_cinnamon_wm (),
311                         actor, x, y, width, height);
312 }
313 
314 static void
gnome_cinnamon_plugin_map(MetaPlugin * plugin,MetaWindowActor * actor)315 gnome_cinnamon_plugin_map (MetaPlugin         *plugin,
316                         MetaWindowActor    *actor)
317 {
318   _cinnamon_wm_map (get_cinnamon_wm (),
319                  actor);
320 }
321 
322 static void
gnome_cinnamon_plugin_destroy(MetaPlugin * plugin,MetaWindowActor * actor)323 gnome_cinnamon_plugin_destroy (MetaPlugin         *plugin,
324                             MetaWindowActor    *actor)
325 {
326   _cinnamon_wm_destroy (get_cinnamon_wm (),
327                      actor);
328 }
329 
330 static void
gnome_cinnamon_plugin_switch_workspace(MetaPlugin * plugin,gint from,gint to,MetaMotionDirection direction)331 gnome_cinnamon_plugin_switch_workspace (MetaPlugin         *plugin,
332                                      gint                from,
333                                      gint                to,
334                                      MetaMotionDirection direction)
335 {
336   _cinnamon_wm_switch_workspace (get_cinnamon_wm(), from, to, direction);
337 }
338 
339 static void
gnome_cinnamon_plugin_kill_window_effects(MetaPlugin * plugin,MetaWindowActor * actor)340 gnome_cinnamon_plugin_kill_window_effects (MetaPlugin         *plugin,
341                                         MetaWindowActor    *actor)
342 {
343   _cinnamon_wm_kill_window_effects (get_cinnamon_wm(), actor);
344 }
345 
346 static void
gnome_cinnamon_plugin_show_tile_preview(MetaPlugin * plugin,MetaWindow * window,MetaRectangle * tile_rect,int tile_monitor,guint snap_queued)347 gnome_cinnamon_plugin_show_tile_preview (MetaPlugin     *plugin,
348                                          MetaWindow     *window,
349                                          MetaRectangle  *tile_rect,
350                                          int            tile_monitor,
351                                          guint          snap_queued)
352 {
353     _cinnamon_wm_show_tile_preview (get_cinnamon_wm (), window, tile_rect,
354                                     tile_monitor, snap_queued);
355 }
356 
357 static void
gnome_cinnamon_plugin_hide_tile_preview(MetaPlugin * plugin)358 gnome_cinnamon_plugin_hide_tile_preview (MetaPlugin *plugin)
359 {
360     _cinnamon_wm_hide_tile_preview (get_cinnamon_wm ());
361 }
362 
363 static void
gnome_cinnamon_plugin_show_hud_preview(MetaPlugin * plugin,guint current_proximity_zone,MetaRectangle * work_area,guint snap_queued)364 gnome_cinnamon_plugin_show_hud_preview (MetaPlugin      *plugin,
365                                         guint           current_proximity_zone,
366                                         MetaRectangle   *work_area,
367                                         guint           snap_queued)
368 {
369     _cinnamon_wm_show_hud_preview (get_cinnamon_wm (), current_proximity_zone,
370                                    work_area, snap_queued);
371 }
372 
373 static void
gnome_cinnamon_plugin_hide_hud_preview(MetaPlugin * plugin)374 gnome_cinnamon_plugin_hide_hud_preview (MetaPlugin *plugin)
375 {
376     _cinnamon_wm_hide_hud_preview (get_cinnamon_wm ());
377 }
378 
379 static gboolean
gnome_cinnamon_plugin_xevent_filter(MetaPlugin * plugin,XEvent * xev)380 gnome_cinnamon_plugin_xevent_filter (MetaPlugin *plugin,
381                                   XEvent     *xev)
382 {
383   MetaScreen *screen = meta_plugin_get_screen (plugin);
384   ClutterStage *stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
385 
386   CinnamonPlugin *cinnamon_plugin = CINNAMON_PLUGIN (plugin);
387 #ifdef GLX_INTEL_swap_event
388   if (cinnamon_plugin->have_swap_event &&
389       xev->type == (cinnamon_plugin->glx_event_base + GLX_BufferSwapComplete))
390     {
391       GLXBufferSwapComplete *swap_complete_event;
392       swap_complete_event = (GLXBufferSwapComplete *)xev;
393 
394       /* Buggy early versions of the INTEL_swap_event implementation in Mesa
395        * can send this with a ust of 0. Simplify life for consumers
396        * by ignoring such events */
397       if (swap_complete_event->ust != 0)
398         cinnamon_perf_log_event_x (cinnamon_perf_log_get_default (),
399                                 "glx.swapComplete",
400                                 swap_complete_event->ust);
401     }
402 #endif
403 
404   if ((xev->xany.type == EnterNotify || xev->xany.type == LeaveNotify)
405       && xev->xcrossing.window == clutter_x11_get_stage_window (stage))
406     {
407       /* If the pointer enters a child of the stage window (eg, a
408        * trayicon), we want to consider it to still be in the stage,
409        * so don't let Clutter see the event.
410        */
411       if (xev->xcrossing.detail == NotifyInferior)
412         return TRUE;
413 
414       /* If the pointer is grabbed by a window it is not currently in,
415        * filter that out as well. In particular, if a trayicon grabs
416        * the pointer after a click on its label, we don't want to hide
417        * the message tray. Filtering out this event will leave Clutter
418        * out of sync, but that happens fairly often with grabs, and we
419        * can work around it. (Eg, cinnamon_global_sync_pointer().)
420        */
421       if (xev->xcrossing.mode == NotifyGrab &&
422           (xev->xcrossing.detail == NotifyNonlinear ||
423            xev->xcrossing.detail == NotifyNonlinearVirtual))
424         return TRUE;
425     }
426 
427   /*
428    * Pass the event to cinnamon-global
429    */
430   if (_cinnamon_global_check_xdnd_event (cinnamon_plugin->global, xev))
431     return TRUE;
432 
433   return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
434 }
435 
436 static const
gnome_cinnamon_plugin_plugin_info(MetaPlugin * plugin)437 MetaPluginInfo *gnome_cinnamon_plugin_plugin_info (MetaPlugin *plugin)
438 {
439   static const MetaPluginInfo info = {
440     .name = "Cinnamon",
441     .version = "0.1",
442     .author = "Various",
443     .license = "GPLv2+",
444     .description = "Provides Cinnamon core functionality"
445   };
446 
447   return &info;
448 }
449