Merge lp:~smspillaz/compiz-core/compiz-core.lim into lp:compiz-core

Proposed by Sam Spilsbury
Status: Superseded
Proposed branch: lp:~smspillaz/compiz-core/compiz-core.lim
Merge into: lp:compiz-core
Diff against target: 1432 lines (+1077/-11)
16 files modified
gtk/window-decorator/CMakeLists.txt (+6/-0)
gtk/window-decorator/decorator.c (+32/-6)
gtk/window-decorator/events.c (+115/-3)
gtk/window-decorator/gtk-window-decorator.c (+56/-0)
gtk/window-decorator/gtk-window-decorator.h (+8/-1)
gtk/window-decorator/local-menus/CMakeLists.txt (+60/-0)
gtk/window-decorator/local-menus/src/local-menus.c (+423/-0)
gtk/window-decorator/local-menus/src/local-menus.h (+120/-0)
gtk/window-decorator/local-menus/tests/CMakeLists.txt (+3/-0)
gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt (+21/-0)
gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp (+27/-0)
gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt (+21/-0)
gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp (+77/-0)
gtk/window-decorator/local-menus/tests/test-local-menu.h (+49/-0)
gtk/window-decorator/metacity.c (+54/-0)
gtk/window-decorator/wnck.c (+5/-1)
To merge this branch: bzr merge lp:~smspillaz/compiz-core/compiz-core.lim
Reviewer Review Type Date Requested Status
Marco Trevisan (Treviño) Needs Fixing
Daniel van Vugt Approve
Alan Griffiths Abstain
Sam Spilsbury Pending
Review via email: mp+94641@code.launchpad.net

This proposal supersedes a proposal from 2012-02-22.

This proposal has been superseded by a proposal from 2012-02-28.

Description of the change

Adds support for Ubuntu's "locally integrated menu bars" into gtk-window-decorator.

To post a comment you must log in.
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

8 +#define GWD_SHOW_LOCAL_MENU (WNCK_WINDOW_ACTION_BELOW << 1)

No plausible reason for using a macro. In C++ constants (enum or unsigned int) are better as they respect scope rules. Also, should this be inside "#ifdef META_HAS_LOCAL_MENUS" conditional?

296 button_layout->right_buttons[2] = META_BUTTON_FUNCTION_CLOSE;
297
298 - for (i = 3; i < MAX_BUTTONS_PER_CORNER; i++)
299 + for (i = 4; i < MAX_BUTTONS_PER_CORNER; i++)
300 button_layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;
301 }

I'm not sure what the magic number "3"->"4" is. But we now seem to miss 3 entirely. Is that correct?

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

> 8 +#define GWD_SHOW_LOCAL_MENU (WNCK_WINDOW_ACTION_BELOW << 1)
>
> No plausible reason for using a macro. In C++ constants (enum or unsigned
> int) are better as they respect scope rules. Also, should this be inside
> "#ifdef META_HAS_LOCAL_MENUS" conditional?
>

Its C

>
> 296 button_layout->right_buttons[2] = META_BUTTON_FUNCTION_CLOSE;
> 297
> 298 - for (i = 3; i < MAX_BUTTONS_PER_CORNER; i++)
> 299 + for (i = 4; i < MAX_BUTTONS_PER_CORNER; i++)
> 300 button_layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;
> 301 }
>
> I'm not sure what the magic number "3"->"4" is. But we now seem to miss 3
> entirely. Is that correct?

Its for initializing the array, though maybe thats wrong. Good find let me check

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Please resubmit for target branch lp:compiz-core (0.9.7)

review: Needs Resubmitting
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

Looks better

review: Approve
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

(More work needed to adopt to trevino;s changes)

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Fails to build ifndef META_HAS_LOCAL_MENUS

[ 0%] Building C object gtk/window-decorator/local-menus/CMakeFiles/gtk_window_decorator_local_menus.dir/src/local-menus.c.o
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/src/local-menus.c:36:1: error: ‘gwd_menu_mode_changed’ defined but not used [-Werror=unused-function]
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/src/local-menus.c:72:1: error: ‘on_local_menu_activated’ defined but not used [-Werror=unused-function]
cc1: all warnings being treated as errors

review: Needs Fixing
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

+ gboolean empty = g_strcmp0 (entry_id, "") == 0;

You can do the same with:
gboolean empty = (!entry_id || entry_id[0] == '\0');

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

If we're trying to be cryptic...

   gboolean empty = g_strcmp0 (entry_id, "") == 0;

can be written:

   gboolean empty = !entry_id || !*entry_id;

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

Ah, Sam remember to update the "EntryActivated" signal against the new signature s(iiuu).

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Yep, done

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Fails to build for a whole new reason now :(

Scanning dependencies of target gtk_window_decorator_force_local_menu_on_test
[ 5%] Building CXX object gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeFiles/gtk_window_decorator_force_local_menu_on_test.dir/test-force-local-menu-on.cpp.o
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp: In member function ‘virtual void GtkWindowDecoratorTestLocalMenuLayout_TestForceNoCloseButtonSet_Test::TestBody()’:
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp:56:5: error: ‘META_BUTTON_FUNCTION_WINDOW_MENU’ was not declared in this scope
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp: In member function ‘virtual void GtkWindowDecoratorTestLocalMenuLayout_TestForceNoCloseMinimizeMaximizeButtonSet_Test::TestBody()’:
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp:69:5: error: ‘META_BUTTON_FUNCTION_WINDOW_MENU’ was not declared in this scope
make[2]: *** [gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeFiles/gtk_window_decorator_force_local_menu_on_test.dir/test-force-local-menu-on.cpp.o] Error 1
make[1]: *** [gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeFiles/gtk_window_decorator_force_local_menu_on_test.dir/all] Error 2
make: *** [all] Error 2

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Tests needed to be disabled on META_HAS_LOCAL_MENUS, thanks for catching that.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Test cases fail (crash). Maybe they need to be disabled if local menus are disabled...

The following tests FAILED:
   1 - gtk_window_decorator_local_menus_on_off (SEGFAULT)
   2 - gtk_window_decorator_force_local_menu_on (SEGFAULT)
Errors while running CTest
make: *** [test] Error 8

-----

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff709bb70 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
(gdb) bt
#0 0x00007ffff709bb70 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#1 0x00007ffff709e2f5 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#2 0x00007ffff6dc29f0 in g_object_unref ()
   from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#3 0x000000000042ef7d in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) ()
#4 0x000000000042751f in testing::Test::Run() ()
#5 0x000000000042763d in testing::TestInfo::Run() ()
#6 0x0000000000427787 in testing::TestCase::Run() ()
#7 0x0000000000427a17 in testing::internal::UnitTestImpl::RunAllTests() ()
#8 0x000000000042eafd in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) ()
#9 0x0000000000426c0b in testing::UnitTest::Run() ()
#10 0x0000000000413145 in main ()

-----

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff709bb70 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
(gdb) bt
#0 0x00007ffff709bb70 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#1 0x00007ffff709e2f5 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#2 0x00007ffff6dc29f0 in g_object_unref ()
   from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#3 0x000000000042ef7d in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) ()
#4 0x000000000042751f in testing::Test::Run() ()
#5 0x000000000042763d in testing::TestInfo::Run() ()
#6 0x0000000000427787 in testing::TestCase::Run() ()
#7 0x0000000000427a17 in testing::internal::UnitTestImpl::RunAllTests() ()
#8 0x000000000042eafd in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) ()
#9 0x0000000000426c0b in testing::UnitTest::Run() ()
#10 0x0000000000413145 in main ()

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Huh, that's weird. I've disabled META_HAS_LOCAL_MENUS here, and make sure to link to the system libmetacity-private and the tests are still passing for me.

Can you post the output of the individual tests before it reaches the point where they crash? Or do they just crash without outputting anything ?

(Could be something to do with the SetUp () function, so I'll give that a try

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Confirmed all unit tests now pass and no obvious bugs in manual testing.

I still think we need to be tidier with the CMake syntax as I've mentioned before...

review: Approve
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Yes, one day I'm going to introduce a bunch of functions to the CMake which reduce the verbosity.

Daniel - if you want to test this for realz, you can use the following:

lp:~smspillaz/metacity/metacity.lim
lp:~3v1n0/unity/lim-panel
lp:~indicator-applet-developers/indicator-appmenu/local-menus-single-menu
lp:~smspillaz/light-themes/light-themes.limv4

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

Looks OK, Builds OK, automated tests run. (With no META_HAS_LOCAL_MENUS)

review: Approve
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

I've tested it, to be even better, maybe you could also sync the title geometries once you opened/closed the menu, but that can be optional.
One thing you need to fix is the double-click over the title, that currently doesn't work as expected.
Look at the logic I've used in PanelTitlebarGrabArea.

Then I guess we need to define with design better one case: if a window is unfocused, and the user presses over the title / icon, should we just focus it or also open the menu? Currently we do both, but I'm not sure this is the best choice.

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

Ah, don't care about syncing geometries. You don't need that and could cause problems to the panel... :)

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

Ah, one more thing. Please check if the window you're decorating has the "_UBUNTU_APPMENU_UNIQUE_NAME" utf8 atom set to check if it has menus.

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

Ah, I forgot... The decorator is still missing to react to the com.canonical.Unity.Panel.Service.EntryActivateRequest signal, that happens when you do Alt+F on terminal for example... :)

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

OK, I'll take care of these two things too. Thanks.

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

No obvious errors

review: Abstain
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Confirmed again all unit tests now pass and no obvious bugs in manual testing.

Though, like Alan, I have not reviewed the theoretical correctness of the code in detail.

review: Approve
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Mostly it works, well.

By the way, I've tested the support for the EntryActivateRequest and even if it's correctly handled if a window is in focus (i.e. pressing Alt+F on the terminal causes the menu to show in the right place), otherwise if you have this scenario: a maximized window in focus, and a restored window unfocused, when pressing Alt+F (or something, that will be managed by the focused window) gtk-window-decorator crashes.

This is the stacktrace I have: http://paste.ubuntu.com/859831/
Actually you should just ignore that signal if no decorated window is in focus.

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Ack.

3002. By Sam Spilsbury

Fix a crash when trying to show the window menu on a non decorated window

3003. By Sam Spilsbury

Tell us if we have them

3004. By Sam Spilsbury

Merged lp:compiz-core

3005. By Sam Spilsbury

We also need to test the property too

3006. By Sam Spilsbury

Grab buttons too so that we can process motion events on the titlebar and
move the window instead.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'gtk/window-decorator/CMakeLists.txt'
--- gtk/window-decorator/CMakeLists.txt 2011-02-16 17:39:56 +0000
+++ gtk/window-decorator/CMakeLists.txt 2012-02-25 05:11:17 +0000
@@ -4,7 +4,10 @@
4 set (CMAKE_INSTALL_RPATH ${libdir})4 set (CMAKE_INSTALL_RPATH ${libdir})
5 endif (COMPIZ_BUILD_WITH_RPATH)5 endif (COMPIZ_BUILD_WITH_RPATH)
66
7 add_subdirectory (local-menus)
8
7 include_directories (9 include_directories (
10 ${CMAKE_CURRENT_SOURCE_DIR}/local-menus/src
8 ${compiz_SOURCE_DIR}/include11 ${compiz_SOURCE_DIR}/include
9 ${CMAKE_BINARY_DIR}/gtk12 ${CMAKE_BINARY_DIR}/gtk
10 ${GTK_WINDOW_DECORATOR_INCLUDE_DIRS}13 ${GTK_WINDOW_DECORATOR_INCLUDE_DIRS}
@@ -63,6 +66,9 @@
63 target_link_libraries (66 target_link_libraries (
64 gtk-window-decorator67 gtk-window-decorator
65 decoration68 decoration
69
70 gtk_window_decorator_local_menus
71
66 ${GTK_WINDOW_DECORATOR_LIBRARIES}72 ${GTK_WINDOW_DECORATOR_LIBRARIES}
67 ${GCONF_LIBRARIES}73 ${GCONF_LIBRARIES}
68 ${DBUS_GLIB_LIBRARIES}74 ${DBUS_GLIB_LIBRARIES}
6975
=== modified file 'gtk/window-decorator/decorator.c'
--- gtk/window-decorator/decorator.c 2012-02-20 07:42:20 +0000
+++ gtk/window-decorator/decorator.c 2012-02-25 05:11:17 +0000
@@ -24,6 +24,7 @@
24 */24 */
2525
26#include "gtk-window-decorator.h"26#include "gtk-window-decorator.h"
27#include "local-menus.h"
2728
28decor_frame_t *29decor_frame_t *
29create_normal_frame (const gchar *type)30create_normal_frame (const gchar *type)
@@ -230,12 +231,16 @@
230void231void
231update_event_windows (WnckWindow *win)232update_event_windows (WnckWindow *win)
232{233{
234#define GWD_SHOW_LOCAL_MENU (WNCK_WINDOW_ACTION_BELOW << 1)
233 Display *xdisplay;235 Display *xdisplay;
234 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");236 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
235 gint x0, y0, width, height, x, y, w, h;237 gint x0, y0, width, height, x, y, w, h;
236 gint i, j, k, l;238 gint i, j, k, l;
237 gint actions = d->actions;239 gint actions = d->actions;
238240
241 if (gwd_window_should_have_local_menu (win))
242 actions |= GWD_SHOW_LOCAL_MENU;
243
239 xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());244 xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
240245
241 /* Get the geometry of the client */246 /* Get the geometry of the client */
@@ -298,11 +303,19 @@
298 XMapWindow (xdisplay, d->event_windows[i][j].window);303 XMapWindow (xdisplay, d->event_windows[i][j].window);
299 XMoveResizeWindow (xdisplay, d->event_windows[i][j].window,304 XMoveResizeWindow (xdisplay, d->event_windows[i][j].window,
300 x, y, w, h);305 x, y, w, h);
306
307 BoxPtr box = &d->event_windows[i][j].pos;
308 box->x1 = x;
309 box->x2 = x + w;
310 box->y1 = y;
311 box->y2 = y + h;
301 }312 }
302 /* No parent and no geometry - unmap all event windows */313 /* No parent and no geometry - unmap all event windows */
303 else if (!d->frame_window)314 else if (!d->frame_window)
304 {315 {
305 XUnmapWindow (xdisplay, d->event_windows[i][j].window);316 XUnmapWindow (xdisplay, d->event_windows[i][j].window);
317
318 memset (&d->event_windows[i][j].pos, 0, sizeof (Box));
306 }319 }
307 }320 }
308 }321 }
@@ -326,12 +339,18 @@
326 WNCK_WINDOW_ACTION_STICK,339 WNCK_WINDOW_ACTION_STICK,
327 WNCK_WINDOW_ACTION_UNSHADE,340 WNCK_WINDOW_ACTION_UNSHADE,
328 WNCK_WINDOW_ACTION_ABOVE,341 WNCK_WINDOW_ACTION_ABOVE,
329 WNCK_WINDOW_ACTION_UNSTICK342 WNCK_WINDOW_ACTION_UNSTICK,
330#else343#else
331 0,344 0,
332 0,345 0,
333 0,346 0,
334 0,347 0,
348 0,
349#endif
350
351#ifdef META_HAS_LOCAL_MENUS
352 GWD_SHOW_LOCAL_MENU
353#else
335 0354 0
336#endif355#endif
337356
@@ -371,10 +390,17 @@
371 Window win = d->button_windows[i].window;390 Window win = d->button_windows[i].window;
372 XMapWindow (xdisplay, win);391 XMapWindow (xdisplay, win);
373 XMoveResizeWindow (xdisplay, win, x, y, w, h);392 XMoveResizeWindow (xdisplay, win, x, y, w, h);
393
394 BoxPtr box = &d->button_windows[i].pos;
395 box->x1 = x;
396 box->x2 = x + w;
397 box->y1 = y;
398 box->y2 = y + h;
374 }399 }
375 else if (!d->frame_window)400 else if (!d->frame_window)
376 {401 {
377 XUnmapWindow (xdisplay, d->button_windows[i].window);402 XUnmapWindow (xdisplay, d->button_windows[i].window);
403 memset (&d->button_windows[i].pos, 0, sizeof (Box));
378 }404 }
379 }405 }
380406
381407
=== modified file 'gtk/window-decorator/events.c'
--- gtk/window-decorator/events.c 2012-02-20 07:46:35 +0000
+++ gtk/window-decorator/events.c 2012-02-25 05:11:17 +0000
@@ -24,6 +24,16 @@
24 */24 */
2525
26#include "gtk-window-decorator.h" 26#include "gtk-window-decorator.h"
27#include "local-menus.h"
28
29typedef struct _delayed_move_info
30{
31 WnckWindow *win;
32 int x_root;
33 int y_root;
34 unsigned int button;
35 unsigned int time;
36} delayed_move_info;
2737
28void38void
29move_resize_window (WnckWindow *win,39move_resize_window (WnckWindow *win,
@@ -65,8 +75,9 @@
65 ev.xclient.data.l[3] = gtkwd_event->button;75 ev.xclient.data.l[3] = gtkwd_event->button;
66 ev.xclient.data.l[4] = 1;76 ev.xclient.data.l[4] = 1;
6777
68 XUngrabPointer (xdisplay, gtkwd_event->time);78 XAllowEvents (xdisplay, AsyncKeyboard | AsyncPointer, CurrentTime);
69 XUngrabKeyboard (xdisplay, gtkwd_event->time);79 XUngrabPointer (xdisplay, CurrentTime);
80 XUngrabKeyboard (xdisplay, CurrentTime);
7081
71 XSendEvent (xdisplay, xroot, FALSE,82 XSendEvent (xdisplay, xroot, FALSE,
72 SubstructureRedirectMask | SubstructureNotifyMask,83 SubstructureRedirectMask | SubstructureNotifyMask,
@@ -75,6 +86,24 @@
75 XSync (xdisplay, FALSE);86 XSync (xdisplay, FALSE);
76}87}
7788
89static gboolean
90move_resize_window_on_timeout (gpointer user_data)
91{
92 delayed_move_info *info = (delayed_move_info *) user_data;
93
94 decor_event event;
95 event.x = 0;
96 event.y = 0;
97 event.x_root = info->x_root;
98 event.y_root = info->y_root;
99 event.button = info->button;
100 event.window = wnck_window_get_xid (info->win);
101
102 move_resize_window (info->win, WM_MOVERESIZE_MOVE, &event);
103
104 return FALSE;
105}
106
78void107void
79common_button_event (WnckWindow *win,108common_button_event (WnckWindow *win,
80 decor_event *gtkwd_event,109 decor_event *gtkwd_event,
@@ -380,6 +409,75 @@
380}409}
381410
382void411void
412on_local_menu_hidden (gpointer user_data)
413{
414 Window xid = GPOINTER_TO_INT (user_data);
415 WnckWindow *win = wnck_window_get (xid);
416 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
417
418 d->button_states[BUTTON_WINDOW_MENU] &= ~PRESSED_EVENT_WINDOW;
419
420 queue_decor_draw (d);
421}
422
423void
424window_menu_button_event (WnckWindow *win,
425 decor_event *gtkwd_event,
426 decor_event_type gtkwd_type)
427{
428 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
429 guint state = d->button_states[BUTTON_WINDOW_MENU];
430
431 common_button_event (win, gtkwd_event, gtkwd_type,
432 BUTTON_WINDOW_MENU, 1, _("Window Menu"));
433
434 switch (gtkwd_type) {
435 case GButtonPress:
436 if (gtkwd_event->button == 1)
437 {
438 if (d->button_states[BUTTON_WINDOW_MENU] & BUTTON_EVENT_ACTION_STATE)
439 {
440 delayed_move_info *info = g_new0 (delayed_move_info, 1);
441
442 info->button = gtkwd_event->button;
443 info->time = gtkwd_event->time;
444 info->win = win;
445 info->x_root = gtkwd_event->x_root;
446 info->y_root = gtkwd_event->y_root;
447
448 gwd_prepare_show_local_menu ((start_move_window_cb) move_resize_window_on_timeout, (gpointer) info);
449 }
450 }
451 break;
452 case GButtonRelease:
453 if (gtkwd_event->button == 1)
454 if (state)
455 {
456 int win_x, win_y;
457 int box_x = d->button_windows[BUTTON_WINDOW_MENU].pos.x1;
458
459 wnck_window_get_geometry (win, &win_x, &win_y, NULL, NULL);
460
461 int x = win_x + box_x;
462 int y = win_y + d->context->extents.top;
463
464 gwd_show_local_menu (gdk_x11_display_get_xdisplay (gdk_display_get_default ()),
465 wnck_window_get_xid (win),
466 x, y,
467 box_x,
468 d->context->extents.top,
469 gtkwd_event->button,
470 gtkwd_event->time,
471 (show_window_menu_hidden_cb) on_local_menu_hidden,
472 GINT_TO_POINTER (wnck_window_get_xid (d->win)));
473 }
474 break;
475 default:
476 break;
477 }
478}
479
480void
383handle_title_button_event (WnckWindow *win,481handle_title_button_event (WnckWindow *win,
384 int action,482 int action,
385 decor_event *gtkwd_event)483 decor_event *gtkwd_event)
@@ -485,7 +583,17 @@
485583
486 restack_window (win, Above);584 restack_window (win, Above);
487585
488 move_resize_window (win, WM_MOVERESIZE_MOVE, gtkwd_event);586 delayed_move_info *info = g_new0 (delayed_move_info, 1);
587
588 info->x_root = gtkwd_event->x_root;
589 info->y_root = gtkwd_event->y_root;
590 info->button = gtkwd_event->button;
591 info->time = gtkwd_event->time;
592 info->win = win;
593
594 move_resize_window_on_timeout ((gpointer) info);
595
596 g_free (info);
489 }597 }
490 }598 }
491 else if (gtkwd_event->button == 2)599 else if (gtkwd_event->button == 2)
@@ -1031,6 +1139,10 @@
1031 if (get_window_prop (xevent->xproperty.window, select_window_atom, &select))1139 if (get_window_prop (xevent->xproperty.window, select_window_atom, &select))
1032 update_switcher_window (xevent->xproperty.window, select);1140 update_switcher_window (xevent->xproperty.window, select);
1033 }1141 }
1142 else if (xevent->xproperty.atom == ubuntu_appmenu_unique_name_atom)
1143 {
1144 local_menu_cache_reload_xwindow (gdk_x11_display_get_xdisplay (gdkdisplay), xevent->xproperty.window);
1145 }
1034 break;1146 break;
1035 case DestroyNotify:1147 case DestroyNotify:
1036 g_hash_table_remove (frame_table,1148 g_hash_table_remove (frame_table,
10371149
=== modified file 'gtk/window-decorator/gtk-window-decorator.c'
--- gtk/window-decorator/gtk-window-decorator.c 2011-10-13 12:22:14 +0000
+++ gtk/window-decorator/gtk-window-decorator.c 2012-02-25 05:11:17 +0000
@@ -24,6 +24,7 @@
24 */24 */
2525
26#include "gtk-window-decorator.h"26#include "gtk-window-decorator.h"
27#include "local-menus.h"
2728
28gboolean minimal = FALSE;29gboolean minimal = FALSE;
2930
@@ -48,6 +49,7 @@
48Atom toolkit_action_atom;49Atom toolkit_action_atom;
49Atom toolkit_action_window_menu_atom;50Atom toolkit_action_window_menu_atom;
50Atom toolkit_action_force_quit_dialog_atom;51Atom toolkit_action_force_quit_dialog_atom;
52Atom ubuntu_appmenu_unique_name_atom;
5153
52Atom net_wm_state_atom;54Atom net_wm_state_atom;
53Atom net_wm_state_modal_atom;55Atom net_wm_state_modal_atom;
@@ -128,6 +130,35 @@
128 "normal", "modal_dialog", "dialog", "menu", "utility"130 "normal", "modal_dialog", "dialog", "menu", "utility"
129};131};
130132
133Box *
134get_active_window_local_menu_rectangle (gpointer user_data, int *dx, int *dy, int *top_height, Window *xid)
135{
136 WnckScreen *screen = (WnckScreen *) user_data;
137 WnckWindow *window = wnck_screen_get_active_window (screen);
138 int width, height;
139 decor_t *d = g_object_get_data (G_OBJECT (window), "decor");
140
141 wnck_window_get_geometry (window, dx, dy, &width, &height);
142
143 *top_height = d->context->extents.top;
144
145 *xid = wnck_window_get_xid (window);
146
147 Box *rect = &d->button_windows[BUTTON_WINDOW_MENU].pos;
148
149 return rect;
150}
151
152void
153on_local_menu_window_menu_updated (gpointer user_data)
154{
155 Window xid = GPOINTER_TO_INT (user_data);
156 WnckWindow *window = wnck_window_get (xid);
157 decor_t *d = g_object_get_data (G_OBJECT (window), "decor");
158
159 queue_decor_draw (d);
160}
161
131int162int
132main (int argc, char *argv[])163main (int argc, char *argv[])
133{164{
@@ -304,6 +335,7 @@
304335
305 net_wm_state_atom = XInternAtom (xdisplay,"_NET_WM_STATE", 0);336 net_wm_state_atom = XInternAtom (xdisplay,"_NET_WM_STATE", 0);
306 net_wm_state_modal_atom = XInternAtom (xdisplay, "_NET_WM_STATE_MODAL", 0);337 net_wm_state_modal_atom = XInternAtom (xdisplay, "_NET_WM_STATE_MODAL", 0);
338 ubuntu_appmenu_unique_name_atom = XInternAtom (xdisplay, "_UBUNTU_APPMENU_UNIQUE_NAME", 0);
307339
308 status = decor_acquire_dm_session (xdisplay,340 status = decor_acquire_dm_session (xdisplay,
309 gdk_screen_get_number (gdkscreen),341 gdk_screen_get_number (gdkscreen),
@@ -448,8 +480,32 @@
448480
449 update_default_decorations (gdkscreen);481 update_default_decorations (gdkscreen);
450482
483#ifdef META_HAS_LOCAL_MENUS
484 GDBusConnection *conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
485 local_menu_entry_activated_request_funcs funcs =
486 {
487 get_active_window_local_menu_rectangle,
488 on_local_menu_window_menu_updated
489 };
490
491 if (conn)
492 {
493 global_lim_listener = g_dbus_proxy_new_sync (conn, 0, NULL, "com.canonical.Unity.Panel.Service",
494 "/com/canonical/Unity/Panel/Service",
495 "com.canonical.Unity.Panel.Service",
496 NULL, NULL);
497
498 g_signal_connect (G_OBJECT (global_lim_listener), "g-signal", G_CALLBACK (local_menu_entry_activated_request), (gpointer) &funcs);
499 }
500#endif
451 gtk_main ();501 gtk_main ();
502#ifdef META_HAS_LOCAL_MENUS
503 if (global_lim_listener)
504 g_object_unref (global_lim_listener);
452505
506 if (conn)
507 g_object_unref (conn);
508#endif
453 win = windows = wnck_screen_get_windows (screen);509 win = windows = wnck_screen_get_windows (screen);
454510
455 while (win != NULL)511 while (win != NULL)
456512
=== modified file 'gtk/window-decorator/gtk-window-decorator.h'
--- gtk/window-decorator/gtk-window-decorator.h 2011-10-13 12:22:14 +0000
+++ gtk/window-decorator/gtk-window-decorator.h 2012-02-25 05:11:17 +0000
@@ -306,6 +306,7 @@
306extern Atom toolkit_action_force_quit_dialog_atom;306extern Atom toolkit_action_force_quit_dialog_atom;
307extern Atom net_wm_state_atom;307extern Atom net_wm_state_atom;
308extern Atom net_wm_state_modal_atom;308extern Atom net_wm_state_modal_atom;
309extern Atom ubuntu_appmenu_unique_name_atom;
309310
310extern Time dm_sn_timestamp;311extern Time dm_sn_timestamp;
311312
@@ -328,7 +329,8 @@
328#define BUTTON_UNSHADE 7329#define BUTTON_UNSHADE 7
329#define BUTTON_UNABOVE 8330#define BUTTON_UNABOVE 8
330#define BUTTON_UNSTICK 9331#define BUTTON_UNSTICK 9
331#define BUTTON_NUM 10332#define BUTTON_WINDOW_MENU 10
333#define BUTTON_NUM 11
332334
333struct _pos {335struct _pos {
334 int x, y, w, h;336 int x, y, w, h;
@@ -1013,6 +1015,11 @@
1013 decor_event_type gtkwd_type);1015 decor_event_type gtkwd_type);
10141016
1015void1017void
1018window_menu_button_event (WnckWindow *win,
1019 decor_event *gtkwd_event,
1020 decor_event_type gtkwd_type);
1021
1022void
1016handle_title_button_event (WnckWindow *win,1023handle_title_button_event (WnckWindow *win,
1017 int action,1024 int action,
1018 decor_event *gtkwd_event);1025 decor_event *gtkwd_event);
10191026
=== added directory 'gtk/window-decorator/local-menus'
=== added file 'gtk/window-decorator/local-menus/CMakeLists.txt'
--- gtk/window-decorator/local-menus/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ gtk/window-decorator/local-menus/CMakeLists.txt 2012-02-25 05:11:17 +0000
@@ -0,0 +1,60 @@
1pkg_check_modules(
2 LOCAL_MENUS
3 REQUIRED
4 glib-2.0 gio-2.0 libwnck-1.0 gtk+-2.0>=2.18.0
5)
6
7INCLUDE_DIRECTORIES(
8 ${CMAKE_CURRENT_SOURCE_DIR}/include
9 ${CMAKE_CURRENT_SOURCE_DIR}/src
10
11 ${compiz_SOURCE_DIR}/gtk/window-decorator
12
13 ${Boost_INCLUDE_DIRS}
14
15 ${LOCAL_MENUS_INCLUDE_DIRS}
16 ${METACITY_INCLUDE_DIRS}
17)
18
19LINK_DIRECTORIES (${LOCAL_MENUS_LIBRARY_DIRS})
20
21SET(
22 PUBLIC_HEADERS
23)
24
25SET(
26 PRIVATE_HEADERS
27 ${CMAKE_CURRENT_SOURCE_DIR}/src/local-menus.h
28)
29
30SET(
31 SRCS
32 ${CMAKE_CURRENT_SOURCE_DIR}/src/local-menus.c
33)
34
35ADD_LIBRARY(
36 gtk_window_decorator_local_menus STATIC
37
38 ${SRCS}
39
40 ${PUBLIC_HEADERS}
41 ${PRIVATE_HEADERS}
42)
43
44IF (COMPIZ_BUILD_TESTING)
45ADD_SUBDIRECTORY( ${CMAKE_CURRENT_SOURCE_DIR}/tests )
46ENDIF (COMPIZ_BUILD_TESTING)
47
48SET_TARGET_PROPERTIES(
49 gtk_window_decorator_local_menus PROPERTIES
50 PUBLIC_HEADER "${PUBLIC_HEADERS}"
51)
52
53install (FILES ${PUBLIC_HEADERS} DESTINATION ${COMPIZ_CORE_INCLUDE_DIR})
54
55TARGET_LINK_LIBRARIES(
56 gtk_window_decorator_local_menus
57
58 ${LOCAL_MENUS_LIBRARIES}
59)
60
061
=== added directory 'gtk/window-decorator/local-menus/include'
=== added directory 'gtk/window-decorator/local-menus/src'
=== added file 'gtk/window-decorator/local-menus/src/local-menus.c'
--- gtk/window-decorator/local-menus/src/local-menus.c 1970-01-01 00:00:00 +0000
+++ gtk/window-decorator/local-menus/src/local-menus.c 2012-02-25 05:11:17 +0000
@@ -0,0 +1,423 @@
1/*
2 * Copyright © 2006 Novell, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 *
19 * Author: David Reveman <davidr@novell.com>
20 *
21 * 2D Mode: Copyright © 2010 Sam Spilsbury <smspillaz@gmail.com>
22 * Frames Management: Copright © 2011 Canonical Ltd.
23 * Authored By: Sam Spilsbury <sam.spilsbury@canonical.com>
24 */
25
26#include <string.h>
27#include "local-menus.h"
28#include <gdk/gdk.h>
29#include <gdk/gdkx.h>
30
31#define GLOBAL 0
32#define LOCAL 1
33
34#define ALLOWED 2
35#define NOT_ALLOWED 1
36
37gint menu_mode = GLOBAL;
38
39GDBusProxy *global_lim_listener;
40
41
42#ifdef META_HAS_LOCAL_MENUS
43static void
44gwd_menu_mode_changed (GSettings *settings,
45 gchar *key,
46 gpointer user_data)
47{
48 menu_mode = g_settings_get_enum (settings, "menu-mode");
49}
50#endif
51
52active_local_menu *active_menu;
53pending_local_menu *pending_menu;
54
55GHashTable *get_windows_with_menus_table ()
56{
57 static GHashTable *windows_with_menus = NULL;
58
59 if (!windows_with_menus)
60 windows_with_menus = g_hash_table_new (NULL, NULL);
61
62 return windows_with_menus;
63}
64
65static gboolean read_xprop_for_window (Display *dpy, Window xid)
66{
67 Atom ubuntu_appmenu_unique_name = XInternAtom (dpy, "_UBUNTU_APPMENU_UNIQUE_NAME", FALSE);
68 Atom utf8_string = XInternAtom (dpy, "UTF8_STRING", FALSE);
69 Atom actual;
70 int fmt;
71 unsigned long nitems, nleft;
72 unsigned char *prop;
73 XGetWindowProperty (dpy, xid, ubuntu_appmenu_unique_name,
74 0L, 16L, FALSE, utf8_string, &actual, &fmt, &nitems, &nleft, &prop);
75
76 if (actual == utf8_string && fmt == 8 && nitems > 1)
77 {
78 g_hash_table_replace (get_windows_with_menus_table (), GINT_TO_POINTER (xid), GINT_TO_POINTER (ALLOWED));
79 return TRUE;
80 }
81 else
82 {
83 g_hash_table_replace (get_windows_with_menus_table (), GINT_TO_POINTER (xid), GINT_TO_POINTER (NOT_ALLOWED));
84 return FALSE;
85 }
86}
87
88gboolean
89local_menu_allowed_on_window (Display *dpy, Window xid)
90{
91 gpointer local_menu_allowed_found = g_hash_table_lookup (get_windows_with_menus_table (), GINT_TO_POINTER (xid));
92
93 if (local_menu_allowed_found)
94 {
95 return GPOINTER_TO_INT (local_menu_allowed_found) == ALLOWED;
96 }
97 else if (dpy && xid)
98 {
99 return read_xprop_for_window (dpy, xid);
100 }
101
102 return FALSE;
103}
104
105gboolean
106gwd_window_should_have_local_menu (WnckWindow *win)
107{
108#ifdef META_HAS_LOCAL_MENUS
109 const gchar * const *schemas = g_settings_list_schemas ();
110 static GSettings *lim_settings = NULL;
111 while (*schemas != NULL && !lim_settings)
112 {
113 if (g_str_equal (*schemas, "com.canonical.indicator.appmenu"))
114 {
115 lim_settings = g_settings_new ("com.canonical.indicator.appmenu");
116 menu_mode = g_settings_get_enum (lim_settings, "menu-mode");
117 g_signal_connect (lim_settings, "changed::menu-mode", G_CALLBACK (gwd_menu_mode_changed), NULL);
118 break;
119 }
120 ++schemas;
121 }
122
123 if (lim_settings && win)
124 return menu_mode == LOCAL && local_menu_allowed_on_window (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), wnck_window_get_xid (win));
125#endif
126
127 return FALSE;
128}
129
130gchar *
131gwd_get_entry_id_from_sync_variant (GVariant *args)
132{
133 /* We need to get the indicator data once we've called show */
134
135 GVariantIter* iter = NULL;
136 gchar* name_hint = NULL;
137 gchar* indicator_id = NULL;
138 gchar* entry_id = NULL;
139 gchar* label = NULL;
140 gboolean label_sensitive = FALSE;
141 gboolean label_visible = FALSE;
142 guint32 image_type = 0;
143 gchar* image_data = NULL;
144 gboolean image_sensitive = FALSE;
145 gboolean image_visible = FALSE;
146 gint32 priority = -1;
147
148 g_variant_get (args, "(a(ssssbbusbbi))", &iter);
149 while (g_variant_iter_loop (iter, "(ssssbbusbbi)",
150 &indicator_id,
151 &entry_id,
152 &name_hint,
153 &label,
154 &label_sensitive,
155 &label_visible,
156 &image_type,
157 &image_data,
158 &image_sensitive,
159 &image_visible,
160 &priority))
161 {
162 g_variant_unref (args);
163 return g_strdup (entry_id);
164 }
165
166 g_variant_unref (args);
167 g_assert_not_reached ();
168 return NULL;
169}
170#ifdef META_HAS_LOCAL_MENUS
171static void
172on_local_menu_activated (GDBusProxy *proxy,
173 gchar *sender_name,
174 gchar *signal_name,
175 GVariant *parameters,
176 gpointer user_data)
177{
178
179 if (g_strcmp0 (signal_name, "EntryActivated") == 0)
180 {
181 show_local_menu_data *d = (show_local_menu_data *) user_data;
182 gchar *entry_id = NULL;
183 gint x_out, y_out;
184 guint width, height;
185
186 g_variant_get (parameters, "(s(iiuu))", &entry_id, &x_out, &y_out, &width, &height, NULL);
187
188 if (!d->local_menu_entry_id)
189 {
190 GError *error = NULL;
191 GVariant *params = g_variant_new ("(s)", "libappmenu.so", NULL);
192 GVariant *args = g_dbus_proxy_call_sync (proxy, "SyncOne", params, 0, 500, NULL, &error);
193
194 g_assert_no_error (error);
195 d->local_menu_entry_id = gwd_get_entry_id_from_sync_variant (args);
196 }
197
198 if (g_strcmp0 (entry_id, d->local_menu_entry_id) == 0)
199 {
200 d->rect->x = x_out - d->dx;
201 d->rect->y = y_out - d->dy;
202 d->rect->width = width;
203 d->rect->height = height;
204 (*d->cb) (d->user_data);
205 }
206 else if (g_strcmp0 (entry_id, "") == 0)
207 {
208 memset (d->rect, 0, sizeof (GdkRectangle));
209 (*d->cb) (d->user_data);
210
211 g_signal_handlers_disconnect_by_func (proxy, on_local_menu_activated, d);
212
213 if (active_menu)
214 {
215 g_free (active_menu);
216 active_menu = NULL;
217 }
218
219 g_free (d->local_menu_entry_id);
220 g_free (d);
221 }
222 }
223
224}
225#endif
226gboolean
227gwd_move_window_instead (gpointer user_data)
228{
229 (*pending_menu->cb) (pending_menu->user_data);
230 g_free (pending_menu->user_data);
231 g_free (pending_menu);
232 pending_menu = NULL;
233 return FALSE;
234}
235
236void
237gwd_prepare_show_local_menu (start_move_window_cb start_move_window,
238 gpointer user_data_start_move_window)
239{
240 if (pending_menu)
241 {
242 g_source_remove (pending_menu->move_timeout_id);
243 g_free (pending_menu->user_data);
244 g_free (pending_menu);
245 pending_menu = NULL;
246 }
247
248 pending_menu = g_new0 (pending_local_menu, 1);
249 pending_menu->cb = start_move_window;
250 pending_menu->user_data = user_data_start_move_window;
251 pending_menu->move_timeout_id = g_timeout_add (120, gwd_move_window_instead, pending_menu);
252}
253
254#ifdef META_HAS_LOCAL_MENUS
255void
256local_menu_entry_activated_request (GDBusProxy *proxy,
257 gchar *sender_name,
258 gchar *signal_name,
259 GVariant *parameters,
260 gpointer user_data)
261{
262 if (g_strcmp0 (signal_name, "EntryActivateRequest") == 0)
263 {
264 gchar *activated_entry_id;
265 gchar *local_menu_entry_id;
266 local_menu_entry_activated_request_funcs *funcs = (local_menu_entry_activated_request_funcs *) user_data;
267
268 g_variant_get (parameters, "(s)", &activated_entry_id, NULL);
269
270 GError *error = NULL;
271 GVariant *params = g_variant_new ("(s)", "libappmenu.so", NULL);
272 GVariant *args = g_dbus_proxy_call_sync (proxy, "SyncOne", params, 0, 500, NULL, &error);
273
274 g_assert_no_error (error);
275 local_menu_entry_id = gwd_get_entry_id_from_sync_variant (args);
276
277 if (g_strcmp0 (activated_entry_id, local_menu_entry_id) == 0)
278 {
279 WnckScreen *screen = wnck_screen_get_for_root (gdk_x11_get_default_root_xwindow ());
280 int dx, dy, top_height;
281 Window xid;
282
283 if (screen)
284 {
285 Box *rect = (*funcs->active_window_local_menu_rect_callback) ((gpointer) screen, &dx, &dy, &top_height, &xid);
286
287 if (rect)
288 {
289 int x = rect->x1;
290 int y = top_height;
291
292 gwd_show_local_menu (gdk_x11_display_get_xdisplay (gdk_display_get_default ()),
293 xid, x + dx, y + dy, x, y, 0, 0,
294 funcs->show_window_menu_hidden_callback, GINT_TO_POINTER (xid));
295 }
296 }
297 }
298 }
299}
300#endif
301
302gboolean
303gwd_show_local_menu (Display *xdisplay,
304 Window frame_xwindow,
305 int x,
306 int y,
307 int x_win,
308 int y_win,
309 int button,
310 guint32 timestamp,
311 show_window_menu_hidden_cb cb,
312 gpointer user_data_show_window_menu)
313{
314 if (pending_menu)
315 {
316 g_source_remove (pending_menu->move_timeout_id);
317 g_free (pending_menu->user_data);
318 g_free (pending_menu);
319 pending_menu = NULL;
320 }
321
322#ifdef META_HAS_LOCAL_MENUS
323
324
325 XUngrabPointer (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), CurrentTime);
326 XUngrabKeyboard (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), CurrentTime);
327 XSync (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), FALSE);
328
329 GDBusProxy *proxy = global_lim_listener;
330
331 if (proxy)
332 {
333 GVariant *message = g_variant_new ("(uiiu)", frame_xwindow, x, y, time);
334 GError *error = NULL;
335 g_dbus_proxy_call_sync (proxy, "ShowAppMenu", message, 0, 500, NULL, &error);
336 if (error)
337 {
338 g_print ("error calling ShowAppMenu: %s\n", error->message);
339 return FALSE;
340 }
341
342 show_local_menu_data *data = g_new0 (show_local_menu_data, 1);
343
344 if (active_menu)
345 g_free (active_menu);
346
347 active_menu = g_new0 (active_local_menu, 1);
348
349 data->cb = cb;
350 data->user_data = user_data_show_window_menu;
351 data->rect = &active_menu->rect;
352 data->dx = x - x_win;
353 data->dy = y - y_win;
354 data->local_menu_entry_id = NULL;
355
356 g_signal_connect (proxy, "g-signal", G_CALLBACK (on_local_menu_activated), data);
357
358 return TRUE;
359 }
360#endif
361 return FALSE;
362}
363
364void
365force_local_menus_on (WnckWindow *win,
366 MetaButtonLayout *button_layout)
367{
368#ifdef META_HAS_LOCAL_MENUS
369 if (gwd_window_should_have_local_menu (win))
370 {
371 if (button_layout->left_buttons[0] != META_BUTTON_FUNCTION_LAST)
372 {
373 unsigned int i = 0;
374 for (; i < MAX_BUTTONS_PER_CORNER; i++)
375 {
376 if (button_layout->left_buttons[i] == META_BUTTON_FUNCTION_WINDOW_MENU)
377 break;
378 else if (button_layout->left_buttons[i] == META_BUTTON_FUNCTION_LAST)
379 {
380 if ((i + 1) < MAX_BUTTONS_PER_CORNER)
381 {
382 button_layout->left_buttons[i + 1] = META_BUTTON_FUNCTION_LAST;
383 button_layout->left_buttons[i] = META_BUTTON_FUNCTION_WINDOW_MENU;
384 break;
385 }
386 }
387 }
388 }
389 if (button_layout->right_buttons[0] != META_BUTTON_FUNCTION_LAST)
390 {
391 unsigned int i = 0;
392 for (; i < MAX_BUTTONS_PER_CORNER; i++)
393 {
394 if (button_layout->right_buttons[i] == META_BUTTON_FUNCTION_WINDOW_MENU)
395 break;
396 else if (button_layout->right_buttons[i] == META_BUTTON_FUNCTION_LAST)
397 {
398 if ((i + 1) < MAX_BUTTONS_PER_CORNER)
399 {
400 button_layout->right_buttons[i + 1] = META_BUTTON_FUNCTION_LAST;
401 button_layout->right_buttons[i] = META_BUTTON_FUNCTION_WINDOW_MENU;
402 break;
403 }
404 }
405 }
406 }
407 }
408#endif
409}
410
411void
412local_menu_cache_notify_window_destroyed (Window xid)
413{
414 g_hash_table_remove (get_windows_with_menus_table (), GINT_TO_POINTER (xid));
415}
416
417void
418local_menu_cache_reload_xwindow (Display *dpy, Window xid)
419{
420 read_xprop_for_window (dpy, xid);
421}
422
423
0424
=== added file 'gtk/window-decorator/local-menus/src/local-menus.h'
--- gtk/window-decorator/local-menus/src/local-menus.h 1970-01-01 00:00:00 +0000
+++ gtk/window-decorator/local-menus/src/local-menus.h 2012-02-25 05:11:17 +0000
@@ -0,0 +1,120 @@
1/*
2 * Copyright © 2006 Novell, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 *
19 * Author: David Reveman <davidr@novell.com>
20 *
21 * 2D Mode: Copyright © 2010 Sam Spilsbury <smspillaz@gmail.com>
22 * Frames Management: Copright © 2011 Canonical Ltd.
23 * Authored By: Sam Spilsbury <sam.spilsbury@canonical.com>
24 */
25
26#ifdef __cplusplus
27extern "C"
28{
29#endif
30
31#include <glib.h>
32#include <gio/gio.h>
33#ifndef WNCK_I_KNOW_THIS_IS_UNSTABLE
34#define WNCK_I_KNOW_THIS_IS_UNSTABLE
35#endif
36#include <libwnck/libwnck.h>
37#include <libwnck/window-action-menu.h>
38#include <metacity-private/theme.h>
39#include <X11/Xregion.h>
40
41
42typedef void (*show_window_menu_hidden_cb) (gpointer);
43typedef void (*start_move_window_cb) (gpointer);
44typedef Box * (*get_active_window_local_menu_rect_cb) (gpointer, int *, int *, int *, Window *);
45
46typedef struct _pending_local_menu
47{
48 gint move_timeout_id;
49 gpointer user_data;
50 start_move_window_cb cb;
51} pending_local_menu;
52
53typedef struct _active_local_menu
54{
55 GdkRectangle rect;
56} active_local_menu;
57
58extern active_local_menu *active_menu;
59extern GDBusProxy *global_lim_listener;
60
61typedef struct _show_local_menu_data
62{
63 show_window_menu_hidden_cb cb;
64 gpointer user_data;
65 GdkRectangle *rect;
66 gint dx;
67 gint dy;
68 gchar *local_menu_entry_id;
69} show_local_menu_data;
70
71typedef struct _local_menu_entry_activated_request_funcs
72{
73 get_active_window_local_menu_rect_cb active_window_local_menu_rect_callback;
74 show_window_menu_hidden_cb show_window_menu_hidden_callback;
75} local_menu_entry_activated_request_funcs;
76
77gboolean
78gwd_window_should_have_local_menu (WnckWindow *win);
79
80void
81force_local_menus_on (WnckWindow *win,
82 MetaButtonLayout *layout);
83
84/* Button Down */
85void
86gwd_prepare_show_local_menu (start_move_window_cb start_move_window,
87 gpointer user_data_start_move_window);
88
89/* Button Up */
90gboolean
91gwd_show_local_menu (Display *xdisplay,
92 Window frame_xwindow,
93 int x,
94 int y,
95 int x_win,
96 int y_win,
97 int button,
98 guint32 timestamp,
99 show_window_menu_hidden_cb cb,
100 gpointer user_data_show_window_menu);
101
102void
103local_menu_cache_notify_window_destroyed (Window xid);
104
105void
106local_menu_cache_reload_xwindow (Display *xdisplay, Window xid);
107
108gboolean
109local_menu_allowed_on_window (Display *dpy, Window xid);
110
111void
112local_menu_entry_activated_request (GDBusProxy *proxy,
113 gchar *sender_name,
114 gchar *signal_name,
115 GVariant *parameters,
116 gpointer user_data);
117
118#ifdef __cplusplus
119}
120#endif
0121
=== added directory 'gtk/window-decorator/local-menus/tests'
=== added file 'gtk/window-decorator/local-menus/tests/CMakeLists.txt'
--- gtk/window-decorator/local-menus/tests/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ gtk/window-decorator/local-menus/tests/CMakeLists.txt 2012-02-25 05:11:17 +0000
@@ -0,0 +1,3 @@
1include_directories (${CMAKE_CURRENT_SOURCE_DIR})
2add_subdirectory (check_local_menu_on_off)
3add_subdirectory (force_local_menu_on)
04
=== added directory 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off'
=== added file 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt'
--- gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt 2012-02-25 05:11:17 +0000
@@ -0,0 +1,21 @@
1include_directories (${CMAKE_CURRENT_SOURCE_DIR}
2 ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/tests
3 ${compiz_SOURCE_DIR}/gtk/window-decorator
4 ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/src)
5
6add_executable(
7 gtk_window_decorator_check_local_menu_on_off_test
8
9 ${CMAKE_CURRENT_SOURCE_DIR}/test-local-menu-on-off.cpp
10)
11
12target_link_libraries(
13 gtk_window_decorator_check_local_menu_on_off_test
14
15 gtk_window_decorator_local_menus
16
17 ${GTEST_BOTH_LIBRARIES}
18 ${CMAKE_THREAD_LIBS_INIT} # Link in pthread.
19)
20
21add_test (gtk_window_decorator_local_menus_on_off gtk_window_decorator_check_local_menu_on_off_test)
022
=== added directory 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/check_local_menu_on_off'
=== added directory 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/check_local_menu_on_off/CMakeFiles'
=== added file 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp'
--- gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp 1970-01-01 00:00:00 +0000
+++ gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp 2012-02-25 05:11:17 +0000
@@ -0,0 +1,27 @@
1#include "test-local-menu.h"
2
3#define GLOBAL 0
4#define LOCAL 1
5#ifdef META_HAS_LOCAL_MENUS
6
7TEST_F (GtkWindowDecoratorTestLocalMenu, TestOn)
8{
9 g_settings_set_enum (getSettings (), "menu-mode", LOCAL);
10 gboolean result = gwd_window_should_have_local_menu (getWindow ());
11
12 EXPECT_TRUE (result);
13}
14
15TEST_F (GtkWindowDecoratorTestLocalMenu, TestOff)
16{
17 g_settings_set_enum (getSettings (), "menu-mode", GLOBAL);
18 gboolean result = gwd_window_should_have_local_menu (getWindow ());
19
20 EXPECT_FALSE (result);
21}
22#else
23TEST_F (GtkWindowDecoratorTestLocalMenu, NoMenus)
24{
25 ASSERT_TRUE (true) << "Local menus tests not enabled because META_HAS_LOCAL_MENUS is off";
26}
27#endif
028
=== added directory 'gtk/window-decorator/local-menus/tests/force_local_menu_on'
=== added file 'gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt'
--- gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt 2012-02-25 05:11:17 +0000
@@ -0,0 +1,21 @@
1include_directories (${CMAKE_CURRENT_SOURCE_DIR}
2 ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/tests
3 ${compiz_SOURCE_DIR}/gtk/window-decorator
4 ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/src)
5
6add_executable(
7 gtk_window_decorator_force_local_menu_on_test
8
9 ${CMAKE_CURRENT_SOURCE_DIR}/test-force-local-menu-on.cpp
10)
11
12target_link_libraries(
13 gtk_window_decorator_force_local_menu_on_test
14
15 gtk_window_decorator_local_menus
16
17 ${GTEST_BOTH_LIBRARIES}
18 ${CMAKE_THREAD_LIBS_INIT} # Link in pthread.
19)
20
21add_test (gtk_window_decorator_force_local_menu_on gtk_window_decorator_force_local_menu_on_test)
022
=== added file 'gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp'
--- gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp 1970-01-01 00:00:00 +0000
+++ gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp 2012-02-25 05:11:17 +0000
@@ -0,0 +1,77 @@
1#include "test-local-menu.h"
2#include <cstring>
3
4#define GLOBAL 0
5#define LOCAL 1
6#ifdef META_HAS_LOCAL_MENUS
7
8namespace
9{
10 void initializeMetaButtonLayout (MetaButtonLayout *layout)
11 {
12 memset (layout, 0, sizeof (MetaButtonLayout));
13
14 unsigned int i;
15
16 for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++)
17 {
18 layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;
19 layout->left_buttons[i] = META_BUTTON_FUNCTION_LAST;
20 }
21 }
22}
23
24class GtkWindowDecoratorTestLocalMenuLayout :
25 public GtkWindowDecoratorTestLocalMenu
26{
27 public:
28
29 MetaButtonLayout * getLayout () { return &mLayout; }
30
31 virtual void SetUp ()
32 {
33 GtkWindowDecoratorTestLocalMenu::SetUp ();
34 ::initializeMetaButtonLayout (&mLayout);
35 g_settings_set_enum (getSettings (), "menu-mode", LOCAL);
36 }
37
38 private:
39
40 MetaButtonLayout mLayout;
41};
42
43TEST_F (GtkWindowDecoratorTestLocalMenuLayout, TestForceNoButtonsSet)
44{
45 force_local_menus_on (getWindow (), getLayout ());
46
47 EXPECT_EQ (getLayout ()->right_buttons[0], META_BUTTON_FUNCTION_LAST);
48 EXPECT_EQ (getLayout ()->left_buttons[0], META_BUTTON_FUNCTION_LAST);
49}
50
51TEST_F (GtkWindowDecoratorTestLocalMenuLayout, TestForceNoCloseButtonSet)
52{
53 getLayout ()->right_buttons[0] = META_BUTTON_FUNCTION_CLOSE;
54
55 force_local_menus_on (getWindow (), getLayout ());
56
57 EXPECT_EQ (getLayout ()->right_buttons[1], META_BUTTON_FUNCTION_WINDOW_MENU);
58 EXPECT_EQ (getLayout ()->left_buttons[0], META_BUTTON_FUNCTION_LAST);
59}
60
61TEST_F (GtkWindowDecoratorTestLocalMenuLayout, TestForceNoCloseMinimizeMaximizeButtonSet)
62{
63 getLayout ()->left_buttons[0] = META_BUTTON_FUNCTION_CLOSE;
64 getLayout ()->left_buttons[1] = META_BUTTON_FUNCTION_CLOSE;
65 getLayout ()->left_buttons[2] = META_BUTTON_FUNCTION_CLOSE;
66
67 force_local_menus_on (getWindow (), getLayout ());
68
69 EXPECT_EQ (getLayout ()->right_buttons[0], META_BUTTON_FUNCTION_LAST);
70 EXPECT_EQ (getLayout ()->left_buttons[3], META_BUTTON_FUNCTION_WINDOW_MENU);
71}
72#else
73TEST_F (GtkWindowDecoratorTestLocalMenu, NoMenus)
74{
75 ASSERT_TRUE (true) << "Local menus tests not enabled because META_HAS_LOCAL_MENUS is off";
76}
77#endif
078
=== added directory 'gtk/window-decorator/local-menus/tests/show_hide_menu'
=== added file 'gtk/window-decorator/local-menus/tests/test-local-menu.h'
--- gtk/window-decorator/local-menus/tests/test-local-menu.h 1970-01-01 00:00:00 +0000
+++ gtk/window-decorator/local-menus/tests/test-local-menu.h 2012-02-25 05:11:17 +0000
@@ -0,0 +1,49 @@
1#include <gtest/gtest.h>
2#include <gmock/gmock.h>
3#include "local-menus.h"
4#include <X11/Xlib.h>
5#include <gio/gio.h>
6
7class GtkWindowDecoratorTestLocalMenu :
8 public ::testing::Test
9{
10 public:
11
12#ifdef META_HAS_LOCAL_MENUS
13 WnckWindow * getWindow () { return mWindow; }
14 GSettings * getSettings () { return mSettings; }
15 virtual void SetUp ()
16 {
17 gtk_init (NULL, NULL);
18
19 mXDisplay = XOpenDisplay (NULL);
20 mXWindow = XCreateSimpleWindow (mXDisplay, DefaultRootWindow (mXDisplay), 0, 0, 100, 100, 0, 0, 0);
21
22 XMapRaised (mXDisplay, mXWindow);
23
24 XFlush (mXDisplay);
25
26 while (g_main_context_iteration (g_main_context_default (), FALSE));
27
28 mWindow = wnck_window_get (mXWindow);
29
30 g_setenv("GSETTINGS_BACKEND", "memory", true);
31 mSettings = g_settings_new ("com.canonical.indicator.appmenu");
32 }
33
34 virtual void TearDown ()
35 {
36 XDestroyWindow (mXDisplay, mXWindow);
37 XCloseDisplay (mXDisplay);
38
39 g_object_unref (mSettings);
40 }
41#endif
42
43 private:
44
45 WnckWindow *mWindow;
46 Window mXWindow;
47 Display *mXDisplay;
48 GSettings *mSettings;
49};
050
=== modified file 'gtk/window-decorator/metacity.c'
--- gtk/window-decorator/metacity.c 2012-02-16 06:51:37 +0000
+++ gtk/window-decorator/metacity.c 2012-02-25 05:11:17 +0000
@@ -24,6 +24,7 @@
24 */24 */
2525
26#include "gtk-window-decorator.h"26#include "gtk-window-decorator.h"
27#include "local-menus.h"
2728
28#ifdef USE_METACITY29#ifdef USE_METACITY
2930
@@ -488,6 +489,8 @@
488 button_layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;489 button_layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;
489 }490 }
490491
492 force_local_menus_on (d->win, button_layout);
493
491 *flags = 0;494 *flags = 0;
492495
493 if (d->actions & WNCK_WINDOW_ACTION_CLOSE)496 if (d->actions & WNCK_WINDOW_ACTION_CLOSE)
@@ -538,6 +541,12 @@
538 *flags |= (MetaFrameFlags ) META_FRAME_ABOVE;541 *flags |= (MetaFrameFlags ) META_FRAME_ABOVE;
539#endif542#endif
540543
544#ifdef META_HAS_LOCAL_MENUS
545 if (d->win &&
546 local_menu_allowed_on_window (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), wnck_window_get_xid (d->win)))
547 *flags |= (MetaFrameFlags ) META_FRAME_ALLOWS_WINDOW_MENU;
548#endif
549
541 meta_theme_get_frame_borders (theme,550 meta_theme_get_frame_borders (theme,
542 frame_type,551 frame_type,
543 d->frame->text_height,552 d->frame->text_height,
@@ -558,6 +567,15 @@
558 else567 else
559 clip->height = d->border_layout.left.y2 - d->border_layout.left.y1;568 clip->height = d->border_layout.left.y2 - d->border_layout.left.y1;
560569
570 memset (fgeom, 0, sizeof (MetaFrameGeometry));
571
572#ifdef META_HAS_LOCAL_MENUS
573
574 if (d->layout)
575 fgeom->text_layout = g_object_ref (d->layout);
576
577#endif
578
561 meta_theme_calc_geometry (theme,579 meta_theme_calc_geometry (theme,
562 frame_type,580 frame_type,
563 d->frame->text_height,581 d->frame->text_height,
@@ -567,6 +585,15 @@
567 button_layout,585 button_layout,
568 fgeom);586 fgeom);
569587
588#ifdef META_HAS_LOCAL_MENUS
589
590 if (d->layout)
591 g_object_unref (fgeom->text_layout);
592
593 fgeom->text_layout = NULL;
594
595#endif
596
570 clip->width += left_width + right_width;597 clip->width += left_width + right_width;
571 clip->height += top_height + bottom_height;598 clip->height += top_height + bottom_height;
572}599}
@@ -601,6 +628,11 @@
601 GdkColor bg_color;628 GdkColor bg_color;
602 double bg_alpha;629 double bg_alpha;
603630
631 memset (&button_layout, 0, sizeof (MetaButtonLayout));
632
633 for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++)
634 button_layout.left_buttons[i] = button_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST;
635
604 if (!d->pixmap || !d->picture)636 if (!d->pixmap || !d->picture)
605 return;637 return;
606638
@@ -675,6 +707,9 @@
675707
676 size = MAX (fgeom.top_height, fgeom.bottom_height);708 size = MAX (fgeom.top_height, fgeom.bottom_height);
677709
710 if (active_menu)
711 g_object_set_data (G_OBJECT (style_window), "local_menu_rect", &active_menu->rect);
712
678 if (rect.width && size)713 if (rect.width && size)
679 {714 {
680 XRenderPictFormat *format;715 XRenderPictFormat *format;
@@ -899,6 +934,9 @@
899 XRenderFreePicture (xdisplay, src);934 XRenderFreePicture (xdisplay, src);
900 }935 }
901936
937 if (active_menu)
938 g_object_set_data (G_OBJECT (style_window), "local_menu_rect", NULL);
939
902 copy_to_front_buffer (d);940 copy_to_front_buffer (d);
903941
904 if (d->frame_window)942 if (d->frame_window)
@@ -1078,6 +1116,15 @@
1078 break;1116 break;
1079#endif1117#endif
10801118
1119#ifdef META_HAS_LOCAL_MENUS
1120 case BUTTON_WINDOW_MENU:
1121 if (!meta_button_present (&button_layout, META_BUTTON_FUNCTION_WINDOW_MENU))
1122 return FALSE;
1123
1124 space = &fgeom.window_menu_rect;
1125 break;
1126#endif
1127
1081 default:1128 default:
1082 return FALSE;1129 return FALSE;
1083 }1130 }
@@ -1266,6 +1313,8 @@
1266 MetaTheme *theme;1313 MetaTheme *theme;
1267 GdkRectangle clip;1314 GdkRectangle clip;
12681315
1316 memset (&fgeom, 0, sizeof (MetaFrameGeometry));
1317
1269 theme = meta_theme_get_current ();1318 theme = meta_theme_get_current ();
12701319
1271 meta_get_decoration_geometry (d, theme, &flags, &fgeom, &button_layout,1320 meta_get_decoration_geometry (d, theme, &flags, &fgeom, &button_layout,
@@ -1490,6 +1539,11 @@
1490 return META_BUTTON_FUNCTION_UNSTICK;1539 return META_BUTTON_FUNCTION_UNSTICK;
1491#endif1540#endif
14921541
1542#ifdef META_HAS_LOCAL_MENUS
1543 else if (strcmp (str, "window_menu") == 0)
1544 return META_BUTTON_FUNCTION_WINDOW_MENU;
1545#endif
1546
1493 else1547 else
1494 return META_BUTTON_FUNCTION_LAST;1548 return META_BUTTON_FUNCTION_LAST;
1495}1549}
14961550
=== modified file 'gtk/window-decorator/wnck.c'
--- gtk/window-decorator/wnck.c 2012-02-10 10:23:27 +0000
+++ gtk/window-decorator/wnck.c 2012-02-25 05:11:17 +0000
@@ -24,6 +24,7 @@
24 */24 */
2525
26#include "gtk-window-decorator.h"26#include "gtk-window-decorator.h"
27#include "local-menus.h"
2728
28const gchar *29const gchar *
29get_frame_type (WnckWindow *win)30get_frame_type (WnckWindow *win)
@@ -738,7 +739,8 @@
738 stick_button_event,739 stick_button_event,
739 unshade_button_event,740 unshade_button_event,
740 unabove_button_event,741 unabove_button_event,
741 unstick_button_event742 unstick_button_event,
743 window_menu_button_event
742 };744 };
743745
744 d = calloc (1, sizeof (decor_t));746 d = calloc (1, sizeof (decor_t));
@@ -784,6 +786,8 @@
784window_closed (WnckScreen *screen,786window_closed (WnckScreen *screen,
785 WnckWindow *win)787 WnckWindow *win)
786{788{
789 local_menu_cache_notify_window_destroyed (wnck_window_get_xid (win));
790
787 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");791 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
788792
789 if (d)793 if (d)

Subscribers

People subscribed via source and target branches