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