Compare commits

...

13 Commits

Author SHA1 Message Date
27bec68c67 wayland: Add support for show_window_menu 2014-03-14 10:22:13 -04:00
8ce4407a9f x11: Add support for a new _GTK_SHOW_WINDOW_MENU request
To show the window menu from a client-side decoration.
2014-03-14 10:22:13 -04:00
cebb6b2779 window: Remove unused and unnecessary parameters from meta_window_window_menu 2014-03-14 10:22:11 -04:00
d7771a30ee Convert window menus to a compositor implementation 2014-03-13 19:36:33 -04:00
10fb5e527f window: Export a bunch of state flags as accessor methods
This is necessary to reimplement window menus in gnome-shell.
2014-03-13 19:33:28 -04:00
7116786ad3 Add a META_MAXIMIZE_BOTH
Which has the value of META_MAXIMIZE_VERTICAL |
META_MAXIMIZE_HORIZONTAL.

This is an obvious code cleanup.
2014-03-13 18:33:17 -04:00
d294fa45d1 wayland: Add support for set_surface_type 2014-03-13 17:14:28 -04:00
e278d65f05 constraints 2014-03-13 17:14:28 -04:00
42408c1c38 hack to get things working for demo
have to figure out something better to put here
2014-03-13 17:14:28 -04:00
9ac086a613 Replace mutter-launch with logind integration
This uses David Herrmann's new logind sessions interface to retrieve
fds for input devices, rather than using a custom setuid helper to do
the management. This vastly simplifies the interface.

This requires systemd v210, at least.

https://bugzilla.gnome.org/show_bug.cgi?id=724604
2014-03-13 17:14:28 -04:00
de06346766 meta-weston-launch: Pause Clutter during VT switches 2014-03-13 17:14:28 -04:00
c22d6ad69c meta-weston-launch: Remove the "VT-switched lock"
We'll replace it by pausing the Clutter master clock.
2014-03-13 17:14:28 -04:00
64be6cf4f0 weston-launch: Pass the DRM device to Cogl
Open the device from weston-launch, and pass it to Cogl/Clutter.
This is a preliminary cleanup for our login1 integration.
2014-03-13 17:14:28 -04:00
42 changed files with 735 additions and 3104 deletions

2
.gitignore vendored
View File

@ -48,7 +48,6 @@ po/*.pot
50-metacity-key.xml
libmutter-wayland.pc
mutter-wayland
mutter-launch
org.gnome.mutter.gschema.valid
org.gnome.mutter.gschema.xml
org.gnome.mutter.wayland.gschema.valid
@ -77,6 +76,7 @@ src/mutter-marshal.[ch]
src/stamp-mutter-marshal.h
src/meta-dbus-xrandr.[ch]
src/meta-dbus-idle-monitor.[ch]
src/meta-dbus-login1.[ch]
src/mutter-plugins.pc
src/wayland/gtk-shell-protocol.c
src/wayland/gtk-shell-server-protocol.h

View File

@ -140,7 +140,6 @@ AM_GLIB_GNU_GETTEXT
## here we get the flags we'll actually use
# GRegex requires Glib-2.14.0
PKG_CHECK_MODULES(ALL, glib-2.0 >= 2.14.0)
PKG_CHECK_MODULES(MUTTER_LAUNCH, libdrm libsystemd-login)
# Unconditionally use this dir to avoid a circular dep with gnomecc
GNOME_KEYBINDINGS_KEYSDIR="${datadir}/gnome-control-center/keybindings"
@ -213,7 +212,7 @@ AS_IF([test "x$WAYLAND_SCANNER" = "xno"],
AC_SUBST([WAYLAND_SCANNER])
AC_SUBST(XWAYLAND_PATH)
MUTTER_PC_MODULES="$MUTTER_PC_MODULES clutter-wayland-1.0 clutter-wayland-compositor-1.0 clutter-egl-1.0 wayland-server libdrm"
MUTTER_PC_MODULES="$MUTTER_PC_MODULES clutter-wayland-1.0 clutter-wayland-compositor-1.0 clutter-egl-1.0 wayland-server libdrm libsystemd"
PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES)
PKG_CHECK_EXISTS([xi >= 1.6.99.1],

View File

@ -201,6 +201,37 @@
<arg name="app_id" type="string"/>
</request>
<enum name="surface_type">
<description summary="surface types">
Surface
</description>
<entry name="normal" value="0" summary="A normal window. This is the default type."/>
<entry name="modal_dialog" value="1" summary="A modal dialog."/>
</enum>
<request name="set_surface_type">
<description summary="set the surface type">
Sets the type of the surface. These are high-level "roles" of
surfaces designed to give the compositor more heuristics about
how to place and manage the window.
</description>
<arg name="surface_type" type="uint" summary="a surface type from surface_type"/>
</request>
<request name="show_window_menu">
<description summary="show the window menu">
Clients implementing client-side decorations might want to show
a context menu when right-clicking on the decorations, giving the
user a menu that they can use to maximize or minimize the window.
The seat passed must have either pointer or keyboard focus to pop
up the window menu for a surface.
</description>
<arg name="seat" type="object" interface="wl_seat" summary="the seat to pop the window up on"/>
<arg name="serial" type="uint" summary="serial of the event to pop up the window for"/>
</request>
<request name="move">
<description summary="start an interactive move">
Start a pointer-driven move of the surface.

View File

@ -39,6 +39,7 @@ INCLUDES += \
mutter_built_sources = \
$(dbus_idle_built_sources) \
$(dbus_xrandr_built_sources) \
$(dbus_login1_built_sources) \
mutter-enum-types.h \
mutter-enum-types.c \
wayland/gtk-shell-protocol.c \
@ -177,10 +178,6 @@ libmutter_wayland_la_SOURCES = \
ui/ui.h \
ui/frames.c \
ui/frames.h \
ui/menu.c \
ui/menu.h \
ui/metaaccellabel.c \
ui/metaaccellabel.h \
ui/resizepopup.c \
ui/resizepopup.h \
ui/theme-parser.c \
@ -207,8 +204,8 @@ libmutter_wayland_la_SOURCES = \
wayland/meta-wayland-surface.h \
wayland/meta-wayland-types.h \
wayland/meta-wayland-versions.h \
wayland/meta-weston-launch.c \
wayland/meta-weston-launch.h
wayland/meta-login1.c \
wayland/meta-login1.h
nodist_libmutter_wayland_la_SOURCES = \
$(mutter_built_sources)
@ -263,17 +260,6 @@ bin_PROGRAMS=mutter-wayland
mutter_wayland_SOURCES = core/mutter.c
mutter_wayland_LDADD = $(MUTTER_LIBS) libmutter-wayland.la
bin_PROGRAMS+=mutter-launch
mutter_launch_SOURCES = wayland/weston-launch.c wayland/weston-launch.h
mutter_launch_CFLAGS = $(MUTTER_LAUNCH_CFLAGS) -DLIBDIR=\"$(libdir)\"
mutter_launch_LDFLAGS = $(MUTTER_LAUNCH_LIBS) -lpam
install-exec-hook:
-chown root $(DESTDIR)$(bindir)/mutter-launch
-chmod u+s $(DESTDIR)$(bindir)/mutter-launch
if HAVE_INTROSPECTION
include $(INTROSPECTION_MAKEFILE)
@ -409,6 +395,15 @@ $(dbus_idle_built_sources) : Makefile.am idle-monitor.xml
--c-generate-object-manager \
$(srcdir)/idle-monitor.xml
dbus_login1_built_sources = meta-dbus-login1.c meta-dbus-login1.h
$(dbus_login1_built_sources) : Makefile.am org.freedesktop.login1.xml
$(AM_V_GEN)gdbus-codegen \
--interface-prefix org.freedesktop.login1 \
--c-namespace Login1 \
--generate-c-code meta-dbus-login1 \
$(srcdir)/org.freedesktop.login1.xml
wayland/%-protocol.c : $(top_builddir)/protocol/%.xml
mkdir -p wayland
$(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@

View File

@ -1723,3 +1723,15 @@ meta_compositor_hide_tile_preview (MetaCompositor *compositor,
meta_plugin_manager_hide_tile_preview (info->plugin_mgr);
}
void
meta_compositor_show_window_menu (MetaCompositor *compositor,
MetaWindow *window)
{
MetaCompScreen *info = meta_screen_get_compositor_data (window->screen);
if (!info->plugin_mgr)
return;
meta_plugin_manager_show_window_menu (info->plugin_mgr, window);
}

View File

@ -365,3 +365,18 @@ meta_plugin_manager_hide_tile_preview (MetaPluginManager *plugin_mgr)
return FALSE;
}
void
meta_plugin_manager_show_window_menu (MetaPluginManager *plugin_mgr,
MetaWindow *window)
{
MetaPlugin *plugin = plugin_mgr->plugin;
MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin);
MetaDisplay *display = meta_screen_get_display (plugin_mgr->screen);
if (display->display_opening)
return;
if (klass->show_window_menu)
klass->show_window_menu (plugin, window);
}

View File

@ -80,4 +80,8 @@ gboolean meta_plugin_manager_show_tile_preview (MetaPluginManager *mgr,
MetaRectangle *tile_rect,
int tile_monitor_number);
gboolean meta_plugin_manager_hide_tile_preview (MetaPluginManager *mgr);
void meta_plugin_manager_show_window_menu (MetaPluginManager *mgr,
MetaWindow *window);
#endif

View File

@ -714,7 +714,7 @@ meta_window_actor_has_shadow (MetaWindowActor *self)
/* Leaving out shadows for maximized and fullscreen windows is an effeciency
* win and also prevents the unsightly effect of the shadow of maximized
* window appearing on an adjacent window */
if ((meta_window_get_maximized (priv->window) == (META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL)) ||
if ((meta_window_get_maximized (priv->window) == META_MAXIMIZE_BOTH) ||
meta_window_is_fullscreen (priv->window))
return FALSE;

View File

@ -817,7 +817,7 @@ constrain_maximization (MetaWindow *window,
active_workspace_struts);
}
/* Now make target_size = maximized size of client window */
unextend_by_frame (window, &target_size);
/* unextend_by_frame (window, &target_size); */
/* Check min size constraints; max size constraints are ignored for maximized
* windows, as per bug 327543.

View File

@ -328,8 +328,7 @@ meta_core_maximize (Display *xdisplay,
if (meta_prefs_get_raise_on_click ())
meta_window_raise (window);
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
meta_window_maximize (window, META_MAXIMIZE_BOTH);
}
void
@ -342,11 +341,9 @@ meta_core_toggle_maximize_vertically (Display *xdisplay,
meta_window_raise (window);
if (META_WINDOW_MAXIMIZED_VERTICALLY (window))
meta_window_unmaximize (window,
META_MAXIMIZE_VERTICAL);
meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL);
else
meta_window_maximize (window,
META_MAXIMIZE_VERTICAL);
meta_window_maximize (window, META_MAXIMIZE_VERTICAL);
}
void
@ -359,11 +356,9 @@ meta_core_toggle_maximize_horizontally (Display *xdisplay,
meta_window_raise (window);
if (META_WINDOW_MAXIMIZED_HORIZONTALLY (window))
meta_window_unmaximize (window,
META_MAXIMIZE_HORIZONTAL);
meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL);
else
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL);
meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL);
}
void
@ -376,11 +371,9 @@ meta_core_toggle_maximize (Display *xdisplay,
meta_window_raise (window);
if (META_WINDOW_MAXIMIZED (window))
meta_window_unmaximize (window,
META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
else
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
meta_window_maximize (window, META_MAXIMIZE_BOTH);
}
void
@ -392,8 +385,7 @@ meta_core_unmaximize (Display *xdisplay,
if (meta_prefs_get_raise_on_click ())
meta_window_raise (window);
meta_window_unmaximize (window,
META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
}
void
@ -477,9 +469,6 @@ meta_core_change_workspace (Display *xdisplay,
void
meta_core_show_window_menu (Display *xdisplay,
Window frame_xwindow,
int root_x,
int root_y,
int button,
guint32 timestamp)
{
MetaWindow *window = get_window (xdisplay, frame_xwindow);
@ -488,121 +477,7 @@ meta_core_show_window_menu (Display *xdisplay,
meta_window_raise (window);
meta_window_focus (window, timestamp);
meta_window_show_menu (window, root_x, root_y, button, timestamp);
}
void
meta_core_get_menu_accelerator (MetaMenuOp menu_op,
int workspace,
unsigned int *keysym,
MetaVirtualModifier *modifiers)
{
const char *name;
name = NULL;
switch (menu_op)
{
case META_MENU_OP_NONE:
/* No keybinding for this one */
break;
case META_MENU_OP_DELETE:
name = "close";
break;
case META_MENU_OP_MINIMIZE:
name = "minimize";
break;
case META_MENU_OP_UNMAXIMIZE:
name = "unmaximize";
break;
case META_MENU_OP_MAXIMIZE:
name = "maximize";
break;
case META_MENU_OP_UNSHADE:
case META_MENU_OP_SHADE:
name = "toggle_shaded";
break;
case META_MENU_OP_UNSTICK:
case META_MENU_OP_STICK:
name = "toggle-on-all-workspaces";
break;
case META_MENU_OP_ABOVE:
case META_MENU_OP_UNABOVE:
name = "toggle-above";
break;
case META_MENU_OP_WORKSPACES:
switch (workspace)
{
case 1:
name = "move-to-workspace-1";
break;
case 2:
name = "move-to-workspace-2";
break;
case 3:
name = "move-to-workspace-3";
break;
case 4:
name = "move-to-workspace-4";
break;
case 5:
name = "move-to-workspace-5";
break;
case 6:
name = "move-to-workspace-6";
break;
case 7:
name = "move-to-workspace-7";
break;
case 8:
name = "move-to-workspace-8";
break;
case 9:
name = "move-to-workspace-9";
break;
case 10:
name = "move-to-workspace-10";
break;
case 11:
name = "move-to-workspace-11";
break;
case 12:
name = "move-to-workspace-12";
break;
}
break;
case META_MENU_OP_MOVE:
name = "begin-move";
break;
case META_MENU_OP_RESIZE:
name = "begin-resize";
break;
case META_MENU_OP_MOVE_LEFT:
name = "move-to-workspace-left";
break;
case META_MENU_OP_MOVE_RIGHT:
name = "move-to-workspace-right";
break;
case META_MENU_OP_MOVE_UP:
name = "move-to-workspace-up";
break;
case META_MENU_OP_MOVE_DOWN:
name = "move-to-workspace-down";
break;
case META_MENU_OP_RECOVER:
/* No keybinding for this one */
break;
}
if (name)
{
meta_prefs_get_window_binding (name, keysym, modifiers);
}
else
{
*keysym = 0;
*modifiers = 0;
}
meta_window_show_menu (window);
}
const char*

View File

@ -159,15 +159,8 @@ const char* meta_core_get_workspace_name_with_index (Display *xdisplay,
void meta_core_show_window_menu (Display *xdisplay,
Window frame_xwindow,
int root_x,
int root_y,
int button,
guint32 timestamp);
void meta_core_get_menu_accelerator (MetaMenuOp menu_op,
int workspace,
unsigned int *keysym,
MetaVirtualModifier *modifiers);
gboolean meta_core_begin_grab_op (Display *xdisplay,
Window frame_xwindow,

View File

@ -263,10 +263,6 @@ struct _MetaDisplay
/* Managed by group.c */
GHashTable *groups_by_leader;
/* currently-active window menu if any */
MetaWindowMenu *window_menu;
MetaWindow *window_with_menu;
/* Managed by window-props.c */
MetaWindowPropHooks *prop_hooks_table;
GHashTable *prop_hooks;

View File

@ -564,9 +564,6 @@ meta_display_open (void)
the_display->monitor_cache_invalidated = TRUE;
the_display->groups_by_leader = NULL;
the_display->window_with_menu = NULL;
the_display->window_menu = NULL;
the_display->screens = NULL;
the_display->active_screen = NULL;
@ -2206,11 +2203,7 @@ meta_display_handle_event (MetaDisplay *display,
{
if (meta_prefs_get_raise_on_click ())
meta_window_raise (window);
meta_window_show_menu (window,
event->button.x,
event->button.y,
event->button.button,
event->any.time);
meta_window_show_menu (window);
bypass_clutter = TRUE;
bypass_wayland = TRUE;
}

View File

@ -1975,9 +1975,7 @@ process_mouse_move_resize_grab (MetaDisplay *display,
* moveresize now to get the position back to the original.
*/
if (window->shaken_loose || window->tile_mode == META_TILE_MAXIMIZED)
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
meta_window_maximize (window, META_MAXIMIZE_BOTH);
else if (window->tile_mode != META_TILE_NONE)
meta_window_tile (window);
else
@ -2038,9 +2036,7 @@ process_keyboard_move_grab (MetaDisplay *display,
* now to get the position back to the original.
*/
if (window->shaken_loose)
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
meta_window_maximize (window, META_MAXIMIZE_BOTH);
else
meta_window_move_resize (display->grab_window,
TRUE,
@ -2786,20 +2782,7 @@ handle_activate_window_menu (MetaDisplay *display,
gpointer dummy)
{
if (display->focus_window)
{
int x, y;
meta_window_get_position (display->focus_window,
&x, &y);
if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
x += display->focus_window->rect.width;
meta_window_show_menu (display->focus_window,
x, y,
0,
event->time);
}
meta_window_show_menu (display->focus_window);
}
static void
@ -2901,11 +2884,9 @@ handle_toggle_tiled (MetaDisplay *display,
: META_TILE_NONE;
if (window->saved_maximize)
meta_window_maximize (window, META_MAXIMIZE_VERTICAL |
META_MAXIMIZE_HORIZONTAL);
meta_window_maximize (window, META_MAXIMIZE_BOTH);
else
meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL |
META_MAXIMIZE_HORIZONTAL);
meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
}
else if (meta_window_can_tile_side_by_side (window))
{
@ -2931,13 +2912,9 @@ handle_toggle_maximized (MetaDisplay *display,
gpointer dummy)
{
if (META_WINDOW_MAXIMIZED (window))
meta_window_unmaximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
else if (window->has_maximize_func)
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
meta_window_maximize (window, META_MAXIMIZE_BOTH);
}
static void
@ -2949,9 +2926,7 @@ handle_maximize (MetaDisplay *display,
gpointer dummy)
{
if (window->has_maximize_func)
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
meta_window_maximize (window, META_MAXIMIZE_BOTH);
}
static void
@ -2963,9 +2938,7 @@ handle_unmaximize (MetaDisplay *display,
gpointer dummy)
{
if (window->maximized_vertically || window->maximized_horizontally)
meta_window_unmaximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
}
static void

View File

@ -2241,43 +2241,6 @@ meta_prefs_get_keybinding_action (const char *name)
: META_KEYBINDING_ACTION_NONE;
}
/* This is used by the menu system to decide what key binding
* to display next to an option. We return the first non-disabled
* binding, if any.
*/
void
meta_prefs_get_window_binding (const char *name,
unsigned int *keysym,
MetaVirtualModifier *modifiers)
{
MetaKeyPref *pref = g_hash_table_lookup (key_bindings, name);
if (pref->per_window)
{
GSList *s = pref->combos;
while (s)
{
MetaKeyCombo *c = s->data;
if (c->keysym != 0 || c->modifiers != 0)
{
*keysym = c->keysym;
*modifiers = c->modifiers;
return;
}
s = s->next;
}
/* Not found; return the disabled value */
*keysym = *modifiers = 0;
return;
}
g_assert_not_reached ();
}
gint
meta_prefs_get_mouse_button_resize (void)
{

View File

@ -310,6 +310,7 @@ set_supported_hint (MetaScreen *screen)
#undef EWMH_ATOMS_ONLY
screen->display->atom__GTK_FRAME_EXTENTS,
screen->display->atom__GTK_SHOW_WINDOW_MENU,
};
XChangeProperty (screen->display->xdisplay, screen->xroot,

View File

@ -628,11 +628,7 @@ void meta_window_set_current_workspace_hint (MetaWindow *window);
unsigned long meta_window_get_net_wm_desktop (MetaWindow *window);
void meta_window_show_menu (MetaWindow *window,
int root_x,
int root_y,
int button,
guint32 timestamp);
void meta_window_show_menu (MetaWindow *window);
gboolean meta_window_titlebar_is_onscreen (MetaWindow *window);
void meta_window_shove_titlebar_onscreen (MetaWindow *window);

View File

@ -1165,6 +1165,11 @@ meta_window_x11_client_message (MetaWindow *window,
meta_window_update_fullscreen_monitors (window, top, bottom, left, right);
}
else if (event->xclient.message_type ==
display->atom__GTK_SHOW_WINDOW_MENU)
{
meta_window_show_menu (window);
}
return FALSE;
}

View File

@ -1368,9 +1368,7 @@ meta_window_apply_session_info (MetaWindow *window,
if (window->has_maximize_func && info->maximized)
{
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
meta_window_maximize (window, META_MAXIMIZE_BOTH);
if (info->saved_rect_set)
{
@ -1524,13 +1522,6 @@ meta_window_unmanage (MetaWindow *window,
meta_compositor_remove_window (window->display->compositor, window);
}
if (window->display->window_with_menu == window)
{
meta_ui_window_menu_free (window->display->window_menu);
window->display->window_menu = NULL;
window->display->window_with_menu = NULL;
}
if (destroying_windows_disallowed > 0)
meta_bug ("Tried to destroy window %s while destruction was not allowed\n",
window->desc);
@ -3370,7 +3361,7 @@ meta_window_tile (MetaWindow *window)
return;
if (window->tile_mode == META_TILE_MAXIMIZED)
directions = META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL;
directions = META_MAXIMIZE_BOTH;
else
directions = META_MAXIMIZE_VERTICAL;
@ -7420,272 +7411,11 @@ meta_window_recalc_features (MetaWindow *window)
*/
}
static void
menu_callback (MetaWindowMenu *menu,
Display *xdisplay,
Window client_xwindow,
guint32 timestamp,
MetaMenuOp op,
int workspace_index,
gpointer data)
{
MetaDisplay *display;
MetaWindow *window;
MetaWorkspace *workspace;
display = meta_display_for_x_display (xdisplay);
window = meta_display_lookup_x_window (display, client_xwindow);
workspace = NULL;
if (window != NULL) /* window can be NULL */
{
meta_verbose ("Menu op %u on %s\n", op, window->desc);
switch (op)
{
case META_MENU_OP_NONE:
/* nothing */
break;
case META_MENU_OP_DELETE:
meta_window_delete (window, timestamp);
break;
case META_MENU_OP_MINIMIZE:
meta_window_minimize (window);
break;
case META_MENU_OP_UNMAXIMIZE:
meta_window_unmaximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
break;
case META_MENU_OP_MAXIMIZE:
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
break;
case META_MENU_OP_UNSHADE:
meta_window_unshade (window, timestamp);
break;
case META_MENU_OP_SHADE:
meta_window_shade (window, timestamp);
break;
case META_MENU_OP_MOVE_LEFT:
workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
META_MOTION_LEFT);
break;
case META_MENU_OP_MOVE_RIGHT:
workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
META_MOTION_RIGHT);
break;
case META_MENU_OP_MOVE_UP:
workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
META_MOTION_UP);
break;
case META_MENU_OP_MOVE_DOWN:
workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
META_MOTION_DOWN);
break;
case META_MENU_OP_WORKSPACES:
workspace = meta_screen_get_workspace_by_index (window->screen,
workspace_index);
break;
case META_MENU_OP_STICK:
meta_window_stick (window);
break;
case META_MENU_OP_UNSTICK:
meta_window_unstick (window);
break;
case META_MENU_OP_ABOVE:
case META_MENU_OP_UNABOVE:
if (window->wm_state_above == FALSE)
meta_window_make_above (window);
else
meta_window_unmake_above (window);
break;
case META_MENU_OP_MOVE:
meta_window_begin_grab_op (window,
META_GRAB_OP_KEYBOARD_MOVING,
TRUE,
timestamp);
break;
case META_MENU_OP_RESIZE:
meta_window_begin_grab_op (window,
META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
TRUE,
timestamp);
break;
case META_MENU_OP_RECOVER:
meta_window_shove_titlebar_onscreen (window);
break;
default:
meta_warning (G_STRLOC": Unknown window op\n");
break;
}
if (workspace)
{
meta_window_change_workspace (window,
workspace);
}
}
else
{
meta_verbose ("Menu callback on nonexistent window\n");
}
if (display->window_menu == menu)
{
display->window_menu = NULL;
display->window_with_menu = NULL;
}
meta_ui_window_menu_free (menu);
}
void
meta_window_show_menu (MetaWindow *window,
int root_x,
int root_y,
int button,
guint32 timestamp)
meta_window_show_menu (MetaWindow *window)
{
MetaMenuOp ops;
MetaMenuOp insensitive;
MetaWindowMenu *menu;
MetaWorkspaceLayout layout;
int n_workspaces;
gboolean ltr;
g_return_if_fail (!window->override_redirect);
if (window->display->window_menu)
{
meta_ui_window_menu_free (window->display->window_menu);
window->display->window_menu = NULL;
window->display->window_with_menu = NULL;
}
ops = META_MENU_OP_NONE;
insensitive = META_MENU_OP_NONE;
ops |= (META_MENU_OP_DELETE | META_MENU_OP_MINIMIZE | META_MENU_OP_MOVE | META_MENU_OP_RESIZE);
if (!meta_window_titlebar_is_onscreen (window) &&
window->type != META_WINDOW_DOCK &&
window->type != META_WINDOW_DESKTOP)
ops |= META_MENU_OP_RECOVER;
if (!meta_prefs_get_workspaces_only_on_primary () ||
meta_window_is_on_primary_monitor (window))
{
n_workspaces = meta_screen_get_n_workspaces (window->screen);
if (n_workspaces > 1)
ops |= META_MENU_OP_WORKSPACES;
meta_screen_calc_workspace_layout (window->screen,
n_workspaces,
meta_workspace_index ( window->screen->active_workspace),
&layout);
if (!window->on_all_workspaces)
{
ltr = meta_ui_get_direction() == META_UI_DIRECTION_LTR;
if (layout.current_col > 0)
ops |= ltr ? META_MENU_OP_MOVE_LEFT : META_MENU_OP_MOVE_RIGHT;
if ((layout.current_col < layout.cols - 1) &&
(layout.current_row * layout.cols + (layout.current_col + 1) < n_workspaces))
ops |= ltr ? META_MENU_OP_MOVE_RIGHT : META_MENU_OP_MOVE_LEFT;
if (layout.current_row > 0)
ops |= META_MENU_OP_MOVE_UP;
if ((layout.current_row < layout.rows - 1) &&
((layout.current_row + 1) * layout.cols + layout.current_col < n_workspaces))
ops |= META_MENU_OP_MOVE_DOWN;
}
meta_screen_free_workspace_layout (&layout);
ops |= META_MENU_OP_UNSTICK;
ops |= META_MENU_OP_STICK;
}
if (META_WINDOW_MAXIMIZED (window))
ops |= META_MENU_OP_UNMAXIMIZE;
else
ops |= META_MENU_OP_MAXIMIZE;
if (window->wm_state_above)
ops |= META_MENU_OP_UNABOVE;
else
ops |= META_MENU_OP_ABOVE;
if (!window->has_maximize_func)
insensitive |= META_MENU_OP_UNMAXIMIZE | META_MENU_OP_MAXIMIZE;
if (!window->has_minimize_func)
insensitive |= META_MENU_OP_MINIMIZE;
if (!window->has_close_func)
insensitive |= META_MENU_OP_DELETE;
if (!window->has_shade_func)
insensitive |= META_MENU_OP_SHADE | META_MENU_OP_UNSHADE;
if (!META_WINDOW_ALLOWS_MOVE (window))
insensitive |= META_MENU_OP_MOVE;
if (!META_WINDOW_ALLOWS_RESIZE (window))
insensitive |= META_MENU_OP_RESIZE;
if (window->always_sticky)
insensitive |= META_MENU_OP_STICK | META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES;
if ((window->type == META_WINDOW_DESKTOP) ||
(window->type == META_WINDOW_DOCK) ||
(window->type == META_WINDOW_SPLASHSCREEN ||
META_WINDOW_MAXIMIZED (window)))
insensitive |= META_MENU_OP_ABOVE | META_MENU_OP_UNABOVE;
/* If all operations are disabled, just quit without showing the menu.
* This is the case, for example, with META_WINDOW_DESKTOP windows.
*/
if ((ops & ~insensitive) == 0)
return;
menu =
meta_ui_window_menu_new (window->screen->ui,
window->xwindow,
ops,
insensitive,
meta_window_get_net_wm_desktop (window),
meta_screen_get_n_workspaces (window->screen),
menu_callback,
NULL);
window->display->window_menu = menu;
window->display->window_with_menu = window;
meta_verbose ("Popping up window menu for %s\n", window->desc);
meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp);
meta_compositor_show_window_menu (window->display->compositor, window);
}
void
@ -7981,10 +7711,7 @@ update_move (MetaWindow *window,
display->grab_anchor_root_x = x;
display->grab_anchor_root_y = y;
meta_window_unmaximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
return;
}
@ -8028,9 +7755,7 @@ update_move (MetaWindow *window,
window->user_rect.x = window->saved_rect.x;
window->user_rect.y = window->saved_rect.y;
meta_window_unmaximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
}
display->grab_initial_window_pos = work_area;
@ -8038,10 +7763,7 @@ update_move (MetaWindow *window,
display->grab_anchor_root_y = y;
window->shaken_loose = FALSE;
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
meta_window_maximize (window, META_MAXIMIZE_BOTH);
return;
}
}
@ -10065,12 +9787,6 @@ meta_window_compute_tile_match (MetaWindow *window)
}
}
gboolean
meta_window_can_close (MetaWindow *window)
{
return window->has_close_func;
}
void
meta_window_set_title (MetaWindow *window,
const char *title)
@ -10412,3 +10128,51 @@ meta_window_set_custom_frame_extents (MetaWindow *window,
meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
}
gboolean
meta_window_can_maximize (MetaWindow *window)
{
return window->has_maximize_func;
}
gboolean
meta_window_can_minimize (MetaWindow *window)
{
return window->has_minimize_func;
}
gboolean
meta_window_can_shade (MetaWindow *window)
{
return window->has_shade_func;
}
gboolean
meta_window_can_close (MetaWindow *window)
{
return window->has_close_func;
}
gboolean
meta_window_is_always_on_all_workspaces (MetaWindow *window)
{
return window->always_sticky;
}
gboolean
meta_window_is_above (MetaWindow *window)
{
return window->wm_state_above;
}
gboolean
meta_window_allows_move (MetaWindow *window)
{
return META_WINDOW_ALLOWS_MOVE (window);
}
gboolean
meta_window_allows_resize (MetaWindow *window)
{
return META_WINDOW_ALLOWS_RESIZE (window);
}

View File

@ -62,6 +62,7 @@ item(_GTK_WINDOW_OBJECT_PATH)
item(_GTK_APP_MENU_OBJECT_PATH)
item(_GTK_MENUBAR_OBJECT_PATH)
item(_GTK_FRAME_EXTENTS)
item(_GTK_SHOW_WINDOW_MENU)
item(_GNOME_WM_KEYBINDINGS)
item(_GNOME_PANEL_ACTION)
item(_GNOME_PANEL_ACTION_MAIN_MENU)

View File

@ -86,61 +86,6 @@ typedef enum
META_FRAME_TILED_RIGHT = 1 << 16
} MetaFrameFlags;
/**
* MetaMenuOp:
* @META_MENU_OP_NONE: No menu operation
* @META_MENU_OP_DELETE: Menu operation delete
* @META_MENU_OP_MINIMIZE: Menu operation minimize
* @META_MENU_OP_UNMAXIMIZE: Menu operation unmaximize
* @META_MENU_OP_MAXIMIZE: Menu operation maximize
* @META_MENU_OP_UNSHADE: Menu operation unshade
* @META_MENU_OP_SHADE: Menu operation shade
* @META_MENU_OP_UNSTICK: Menu operation unstick
* @META_MENU_OP_STICK: Menu operation stick
* @META_MENU_OP_WORKSPACES: Menu operation workspaces
* @META_MENU_OP_MOVE: Menu operation move
* @META_MENU_OP_RESIZE: Menu operation resize
* @META_MENU_OP_ABOVE: Menu operation above
* @META_MENU_OP_UNABOVE: Menu operation unabove
* @META_MENU_OP_MOVE_LEFT: Menu operation left
* @META_MENU_OP_MOVE_RIGHT: Menu operation right
* @META_MENU_OP_MOVE_UP: Menu operation up
* @META_MENU_OP_MOVE_DOWN: Menu operation down
* @META_MENU_OP_RECOVER: Menu operation recover
*/
typedef enum
{
META_MENU_OP_NONE = 0,
META_MENU_OP_DELETE = 1 << 0,
META_MENU_OP_MINIMIZE = 1 << 1,
META_MENU_OP_UNMAXIMIZE = 1 << 2,
META_MENU_OP_MAXIMIZE = 1 << 3,
META_MENU_OP_UNSHADE = 1 << 4,
META_MENU_OP_SHADE = 1 << 5,
META_MENU_OP_UNSTICK = 1 << 6,
META_MENU_OP_STICK = 1 << 7,
META_MENU_OP_WORKSPACES = 1 << 8,
META_MENU_OP_MOVE = 1 << 9,
META_MENU_OP_RESIZE = 1 << 10,
META_MENU_OP_ABOVE = 1 << 11,
META_MENU_OP_UNABOVE = 1 << 12,
META_MENU_OP_MOVE_LEFT = 1 << 13,
META_MENU_OP_MOVE_RIGHT = 1 << 14,
META_MENU_OP_MOVE_UP = 1 << 15,
META_MENU_OP_MOVE_DOWN = 1 << 16,
META_MENU_OP_RECOVER = 1 << 17
} MetaMenuOp;
typedef struct _MetaWindowMenu MetaWindowMenu;
typedef void (* MetaWindowMenuFunc) (MetaWindowMenu *menu,
Display *xdisplay,
Window client_xwindow,
guint32 timestamp,
MetaMenuOp op,
int workspace,
gpointer user_data);
/**
* MetaGrabOp:
* @META_GRAB_OP_NONE: None

View File

@ -130,5 +130,7 @@ void meta_compositor_show_tile_preview (MetaCompositor *compositor,
int tile_monitor_number);
void meta_compositor_hide_tile_preview (MetaCompositor *compositor,
MetaScreen *screen);
void meta_compositor_show_window_menu (MetaCompositor *compositor,
MetaWindow *window);
#endif /* META_COMPOSITOR_H */

View File

@ -164,6 +164,9 @@ struct _MetaPluginClass
int tile_monitor_number);
void (*hide_tile_preview) (MetaPlugin *plugin);
void (*show_window_menu) (MetaPlugin *plugin,
MetaWindow *window);
/**
* MetaPluginClass::kill_window_effects:
* @actor: a #MetaWindowActor

View File

@ -392,10 +392,6 @@ GType meta_key_binding_get_type (void);
MetaKeyBindingAction meta_prefs_get_keybinding_action (const char *name);
void meta_prefs_get_window_binding (const char *name,
unsigned int *keysym,
MetaVirtualModifier *modifiers);
gboolean meta_prefs_get_visual_bell (void);
gboolean meta_prefs_bell_is_audible (void);
GDesktopVisualBellType meta_prefs_get_visual_bell_type (void);

View File

@ -72,11 +72,13 @@ typedef enum
* MetaMaximizeFlags:
* @META_MAXIMIZE_HORIZONTAL: Horizontal
* @META_MAXIMIZE_VERTICAL: Vertical
* @META_MAXIMIZE_BOTH: Both
*/
typedef enum
{
META_MAXIMIZE_HORIZONTAL = 1 << 0,
META_MAXIMIZE_VERTICAL = 1 << 1
META_MAXIMIZE_VERTICAL = 1 << 1,
META_MAXIMIZE_BOTH = (1 << 0 | 1 << 1),
} MetaMaximizeFlags;
/**
@ -252,6 +254,13 @@ void meta_window_begin_grab_op (MetaWindow *window,
gboolean frame_action,
guint32 timestamp);
gboolean meta_window_can_maximize (MetaWindow *window);
gboolean meta_window_can_minimize (MetaWindow *window);
gboolean meta_window_can_shade (MetaWindow *window);
gboolean meta_window_can_close (MetaWindow *window);
gboolean meta_window_is_always_on_all_workspaces (MetaWindow *window);
gboolean meta_window_is_above (MetaWindow *window);
gboolean meta_window_allows_move (MetaWindow *window);
gboolean meta_window_allows_resize (MetaWindow *window);
#endif

View File

@ -1,7 +1,7 @@
[Desktop Entry]
Type=Application
_Name=Mutter (wayland compositor)
Exec=mutter-launch -- mutter --wayland --display-server
Exec=mutter --wayland --display-server
NoDisplay=true
# name of loadable control center module
X-GNOME-WMSettingsModule=metacity

View File

@ -0,0 +1,44 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.login1.Session">
<property name="Active" type="b" access="read" />
<method name="Activate">
</method>
<method name="TakeControl">
<arg name="force" type="b"/>
</method>
<method name="TakeDevice">
<annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
<arg name="major" type="u" direction="in"/>
<arg name="minor" type="u" direction="in"/>
<arg name="fd" type="h" direction="out"/>
<arg name="paused" type="b" direction="out"/>
</method>
<method name="ReleaseDevice">
<arg name="major" type="u"/>
<arg name="minor" type="u"/>
</method>
<method name="PauseDeviceComplete">
<arg name="major" type="u"/>
<arg name="minor" type="u"/>
</method>
<signal name="PauseDevice">
<arg name="major" type="u"/>
<arg name="minor" type="u"/>
<arg name="type" type="s"/>
</signal>
<signal name="ResumeDevice">
<arg name="major" type="u"/>
<arg name="minor" type="u"/>
<arg name="fd" type="h"/>
</signal>
</interface>
<interface name="org.freedesktop.login1.Seat">
<method name="SwitchTo">
<arg name="vt" type="u"/>
</method>
</interface>
</node>

View File

@ -1106,9 +1106,6 @@ meta_frame_titlebar_event (MetaUIFrame *frame,
case G_DESKTOP_TITLEBAR_ACTION_MENU:
meta_core_show_window_menu (display,
frame->xwindow,
event->x_root,
event->y_root,
event->button,
event->time);
break;
}
@ -1268,30 +1265,9 @@ meta_frames_button_press_event (GtkWidget *widget,
redraw_control (frames, frame, control);
if (op == META_GRAB_OP_CLICKING_MENU)
{
MetaFrameGeometry fgeom;
GdkRectangle *rect;
int dx, dy;
meta_frames_calc_geometry (frames, frame, &fgeom);
rect = control_rect (META_FRAME_CONTROL_MENU, &fgeom);
/* get delta to convert to root coords */
dx = event->x_root - event->x;
dy = event->y_root - event->y;
/* Align to the right end of the menu rectangle if RTL */
if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
dx += rect->width;
meta_core_show_window_menu (display,
frame->xwindow,
rect->x + dx,
rect->y + rect->height + dy,
event->button,
event->time);
}
meta_core_show_window_menu (display,
frame->xwindow,
event->time);
}
else if (event->button == 1 &&
(control == META_FRAME_CONTROL_RESIZE_SE ||

View File

@ -1,518 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Mutter window menu */
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2004 Rob Adams
* Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include "menu.h"
#include <meta/main.h>
#include "util-private.h"
#include "core.h"
#include "metaaccellabel.h"
#include "ui.h"
typedef struct _MenuItem MenuItem;
typedef struct _MenuData MenuData;
typedef enum
{
MENU_ITEM_SEPARATOR = 0,
MENU_ITEM_NORMAL,
MENU_ITEM_CHECKBOX,
MENU_ITEM_RADIOBUTTON,
MENU_ITEM_WORKSPACE_LIST,
} MetaMenuItemType;
struct _MenuItem
{
MetaMenuOp op;
MetaMenuItemType type;
const gboolean checked;
const char *label;
};
struct _MenuData
{
MetaWindowMenu *menu;
MetaMenuOp op;
};
static void activate_cb (GtkWidget *menuitem, gpointer data);
static MenuItem menuitems[] = {
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MINIMIZE, MENU_ITEM_NORMAL, FALSE, N_("Mi_nimize") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MAXIMIZE, MENU_ITEM_NORMAL, FALSE, N_("Ma_ximize") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_UNMAXIMIZE, MENU_ITEM_NORMAL, FALSE, N_("Unma_ximize") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_SHADE, MENU_ITEM_NORMAL, FALSE, N_("Roll _Up") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_UNSHADE, MENU_ITEM_NORMAL, FALSE, N_("_Unroll") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MOVE, MENU_ITEM_NORMAL, FALSE, N_("_Move") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_RESIZE, MENU_ITEM_NORMAL, FALSE, N_("_Resize") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_RECOVER, MENU_ITEM_NORMAL, FALSE, N_("Move Titlebar On_screen") },
{ META_MENU_OP_WORKSPACES, MENU_ITEM_SEPARATOR, FALSE, NULL }, /* separator */
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_ABOVE, MENU_ITEM_CHECKBOX, FALSE, N_("Always on _Top") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_UNABOVE, MENU_ITEM_CHECKBOX, TRUE, N_("Always on _Top") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_STICK, MENU_ITEM_RADIOBUTTON, FALSE, N_("_Always on Visible Workspace") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_UNSTICK, MENU_ITEM_RADIOBUTTON, FALSE, N_("_Only on This Workspace") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MOVE_LEFT, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace _Left") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MOVE_RIGHT, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace R_ight") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MOVE_UP, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace _Up") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MOVE_DOWN, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace _Down") },
{ 0, MENU_ITEM_WORKSPACE_LIST, FALSE, NULL },
{ 0, MENU_ITEM_SEPARATOR, FALSE, NULL }, /* separator */
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_DELETE, MENU_ITEM_NORMAL, FALSE, N_("_Close") }
};
static void
popup_position_func (GtkMenu *menu,
gint *x,
gint *y,
gboolean *push_in,
gpointer user_data)
{
GtkRequisition req;
GdkPoint *pos;
pos = user_data;
gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
*x = pos->x;
*y = pos->y;
if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
*x = MAX (0, *x - req.width);
/* Ensure onscreen */
*x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
*y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
}
static void
menu_closed (GtkMenu *widget,
gpointer data)
{
MetaWindowMenu *menu;
menu = data;
meta_frames_notify_menu_hide (menu->frames);
(* menu->func) (menu,
GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
menu->client_xwindow,
gtk_get_current_event_time (),
0, 0,
menu->data);
/* menu may now be freed */
}
static void
activate_cb (GtkWidget *menuitem, gpointer data)
{
MenuData *md;
g_return_if_fail (GTK_IS_WIDGET (menuitem));
md = data;
meta_frames_notify_menu_hide (md->menu->frames);
(* md->menu->func) (md->menu,
GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
md->menu->client_xwindow,
gtk_get_current_event_time (),
md->op,
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menuitem),
"workspace")),
md->menu->data);
/* menu may now be freed */
}
/*
* Given a Display and an index, get the workspace name and add any
* accelerators. At the moment this means adding a _ if the name is of
* the form "Workspace n" where n is less than 10, and escaping any
* other '_'s so they do not create inadvertant accelerators.
*
* The calling code owns the string, and is reponsible to free the
* memory after use.
*
* See also http://mail.gnome.org/archives/gnome-i18n/2008-March/msg00380.html
* which discusses possible i18n concerns.
*/
static char*
get_workspace_name_with_accel (Display *display,
Window xroot,
int index)
{
const char *name;
int number;
int charcount=0;
name = meta_core_get_workspace_name_with_index (display, xroot, index);
g_assert (name != NULL);
/*
* If the name is of the form "Workspace x" where x is an unsigned
* integer, insert a '_' before the number if it is less than 10 and
* return it
*/
number = 0;
if (sscanf (name, _("Workspace %d%n"), &number, &charcount) != 0 &&
*(name + charcount)=='\0')
{
char *new_name;
/*
* Above name is a pointer into the Workspace struct. Here we make
* a copy copy so we can have our wicked way with it.
*/
if (number == 10)
new_name = g_strdup_printf (_("Workspace 1_0"));
else
new_name = g_strdup_printf (_("Workspace %s%d"),
number < 10 ? "_" : "",
number);
return new_name;
}
else
{
/*
* Otherwise this is just a normal name. Escape any _ characters so that
* the user's workspace names do not get mangled. If the number is less
* than 10 we provide an accelerator.
*/
char *new_name;
const char *source;
char *dest;
/*
* Assume the worst case, that every character is a _. We also
* provide memory for " (_#)"
*/
new_name = g_malloc0 (strlen (name) * 2 + 6 + 1);
/*
* Now iterate down the strings, adding '_' to escape as we go
*/
dest = new_name;
source = name;
while (*source != '\0')
{
if (*source == '_')
*dest++ = '_';
*dest++ = *source++;
}
/* People don't start at workspace 0, but workspace 1 */
if (index < 9)
{
g_snprintf (dest, 6, " (_%d)", index + 1);
}
else if (index == 9)
{
g_snprintf (dest, 6, " (_0)");
}
return new_name;
}
}
static GtkWidget *
menu_item_new (MenuItem *menuitem, int workspace_id)
{
unsigned int key;
MetaVirtualModifier mods;
const char *i18n_label;
GtkWidget *mi;
GtkWidget *accel_label;
if (menuitem->type == MENU_ITEM_NORMAL)
{
mi = gtk_menu_item_new ();
}
else if (menuitem->type == MENU_ITEM_CHECKBOX)
{
mi = gtk_check_menu_item_new ();
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
menuitem->checked);
}
else if (menuitem->type == MENU_ITEM_RADIOBUTTON)
{
mi = gtk_check_menu_item_new ();
gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (mi),
TRUE);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
menuitem->checked);
}
else if (menuitem->type == MENU_ITEM_WORKSPACE_LIST)
return NULL;
else
return gtk_separator_menu_item_new ();
i18n_label = _(menuitem->label);
meta_core_get_menu_accelerator (menuitem->op, workspace_id, &key, &mods);
accel_label = meta_accel_label_new_with_mnemonic (i18n_label);
gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
gtk_container_add (GTK_CONTAINER (mi), accel_label);
gtk_widget_show (accel_label);
meta_accel_label_set_accelerator (META_ACCEL_LABEL (accel_label),
key, mods);
return mi;
}
MetaWindowMenu*
meta_window_menu_new (MetaFrames *frames,
MetaMenuOp ops,
MetaMenuOp insensitive,
Window client_xwindow,
unsigned long active_workspace,
int n_workspaces,
MetaWindowMenuFunc func,
gpointer data)
{
int i;
MetaWindowMenu *menu;
/* FIXME: Modifications to 'ops' should happen in meta_window_show_menu */
if (n_workspaces < 2)
ops &= ~(META_MENU_OP_STICK | META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES);
else if (n_workspaces == 2)
/* #151183: If we only have two workspaces, disable the menu listing them. */
ops &= ~(META_MENU_OP_WORKSPACES);
menu = g_new (MetaWindowMenu, 1);
menu->frames = frames;
menu->client_xwindow = client_xwindow;
menu->func = func;
menu->data = data;
menu->ops = ops;
menu->insensitive = insensitive;
menu->menu = gtk_menu_new ();
gtk_menu_set_screen (GTK_MENU (menu->menu),
gtk_widget_get_screen (GTK_WIDGET (frames)));
for (i = 0; i < (int) G_N_ELEMENTS (menuitems); i++)
{
MenuItem menuitem = menuitems[i];
if (ops & menuitem.op || menuitem.op == 0)
{
GtkWidget *mi;
MenuData *md;
unsigned int key;
MetaVirtualModifier mods;
mi = menu_item_new (&menuitem, -1);
/* Set the activeness of radiobuttons. */
switch (menuitem.op)
{
case META_MENU_OP_STICK:
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
active_workspace == 0xFFFFFFFF);
break;
case META_MENU_OP_UNSTICK:
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
active_workspace != 0xFFFFFFFF);
break;
default:
break;
}
if (menuitem.type == MENU_ITEM_WORKSPACE_LIST)
{
if (ops & META_MENU_OP_WORKSPACES)
{
Display *display;
Window xroot;
GdkScreen *screen;
GdkWindow *window;
GtkWidget *submenu;
int j;
MenuItem to_another_workspace = {
0, MENU_ITEM_NORMAL, FALSE,
N_("Move to Another _Workspace")
};
meta_verbose ("Creating %d-workspace menu current space %lu\n",
n_workspaces, active_workspace);
window = gtk_widget_get_window (GTK_WIDGET (frames));
display = GDK_WINDOW_XDISPLAY (window);
screen = gdk_window_get_screen (window);
xroot = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
submenu = gtk_menu_new ();
g_assert (mi==NULL);
mi = menu_item_new (&to_another_workspace, -1);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), submenu);
for (j = 0; j < n_workspaces; j++)
{
char *label;
MenuData *md;
unsigned int key;
MetaVirtualModifier mods;
MenuItem moveitem;
GtkWidget *submi;
meta_core_get_menu_accelerator (META_MENU_OP_WORKSPACES,
j + 1,
&key, &mods);
label = get_workspace_name_with_accel (display, xroot, j);
moveitem.type = MENU_ITEM_NORMAL;
moveitem.op = META_MENU_OP_WORKSPACES;
moveitem.label = label;
submi = menu_item_new (&moveitem, j + 1);
g_free (label);
if ((active_workspace == (unsigned)j) && (ops & META_MENU_OP_UNSTICK))
gtk_widget_set_sensitive (submi, FALSE);
md = g_new (MenuData, 1);
md->menu = menu;
md->op = META_MENU_OP_WORKSPACES;
g_object_set_data (G_OBJECT (submi),
"workspace",
GINT_TO_POINTER (j));
g_signal_connect_data (G_OBJECT (submi),
"activate",
G_CALLBACK (activate_cb),
md,
(GClosureNotify) g_free, 0);
gtk_menu_shell_append (GTK_MENU_SHELL (submenu), submi);
gtk_widget_show (submi);
}
}
else
meta_verbose ("not creating workspace menu\n");
}
else if (menuitem.type != MENU_ITEM_SEPARATOR)
{
meta_core_get_menu_accelerator (menuitems[i].op, -1,
&key, &mods);
if (insensitive & menuitem.op)
gtk_widget_set_sensitive (mi, FALSE);
md = g_new (MenuData, 1);
md->menu = menu;
md->op = menuitem.op;
g_signal_connect_data (G_OBJECT (mi),
"activate",
G_CALLBACK (activate_cb),
md,
(GClosureNotify) g_free, 0);
}
if (mi)
{
gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), mi);
gtk_widget_show (mi);
}
}
}
g_signal_connect (menu->menu, "selection_done",
G_CALLBACK (menu_closed), menu);
return menu;
}
void
meta_window_menu_popup (MetaWindowMenu *menu,
int root_x,
int root_y,
int button,
guint32 timestamp)
{
GdkPoint *pt;
pt = g_new (GdkPoint, 1);
g_object_set_data_full (G_OBJECT (menu->menu),
"destroy-point",
pt,
g_free);
pt->x = root_x;
pt->y = root_y;
gtk_menu_popup (GTK_MENU (menu->menu),
NULL, NULL,
popup_position_func, pt,
button,
timestamp);
if (!gtk_widget_get_visible (menu->menu))
meta_warning ("GtkMenu failed to grab the pointer\n");
}
void
meta_window_menu_free (MetaWindowMenu *menu)
{
gtk_widget_destroy (menu->menu);
g_free (menu);
}

View File

@ -1,55 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Mutter window menu */
/*
* Copyright (C) 2001 Havoc Pennington
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef META_MENU_H
#define META_MENU_H
#include <gtk/gtk.h>
#include "frames.h"
struct _MetaWindowMenu
{
MetaFrames *frames;
Window client_xwindow;
GtkWidget *menu;
MetaWindowMenuFunc func;
gpointer data;
MetaMenuOp ops;
MetaMenuOp insensitive;
};
MetaWindowMenu* meta_window_menu_new (MetaFrames *frames,
MetaMenuOp ops,
MetaMenuOp insensitive,
Window client_xwindow,
unsigned long active_workspace,
int n_workspaces,
MetaWindowMenuFunc func,
gpointer data);
void meta_window_menu_popup (MetaWindowMenu *menu,
int root_x,
int root_y,
int button,
guint32 timestamp);
void meta_window_menu_free (MetaWindowMenu *menu);
#endif

View File

@ -1,451 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Metacity hacked-up GtkAccelLabel */
/* Copyright (C) 2002 Red Hat, Inc. */
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* MetaAccelLabel: GtkLabel with accelerator monitoring facilities.
* Copyright (C) 1998 Tim Janik
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2001. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include <config.h>
#include "metaaccellabel.h"
#include <gtk/gtk.h>
#include <string.h>
#include "util-private.h"
static void meta_accel_label_destroy (GtkWidget *object);
static void meta_accel_label_finalize (GObject *object);
static void meta_accel_label_get_preferred_width (GtkWidget *widget,
gint *minimum,
gint *natural);
static void meta_accel_label_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural);
static gboolean meta_accel_label_draw (GtkWidget *widget,
cairo_t *cr);
static void meta_accel_label_update (MetaAccelLabel *accel_label);
static int meta_accel_label_get_accel_width (MetaAccelLabel *accel_label);
G_DEFINE_TYPE (MetaAccelLabel, meta_accel_label, GTK_TYPE_LABEL);
static void
meta_accel_label_class_init (MetaAccelLabelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
gobject_class->finalize = meta_accel_label_finalize;
widget_class->destroy = meta_accel_label_destroy;
widget_class->get_preferred_width = meta_accel_label_get_preferred_width;
widget_class->get_preferred_height = meta_accel_label_get_preferred_height;
widget_class->draw = meta_accel_label_draw;
class->signal_quote1 = g_strdup ("<:");
class->signal_quote2 = g_strdup (":>");
/* This is the text that should appear next to menu accelerators
* that use the shift key. If the text on this key isn't typically
* translated on keyboards used for your language, don't translate
* this.
*/
class->mod_name_shift = g_strdup (_("Shift"));
/* This is the text that should appear next to menu accelerators
* that use the control key. If the text on this key isn't typically
* translated on keyboards used for your language, don't translate
* this.
*/
class->mod_name_control = g_strdup (_("Ctrl"));
/* This is the text that should appear next to menu accelerators
* that use the alt key. If the text on this key isn't typically
* translated on keyboards used for your language, don't translate
* this.
*/
class->mod_name_alt = g_strdup (_("Alt"));
/* This is the text that should appear next to menu accelerators
* that use the meta key. If the text on this key isn't typically
* translated on keyboards used for your language, don't translate
* this.
*/
class->mod_name_meta = g_strdup (_("Meta"));
/* This is the text that should appear next to menu accelerators
* that use the super key. If the text on this key isn't typically
* translated on keyboards used for your language, don't translate
* this.
*/
class->mod_name_super = g_strdup (_("Super"));
/* This is the text that should appear next to menu accelerators
* that use the hyper key. If the text on this key isn't typically
* translated on keyboards used for your language, don't translate
* this.
*/
class->mod_name_hyper = g_strdup (_("Hyper"));
/* This is the text that should appear next to menu accelerators
* that use the mod2 key. If the text on this key isn't typically
* translated on keyboards used for your language, don't translate
* this.
*/
class->mod_name_mod2 = g_strdup (_("Mod2"));
/* This is the text that should appear next to menu accelerators
* that use the mod3 key. If the text on this key isn't typically
* translated on keyboards used for your language, don't translate
* this.
*/
class->mod_name_mod3 = g_strdup (_("Mod3"));
/* This is the text that should appear next to menu accelerators
* that use the mod4 key. If the text on this key isn't typically
* translated on keyboards used for your language, don't translate
* this.
*/
class->mod_name_mod4 = g_strdup (_("Mod4"));
/* This is the text that should appear next to menu accelerators
* that use the mod5 key. If the text on this key isn't typically
* translated on keyboards used for your language, don't translate
* this.
*/
class->mod_name_mod5 = g_strdup (_("Mod5"));
class->mod_separator = g_strdup ("+");
class->accel_seperator = g_strdup (" / ");
class->latin1_to_char = TRUE;
}
static void
meta_accel_label_init (MetaAccelLabel *accel_label)
{
accel_label->accel_padding = 3;
accel_label->accel_string = NULL;
meta_accel_label_update (accel_label);
}
GtkWidget*
meta_accel_label_new_with_mnemonic (const gchar *string)
{
MetaAccelLabel *accel_label;
g_return_val_if_fail (string != NULL, NULL);
accel_label = g_object_new (META_TYPE_ACCEL_LABEL, NULL);
gtk_label_set_text_with_mnemonic (GTK_LABEL (accel_label), string);
return GTK_WIDGET (accel_label);
}
static void
meta_accel_label_destroy (GtkWidget *object)
{
MetaAccelLabel *accel_label = META_ACCEL_LABEL (object);
g_free (accel_label->accel_string);
accel_label->accel_string = NULL;
accel_label->accel_mods = 0;
accel_label->accel_key = 0;
GTK_WIDGET_CLASS (meta_accel_label_parent_class)->destroy (object);
}
static void
meta_accel_label_finalize (GObject *object)
{
MetaAccelLabel *accel_label = META_ACCEL_LABEL (object);
g_free (accel_label->accel_string);
G_OBJECT_CLASS (meta_accel_label_parent_class)->finalize (object);
}
void
meta_accel_label_set_accelerator (MetaAccelLabel *accel_label,
guint accelerator_key,
MetaVirtualModifier accelerator_mods)
{
g_return_if_fail (META_IS_ACCEL_LABEL (accel_label));
if (accelerator_key != accel_label->accel_key ||
accelerator_mods != accel_label->accel_mods)
{
accel_label->accel_mods = accelerator_mods;
accel_label->accel_key = accelerator_key;
meta_accel_label_update (accel_label);
}
}
static int
meta_accel_label_get_accel_width (MetaAccelLabel *accel_label)
{
g_return_val_if_fail (META_IS_ACCEL_LABEL (accel_label), 0);
return (accel_label->accel_string_width +
(accel_label->accel_string_width ? accel_label->accel_padding : 0));
}
static void
meta_accel_label_get_preferred_width (GtkWidget *widget,
gint *minimum,
gint *natural)
{
MetaAccelLabel *accel_label = META_ACCEL_LABEL (widget);
PangoLayout *layout;
gint width;
GTK_WIDGET_CLASS (meta_accel_label_parent_class)->get_preferred_width (widget, minimum, natural);
layout = gtk_widget_create_pango_layout (widget, accel_label->accel_string);
pango_layout_get_pixel_size (layout, &width, NULL);
accel_label->accel_string_width = width;
g_object_unref (G_OBJECT (layout));
}
static void
meta_accel_label_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural)
{
GTK_WIDGET_CLASS (meta_accel_label_parent_class)->get_preferred_height (widget, minimum, natural);
}
/* Mostly taken from GTK3. */
static gboolean
meta_accel_label_draw (GtkWidget *widget,
cairo_t *cr)
{
MetaAccelLabel *accel_label = META_ACCEL_LABEL (widget);
GtkMisc *misc = GTK_MISC (accel_label);
GtkTextDirection direction;
int ac_width;
GtkAllocation allocation;
GtkRequisition requisition;
direction = gtk_widget_get_direction (widget);
ac_width = meta_accel_label_get_accel_width (accel_label);
gtk_widget_get_allocation (widget, &allocation);
gtk_widget_get_preferred_size (widget,
&requisition, NULL);
if (allocation.width >= requisition.width + ac_width)
{
GtkStyleContext *style;
PangoLayout *label_layout;
PangoLayout *accel_layout;
GtkLabel *label = GTK_LABEL (widget);
gint x, y, xpad, ypad;
gfloat xalign, yalign;
label_layout = gtk_label_get_layout (GTK_LABEL (accel_label));
gtk_misc_get_alignment (misc, &xalign, &yalign);
cairo_save (cr);
/* XXX: Mad hack: We modify the label's width so it renders
* properly in its draw function that we chain to. */
if (direction == GTK_TEXT_DIR_RTL)
cairo_translate (cr, ac_width, 0);
if (gtk_label_get_ellipsize (label))
pango_layout_set_width (label_layout,
pango_layout_get_width (label_layout)
- ac_width * PANGO_SCALE);
allocation.width -= ac_width;
gtk_widget_set_allocation (widget, &allocation);
if (GTK_WIDGET_CLASS (meta_accel_label_parent_class)->draw)
GTK_WIDGET_CLASS (meta_accel_label_parent_class)->draw (widget,
cr);
allocation.width += ac_width;
gtk_widget_set_allocation (widget, &allocation);
if (gtk_label_get_ellipsize (label))
pango_layout_set_width (label_layout,
pango_layout_get_width (label_layout)
+ ac_width * PANGO_SCALE);
cairo_restore (cr);
gtk_misc_get_padding (misc, &xpad, &ypad);
if (direction == GTK_TEXT_DIR_RTL)
x = xpad;
else
x = gtk_widget_get_allocated_width (widget) - xpad - ac_width;
gtk_label_get_layout_offsets (GTK_LABEL (accel_label), NULL, &y);
accel_layout = gtk_widget_create_pango_layout (widget, accel_label->accel_string);
y = (allocation.height - (requisition.height - ypad * 2)) * yalign + 1.5;
style = gtk_widget_get_style_context (widget);
gtk_style_context_save (style);
gtk_style_context_set_state (style,
gtk_widget_get_state_flags (widget));
gtk_render_layout (gtk_widget_get_style_context (widget),
cr,
x, y,
accel_layout);
gtk_style_context_restore (style);
g_object_unref (accel_layout);
}
else
{
if (GTK_WIDGET_CLASS (meta_accel_label_parent_class)->draw)
GTK_WIDGET_CLASS (meta_accel_label_parent_class)->draw (widget, cr);
}
return FALSE;
}
static void
meta_accel_label_update (MetaAccelLabel *accel_label)
{
MetaAccelLabelClass *class;
GString *gstring;
gboolean seen_mod = FALSE;
gunichar ch;
g_return_if_fail (META_IS_ACCEL_LABEL (accel_label));
class = META_ACCEL_LABEL_GET_CLASS (accel_label);
g_free (accel_label->accel_string);
accel_label->accel_string = NULL;
gstring = g_string_new (accel_label->accel_string);
g_string_append (gstring, gstring->len ? class->accel_seperator : " ");
if (accel_label->accel_mods & META_VIRTUAL_SHIFT_MASK)
{
g_string_append (gstring, class->mod_name_shift);
seen_mod = TRUE;
}
if (accel_label->accel_mods & META_VIRTUAL_CONTROL_MASK)
{
if (seen_mod)
g_string_append (gstring, class->mod_separator);
g_string_append (gstring, class->mod_name_control);
seen_mod = TRUE;
}
if (accel_label->accel_mods & META_VIRTUAL_ALT_MASK)
{
if (seen_mod)
g_string_append (gstring, class->mod_separator);
g_string_append (gstring, class->mod_name_alt);
seen_mod = TRUE;
}
if (accel_label->accel_mods & META_VIRTUAL_META_MASK)
{
if (seen_mod)
g_string_append (gstring, class->mod_separator);
g_string_append (gstring, class->mod_name_meta);
seen_mod = TRUE;
}
if (accel_label->accel_mods & META_VIRTUAL_SUPER_MASK)
{
if (seen_mod)
g_string_append (gstring, class->mod_separator);
g_string_append (gstring, class->mod_name_super);
seen_mod = TRUE;
}
if (accel_label->accel_mods & META_VIRTUAL_HYPER_MASK)
{
if (seen_mod)
g_string_append (gstring, class->mod_separator);
g_string_append (gstring, class->mod_name_hyper);
seen_mod = TRUE;
}
if (accel_label->accel_mods & META_VIRTUAL_MOD2_MASK)
{
if (seen_mod)
g_string_append (gstring, class->mod_separator);
g_string_append (gstring, class->mod_name_mod2);
seen_mod = TRUE;
}
if (accel_label->accel_mods & META_VIRTUAL_MOD3_MASK)
{
if (seen_mod)
g_string_append (gstring, class->mod_separator);
g_string_append (gstring, class->mod_name_mod3);
seen_mod = TRUE;
}
if (accel_label->accel_mods & META_VIRTUAL_MOD4_MASK)
{
if (seen_mod)
g_string_append (gstring, class->mod_separator);
g_string_append (gstring, class->mod_name_mod4);
seen_mod = TRUE;
}
if (accel_label->accel_mods & META_VIRTUAL_MOD5_MASK)
{
if (seen_mod)
g_string_append (gstring, class->mod_separator);
g_string_append (gstring, class->mod_name_mod5);
seen_mod = TRUE;
}
if (seen_mod)
g_string_append (gstring, class->mod_separator);
ch = gdk_keyval_to_unicode (accel_label->accel_key);
if (ch && (g_unichar_isgraph (ch) || ch == ' ') &&
(ch < 0x80 || class->latin1_to_char))
{
switch (ch)
{
case ' ':
g_string_append (gstring, "Space");
break;
case '\\':
g_string_append (gstring, "Backslash");
break;
default:
g_string_append_unichar (gstring, g_unichar_toupper (ch));
break;
}
}
else
{
gchar *tmp;
tmp = gtk_accelerator_name (accel_label->accel_key, 0);
if (tmp[0] != 0 && tmp[1] == 0)
tmp[0] = g_ascii_toupper (tmp[0]);
g_string_append (gstring, tmp);
g_free (tmp);
}
g_free (accel_label->accel_string);
accel_label->accel_string = gstring->str;
g_string_free (gstring, FALSE);
g_assert (accel_label->accel_string);
/* accel_label->accel_string = g_strdup ("-/-"); */
gtk_widget_queue_resize (GTK_WIDGET (accel_label));
}

View File

@ -1,104 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Metacity hacked-up GtkAccelLabel */
/* Copyright (C) 2002 Red Hat, Inc. */
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* MetaAccelLabel: GtkLabel with accelerator monitoring facilities.
* Copyright (C) 1998 Tim Janik
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2001. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#ifndef __META_ACCEL_LABEL_H__
#define __META_ACCEL_LABEL_H__
#include <gtk/gtk.h>
#include <meta/common.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define META_TYPE_ACCEL_LABEL (meta_accel_label_get_type ())
#define META_ACCEL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_ACCEL_LABEL, MetaAccelLabel))
#define META_ACCEL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_ACCEL_LABEL, MetaAccelLabelClass))
#define META_IS_ACCEL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_ACCEL_LABEL))
#define META_IS_ACCEL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_ACCEL_LABEL))
#define META_ACCEL_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_ACCEL_LABEL, MetaAccelLabelClass))
typedef struct _MetaAccelLabel MetaAccelLabel;
typedef struct _MetaAccelLabelClass MetaAccelLabelClass;
struct _MetaAccelLabel
{
GtkLabel label;
MetaVirtualModifier accel_mods;
guint accel_key;
guint accel_padding;
gchar *accel_string;
guint16 accel_string_width;
};
struct _MetaAccelLabelClass
{
GtkLabelClass parent_class;
gchar *signal_quote1;
gchar *signal_quote2;
gchar *mod_name_shift;
gchar *mod_name_control;
gchar *mod_name_alt;
gchar *mod_name_meta;
gchar *mod_name_super;
gchar *mod_name_hyper;
gchar *mod_name_mod2;
gchar *mod_name_mod3;
gchar *mod_name_mod4;
gchar *mod_name_mod5;
gchar *mod_separator;
gchar *accel_seperator;
guint latin1_to_char : 1;
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
void (*_gtk_reserved4) (void);
};
GType meta_accel_label_get_type (void) G_GNUC_CONST;
GtkWidget* meta_accel_label_new_with_mnemonic (const gchar *string);
void meta_accel_label_set_accelerator (MetaAccelLabel *accel_label,
guint accelerator_key,
MetaVirtualModifier accelerator_mods);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __META_ACCEL_LABEL_H__ */

View File

@ -514,40 +514,6 @@ meta_ui_set_frame_title (MetaUI *ui,
meta_frames_set_title (ui->frames, xwindow, title);
}
MetaWindowMenu*
meta_ui_window_menu_new (MetaUI *ui,
Window client_xwindow,
MetaMenuOp ops,
MetaMenuOp insensitive,
unsigned long active_workspace,
int n_workspaces,
MetaWindowMenuFunc func,
gpointer data)
{
return meta_window_menu_new (ui->frames,
ops, insensitive,
client_xwindow,
active_workspace,
n_workspaces,
func, data);
}
void
meta_ui_window_menu_popup (MetaWindowMenu *menu,
int root_x,
int root_y,
int button,
guint32 timestamp)
{
meta_window_menu_popup (menu, root_x, root_y, button, timestamp);
}
void
meta_ui_window_menu_free (MetaWindowMenu *menu)
{
meta_window_menu_free (menu);
}
GdkPixbuf*
meta_gdk_pixbuf_get_from_pixmap (Pixmap xpixmap,
int src_x,

View File

@ -120,21 +120,6 @@ void meta_ui_update_frame_style (MetaUI *ui,
void meta_ui_repaint_frame (MetaUI *ui,
Window xwindow);
MetaWindowMenu* meta_ui_window_menu_new (MetaUI *ui,
Window client_xwindow,
MetaMenuOp ops,
MetaMenuOp insensitive,
unsigned long active_workspace,
int n_workspaces,
MetaWindowMenuFunc func,
gpointer data);
void meta_ui_window_menu_popup (MetaWindowMenu *menu,
int root_x,
int root_y,
int button,
guint32 timestamp);
void meta_ui_window_menu_free (MetaWindowMenu *menu);
/* FIXME these lack a display arg */
GdkPixbuf* meta_gdk_pixbuf_get_from_pixmap (Pixmap xpixmap,

441
src/wayland/meta-login1.c Normal file
View File

@ -0,0 +1,441 @@
/*
* Copyright (C) 2014 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "config.h"
#include "meta-login1.h"
#include "meta-dbus-login1.h"
#include "meta-wayland-private.h"
#include "meta-cursor-tracker-private.h"
#include <gio/gunixfdlist.h>
#include <clutter/clutter.h>
#include <clutter/evdev/clutter-evdev.h>
#include <clutter/egl/clutter-egl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <malloc.h>
#include <fcntl.h>
#include <unistd.h>
#include <systemd/sd-login.h>
struct _MetaLogin1
{
Login1Session *session_proxy;
Login1Seat *seat_proxy;
gboolean session_active;
};
/* Stolen from tp_escape_as_identifier, from tp-glib,
* which follows the same escaping convention as systemd.
*/
static inline gboolean
_esc_ident_bad (gchar c, gboolean is_first)
{
return ((c < 'a' || c > 'z') &&
(c < 'A' || c > 'Z') &&
(c < '0' || c > '9' || is_first));
}
static gchar *
escape_dbus_component (const gchar *name)
{
gboolean bad = FALSE;
size_t len = 0;
GString *op;
const gchar *ptr, *first_ok;
g_return_val_if_fail (name != NULL, NULL);
/* fast path for empty name */
if (name[0] == '\0')
return g_strdup ("_");
for (ptr = name; *ptr; ptr++)
{
if (_esc_ident_bad (*ptr, ptr == name))
{
bad = TRUE;
len += 3;
}
else
len++;
}
/* fast path if it's clean */
if (!bad)
return g_strdup (name);
/* If strictly less than ptr, first_ok is the first uncopied safe character.
*/
first_ok = name;
op = g_string_sized_new (len);
for (ptr = name; *ptr; ptr++)
{
if (_esc_ident_bad (*ptr, ptr == name))
{
/* copy preceding safe characters if any */
if (first_ok < ptr)
{
g_string_append_len (op, first_ok, ptr - first_ok);
}
/* escape the unsafe character */
g_string_append_printf (op, "_%02x", (unsigned char)(*ptr));
/* restart after it */
first_ok = ptr + 1;
}
}
/* copy trailing safe characters if any */
if (first_ok < ptr)
{
g_string_append_len (op, first_ok, ptr - first_ok);
}
return g_string_free (op, FALSE);
}
static char *
get_escaped_dbus_path (const char *prefix,
const char *component)
{
char *escaped_component = escape_dbus_component (component);
char *path = g_strconcat (prefix, "/", component, NULL);
g_free (escaped_component);
return path;
}
static Login1Session *
get_session_proxy (GCancellable *cancellable)
{
char *proxy_path;
char *session_id;
Login1Session *session_proxy;
if (sd_pid_get_session (getpid (), &session_id) < 0)
return NULL;
proxy_path = get_escaped_dbus_path ("/org/freedesktop/login1/session", session_id);
session_proxy = login1_session_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
"org.freedesktop.login1",
proxy_path,
cancellable, NULL);
free (proxy_path);
return session_proxy;
}
static Login1Seat *
get_seat_proxy (GCancellable *cancellable)
{
return login1_seat_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
"org.freedesktop.login1",
"/org/freedesktop/login1/seat/self",
cancellable, NULL);
}
static void
session_unpause (void)
{
ClutterBackend *backend;
CoglContext *cogl_context;
CoglDisplay *cogl_display;
backend = clutter_get_default_backend ();
cogl_context = clutter_backend_get_cogl_context (backend);
cogl_display = cogl_context_get_display (cogl_context);
cogl_kms_display_queue_modes_reset (cogl_display);
clutter_set_paused (FALSE);
/* clutter_evdev_reclaim_devices (); */
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
/* When we mode-switch back, we need to immediately queue a redraw
* in case nothing else quueed one for us, and force the cursor to
* update. */
clutter_actor_queue_redraw (compositor->stage);
meta_cursor_tracker_force_update (compositor->seat->cursor_tracker);
}
}
static void
session_pause (void)
{
clutter_set_paused (TRUE);
/* clutter_evdev_release_devices (); */
}
static void
sync_active (MetaLogin1 *self)
{
gboolean active = login1_session_get_active (LOGIN1_SESSION (self->session_proxy));
if (active == self->session_active)
return;
self->session_active = active;
if (active)
session_unpause ();
else
session_pause ();
}
static void
on_active_changed (Login1Session *session,
GParamSpec *pspec,
gpointer user_data)
{
MetaLogin1 *self = user_data;
sync_active (self);
}
static gboolean
take_device (Login1Session *session_proxy,
int dev_major,
int dev_minor,
int *out_fd,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
GVariant *fd_variant = NULL;
int fd = -1;
GUnixFDList *fd_list;
if (!login1_session_call_take_device_sync (session_proxy,
dev_major,
dev_minor,
NULL,
&fd_variant,
NULL, /* paused */
&fd_list,
cancellable,
error))
goto out;
fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_variant), error);
if (fd == -1)
goto out;
*out_fd = fd;
ret = TRUE;
out:
if (fd_variant)
g_variant_unref (fd_variant);
if (fd_list)
g_object_unref (fd_list);
return ret;
}
static gboolean
get_device_info_from_path (const char *path,
int *out_major,
int *out_minor)
{
gboolean ret = FALSE;
int r;
struct stat st;
r = stat (path, &st);
if (r < 0)
goto out;
if (!S_ISCHR (st.st_mode))
goto out;
*out_major = major (st.st_rdev);
*out_minor = minor (st.st_rdev);
ret = TRUE;
out:
return ret;
}
static gboolean
get_device_info_from_fd (int fd,
int *out_major,
int *out_minor)
{
gboolean ret = FALSE;
int r;
struct stat st;
r = fstat (fd, &st);
if (r < 0)
goto out;
if (!S_ISCHR (st.st_mode))
goto out;
*out_major = major (st.st_rdev);
*out_minor = minor (st.st_rdev);
ret = TRUE;
out:
return ret;
}
static int
open_evdev_device (const char *path,
int flags,
gpointer user_data,
GError **error)
{
MetaLogin1 *self = user_data;
int fd;
int major, minor;
if (!get_device_info_from_path (path, &major, &minor))
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"Could not get device info for path %s: %m", path);
return -1;
}
if (!take_device (self->session_proxy, major, minor, &fd, NULL, error))
return -1;
return fd;
}
static void
close_evdev_device (int fd,
gpointer user_data)
{
MetaLogin1 *self = user_data;
int major, minor;
GError *error = NULL;
if (!get_device_info_from_fd (fd, &major, &minor))
{
g_warning ("Could not get device info for fd %d: %m", fd);
return;
}
if (!login1_session_call_release_device_sync (self->session_proxy,
major, minor,
NULL, &error))
{
g_warning ("Could not release device %d,%d: %s", major, minor, error->message);
}
}
static gboolean
get_kms_fd (Login1Session *session_proxy,
int *fd_out)
{
int major, minor;
int fd;
GError *error = NULL;
/* XXX -- use udev to find the DRM master device */
if (!get_device_info_from_path ("/dev/dri/card0", &major, &minor))
{
g_warning ("Could not stat /dev/dri/card0: %m");
return FALSE;
}
if (!take_device (session_proxy, major, minor, &fd, NULL, &error))
{
g_warning ("Could not open DRM device: %s\n", error->message);
g_error_free (error);
return FALSE;
}
*fd_out = fd;
return TRUE;
}
MetaLogin1 *
meta_login1_new (void)
{
MetaLogin1 *self;
Login1Session *session_proxy;
GError *error = NULL;
int kms_fd;
session_proxy = get_session_proxy (NULL);
if (!login1_session_call_take_control_sync (session_proxy, FALSE, NULL, &error))
{
g_warning ("Could not take control: %s", error->message);
g_error_free (error);
return NULL;
}
if (!get_kms_fd (session_proxy, &kms_fd))
return NULL;
self = g_slice_new0 (MetaLogin1);
self->session_proxy = session_proxy;
self->seat_proxy = get_seat_proxy (NULL);
/* Clutter/Cogl start out in a state that assumes the session is active */
self->session_active = TRUE;
clutter_egl_set_kms_fd (kms_fd);
clutter_evdev_set_device_callbacks (open_evdev_device,
close_evdev_device,
self);
g_signal_connect (self->session_proxy, "notify::active", G_CALLBACK (on_active_changed), self);
sync_active (self);
return self;
}
void
meta_login1_free (MetaLogin1 *self)
{
g_object_unref (self->seat_proxy);
g_object_unref (self->session_proxy);
g_slice_free (MetaLogin1, self);
}
gboolean
meta_login1_activate_session (MetaLogin1 *self,
GError **error)
{
if (!login1_session_call_activate_sync (self->session_proxy, NULL, error))
return FALSE;
sync_active (self);
return TRUE;
}
gboolean
meta_login1_activate_vt (MetaLogin1 *self,
int vt,
GError **error)
{
return login1_seat_call_switch_to_sync (self->seat_proxy, vt, NULL, error);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Red Hat, Inc.
* Copyright (C) 2014 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -17,22 +17,19 @@
* 02111-1307, USA.
*/
#ifndef META_WESTON_LAUNCH_H
#define META_WESTON_LAUNCH_H
#ifndef META_LOGIN1_H
#define META_LOGIN1_H
#include <glib-object.h>
#include "weston-launch.h"
typedef struct _MetaLauncher MetaLauncher;
typedef struct _MetaLogin1 MetaLogin1;
MetaLauncher *meta_launcher_new (void);
void meta_launcher_free (MetaLauncher *self);
MetaLogin1 *meta_login1_new (void);
void meta_login1_free (MetaLogin1 *self);
gboolean meta_login1_activate_session (MetaLogin1 *self,
GError **error);
gboolean meta_login1_activate_vt (MetaLogin1 *self,
int vt,
GError **error);
gboolean meta_launcher_activate_vt (MetaLauncher *self,
signed char vt,
GError **error);
gboolean meta_launcher_set_drm_fd (MetaLauncher *self,
int drm_fd,
GError **error);
#endif

View File

@ -27,7 +27,7 @@
#include <cairo.h>
#include "window-private.h"
#include "meta-weston-launch.h"
#include "meta-login1.h"
#include <meta/meta-cursor-tracker.h>
#include "meta-wayland.h"
@ -85,7 +85,7 @@ struct _MetaWaylandCompositor
MetaXWaylandManager xwayland_manager;
MetaLauncher *launcher;
MetaLogin1 *login1;
MetaWaylandSeat *seat;
};

View File

@ -749,6 +749,49 @@ xdg_surface_set_app_id (struct wl_client *client,
meta_window_set_wm_class (surface->window, app_id, app_id);
}
static MetaWindowType
window_type_from_surface_type (uint32_t surface_type)
{
switch (surface_type)
{
case XDG_SURFACE_SURFACE_TYPE_NORMAL:
return META_WINDOW_NORMAL;
case XDG_SURFACE_SURFACE_TYPE_MODAL_DIALOG:
return META_WINDOW_MODAL_DIALOG;
default:
g_warning ("Unknown surface type: %d\n", surface_type);
return META_WINDOW_NORMAL;
}
}
static void
xdg_surface_set_surface_type (struct wl_client *client,
struct wl_resource *resource,
uint32_t surface_type)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
meta_window_set_type (surface->window,
window_type_from_surface_type (surface_type));
}
static void
xdg_surface_show_window_menu (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat_resource,
uint32_t serial)
{
MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
if (seat->pointer.button_count == 0 ||
seat->pointer.grab_serial != serial ||
seat->pointer.focus_surface != surface)
return;
meta_window_show_menu (surface->window);
}
static gboolean
begin_grab_op_on_surface (MetaWaylandSurface *surface,
MetaWaylandSeat *seat,
@ -857,9 +900,9 @@ xdg_surface_request_change_state (struct wl_client *client,
{
case XDG_SURFACE_STATE_MAXIMIZED:
if (value)
meta_window_maximize (surface->window, META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
meta_window_maximize (surface->window, META_MAXIMIZE_BOTH);
else
meta_window_unmaximize (surface->window, META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH);
break;
case XDG_SURFACE_STATE_FULLSCREEN:
if (value)
@ -898,6 +941,8 @@ static const struct xdg_surface_interface meta_wayland_xdg_surface_interface = {
xdg_surface_set_margin,
xdg_surface_set_title,
xdg_surface_set_app_id,
xdg_surface_set_surface_type,
xdg_surface_show_window_menu,
xdg_surface_move,
xdg_surface_resize,
xdg_surface_set_output,
@ -1153,9 +1198,9 @@ wl_shell_surface_set_state (MetaWaylandSurface *surface,
meta_window_unmake_fullscreen (surface->window);
if (state == SURFACE_STATE_MAXIMIZED)
meta_window_maximize (surface->window, META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL);
meta_window_maximize (surface->window, META_MAXIMIZE_BOTH);
else
meta_window_unmaximize (surface->window, META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL);
meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH);
}
static void

View File

@ -52,7 +52,7 @@
#include <meta/main.h>
#include "frame.h"
#include "meta-idle-monitor-private.h"
#include "meta-weston-launch.h"
#include "meta-login1.h"
#include "monitor-private.h"
static MetaWaylandCompositor _meta_wayland_compositor;
@ -644,26 +644,15 @@ meta_wayland_init (void)
clutter_wayland_set_compositor_display (compositor->wayland_display);
/* If we're running on bare metal, we're a display server,
* so start talking to weston-launch. */
* so start talking to logind. */
#if defined(CLUTTER_WINDOWING_EGL)
if (clutter_check_windowing_backend (CLUTTER_WINDOWING_EGL))
compositor->launcher = meta_launcher_new ();
compositor->login1 = meta_login1_new ();
#endif
if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS)
g_error ("Failed to initialize Clutter");
#if defined(CLUTTER_WINDOWING_EGL)
if (clutter_check_windowing_backend (CLUTTER_WINDOWING_EGL))
{
ClutterBackend *backend = clutter_get_default_backend ();
CoglContext *cogl_context = clutter_backend_get_cogl_context (backend);
CoglRenderer *cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (cogl_context));
int drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
meta_launcher_set_drm_fd (compositor->launcher, drm_fd, NULL);
}
#endif
meta_monitor_manager_initialize ();
monitors = meta_monitor_manager_get ();
g_signal_connect (monitors, "monitors-changed",
@ -715,8 +704,8 @@ meta_wayland_finalize (void)
meta_xwayland_stop (&compositor->xwayland_manager);
if (compositor->launcher)
meta_launcher_free (compositor->launcher);
if (compositor->login1)
meta_login1_free (compositor->login1);
}
gboolean
@ -724,9 +713,9 @@ meta_wayland_compositor_activate_vt (MetaWaylandCompositor *compositor,
int vt,
GError **error)
{
if (compositor->launcher)
if (compositor->login1)
{
return meta_launcher_activate_vt (compositor->launcher, vt, error);
return meta_login1_activate_vt (compositor->login1, vt, error);
}
else
{
@ -739,9 +728,9 @@ gboolean
meta_wayland_compositor_activate_session (MetaWaylandCompositor *compositor,
GError **error)
{
if (compositor->launcher)
if (compositor->login1)
{
return meta_launcher_activate_vt (compositor->launcher, -1, error);
return meta_login1_activate_session (compositor->login1, error);
}
else
{

View File

@ -1,423 +0,0 @@
/*
* Copyright (C) 2013 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <config.h>
#include <gio/gio.h>
#include <gio/gunixfdmessage.h>
#include <clutter/clutter.h>
#include <clutter/evdev/clutter-evdev.h>
#include <glib.h>
#include <sys/time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <drm.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "meta-wayland-private.h"
#include "meta-cursor-tracker-private.h"
#include "meta-weston-launch.h"
struct _MetaLauncher
{
GSocket *weston_launch;
gboolean vt_switched;
GMainContext *nested_context;
GMainLoop *nested_loop;
GSource *inner_source;
GSource *outer_source;
};
static void handle_request_vt_switch (MetaLauncher *self);
static gboolean
request_vt_switch_idle (gpointer user_data)
{
handle_request_vt_switch (user_data);
return FALSE;
}
static gboolean
send_message_to_wl (MetaLauncher *self,
void *message,
gsize size,
GSocketControlMessage *out_cmsg,
GSocketControlMessage **in_cmsg,
GError **error)
{
struct weston_launcher_reply reply;
GInputVector in_iov = { &reply, sizeof (reply) };
GOutputVector out_iov = { message, size };
GSocketControlMessage *out_all_cmsg[2];
GSocketControlMessage **in_all_cmsg;
int flags = 0;
int i;
out_all_cmsg[0] = out_cmsg;
out_all_cmsg[1] = NULL;
if (g_socket_send_message (self->weston_launch, NULL,
&out_iov, 1,
out_all_cmsg, -1,
flags, NULL, error) != (gssize)size)
return FALSE;
if (g_socket_receive_message (self->weston_launch, NULL,
&in_iov, 1,
&in_all_cmsg, NULL,
&flags, NULL, error) != sizeof (reply))
return FALSE;
while (reply.header.opcode != ((struct weston_launcher_message*)message)->opcode)
{
/* There were events queued */
g_assert ((reply.header.opcode & WESTON_LAUNCHER_EVENT) == WESTON_LAUNCHER_EVENT);
/* This can never happen, because the only time mutter-launch can queue
this event is after confirming a VT switch, and we don't make requests
during that time.
Note that getting this event would be really bad, because we would be
in the wrong loop/context.
*/
g_assert (reply.header.opcode != WESTON_LAUNCHER_SERVER_VT_ENTER);
switch (reply.header.opcode)
{
case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH:
g_idle_add (request_vt_switch_idle, self);
break;
default:
g_assert_not_reached ();
}
if (g_socket_receive_message (self->weston_launch, NULL,
&in_iov, 1,
NULL, NULL,
&flags, NULL, error) != sizeof (reply))
return FALSE;
}
if (reply.ret != 0)
{
if (reply.ret == -1)
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Got failure from weston-launch");
else
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-reply.ret),
"Got failure from weston-launch: %s", strerror (-reply.ret));
for (i = 0; in_all_cmsg && in_all_cmsg[i]; i++)
g_object_unref (in_all_cmsg[i]);
g_free (in_all_cmsg);
return FALSE;
}
if (in_all_cmsg && in_all_cmsg[0])
{
for (i = 1; in_all_cmsg[i]; i++)
g_object_unref (in_all_cmsg[i]);
*in_cmsg = in_all_cmsg[0];
}
g_free (in_all_cmsg);
return TRUE;
}
gboolean
meta_launcher_set_drm_fd (MetaLauncher *self,
int drm_fd,
GError **error)
{
struct weston_launcher_message message;
GSocketControlMessage *cmsg;
gboolean ok;
message.opcode = WESTON_LAUNCHER_DRM_SET_FD;
cmsg = g_unix_fd_message_new ();
if (g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (cmsg),
drm_fd, error) == FALSE)
{
g_object_unref (cmsg);
return FALSE;
}
ok = send_message_to_wl (self, &message, sizeof message, cmsg, NULL, error);
g_object_unref (cmsg);
return ok;
}
static int
meta_launcher_open_input_device (MetaLauncher *self,
const char *name,
int flags,
GError **error)
{
struct weston_launcher_open *message;
GSocketControlMessage *cmsg;
gboolean ok;
gsize size;
int *fds, n_fd;
int ret;
size = sizeof (struct weston_launcher_open) + strlen (name) + 1;
message = g_malloc (size);
message->header.opcode = WESTON_LAUNCHER_OPEN;
message->flags = flags;
strcpy (message->path, name);
message->path[strlen(name)] = 0;
ok = send_message_to_wl (self, message, size, NULL, &cmsg, error);
if (ok)
{
g_assert (G_IS_UNIX_FD_MESSAGE (cmsg));
fds = g_unix_fd_message_steal_fds (G_UNIX_FD_MESSAGE (cmsg), &n_fd);
g_assert (n_fd == 1);
ret = fds[0];
g_free (fds);
g_object_unref (cmsg);
}
else
ret = -1;
g_free (message);
return ret;
}
static void
meta_launcher_enter (MetaLauncher *launcher)
{
ClutterBackend *backend;
CoglContext *cogl_context;
CoglDisplay *cogl_display;
backend = clutter_get_default_backend ();
cogl_context = clutter_backend_get_cogl_context (backend);
cogl_display = cogl_context_get_display (cogl_context);
cogl_kms_display_queue_modes_reset (cogl_display);
clutter_evdev_reclaim_devices ();
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
/* When we mode-switch back, we need to immediately queue a redraw
* in case nothing else queued one for us, and force the cursor to
* update. */
clutter_actor_queue_redraw (compositor->stage);
meta_cursor_tracker_force_update (compositor->seat->cursor_tracker);
}
}
static void
meta_launcher_leave (MetaLauncher *launcher)
{
clutter_evdev_release_devices ();
}
static int
on_evdev_device_open (const char *path,
int flags,
gpointer user_data,
GError **error)
{
MetaLauncher *launcher = user_data;
return meta_launcher_open_input_device (launcher, path, flags, error);
}
static void
on_evdev_device_close (int fd,
gpointer user_data)
{
close (fd);
}
static void
handle_vt_enter (MetaLauncher *launcher)
{
g_assert (launcher->vt_switched);
g_main_loop_quit (launcher->nested_loop);
}
static void
handle_request_vt_switch (MetaLauncher *launcher)
{
struct weston_launcher_message message;
GError *error;
gboolean ok;
meta_launcher_leave (launcher);
message.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH;
error = NULL;
ok = send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, &error);
if (!ok) {
g_warning ("Failed to acknowledge VT switch: %s", error->message);
g_error_free (error);
return;
}
g_assert (!launcher->vt_switched);
launcher->vt_switched = TRUE;
/* We can't do anything at this point, because we don't
have input devices and we don't have the DRM master,
so let's run a nested busy loop until the VT is reentered */
g_main_loop_run (launcher->nested_loop);
g_assert (launcher->vt_switched);
launcher->vt_switched = FALSE;
meta_launcher_enter (launcher);
}
static gboolean
on_socket_readable (GSocket *socket,
GIOCondition condition,
gpointer user_data)
{
MetaLauncher *launcher = user_data;
struct weston_launcher_event event;
gssize read;
GError *error;
if ((condition & G_IO_IN) == 0)
return TRUE;
error = NULL;
read = g_socket_receive (socket, (char*)&event, sizeof(event), NULL, &error);
if (read < (gssize)sizeof(event))
{
g_warning ("Error reading from weston-launcher socket: %s", error->message);
g_error_free (error);
return TRUE;
}
switch (event.header.opcode)
{
case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH:
handle_request_vt_switch (launcher);
break;
case WESTON_LAUNCHER_SERVER_VT_ENTER:
handle_vt_enter (launcher);
break;
}
return TRUE;
}
static int
env_get_fd (const char *env)
{
const char *value;
value = g_getenv (env);
if (value == NULL)
return -1;
else
return g_ascii_strtoll (value, NULL, 10);
}
MetaLauncher *
meta_launcher_new (void)
{
MetaLauncher *self = g_slice_new0 (MetaLauncher);
int launch_fd;
launch_fd = env_get_fd ("WESTON_LAUNCHER_SOCK");
if (launch_fd < 0)
g_error ("Invalid mutter-launch socket");
self->weston_launch = g_socket_new_from_fd (launch_fd, NULL);
self->nested_context = g_main_context_new ();
self->nested_loop = g_main_loop_new (self->nested_context, FALSE);
self->outer_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL);
g_source_set_callback (self->outer_source, (GSourceFunc)on_socket_readable, self, NULL);
g_source_attach (self->outer_source, NULL);
g_source_unref (self->outer_source);
self->inner_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL);
g_source_set_callback (self->inner_source, (GSourceFunc)on_socket_readable, self, NULL);
g_source_attach (self->inner_source, self->nested_context);
g_source_unref (self->inner_source);
clutter_evdev_set_device_callbacks (on_evdev_device_open,
on_evdev_device_close,
self);
return self;
}
void
meta_launcher_free (MetaLauncher *launcher)
{
g_source_destroy (launcher->outer_source);
g_source_destroy (launcher->inner_source);
g_main_loop_unref (launcher->nested_loop);
g_main_context_unref (launcher->nested_context);
g_object_unref (launcher->weston_launch);
g_slice_free (MetaLauncher, launcher);
}
gboolean
meta_launcher_activate_vt (MetaLauncher *launcher,
signed char vt,
GError **error)
{
struct weston_launcher_activate_vt message;
message.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT;
message.vt = vt;
return send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, error);
}

View File

@ -1,763 +0,0 @@
/*
* Copyright © 2012 Benjamin Franzke
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <poll.h>
#include <errno.h>
#include <error.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/signalfd.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <termios.h>
#include <linux/vt.h>
#include <linux/major.h>
#include <linux/kd.h>
#include <pwd.h>
#include <grp.h>
#include <xf86drm.h>
#include <systemd/sd-login.h>
#include "weston-launch.h"
#define MAX_ARGV_SIZE 256
#define DRM_MAJOR 226
enum vt_state {
VT_HAS_VT,
VT_PENDING_CONFIRM,
VT_NOT_HAVE_VT,
};
struct weston_launch {
int tty;
int ttynr;
int sock[2];
struct passwd *pw;
int signalfd;
pid_t child;
int verbose;
struct termios terminal_attributes;
int kb_mode;
enum vt_state vt_state;
unsigned vt;
int drm_fd;
};
union cmsg_data { unsigned char b[4]; int fd; };
static void quit (struct weston_launch *wl, int status);
static int
weston_launch_allowed(struct weston_launch *wl)
{
char *session, *seat;
int err;
if (getuid() == 0)
return 1;
err = sd_pid_get_session(getpid(), &session);
if (err == 0 && session) {
if (sd_session_is_active(session) &&
sd_session_get_seat(session, &seat) == 0) {
free(seat);
free(session);
return 1;
}
free(session);
}
return 0;
}
static int
setup_launcher_socket(struct weston_launch *wl)
{
if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, wl->sock) < 0)
error(1, errno, "socketpair failed");
fcntl(wl->sock[0], F_SETFD, O_CLOEXEC);
return 0;
}
static int
setup_signals(struct weston_launch *wl)
{
int ret;
sigset_t mask;
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = SIG_DFL;
sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
ret = sigaction(SIGCHLD, &sa, NULL);
assert(ret == 0);
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
sigaction(SIGHUP, &sa, NULL);
ret = sigemptyset(&mask);
assert(ret == 0);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGUSR1);
ret = sigprocmask(SIG_BLOCK, &mask, NULL);
assert(ret == 0);
wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (wl->signalfd < 0)
return -errno;
return 0;
}
static void
setenv_fd(const char *env, int fd)
{
char buf[32];
snprintf(buf, sizeof buf, "%d", fd);
setenv(env, buf, 1);
}
static int
handle_setdrmfd(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
{
struct weston_launcher_reply reply;
struct cmsghdr *cmsg;
union cmsg_data *data;
struct stat s;
reply.header.opcode = WESTON_LAUNCHER_DRM_SET_FD;
reply.ret = -1;
if (wl->drm_fd != -1) {
error(0, 0, "DRM FD already set");
reply.ret = -EINVAL;
goto out;
}
cmsg = CMSG_FIRSTHDR(msg);
if (!cmsg ||
cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS) {
error(0, 0, "invalid control message");
reply.ret = -EINVAL;
goto out;
}
data = (union cmsg_data *) CMSG_DATA(cmsg);
if (data->fd < 0) {
error(0, 0, "missing drm fd in socket request");
reply.ret = -EINVAL;
goto out;
}
if (fstat(data->fd, &s) < 0) {
reply.ret = -errno;
goto out;
}
if (major(s.st_rdev) != DRM_MAJOR) {
fprintf(stderr, "FD is not for DRM\n");
reply.ret = -EPERM;
goto out;
}
wl->drm_fd = data->fd;
reply.ret = drmSetMaster(data->fd);
if (reply.ret < 0)
reply.ret = -errno;
if (wl->verbose)
fprintf(stderr, "mutter-launch: set drm FD, ret: %d, fd: %d\n",
reply.ret, data->fd);
out:
do {
len = send(wl->sock[0], &reply, sizeof reply, 0);
} while (len < 0 && errno == EINTR);
if (len < 0)
return -1;
return 0;
}
static int
handle_confirm_vt_switch(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
{
struct weston_launcher_reply reply;
reply.header.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH;
reply.ret = -1;
if (wl->vt_state != VT_PENDING_CONFIRM) {
error(0, 0, "unexpected CONFIRM_VT_SWITCH");
goto out;
}
if (wl->drm_fd != -1) {
int ret;
ret = drmDropMaster(wl->drm_fd);
if (ret < 0) {
fprintf(stderr, "failed to drop DRM master: %m\n");
} else if (wl->verbose) {
fprintf(stderr, "dropped DRM master for VT switch\n");
}
}
wl->vt_state = VT_NOT_HAVE_VT;
ioctl(wl->tty, VT_RELDISP, 1);
if (wl->verbose)
fprintf(stderr, "mutter-launcher: confirmed VT switch\n");
reply.ret = 0;
out:
do {
len = send(wl->sock[0], &reply, sizeof reply, 0);
} while (len < 0 && errno == EINTR);
if (len < 0)
return -1;
return 0;
}
static int
handle_activate_vt(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
{
struct weston_launcher_reply reply;
struct weston_launcher_activate_vt *message;
unsigned vt;
reply.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT;
reply.ret = -1;
if (len != sizeof(*message)) {
error(0, 0, "missing value in activate_vt request");
goto out;
}
message = msg->msg_iov->iov_base;
/* Negative values mean that we're activating our own VT */
if (message->vt > 0)
vt = message->vt;
else
vt = wl->vt;
reply.ret = ioctl(wl->tty, VT_ACTIVATE, vt);
if (reply.ret < 0)
reply.ret = -errno;
if (wl->verbose)
fprintf(stderr, "mutter-launch: activate VT, ret: %d\n", reply.ret);
out:
do {
len = send(wl->sock[0], &reply, sizeof reply, 0);
} while (len < 0 && errno == EINTR);
if (len < 0)
return -1;
return 0;
}
static int
handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
{
struct weston_launcher_reply reply;
int fd = -1;
char control[CMSG_SPACE(sizeof(fd))];
struct cmsghdr *cmsg;
struct stat s;
struct msghdr nmsg;
struct iovec iov;
struct weston_launcher_open *message;
union cmsg_data *data;
reply.header.opcode = WESTON_LAUNCHER_OPEN;
reply.ret = -1;
message = msg->msg_iov->iov_base;
if ((size_t)len < sizeof(*message))
goto err0;
/* Ensure path is null-terminated */
((char *) message)[len-1] = '\0';
if (stat(message->path, &s) < 0) {
reply.ret = -errno;
goto err0;
}
if (major(s.st_rdev) != INPUT_MAJOR) {
fprintf(stderr, "Device %s is not an input device\n",
message->path);
reply.ret = -EPERM;
goto err0;
}
fd = open(message->path, message->flags);
if (fd < 0) {
fprintf(stderr, "Error opening device %s: %m\n",
message->path);
reply.ret = -errno;
goto err0;
}
err0:
memset(&nmsg, 0, sizeof nmsg);
nmsg.msg_iov = &iov;
nmsg.msg_iovlen = 1;
if (fd != -1) {
nmsg.msg_control = control;
nmsg.msg_controllen = sizeof control;
cmsg = CMSG_FIRSTHDR(&nmsg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
data = (union cmsg_data *) CMSG_DATA(cmsg);
data->fd = fd;
nmsg.msg_controllen = cmsg->cmsg_len;
reply.ret = 0;
}
iov.iov_base = &reply;
iov.iov_len = sizeof reply;
if (wl->verbose)
fprintf(stderr, "mutter-launch: opened %s: ret: %d, fd: %d\n",
message->path, reply.ret, fd);
do {
len = sendmsg(wl->sock[0], &nmsg, 0);
} while (len < 0 && errno == EINTR);
close(fd);
if (len < 0)
return -1;
return 0;
}
static int
handle_socket_msg(struct weston_launch *wl)
{
char control[CMSG_SPACE(sizeof(int))];
char buf[BUFSIZ];
struct msghdr msg;
struct iovec iov;
int ret = -1;
ssize_t len;
struct weston_launcher_message *message;
memset(&msg, 0, sizeof(msg));
iov.iov_base = buf;
iov.iov_len = sizeof buf;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof control;
do {
len = recvmsg(wl->sock[0], &msg, 0);
} while (len < 0 && errno == EINTR);
if (len < 1)
return -1;
message = (void *) buf;
switch (message->opcode) {
case WESTON_LAUNCHER_OPEN:
ret = handle_open(wl, &msg, len);
break;
case WESTON_LAUNCHER_DRM_SET_FD:
ret = handle_setdrmfd(wl, &msg, len);
break;
case WESTON_LAUNCHER_CONFIRM_VT_SWITCH:
ret = handle_confirm_vt_switch(wl, &msg, len);
break;
case WESTON_LAUNCHER_ACTIVATE_VT:
ret = handle_activate_vt(wl, &msg, len);
break;
}
return ret;
}
static void
tty_reset(struct weston_launch *wl)
{
struct vt_mode mode = { 0 };
if (ioctl(wl->tty, KDSKBMODE, wl->kb_mode))
fprintf(stderr, "failed to restore keyboard mode: %m\n");
if (ioctl(wl->tty, KDSETMODE, KD_TEXT))
fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n");
if (tcsetattr(wl->tty, TCSANOW, &wl->terminal_attributes) < 0)
fprintf(stderr, "could not restore terminal to canonical mode\n");
mode.mode = VT_AUTO;
if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
fprintf(stderr, "could not reset vt handling\n");
}
static void
quit(struct weston_launch *wl, int status)
{
if (wl->child > 0)
kill(wl->child, SIGKILL);
close(wl->signalfd);
close(wl->sock[0]);
if (wl->drm_fd > 0)
close(wl->drm_fd);
tty_reset(wl);
exit(status);
}
static int
handle_vt_switch(struct weston_launch *wl)
{
struct weston_launcher_event message;
ssize_t len;
if (wl->vt_state == VT_HAS_VT) {
wl->vt_state = VT_PENDING_CONFIRM;
message.header.opcode = WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH;
} else if (wl->vt_state == VT_NOT_HAVE_VT) {
wl->vt_state = VT_HAS_VT;
ioctl(wl->tty, VT_RELDISP, VT_ACKACQ);
if (wl->drm_fd != -1) {
int ret;
ret = drmSetMaster(wl->drm_fd);
if (ret < 0) {
fprintf(stderr, "failed to become DRM master: %m\n");
/* This is very, very bad, and the compositor will crash soon,
but oh well... */
} else if (wl->verbose) {
fprintf(stderr, "became DRM master after VT switch\n");
}
}
message.header.opcode = WESTON_LAUNCHER_SERVER_VT_ENTER;
} else
return -1;
message.detail = 0;
do {
len = send(wl->sock[0], &message, sizeof(message), 0);
} while (len < 0 && errno == EINTR);
return 0;
}
static int
handle_signal(struct weston_launch *wl)
{
struct signalfd_siginfo sig;
int pid, status, ret;
if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) {
error(0, errno, "reading signalfd failed");
return -1;
}
switch (sig.ssi_signo) {
case SIGCHLD:
pid = waitpid(-1, &status, 0);
if (pid == wl->child) {
wl->child = 0;
if (WIFEXITED(status))
ret = WEXITSTATUS(status);
else if (WIFSIGNALED(status))
/*
* If weston dies because of signal N, we
* return 10+N. This is distinct from
* weston-launch dying because of a signal
* (128+N).
*/
ret = 10 + WTERMSIG(status);
else
ret = 0;
quit(wl, ret);
}
break;
case SIGTERM:
case SIGINT:
if (wl->child)
kill(wl->child, sig.ssi_signo);
break;
case SIGUSR1:
return handle_vt_switch(wl);
default:
return -1;
}
return 0;
}
static int
setup_tty(struct weston_launch *wl)
{
struct stat buf;
struct termios raw_attributes;
struct vt_mode mode = { 0 };
char *session;
char path[PATH_MAX];
int ok;
ok = sd_pid_get_session(getpid(), &session);
if (ok < 0)
error(1, -ok, "could not determine current session");
ok = sd_session_get_vt(session, &wl->vt);
if (ok < 0)
error(1, -ok, "could not determine current TTY");
snprintf(path, PATH_MAX, "/dev/tty%u", wl->vt);
wl->tty = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC);
if (wl->tty < 0)
error(1, errno, "failed to open tty");
if (fstat(wl->tty, &buf) < 0)
error(1, errno, "stat %s failed", path);
if (major(buf.st_rdev) != TTY_MAJOR)
error(1, 0, "invalid tty device: %s", path);
wl->ttynr = minor(buf.st_rdev);
if (tcgetattr(wl->tty, &wl->terminal_attributes) < 0)
error(1, errno, "could not get terminal attributes");
/* Ignore control characters and disable echo */
raw_attributes = wl->terminal_attributes;
cfmakeraw(&raw_attributes);
/* Fix up line endings to be normal (cfmakeraw hoses them) */
raw_attributes.c_oflag |= OPOST | OCRNL;
/* Don't generate ttou signals */
raw_attributes.c_oflag &= ~TOSTOP;
if (tcsetattr(wl->tty, TCSANOW, &raw_attributes) < 0)
error(1, errno, "could not put terminal into raw mode");
ioctl(wl->tty, KDGKBMODE, &wl->kb_mode);
ok = ioctl(wl->tty, KDSKBMODE, K_OFF);
if (ok < 0) {
ok = ioctl(wl->tty, KDSKBMODE, K_RAW);
if (ok < 0)
error(1, errno, "failed to set keyboard mode on tty");
}
ok = ioctl(wl->tty, KDSETMODE, KD_GRAPHICS);
if (ok < 0)
error(1, errno, "failed to set KD_GRAPHICS mode on tty");
wl->vt_state = VT_HAS_VT;
mode.mode = VT_PROCESS;
mode.relsig = SIGUSR1;
mode.acqsig = SIGUSR1;
ok = ioctl(wl->tty, VT_SETMODE, &mode);
if (ok < 0)
error(1, errno, "failed to take control of vt handling");
return 0;
}
static void
drop_privileges(struct weston_launch *wl)
{
if (setgid(wl->pw->pw_gid) < 0 ||
#ifdef HAVE_INITGROUPS
initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 ||
#endif
setuid(wl->pw->pw_uid) < 0)
error(1, errno, "dropping privileges failed");
}
static void
launch_compositor(struct weston_launch *wl, int argc, char *argv[])
{
char command[PATH_MAX];
char *child_argv[MAX_ARGV_SIZE];
sigset_t mask;
int i;
if (wl->verbose)
printf("weston-launch: spawned weston with pid: %d\n", getpid());
drop_privileges(wl);
setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]);
setenv("LD_LIBRARY_PATH", LIBDIR, 1);
unsetenv("DISPLAY");
/* Do not give our signal mask to the new process. */
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
snprintf (command, PATH_MAX, "%s \"$@\"", argv[0]);
child_argv[0] = wl->pw->pw_shell;
child_argv[1] = "-l";
child_argv[2] = "-c";
child_argv[3] = command;
for (i = 0; i < argc; ++i)
child_argv[4 + i] = argv[i];
child_argv[4 + i] = NULL;
execv(child_argv[0], child_argv);
error(1, errno, "exec failed");
}
static void
help(const char *name)
{
fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name);
fprintf(stderr, " -u, --user Start session as specified username\n");
fprintf(stderr, " -v, --verbose Be verbose\n");
fprintf(stderr, " -h, --help Display this help message\n");
}
int
main(int argc, char *argv[])
{
struct weston_launch wl;
int i, c;
struct option opts[] = {
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ 0, 0, NULL, 0 }
};
memset(&wl, 0, sizeof wl);
wl.drm_fd = -1;
while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) {
switch (c) {
case 'v':
wl.verbose = 1;
break;
case 'h':
help("mutter-launch");
exit(EXIT_FAILURE);
}
}
if ((argc - optind) > (MAX_ARGV_SIZE - 6))
error(1, E2BIG, "Too many arguments to pass to weston");
if (optind >= argc)
error(1, 0, "Expected program argument");
wl.pw = getpwuid(getuid());
if (wl.pw == NULL)
error(1, errno, "failed to get username");
if (!weston_launch_allowed(&wl))
error(1, 0, "Permission denied. You must run from an active and local (systemd) session.");
if (setup_tty(&wl) < 0)
exit(EXIT_FAILURE);
if (setup_launcher_socket(&wl) < 0)
exit(EXIT_FAILURE);
if (setup_signals(&wl) < 0)
exit(EXIT_FAILURE);
wl.child = fork();
if (wl.child == -1) {
error(1, errno, "fork failed");
exit(EXIT_FAILURE);
}
if (wl.child == 0)
launch_compositor(&wl, argc - optind, argv + optind);
close(wl.sock[1]);
while (1) {
struct pollfd fds[2];
int n;
fds[0].fd = wl.sock[0];
fds[0].events = POLLIN;
fds[1].fd = wl.signalfd;
fds[1].events = POLLIN;
n = poll(fds, 2, -1);
if (n < 0)
error(0, errno, "poll failed");
if (fds[0].revents & POLLIN)
handle_socket_msg(&wl);
if (fds[1].revents)
handle_signal(&wl);
}
return 0;
}

View File

@ -1,69 +0,0 @@
/*
* Copyright © 2012 Benjamin Franzke
* 2013 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _WESTON_LAUNCH_H_
#define _WESTON_LAUNCH_H_
enum weston_launcher_message_type {
WESTON_LAUNCHER_REQUEST,
WESTON_LAUNCHER_EVENT,
};
enum weston_launcher_opcode {
WESTON_LAUNCHER_OPEN = (1 << 1 | WESTON_LAUNCHER_REQUEST),
WESTON_LAUNCHER_DRM_SET_FD = (2 << 1 | WESTON_LAUNCHER_REQUEST),
WESTON_LAUNCHER_ACTIVATE_VT = (3 << 1 | WESTON_LAUNCHER_REQUEST),
WESTON_LAUNCHER_CONFIRM_VT_SWITCH = (4 << 1 | WESTON_LAUNCHER_REQUEST),
};
enum weston_launcher_server_opcode {
WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH = (1 << 1 | WESTON_LAUNCHER_EVENT),
WESTON_LAUNCHER_SERVER_VT_ENTER = (2 << 1 | WESTON_LAUNCHER_EVENT),
};
struct weston_launcher_message {
int opcode;
};
struct weston_launcher_open {
struct weston_launcher_message header;
int flags;
char path[0];
};
struct weston_launcher_activate_vt {
struct weston_launcher_message header;
signed char vt;
};
struct weston_launcher_reply {
struct weston_launcher_message header;
int ret;
};
struct weston_launcher_event {
struct weston_launcher_message header;
int detail; /* unused, but makes sure replies and events are serialized the same */
};
#endif