Compare commits
54 Commits
3.15.4
...
wip/waylan
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e2c768d682 | ||
![]() |
a67c12e873 | ||
![]() |
1dd31d67ab | ||
![]() |
b1419e2966 | ||
![]() |
6924e760c4 | ||
![]() |
fa9e90fe25 | ||
![]() |
806d9ddb22 | ||
![]() |
a8425496ad | ||
![]() |
e9434732d4 | ||
![]() |
bbf07c9b17 | ||
![]() |
804c2be976 | ||
![]() |
9bdcc9c418 | ||
![]() |
cee22daf55 | ||
![]() |
63fdf8bcb8 | ||
![]() |
62442cf0ce | ||
![]() |
c1549af840 | ||
![]() |
8325f0eda5 | ||
![]() |
14931ed391 | ||
![]() |
c06762518e | ||
![]() |
8483dec921 | ||
![]() |
d7e970cb0a | ||
![]() |
b383d55988 | ||
![]() |
25f82424b8 | ||
![]() |
7764e1f3ff | ||
![]() |
ce3ec40036 | ||
![]() |
22a668f2cf | ||
![]() |
97ff406102 | ||
![]() |
e2f06dfa8c | ||
![]() |
ee6b729196 | ||
![]() |
0d1647cbc4 | ||
![]() |
49a83138f4 | ||
![]() |
1e3aad2d8c | ||
![]() |
33c0c1dc18 | ||
![]() |
e261f0ac39 | ||
![]() |
528219fcbf | ||
![]() |
d9a5f2638a | ||
![]() |
738b0eee1d | ||
![]() |
0480d6c898 | ||
![]() |
2777dfc533 | ||
![]() |
cf296f26b1 | ||
![]() |
b8f6801d20 | ||
![]() |
2a1c0429dd | ||
![]() |
5ae9457176 | ||
![]() |
e158500ef0 | ||
![]() |
b29d8046b0 | ||
![]() |
fa3ca2bf10 | ||
![]() |
fc42b478bd | ||
![]() |
0e0e0a4c7e | ||
![]() |
c96fd23e79 | ||
![]() |
045d03014d | ||
![]() |
a83049c103 | ||
![]() |
5f4121830c | ||
![]() |
43d499318f | ||
![]() |
f2fab33551 |
@@ -75,6 +75,7 @@ MUTTER_PC_MODULES="
|
|||||||
xcomposite >= 0.2 xfixes xrender xdamage xi >= 1.6.0
|
xcomposite >= 0.2 xfixes xrender xdamage xi >= 1.6.0
|
||||||
$CLUTTER_PACKAGE >= 1.14.3
|
$CLUTTER_PACKAGE >= 1.14.3
|
||||||
cogl-1.0 >= 1.13.3
|
cogl-1.0 >= 1.13.3
|
||||||
|
upower-glib > 0.9.11
|
||||||
"
|
"
|
||||||
|
|
||||||
GLIB_GSETTINGS
|
GLIB_GSETTINGS
|
||||||
@@ -138,6 +139,7 @@ AM_GLIB_GNU_GETTEXT
|
|||||||
## here we get the flags we'll actually use
|
## here we get the flags we'll actually use
|
||||||
# GRegex requires Glib-2.14.0
|
# GRegex requires Glib-2.14.0
|
||||||
PKG_CHECK_MODULES(ALL, glib-2.0 >= 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
|
# Unconditionally use this dir to avoid a circular dep with gnomecc
|
||||||
GNOME_KEYBINDINGS_KEYSDIR="${datadir}/gnome-control-center/keybindings"
|
GNOME_KEYBINDINGS_KEYSDIR="${datadir}/gnome-control-center/keybindings"
|
||||||
@@ -232,7 +234,7 @@ if test x$enable_wayland = "xyes"; then
|
|||||||
|
|
||||||
AC_SUBST(XWAYLAND_PATH)
|
AC_SUBST(XWAYLAND_PATH)
|
||||||
|
|
||||||
MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server"
|
MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server libdrm"
|
||||||
AC_DEFINE(HAVE_WAYLAND, , [Building with Wayland support])
|
AC_DEFINE(HAVE_WAYLAND, , [Building with Wayland support])
|
||||||
have_wayland=yes
|
have_wayland=yes
|
||||||
fi
|
fi
|
||||||
|
@@ -36,6 +36,8 @@ INCLUDES += \
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
mutter_built_sources = \
|
mutter_built_sources = \
|
||||||
|
$(dbus_xrandr_built_sources) \
|
||||||
|
$(dbus_idle_built_sources) \
|
||||||
mutter-enum-types.h \
|
mutter-enum-types.h \
|
||||||
mutter-enum-types.c
|
mutter-enum-types.c
|
||||||
|
|
||||||
@@ -123,6 +125,16 @@ libmutter_la_SOURCES = \
|
|||||||
core/keybindings.c \
|
core/keybindings.c \
|
||||||
core/keybindings-private.h \
|
core/keybindings-private.h \
|
||||||
core/main.c \
|
core/main.c \
|
||||||
|
core/meta-cursor-tracker.c \
|
||||||
|
core/meta-cursor-tracker-private.h \
|
||||||
|
core/meta-idle-monitor.c \
|
||||||
|
core/meta-idle-monitor-private.h \
|
||||||
|
core/meta-xrandr-shared.h \
|
||||||
|
core/monitor.c \
|
||||||
|
core/monitor-config.c \
|
||||||
|
core/monitor-kms.c \
|
||||||
|
core/monitor-private.h \
|
||||||
|
core/monitor-xrandr.c \
|
||||||
core/mutter-Xatomtype.h \
|
core/mutter-Xatomtype.h \
|
||||||
core/place.c \
|
core/place.c \
|
||||||
core/place.h \
|
core/place.h \
|
||||||
@@ -173,6 +185,8 @@ libmutter_la_SOURCES = \
|
|||||||
|
|
||||||
if HAVE_WAYLAND
|
if HAVE_WAYLAND
|
||||||
libmutter_la_SOURCES += \
|
libmutter_la_SOURCES += \
|
||||||
|
wayland/meta-tty.c \
|
||||||
|
wayland/meta-tty.h \
|
||||||
wayland/meta-wayland.c \
|
wayland/meta-wayland.c \
|
||||||
wayland/meta-wayland-private.h \
|
wayland/meta-wayland-private.h \
|
||||||
wayland/meta-xwayland-private.h \
|
wayland/meta-xwayland-private.h \
|
||||||
@@ -186,7 +200,9 @@ libmutter_la_SOURCES += \
|
|||||||
wayland/meta-wayland-seat.c \
|
wayland/meta-wayland-seat.c \
|
||||||
wayland/meta-wayland-seat.h \
|
wayland/meta-wayland-seat.h \
|
||||||
wayland/meta-wayland-stage.h \
|
wayland/meta-wayland-stage.h \
|
||||||
wayland/meta-wayland-stage.c
|
wayland/meta-wayland-stage.c \
|
||||||
|
wayland/meta-weston-launch.c \
|
||||||
|
wayland/meta-weston-launch.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
libmutter_la_LDFLAGS = -no-undefined
|
libmutter_la_LDFLAGS = -no-undefined
|
||||||
@@ -209,6 +225,8 @@ libmutterinclude_base_headers = \
|
|||||||
meta/meta-background-actor.h \
|
meta/meta-background-actor.h \
|
||||||
meta/meta-background-group.h \
|
meta/meta-background-group.h \
|
||||||
meta/meta-background.h \
|
meta/meta-background.h \
|
||||||
|
meta/meta-cursor-tracker.h \
|
||||||
|
meta/meta-idle-monitor.h \
|
||||||
meta/meta-plugin.h \
|
meta/meta-plugin.h \
|
||||||
meta/meta-shaped-texture.h \
|
meta/meta-shaped-texture.h \
|
||||||
meta/meta-shadow-factory.h \
|
meta/meta-shadow-factory.h \
|
||||||
@@ -237,6 +255,19 @@ bin_PROGRAMS=mutter
|
|||||||
mutter_SOURCES = core/mutter.c
|
mutter_SOURCES = core/mutter.c
|
||||||
mutter_LDADD = $(MUTTER_LIBS) libmutter.la
|
mutter_LDADD = $(MUTTER_LIBS) libmutter.la
|
||||||
|
|
||||||
|
if HAVE_WAYLAND
|
||||||
|
bin_PROGRAMS+=mutter-launch
|
||||||
|
|
||||||
|
mutter_launch_SOURCES = wayland/weston-launch.c wayland/weston-launch.h
|
||||||
|
|
||||||
|
mutter_launch_CFLAGS = $(MUTTER_LAUNCH_CFLAGS)
|
||||||
|
mutter_launch_LDFLAGS = $(MUTTER_LAUNCH_LIBS) -lpam
|
||||||
|
|
||||||
|
install-exec-hook:
|
||||||
|
-chown root $(DESTDIR)$(bindir)/mutter-launch
|
||||||
|
-chmod u+s $(DESTDIR)$(bindir)/mutter-launch
|
||||||
|
endif
|
||||||
|
|
||||||
if HAVE_INTROSPECTION
|
if HAVE_INTROSPECTION
|
||||||
include $(INTROSPECTION_MAKEFILE)
|
include $(INTROSPECTION_MAKEFILE)
|
||||||
|
|
||||||
@@ -355,6 +386,25 @@ mutter-enum-types.c: stamp-mutter-enum-types.h mutter-enum-types.c.in
|
|||||||
cp xgen-tetc mutter-enum-types.c && \
|
cp xgen-tetc mutter-enum-types.c && \
|
||||||
rm -f xgen-tetc
|
rm -f xgen-tetc
|
||||||
|
|
||||||
|
dbus_xrandr_built_sources = meta-dbus-xrandr.c meta-dbus-xrandr.h
|
||||||
|
|
||||||
|
$(dbus_xrandr_built_sources) : Makefile.am xrandr.xml
|
||||||
|
$(AM_V_GEN)gdbus-codegen \
|
||||||
|
--interface-prefix org.gnome.Mutter \
|
||||||
|
--c-namespace MetaDBus \
|
||||||
|
--generate-c-code meta-dbus-xrandr \
|
||||||
|
xrandr.xml
|
||||||
|
|
||||||
|
dbus_idle_built_sources = meta-dbus-idle-monitor.c meta-dbus-idle-monitor.h
|
||||||
|
|
||||||
|
$(dbus_idle_built_sources) : Makefile.am idle-monitor.xml
|
||||||
|
$(AM_V_GEN)gdbus-codegen \
|
||||||
|
--interface-prefix org.gnome.Mutter \
|
||||||
|
--c-namespace MetaDBus \
|
||||||
|
--generate-c-code meta-dbus-idle-monitor \
|
||||||
|
--c-generate-object-manager \
|
||||||
|
idle-monitor.xml
|
||||||
|
|
||||||
if HAVE_WAYLAND
|
if HAVE_WAYLAND
|
||||||
wayland/%-protocol.c : $(top_builddir)/protocol/%.xml
|
wayland/%-protocol.c : $(top_builddir)/protocol/%.xml
|
||||||
$(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
|
$(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
|
||||||
|
@@ -66,8 +66,6 @@ void meta_switch_workspace_completed (MetaScreen *screen);
|
|||||||
|
|
||||||
gboolean meta_begin_modal_for_plugin (MetaScreen *screen,
|
gboolean meta_begin_modal_for_plugin (MetaScreen *screen,
|
||||||
MetaPlugin *plugin,
|
MetaPlugin *plugin,
|
||||||
Window grab_window,
|
|
||||||
Cursor cursor,
|
|
||||||
MetaModalOptions options,
|
MetaModalOptions options,
|
||||||
guint32 timestamp);
|
guint32 timestamp);
|
||||||
void meta_end_modal_for_plugin (MetaScreen *screen,
|
void meta_end_modal_for_plugin (MetaScreen *screen,
|
||||||
|
@@ -86,6 +86,8 @@
|
|||||||
#include "display-private.h" /* for meta_display_lookup_x_window() */
|
#include "display-private.h" /* for meta_display_lookup_x_window() */
|
||||||
#ifdef HAVE_WAYLAND
|
#ifdef HAVE_WAYLAND
|
||||||
#include "meta-wayland-private.h"
|
#include "meta-wayland-private.h"
|
||||||
|
#include "meta-wayland-pointer.h"
|
||||||
|
#include "meta-wayland-keyboard.h"
|
||||||
#endif
|
#endif
|
||||||
#include <X11/extensions/shape.h>
|
#include <X11/extensions/shape.h>
|
||||||
#include <X11/extensions/Xcomposite.h>
|
#include <X11/extensions/Xcomposite.h>
|
||||||
@@ -391,56 +393,55 @@ meta_focus_stage_window (MetaScreen *screen,
|
|||||||
if (!stage)
|
if (!stage)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
window = clutter_x11_get_stage_window (stage);
|
if (!meta_is_wayland_compositor ())
|
||||||
|
{
|
||||||
|
window = clutter_x11_get_stage_window (stage);
|
||||||
|
|
||||||
if (window == None)
|
if (window == None)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
meta_display_set_input_focus_xwindow (screen->display,
|
meta_display_set_input_focus_xwindow (screen->display,
|
||||||
screen,
|
screen,
|
||||||
window,
|
META_FOCUS_STAGE,
|
||||||
timestamp);
|
window,
|
||||||
|
timestamp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
meta_display_set_input_focus_xwindow (screen->display,
|
||||||
|
screen,
|
||||||
|
META_FOCUS_STAGE,
|
||||||
|
None,
|
||||||
|
timestamp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
meta_stage_is_focused (MetaScreen *screen)
|
meta_stage_is_focused (MetaScreen *screen)
|
||||||
{
|
{
|
||||||
ClutterStage *stage;
|
ClutterStage *stage;
|
||||||
Window window;
|
|
||||||
|
|
||||||
stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
|
stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
|
||||||
if (!stage)
|
if (!stage)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
window = clutter_x11_get_stage_window (stage);
|
return (screen->display->focus_type == META_FOCUS_STAGE);
|
||||||
|
|
||||||
if (window == None)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return (screen->display->focus_xwindow == window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
static gboolean
|
||||||
meta_begin_modal_for_plugin (MetaScreen *screen,
|
begin_modal_x11 (MetaScreen *screen,
|
||||||
MetaPlugin *plugin,
|
MetaPlugin *plugin,
|
||||||
Window grab_window,
|
MetaModalOptions options,
|
||||||
Cursor cursor,
|
guint32 timestamp)
|
||||||
MetaModalOptions options,
|
|
||||||
guint32 timestamp)
|
|
||||||
{
|
{
|
||||||
/* To some extent this duplicates code in meta_display_begin_grab_op(), but there
|
MetaDisplay *display = meta_screen_get_display (screen);
|
||||||
* are significant differences in how we handle grabs that make it difficult to
|
Display *xdpy = meta_display_get_xdisplay (display);
|
||||||
* merge the two.
|
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
|
||||||
*/
|
Window grab_window = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
|
||||||
MetaDisplay *display = meta_screen_get_display (screen);
|
Cursor cursor = None;
|
||||||
Display *xdpy = meta_display_get_xdisplay (display);
|
int result;
|
||||||
MetaCompositor *compositor = display->compositor;
|
gboolean pointer_grabbed = FALSE;
|
||||||
gboolean pointer_grabbed = FALSE;
|
gboolean keyboard_grabbed = FALSE;
|
||||||
gboolean keyboard_grabbed = FALSE;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
if (compositor->modal_plugin != NULL || display->grab_op != META_GRAB_OP_NONE)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0)
|
if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0)
|
||||||
{
|
{
|
||||||
@@ -490,14 +491,6 @@ meta_begin_modal_for_plugin (MetaScreen *screen,
|
|||||||
keyboard_grabbed = TRUE;
|
keyboard_grabbed = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
display->grab_op = META_GRAB_OP_COMPOSITOR;
|
|
||||||
display->grab_window = NULL;
|
|
||||||
display->grab_screen = screen;
|
|
||||||
display->grab_have_pointer = TRUE;
|
|
||||||
display->grab_have_keyboard = TRUE;
|
|
||||||
|
|
||||||
compositor->modal_plugin = plugin;
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@@ -509,6 +502,79 @@ meta_begin_modal_for_plugin (MetaScreen *screen,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
begin_modal_wayland (MetaScreen *screen,
|
||||||
|
MetaPlugin *plugin,
|
||||||
|
MetaModalOptions options,
|
||||||
|
guint32 timestamp)
|
||||||
|
{
|
||||||
|
MetaWaylandCompositor *compositor;
|
||||||
|
gboolean pointer_grabbed = FALSE;
|
||||||
|
gboolean keyboard_grabbed = FALSE;
|
||||||
|
|
||||||
|
compositor = meta_wayland_compositor_get_default ();
|
||||||
|
|
||||||
|
if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0)
|
||||||
|
{
|
||||||
|
if (!meta_wayland_pointer_begin_modal (&compositor->seat->pointer))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
pointer_grabbed = TRUE;
|
||||||
|
}
|
||||||
|
if ((options & META_MODAL_KEYBOARD_ALREADY_GRABBED) == 0)
|
||||||
|
{
|
||||||
|
if (!meta_wayland_keyboard_begin_modal (&compositor->seat->keyboard))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
keyboard_grabbed = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (pointer_grabbed)
|
||||||
|
meta_wayland_pointer_end_modal (&compositor->seat->pointer);
|
||||||
|
if (keyboard_grabbed)
|
||||||
|
meta_wayland_keyboard_end_modal (&compositor->seat->keyboard);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_begin_modal_for_plugin (MetaScreen *screen,
|
||||||
|
MetaPlugin *plugin,
|
||||||
|
MetaModalOptions options,
|
||||||
|
guint32 timestamp)
|
||||||
|
{
|
||||||
|
/* To some extent this duplicates code in meta_display_begin_grab_op(), but there
|
||||||
|
* are significant differences in how we handle grabs that make it difficult to
|
||||||
|
* merge the two.
|
||||||
|
*/
|
||||||
|
MetaDisplay *display = meta_screen_get_display (screen);
|
||||||
|
MetaCompositor *compositor = display->compositor;
|
||||||
|
gboolean ok;
|
||||||
|
|
||||||
|
if (compositor->modal_plugin != NULL || display->grab_op != META_GRAB_OP_NONE)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (meta_is_wayland_compositor ())
|
||||||
|
ok = begin_modal_wayland (screen, plugin, options, timestamp);
|
||||||
|
else
|
||||||
|
ok = begin_modal_x11 (screen, plugin, options, timestamp);
|
||||||
|
if (!ok)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
display->grab_op = META_GRAB_OP_COMPOSITOR;
|
||||||
|
display->grab_window = NULL;
|
||||||
|
display->grab_screen = screen;
|
||||||
|
display->grab_have_pointer = TRUE;
|
||||||
|
display->grab_have_keyboard = TRUE;
|
||||||
|
|
||||||
|
compositor->modal_plugin = plugin;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_end_modal_for_plugin (MetaScreen *screen,
|
meta_end_modal_for_plugin (MetaScreen *screen,
|
||||||
MetaPlugin *plugin,
|
MetaPlugin *plugin,
|
||||||
@@ -520,8 +586,18 @@ meta_end_modal_for_plugin (MetaScreen *screen,
|
|||||||
|
|
||||||
g_return_if_fail (compositor->modal_plugin == plugin);
|
g_return_if_fail (compositor->modal_plugin == plugin);
|
||||||
|
|
||||||
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_POINTER_ID, timestamp);
|
if (meta_is_wayland_compositor ())
|
||||||
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp);
|
{
|
||||||
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
||||||
|
|
||||||
|
meta_wayland_pointer_end_modal (&compositor->seat->pointer);
|
||||||
|
meta_wayland_keyboard_end_modal (&compositor->seat->keyboard);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_POINTER_ID, timestamp);
|
||||||
|
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
display->grab_op = META_GRAB_OP_NONE;
|
display->grab_op = META_GRAB_OP_NONE;
|
||||||
display->grab_window = NULL;
|
display->grab_window = NULL;
|
||||||
@@ -618,7 +694,7 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
|
|||||||
MetaCompScreen *info;
|
MetaCompScreen *info;
|
||||||
MetaDisplay *display = meta_screen_get_display (screen);
|
MetaDisplay *display = meta_screen_get_display (screen);
|
||||||
Display *xdisplay = meta_display_get_xdisplay (display);
|
Display *xdisplay = meta_display_get_xdisplay (display);
|
||||||
Window xwin;
|
Window xwin = None;
|
||||||
gint width, height;
|
gint width, height;
|
||||||
#ifdef HAVE_WAYLAND
|
#ifdef HAVE_WAYLAND
|
||||||
MetaWaylandCompositor *wayland_compositor;
|
MetaWaylandCompositor *wayland_compositor;
|
||||||
@@ -660,6 +736,9 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
|
|||||||
{
|
{
|
||||||
wayland_compositor = meta_wayland_compositor_get_default ();
|
wayland_compositor = meta_wayland_compositor_get_default ();
|
||||||
info->stage = wayland_compositor->stage;
|
info->stage = wayland_compositor->stage;
|
||||||
|
|
||||||
|
meta_screen_get_size (screen, &width, &height);
|
||||||
|
clutter_actor_set_size (info->stage, width, height);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif /* HAVE_WAYLAND */
|
#endif /* HAVE_WAYLAND */
|
||||||
@@ -761,6 +840,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
|
|||||||
|
|
||||||
redirect_windows (compositor, screen);
|
redirect_windows (compositor, screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clutter_actor_show (info->stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1409,18 +1490,25 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
|
|||||||
guint width,
|
guint width,
|
||||||
guint height)
|
guint height)
|
||||||
{
|
{
|
||||||
|
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
|
||||||
|
|
||||||
if (meta_is_wayland_compositor ())
|
if (meta_is_wayland_compositor ())
|
||||||
{
|
{
|
||||||
/* It's not clear at the moment how we will be dealing with screen
|
/* FIXME: when we support a sliced stage, this is the place to do it
|
||||||
* resizing as a Wayland compositor so for now just abort if we
|
But! This is not the place to apply KMS config, here we only
|
||||||
* hit this code. */
|
notify Clutter/Cogl/GL that the framebuffer sizes changed.
|
||||||
g_critical ("Unexpected call to meta_compositor_sync_screen_size() "
|
|
||||||
"when running as a wayland compositor");
|
And because for now clutter does not do sliced, we use one
|
||||||
|
framebuffer the size of the whole screen, and when running on
|
||||||
|
bare metal MetaMonitorManager will do the necessary tricks to
|
||||||
|
show the right portions on the right screens.
|
||||||
|
*/
|
||||||
|
|
||||||
|
clutter_actor_set_size (info->stage, width, height);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MetaDisplay *display = meta_screen_get_display (screen);
|
MetaDisplay *display = meta_screen_get_display (screen);
|
||||||
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
|
|
||||||
Display *xdisplay;
|
Display *xdisplay;
|
||||||
Window xwin;
|
Window xwin;
|
||||||
|
|
||||||
@@ -1431,11 +1519,11 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
|
|||||||
xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
|
xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
|
||||||
|
|
||||||
XResizeWindow (xdisplay, xwin, width, height);
|
XResizeWindow (xdisplay, xwin, width, height);
|
||||||
|
|
||||||
meta_verbose ("Changed size for stage on screen %d to %dx%d\n",
|
|
||||||
meta_screen_get_screen_number (screen),
|
|
||||||
width, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meta_verbose ("Changed size for stage on screen %d to %dx%d\n",
|
||||||
|
meta_screen_get_screen_number (screen),
|
||||||
|
width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@@ -85,12 +85,20 @@ meta_plugin_manager_load (const gchar *plugin_name)
|
|||||||
g_free (path);
|
g_free (path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_confirm_display_change (MetaMonitorManager *monitors,
|
||||||
|
MetaPluginManager *plugin_mgr)
|
||||||
|
{
|
||||||
|
meta_plugin_manager_confirm_display_change (plugin_mgr);
|
||||||
|
}
|
||||||
|
|
||||||
MetaPluginManager *
|
MetaPluginManager *
|
||||||
meta_plugin_manager_new (MetaScreen *screen)
|
meta_plugin_manager_new (MetaScreen *screen)
|
||||||
{
|
{
|
||||||
MetaPluginManager *plugin_mgr;
|
MetaPluginManager *plugin_mgr;
|
||||||
MetaPluginClass *klass;
|
MetaPluginClass *klass;
|
||||||
MetaPlugin *plugin;
|
MetaPlugin *plugin;
|
||||||
|
MetaMonitorManager *monitors;
|
||||||
|
|
||||||
plugin_mgr = g_new0 (MetaPluginManager, 1);
|
plugin_mgr = g_new0 (MetaPluginManager, 1);
|
||||||
plugin_mgr->screen = screen;
|
plugin_mgr->screen = screen;
|
||||||
@@ -101,6 +109,10 @@ meta_plugin_manager_new (MetaScreen *screen)
|
|||||||
if (klass->start)
|
if (klass->start)
|
||||||
klass->start (plugin);
|
klass->start (plugin);
|
||||||
|
|
||||||
|
monitors = meta_monitor_manager_get ();
|
||||||
|
g_signal_connect (monitors, "confirm-display-change",
|
||||||
|
G_CALLBACK (on_confirm_display_change), plugin_mgr);
|
||||||
|
|
||||||
return plugin_mgr;
|
return plugin_mgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,3 +342,15 @@ meta_plugin_manager_xevent_filter (MetaPluginManager *plugin_mgr,
|
|||||||
|
|
||||||
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
|
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_plugin_manager_confirm_display_change (MetaPluginManager *plugin_mgr)
|
||||||
|
{
|
||||||
|
MetaPlugin *plugin = plugin_mgr->plugin;
|
||||||
|
MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin);
|
||||||
|
|
||||||
|
if (klass->confirm_display_change)
|
||||||
|
return klass->confirm_display_change (plugin);
|
||||||
|
else
|
||||||
|
return meta_plugin_complete_display_change (plugin, TRUE);
|
||||||
|
}
|
||||||
|
@@ -73,4 +73,6 @@ gboolean meta_plugin_manager_filter_keybinding (MetaPluginManager *mgr,
|
|||||||
gboolean meta_plugin_manager_xevent_filter (MetaPluginManager *mgr,
|
gboolean meta_plugin_manager_xevent_filter (MetaPluginManager *mgr,
|
||||||
XEvent *xev);
|
XEvent *xev);
|
||||||
|
|
||||||
|
void meta_plugin_manager_confirm_display_change (MetaPluginManager *mgr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
#include "compositor-private.h"
|
#include "compositor-private.h"
|
||||||
#include "meta-window-actor-private.h"
|
#include "meta-window-actor-private.h"
|
||||||
|
#include "monitor-private.h"
|
||||||
|
|
||||||
G_DEFINE_ABSTRACT_TYPE (MetaPlugin, meta_plugin, G_TYPE_OBJECT);
|
G_DEFINE_ABSTRACT_TYPE (MetaPlugin, meta_plugin, G_TYPE_OBJECT);
|
||||||
|
|
||||||
@@ -266,10 +267,6 @@ meta_plugin_destroy_completed (MetaPlugin *plugin,
|
|||||||
/**
|
/**
|
||||||
* meta_plugin_begin_modal:
|
* meta_plugin_begin_modal:
|
||||||
* @plugin: a #MetaPlugin
|
* @plugin: a #MetaPlugin
|
||||||
* @grab_window: the X window to grab the keyboard and mouse on
|
|
||||||
* @cursor: the cursor to use for the pointer grab, or None,
|
|
||||||
* to use the normal cursor for the grab window and
|
|
||||||
* its descendants.
|
|
||||||
* @options: flags that modify the behavior of the modal grab
|
* @options: flags that modify the behavior of the modal grab
|
||||||
* @timestamp: the timestamp used for establishing grabs
|
* @timestamp: the timestamp used for establishing grabs
|
||||||
*
|
*
|
||||||
@@ -290,15 +287,13 @@ meta_plugin_destroy_completed (MetaPlugin *plugin,
|
|||||||
*/
|
*/
|
||||||
gboolean
|
gboolean
|
||||||
meta_plugin_begin_modal (MetaPlugin *plugin,
|
meta_plugin_begin_modal (MetaPlugin *plugin,
|
||||||
Window grab_window,
|
|
||||||
Cursor cursor,
|
|
||||||
MetaModalOptions options,
|
MetaModalOptions options,
|
||||||
guint32 timestamp)
|
guint32 timestamp)
|
||||||
{
|
{
|
||||||
MetaPluginPrivate *priv = META_PLUGIN (plugin)->priv;
|
MetaPluginPrivate *priv = META_PLUGIN (plugin)->priv;
|
||||||
|
|
||||||
return meta_begin_modal_for_plugin (priv->screen, plugin,
|
return meta_begin_modal_for_plugin (priv->screen, plugin,
|
||||||
grab_window, cursor, options, timestamp);
|
options, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -338,3 +333,13 @@ meta_plugin_get_screen (MetaPlugin *plugin)
|
|||||||
|
|
||||||
return priv->screen;
|
return priv->screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_plugin_complete_display_change (MetaPlugin *plugin,
|
||||||
|
gboolean ok)
|
||||||
|
{
|
||||||
|
MetaMonitorManager *manager;
|
||||||
|
|
||||||
|
manager = meta_monitor_manager_get ();
|
||||||
|
meta_monitor_manager_confirm_configuration (manager, ok);
|
||||||
|
}
|
||||||
|
@@ -24,7 +24,10 @@
|
|||||||
#include <meta/meta-plugin.h>
|
#include <meta/meta-plugin.h>
|
||||||
#include <meta/window.h>
|
#include <meta/window.h>
|
||||||
#include <meta/util.h>
|
#include <meta/util.h>
|
||||||
|
#include <meta/meta-background-group.h>
|
||||||
|
#include <meta/meta-background-actor.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <libintl.h>
|
#include <libintl.h>
|
||||||
#define _(x) dgettext (GETTEXT_PACKAGE, x)
|
#define _(x) dgettext (GETTEXT_PACKAGE, x)
|
||||||
#define N_(x) x
|
#define N_(x) x
|
||||||
@@ -98,6 +101,8 @@ static void kill_window_effects (MetaPlugin *plugin,
|
|||||||
MetaWindowActor *actor);
|
MetaWindowActor *actor);
|
||||||
static void kill_switch_workspace (MetaPlugin *plugin);
|
static void kill_switch_workspace (MetaPlugin *plugin);
|
||||||
|
|
||||||
|
static void confirm_display_change (MetaPlugin *plugin);
|
||||||
|
|
||||||
static const MetaPluginInfo * plugin_info (MetaPlugin *plugin);
|
static const MetaPluginInfo * plugin_info (MetaPlugin *plugin);
|
||||||
|
|
||||||
META_PLUGIN_DECLARE(MetaDefaultPlugin, meta_default_plugin);
|
META_PLUGIN_DECLARE(MetaDefaultPlugin, meta_default_plugin);
|
||||||
@@ -113,6 +118,8 @@ struct _MetaDefaultPluginPrivate
|
|||||||
ClutterActor *desktop1;
|
ClutterActor *desktop1;
|
||||||
ClutterActor *desktop2;
|
ClutterActor *desktop2;
|
||||||
|
|
||||||
|
ClutterActor *background_group;
|
||||||
|
|
||||||
MetaPluginInfo info;
|
MetaPluginInfo info;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -203,6 +210,7 @@ meta_default_plugin_class_init (MetaDefaultPluginClass *klass)
|
|||||||
plugin_class->plugin_info = plugin_info;
|
plugin_class->plugin_info = plugin_info;
|
||||||
plugin_class->kill_window_effects = kill_window_effects;
|
plugin_class->kill_window_effects = kill_window_effects;
|
||||||
plugin_class->kill_switch_workspace = kill_switch_workspace;
|
plugin_class->kill_switch_workspace = kill_switch_workspace;
|
||||||
|
plugin_class->confirm_display_change = confirm_display_change;
|
||||||
|
|
||||||
g_type_class_add_private (gobject_class, sizeof (MetaDefaultPluginPrivate));
|
g_type_class_add_private (gobject_class, sizeof (MetaDefaultPluginPrivate));
|
||||||
}
|
}
|
||||||
@@ -299,9 +307,58 @@ show_stage (MetaPlugin *plugin)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_monitors_changed (MetaScreen *screen,
|
||||||
|
MetaPlugin *plugin)
|
||||||
|
{
|
||||||
|
MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin);
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
clutter_actor_destroy_all_children (self->priv->background_group);
|
||||||
|
|
||||||
|
n = meta_screen_get_n_monitors (screen);
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
MetaRectangle rect;
|
||||||
|
ClutterActor *background;
|
||||||
|
ClutterColor color;
|
||||||
|
|
||||||
|
meta_screen_get_monitor_geometry (screen, i, &rect);
|
||||||
|
|
||||||
|
background = meta_background_actor_new ();
|
||||||
|
|
||||||
|
clutter_actor_set_position (background, rect.x, rect.y);
|
||||||
|
clutter_actor_set_size (background, rect.width, rect.height);
|
||||||
|
|
||||||
|
/* Don't use rand() here, mesa calls srand() internally when
|
||||||
|
parsing the driconf XML, but it's nice if the colors are
|
||||||
|
reproducible.
|
||||||
|
*/
|
||||||
|
clutter_color_init (&color,
|
||||||
|
g_random_int () % 255,
|
||||||
|
g_random_int () % 255,
|
||||||
|
g_random_int () % 255,
|
||||||
|
255);
|
||||||
|
clutter_actor_set_background_color (background, &color);
|
||||||
|
|
||||||
|
clutter_actor_add_child (self->priv->background_group, background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
start (MetaPlugin *plugin)
|
start (MetaPlugin *plugin)
|
||||||
{
|
{
|
||||||
|
MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin);
|
||||||
|
MetaScreen *screen = meta_plugin_get_screen (plugin);
|
||||||
|
|
||||||
|
self->priv->background_group = meta_background_group_new ();
|
||||||
|
clutter_actor_insert_child_below (meta_get_window_group_for_screen (screen),
|
||||||
|
self->priv->background_group, NULL);
|
||||||
|
|
||||||
|
g_signal_connect (screen, "monitors-changed",
|
||||||
|
G_CALLBACK (on_monitors_changed), plugin);
|
||||||
|
on_monitors_changed (screen, plugin);
|
||||||
|
|
||||||
meta_later_add (META_LATER_BEFORE_REDRAW,
|
meta_later_add (META_LATER_BEFORE_REDRAW,
|
||||||
(GSourceFunc) show_stage,
|
(GSourceFunc) show_stage,
|
||||||
plugin,
|
plugin,
|
||||||
@@ -782,3 +839,33 @@ plugin_info (MetaPlugin *plugin)
|
|||||||
|
|
||||||
return &priv->info;
|
return &priv->info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_dialog_closed (GPid pid,
|
||||||
|
gint status,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaPlugin *plugin = user_data;
|
||||||
|
gboolean ok;
|
||||||
|
|
||||||
|
ok = g_spawn_check_exit_status (status, NULL);
|
||||||
|
meta_plugin_complete_display_change (plugin, ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
confirm_display_change (MetaPlugin *plugin)
|
||||||
|
{
|
||||||
|
GPid pid;
|
||||||
|
|
||||||
|
pid = meta_show_dialog ("--question",
|
||||||
|
"Does the display look OK?",
|
||||||
|
"20",
|
||||||
|
NULL,
|
||||||
|
"_Keep This Configuration",
|
||||||
|
"_Restore Previous Configuration",
|
||||||
|
"preferences-desktop-display",
|
||||||
|
0,
|
||||||
|
NULL, NULL);
|
||||||
|
|
||||||
|
g_child_watch_add (pid, on_dialog_closed, plugin);
|
||||||
|
}
|
||||||
|
@@ -86,6 +86,14 @@ typedef enum {
|
|||||||
META_TILE_MAXIMIZED
|
META_TILE_MAXIMIZED
|
||||||
} MetaTileMode;
|
} MetaTileMode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
META_FOCUS_NONE = 0,
|
||||||
|
META_FOCUS_X_CLIENT = 1,
|
||||||
|
META_FOCUS_WAYLAND_CLIENT = 2,
|
||||||
|
META_FOCUS_NO_FOCUS_WINDOW = 3,
|
||||||
|
META_FOCUS_STAGE = 4
|
||||||
|
} MetaFocusType;
|
||||||
|
|
||||||
struct _MetaDisplay
|
struct _MetaDisplay
|
||||||
{
|
{
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
@@ -117,6 +125,7 @@ struct _MetaDisplay
|
|||||||
* like the no_focus_window or the stage X window. */
|
* like the no_focus_window or the stage X window. */
|
||||||
Window focus_xwindow;
|
Window focus_xwindow;
|
||||||
gulong focus_serial;
|
gulong focus_serial;
|
||||||
|
MetaFocusType focus_type;
|
||||||
|
|
||||||
/* last timestamp passed to XSetInputFocus */
|
/* last timestamp passed to XSetInputFocus */
|
||||||
guint32 last_focus_time;
|
guint32 last_focus_time;
|
||||||
@@ -475,9 +484,10 @@ gboolean meta_display_process_barrier_event (MetaDisplay *display,
|
|||||||
XIBarrierEvent *event);
|
XIBarrierEvent *event);
|
||||||
#endif /* HAVE_XI23 */
|
#endif /* HAVE_XI23 */
|
||||||
|
|
||||||
void meta_display_set_input_focus_xwindow (MetaDisplay *display,
|
void meta_display_set_input_focus_xwindow (MetaDisplay *display,
|
||||||
MetaScreen *screen,
|
MetaScreen *screen,
|
||||||
Window window,
|
MetaFocusType type,
|
||||||
guint32 timestamp);
|
Window window,
|
||||||
|
guint32 timestamp);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -53,6 +53,7 @@
|
|||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#include <X11/cursorfont.h>
|
#include <X11/cursorfont.h>
|
||||||
#include "mutter-enum-types.h"
|
#include "mutter-enum-types.h"
|
||||||
|
#include "meta-idle-monitor-private.h"
|
||||||
|
|
||||||
#ifdef HAVE_RANDR
|
#ifdef HAVE_RANDR
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include <X11/extensions/Xrandr.h>
|
||||||
@@ -517,7 +518,7 @@ meta_display_open (void)
|
|||||||
|
|
||||||
if (meta_is_syncing ())
|
if (meta_is_syncing ())
|
||||||
XSynchronize (xdisplay, True);
|
XSynchronize (xdisplay, True);
|
||||||
|
|
||||||
g_assert (the_display == NULL);
|
g_assert (the_display == NULL);
|
||||||
the_display = g_object_new (META_TYPE_DISPLAY, NULL);
|
the_display = g_object_new (META_TYPE_DISPLAY, NULL);
|
||||||
|
|
||||||
@@ -1897,10 +1898,11 @@ get_input_event (MetaDisplay *display,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
update_focus_window (MetaDisplay *display,
|
update_focus_window (MetaDisplay *display,
|
||||||
MetaWindow *window,
|
MetaFocusType type,
|
||||||
Window xwindow,
|
MetaWindow *window,
|
||||||
gulong serial)
|
Window xwindow,
|
||||||
|
gulong serial)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_WAYLAND
|
#ifdef HAVE_WAYLAND
|
||||||
MetaWaylandCompositor *compositor;
|
MetaWaylandCompositor *compositor;
|
||||||
@@ -1909,6 +1911,7 @@ update_focus_window (MetaDisplay *display,
|
|||||||
display->focus_serial = serial;
|
display->focus_serial = serial;
|
||||||
|
|
||||||
if (display->focus_xwindow == xwindow &&
|
if (display->focus_xwindow == xwindow &&
|
||||||
|
display->focus_type == type &&
|
||||||
display->focus_window == window)
|
display->focus_window == window)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -1931,6 +1934,7 @@ update_focus_window (MetaDisplay *display,
|
|||||||
meta_window_set_focused_internal (previous, FALSE);
|
meta_window_set_focused_internal (previous, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
display->focus_type = type;
|
||||||
display->focus_window = window;
|
display->focus_window = window;
|
||||||
display->focus_xwindow = xwindow;
|
display->focus_xwindow = xwindow;
|
||||||
|
|
||||||
@@ -1948,7 +1952,8 @@ update_focus_window (MetaDisplay *display,
|
|||||||
{
|
{
|
||||||
compositor = meta_wayland_compositor_get_default ();
|
compositor = meta_wayland_compositor_get_default ();
|
||||||
|
|
||||||
if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
|
if (display->focus_type == META_FOCUS_NO_FOCUS_WINDOW ||
|
||||||
|
display->focus_type == META_FOCUS_STAGE)
|
||||||
meta_wayland_compositor_set_input_focus (compositor, NULL);
|
meta_wayland_compositor_set_input_focus (compositor, NULL);
|
||||||
else if (window && window->surface)
|
else if (window && window->surface)
|
||||||
meta_wayland_compositor_set_input_focus (compositor, window);
|
meta_wayland_compositor_set_input_focus (compositor, window);
|
||||||
@@ -1991,11 +1996,12 @@ timestamp_too_old (MetaDisplay *display,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
request_xserver_input_focus_change (MetaDisplay *display,
|
request_xserver_input_focus_change (MetaDisplay *display,
|
||||||
MetaScreen *screen,
|
MetaScreen *screen,
|
||||||
MetaWindow *meta_window,
|
MetaFocusType type,
|
||||||
Window xwindow,
|
MetaWindow *meta_window,
|
||||||
guint32 timestamp)
|
Window xwindow,
|
||||||
|
guint32 timestamp)
|
||||||
{
|
{
|
||||||
gulong serial;
|
gulong serial;
|
||||||
|
|
||||||
@@ -2028,6 +2034,7 @@ request_xserver_input_focus_change (MetaDisplay *display,
|
|||||||
meta_display_ungrab (display);
|
meta_display_ungrab (display);
|
||||||
|
|
||||||
update_focus_window (display,
|
update_focus_window (display,
|
||||||
|
type,
|
||||||
meta_window,
|
meta_window,
|
||||||
xwindow,
|
xwindow,
|
||||||
serial);
|
serial);
|
||||||
@@ -2048,9 +2055,12 @@ handle_window_focus_event (MetaDisplay *display,
|
|||||||
unsigned long serial)
|
unsigned long serial)
|
||||||
{
|
{
|
||||||
MetaWindow *focus_window;
|
MetaWindow *focus_window;
|
||||||
|
MetaFocusType type;
|
||||||
#ifdef WITH_VERBOSE_MODE
|
#ifdef WITH_VERBOSE_MODE
|
||||||
const char *window_type;
|
const char *window_type;
|
||||||
|
|
||||||
|
type = META_FOCUS_NONE;
|
||||||
|
|
||||||
/* Note the event can be on either the window or the frame,
|
/* Note the event can be on either the window or the frame,
|
||||||
* we focus the frame for shaded windows
|
* we focus the frame for shaded windows
|
||||||
*/
|
*/
|
||||||
@@ -2062,14 +2072,26 @@ handle_window_focus_event (MetaDisplay *display,
|
|||||||
window_type = "frame window";
|
window_type = "frame window";
|
||||||
else
|
else
|
||||||
window_type = "unknown client window";
|
window_type = "unknown client window";
|
||||||
|
|
||||||
|
if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
|
||||||
|
type = META_FOCUS_WAYLAND_CLIENT;
|
||||||
|
else
|
||||||
|
type = META_FOCUS_X_CLIENT;
|
||||||
}
|
}
|
||||||
else if (meta_display_xwindow_is_a_no_focus_window (display, event->event))
|
else if (meta_display_xwindow_is_a_no_focus_window (display, event->event))
|
||||||
window_type = "no_focus_window";
|
{
|
||||||
|
window_type = "no_focus_window";
|
||||||
|
type = META_FOCUS_NO_FOCUS_WINDOW;
|
||||||
|
}
|
||||||
else if (meta_display_screen_for_root (display, event->event))
|
else if (meta_display_screen_for_root (display, event->event))
|
||||||
window_type = "root window";
|
window_type = "root window";
|
||||||
else
|
else
|
||||||
window_type = "unknown window";
|
window_type = "unknown window";
|
||||||
|
|
||||||
|
/* Don't change type if we don't know the new window */
|
||||||
|
if (type == META_FOCUS_NONE)
|
||||||
|
type = display->focus_type;
|
||||||
|
|
||||||
meta_topic (META_DEBUG_FOCUS,
|
meta_topic (META_DEBUG_FOCUS,
|
||||||
"Focus %s event received on %s 0x%lx (%s) "
|
"Focus %s event received on %s 0x%lx (%s) "
|
||||||
"mode %s detail %s serial %lu\n",
|
"mode %s detail %s serial %lu\n",
|
||||||
@@ -2147,6 +2169,7 @@ handle_window_focus_event (MetaDisplay *display,
|
|||||||
if (display->server_focus_serial > display->focus_serial)
|
if (display->server_focus_serial > display->focus_serial)
|
||||||
{
|
{
|
||||||
update_focus_window (display,
|
update_focus_window (display,
|
||||||
|
type,
|
||||||
focus_window,
|
focus_window,
|
||||||
focus_window ? focus_window->xwindow : None,
|
focus_window ? focus_window->xwindow : None,
|
||||||
display->server_focus_serial);
|
display->server_focus_serial);
|
||||||
@@ -2177,6 +2200,8 @@ meta_display_handle_event (MetaDisplay *display,
|
|||||||
gboolean bypass_compositor;
|
gboolean bypass_compositor;
|
||||||
gboolean filter_out_event;
|
gboolean filter_out_event;
|
||||||
XIEvent *input_event;
|
XIEvent *input_event;
|
||||||
|
MetaMonitorManager *monitor;
|
||||||
|
MetaScreen *screen;
|
||||||
|
|
||||||
#ifdef WITH_VERBOSE_MODE
|
#ifdef WITH_VERBOSE_MODE
|
||||||
if (dump_events)
|
if (dump_events)
|
||||||
@@ -2186,6 +2211,14 @@ meta_display_handle_event (MetaDisplay *display,
|
|||||||
#ifdef HAVE_STARTUP_NOTIFICATION
|
#ifdef HAVE_STARTUP_NOTIFICATION
|
||||||
sn_display_process_event (display->sn_display, event);
|
sn_display_process_event (display->sn_display, event);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Intercept XRandR events early and don't attempt any
|
||||||
|
processing for them. We still let them through to Gdk though,
|
||||||
|
so it can update its own internal state.
|
||||||
|
*/
|
||||||
|
monitor = meta_monitor_manager_get ();
|
||||||
|
if (meta_monitor_manager_handle_xevent (monitor, event))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
bypass_compositor = FALSE;
|
bypass_compositor = FALSE;
|
||||||
filter_out_event = FALSE;
|
filter_out_event = FALSE;
|
||||||
@@ -2199,11 +2232,19 @@ meta_display_handle_event (MetaDisplay *display,
|
|||||||
meta_topic (META_DEBUG_FOCUS, "Earlier attempt to focus %s failed\n",
|
meta_topic (META_DEBUG_FOCUS, "Earlier attempt to focus %s failed\n",
|
||||||
display->focus_window->desc);
|
display->focus_window->desc);
|
||||||
update_focus_window (display,
|
update_focus_window (display,
|
||||||
|
META_FOCUS_NONE,
|
||||||
meta_display_lookup_x_window (display, display->server_focus_window),
|
meta_display_lookup_x_window (display, display->server_focus_window),
|
||||||
display->server_focus_window,
|
display->server_focus_window,
|
||||||
display->server_focus_serial);
|
display->server_focus_serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
screen = meta_display_screen_for_root (display, event->xany.window);
|
||||||
|
if (screen)
|
||||||
|
{
|
||||||
|
if (meta_screen_handle_xevent (screen, event))
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
modified = event_get_modified_window (display, event);
|
modified = event_get_modified_window (display, event);
|
||||||
|
|
||||||
input_event = get_input_event (display, event);
|
input_event = get_input_event (display, event);
|
||||||
@@ -2276,6 +2317,8 @@ meta_display_handle_event (MetaDisplay *display,
|
|||||||
meta_window_update_sync_request_counter (alarm_window, new_counter_value);
|
meta_window_update_sync_request_counter (alarm_window, new_counter_value);
|
||||||
filter_out_event = TRUE; /* GTK doesn't want to see this really */
|
filter_out_event = TRUE; /* GTK doesn't want to see this really */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meta_idle_monitor_handle_xevent_all (event);
|
||||||
}
|
}
|
||||||
#endif /* HAVE_XSYNC */
|
#endif /* HAVE_XSYNC */
|
||||||
|
|
||||||
@@ -2878,32 +2921,10 @@ meta_display_handle_event (MetaDisplay *display,
|
|||||||
meta_stack_tracker_configure_event (screen->stack_tracker,
|
meta_stack_tracker_configure_event (screen->stack_tracker,
|
||||||
&event->xconfigure);
|
&event->xconfigure);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window && window->override_redirect)
|
if (window && window->override_redirect)
|
||||||
meta_window_configure_notify (window, &event->xconfigure);
|
meta_window_configure_notify (window, &event->xconfigure);
|
||||||
else
|
|
||||||
/* Handle screen resize */
|
|
||||||
{
|
|
||||||
MetaScreen *screen;
|
|
||||||
|
|
||||||
screen = meta_display_screen_for_root (display,
|
|
||||||
event->xconfigure.window);
|
|
||||||
|
|
||||||
if (screen != NULL)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_RANDR
|
|
||||||
/* do the resize the official way */
|
|
||||||
XRRUpdateConfiguration (event);
|
|
||||||
#else
|
|
||||||
/* poke around in Xlib */
|
|
||||||
screen->xscreen->width = event->xconfigure.width;
|
|
||||||
screen->xscreen->height = event->xconfigure.height;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
meta_screen_resize (screen,
|
|
||||||
event->xconfigure.width,
|
|
||||||
event->xconfigure.height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ConfigureRequest:
|
case ConfigureRequest:
|
||||||
/* This comment and code is found in both twm and fvwm */
|
/* This comment and code is found in both twm and fvwm */
|
||||||
@@ -5871,6 +5892,8 @@ meta_display_set_input_focus_window (MetaDisplay *display,
|
|||||||
{
|
{
|
||||||
request_xserver_input_focus_change (display,
|
request_xserver_input_focus_change (display,
|
||||||
window->screen,
|
window->screen,
|
||||||
|
window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND ?
|
||||||
|
META_FOCUS_WAYLAND_CLIENT : META_FOCUS_X_CLIENT,
|
||||||
window,
|
window,
|
||||||
focus_frame ? window->frame->xwindow : window->xwindow,
|
focus_frame ? window->frame->xwindow : window->xwindow,
|
||||||
timestamp);
|
timestamp);
|
||||||
@@ -5912,13 +5935,15 @@ meta_display_request_take_focus (MetaDisplay *display,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_display_set_input_focus_xwindow (MetaDisplay *display,
|
meta_display_set_input_focus_xwindow (MetaDisplay *display,
|
||||||
MetaScreen *screen,
|
MetaScreen *screen,
|
||||||
Window window,
|
MetaFocusType type,
|
||||||
guint32 timestamp)
|
Window window,
|
||||||
|
guint32 timestamp)
|
||||||
{
|
{
|
||||||
request_xserver_input_focus_change (display,
|
request_xserver_input_focus_change (display,
|
||||||
screen,
|
screen,
|
||||||
|
type,
|
||||||
NULL,
|
NULL,
|
||||||
window,
|
window,
|
||||||
timestamp);
|
timestamp);
|
||||||
@@ -5931,6 +5956,7 @@ meta_display_focus_the_no_focus_window (MetaDisplay *display,
|
|||||||
{
|
{
|
||||||
request_xserver_input_focus_change (display,
|
request_xserver_input_focus_change (display,
|
||||||
screen,
|
screen,
|
||||||
|
META_FOCUS_NO_FOCUS_WINDOW,
|
||||||
NULL,
|
NULL,
|
||||||
screen->no_focus_window,
|
screen->no_focus_window,
|
||||||
timestamp);
|
timestamp);
|
||||||
|
@@ -53,6 +53,10 @@
|
|||||||
#include <X11/XKBlib.h>
|
#include <X11/XKBlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_WAYLAND
|
||||||
|
#include "meta-wayland-private.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define SCHEMA_COMMON_KEYBINDINGS "org.gnome.desktop.wm.keybindings"
|
#define SCHEMA_COMMON_KEYBINDINGS "org.gnome.desktop.wm.keybindings"
|
||||||
#define SCHEMA_MUTTER_KEYBINDINGS "org.gnome.mutter.keybindings"
|
#define SCHEMA_MUTTER_KEYBINDINGS "org.gnome.mutter.keybindings"
|
||||||
|
|
||||||
@@ -4081,6 +4085,40 @@ handle_set_spew_mark (MetaDisplay *display,
|
|||||||
meta_verbose ("-- MARK MARK MARK MARK --\n");
|
meta_verbose ("-- MARK MARK MARK MARK --\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_WAYLAND
|
||||||
|
static void
|
||||||
|
handle_switch_vt (MetaDisplay *display,
|
||||||
|
MetaScreen *screen,
|
||||||
|
MetaWindow *window,
|
||||||
|
XIDeviceEvent *event,
|
||||||
|
MetaKeyBinding *binding,
|
||||||
|
gpointer dummy)
|
||||||
|
{
|
||||||
|
gint vt = binding->handler->data;
|
||||||
|
MetaWaylandCompositor *compositor;
|
||||||
|
MetaTTY *tty;
|
||||||
|
|
||||||
|
compositor = meta_wayland_compositor_get_default ();
|
||||||
|
tty = meta_wayland_compositor_get_tty (compositor);
|
||||||
|
|
||||||
|
if (tty)
|
||||||
|
{
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
if (!meta_tty_activate_vt (tty, vt, &error))
|
||||||
|
{
|
||||||
|
g_warning ("Failed to switch VT: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_debug ("Ignoring VT switch keybinding, not running as VT manager");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* meta_keybindings_set_custom_handler:
|
* meta_keybindings_set_custom_handler:
|
||||||
* @name: The name of the keybinding to set
|
* @name: The name of the keybinding to set
|
||||||
@@ -4405,6 +4443,60 @@ init_builtin_key_bindings (MetaDisplay *display)
|
|||||||
META_KEYBINDING_ACTION_SET_SPEW_MARK,
|
META_KEYBINDING_ACTION_SET_SPEW_MARK,
|
||||||
handle_set_spew_mark, 0);
|
handle_set_spew_mark, 0);
|
||||||
|
|
||||||
|
#ifdef HAVE_WAYLAND
|
||||||
|
if (meta_is_wayland_compositor ())
|
||||||
|
{
|
||||||
|
add_builtin_keybinding (display,
|
||||||
|
"switch-to-session-1",
|
||||||
|
mutter_keybindings,
|
||||||
|
META_KEY_BINDING_NONE,
|
||||||
|
META_KEYBINDING_ACTION_NONE,
|
||||||
|
handle_switch_vt, 1);
|
||||||
|
|
||||||
|
add_builtin_keybinding (display,
|
||||||
|
"switch-to-session-2",
|
||||||
|
mutter_keybindings,
|
||||||
|
META_KEY_BINDING_NONE,
|
||||||
|
META_KEYBINDING_ACTION_NONE,
|
||||||
|
handle_switch_vt, 2);
|
||||||
|
|
||||||
|
add_builtin_keybinding (display,
|
||||||
|
"switch-to-session-3",
|
||||||
|
mutter_keybindings,
|
||||||
|
META_KEY_BINDING_NONE,
|
||||||
|
META_KEYBINDING_ACTION_NONE,
|
||||||
|
handle_switch_vt, 3);
|
||||||
|
|
||||||
|
add_builtin_keybinding (display,
|
||||||
|
"switch-to-session-4",
|
||||||
|
mutter_keybindings,
|
||||||
|
META_KEY_BINDING_NONE,
|
||||||
|
META_KEYBINDING_ACTION_NONE,
|
||||||
|
handle_switch_vt, 4);
|
||||||
|
|
||||||
|
add_builtin_keybinding (display,
|
||||||
|
"switch-to-session-5",
|
||||||
|
mutter_keybindings,
|
||||||
|
META_KEY_BINDING_NONE,
|
||||||
|
META_KEYBINDING_ACTION_NONE,
|
||||||
|
handle_switch_vt, 5);
|
||||||
|
|
||||||
|
add_builtin_keybinding (display,
|
||||||
|
"switch-to-session-6",
|
||||||
|
mutter_keybindings,
|
||||||
|
META_KEY_BINDING_NONE,
|
||||||
|
META_KEYBINDING_ACTION_NONE,
|
||||||
|
handle_switch_vt, 6);
|
||||||
|
|
||||||
|
add_builtin_keybinding (display,
|
||||||
|
"switch-to-session-7",
|
||||||
|
mutter_keybindings,
|
||||||
|
META_KEY_BINDING_NONE,
|
||||||
|
META_KEYBINDING_ACTION_NONE,
|
||||||
|
handle_switch_vt, 7);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#undef REVERSES_AND_REVERSED
|
#undef REVERSES_AND_REVERSED
|
||||||
|
|
||||||
/************************ PER WINDOW BINDINGS ************************/
|
/************************ PER WINDOW BINDINGS ************************/
|
||||||
|
108
src/core/main.c
108
src/core/main.c
@@ -60,6 +60,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
#include <gdk/gdkx.h>
|
#include <gdk/gdkx.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -356,67 +357,34 @@ meta_finalize (void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int signal_pipe_fds[2] = { -1, -1 };
|
static gboolean
|
||||||
|
on_sigterm (gpointer user_data)
|
||||||
static void
|
|
||||||
signal_handler (int signum)
|
|
||||||
{
|
{
|
||||||
if (signal_pipe_fds[1] >= 0)
|
meta_quit (EXIT_SUCCESS);
|
||||||
{
|
|
||||||
switch (signum)
|
return G_SOURCE_REMOVE;
|
||||||
{
|
|
||||||
case SIGTERM:
|
|
||||||
write (signal_pipe_fds[1], "T", 1);
|
|
||||||
break;
|
|
||||||
case SIGCHLD:
|
|
||||||
write (signal_pipe_fds[1], "C", 1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static void
|
||||||
on_signal (GIOChannel *source,
|
crash_handler (int signum)
|
||||||
GIOCondition condition,
|
|
||||||
void *data)
|
|
||||||
{
|
{
|
||||||
char signal;
|
char buffer[256];
|
||||||
int count;
|
MetaWaylandCompositor *compositor;
|
||||||
|
MetaTTY *tty;
|
||||||
|
|
||||||
for (;;)
|
snprintf (buffer, 256, "Fatal server error: %d\n", signum);
|
||||||
{
|
write (STDERR_FILENO, buffer, strlen (buffer));
|
||||||
count = read (signal_pipe_fds[0], &signal, 1);
|
|
||||||
if (count == EINTR)
|
|
||||||
continue;
|
|
||||||
if (count < 0)
|
|
||||||
{
|
|
||||||
const char *msg = strerror (errno);
|
|
||||||
g_warning ("Error handling signal: %s", msg);
|
|
||||||
}
|
|
||||||
if (count != 1)
|
|
||||||
{
|
|
||||||
g_warning ("Unexpectedly failed to read byte from signal pipe\n");
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (signal)
|
|
||||||
{
|
|
||||||
case 'T': /* SIGTERM */
|
|
||||||
meta_quit (META_EXIT_SUCCESS);
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_WAYLAND
|
|
||||||
case 'C': /* SIGCHLD */
|
|
||||||
meta_wayland_handle_sig_child ();
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
g_warning ("Spurious character '%c' read from signal pipe", signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
compositor = meta_wayland_compositor_get_default ();
|
||||||
|
tty = meta_wayland_compositor_get_tty (compositor);
|
||||||
|
|
||||||
|
/* Passing FALSE ensures that we only do ioctls, which is
|
||||||
|
safe from a signal handler */
|
||||||
|
if (tty)
|
||||||
|
meta_tty_reset (tty, FALSE);
|
||||||
|
|
||||||
|
/* We can't continue with the default handling, so just exit here */
|
||||||
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -430,7 +398,6 @@ meta_init (void)
|
|||||||
{
|
{
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
sigset_t empty_mask;
|
sigset_t empty_mask;
|
||||||
GIOChannel *channel;
|
|
||||||
|
|
||||||
sigemptyset (&empty_mask);
|
sigemptyset (&empty_mask);
|
||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
@@ -445,28 +412,21 @@ meta_init (void)
|
|||||||
g_strerror (errno));
|
g_strerror (errno));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (pipe (signal_pipe_fds) != 0)
|
|
||||||
g_printerr ("Failed to create signal pipe: %s\n",
|
|
||||||
g_strerror (errno));
|
|
||||||
|
|
||||||
channel = g_io_channel_unix_new (signal_pipe_fds[0]);
|
|
||||||
g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
|
|
||||||
g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_signal, NULL);
|
|
||||||
g_io_channel_set_close_on_unref (channel, TRUE);
|
|
||||||
g_io_channel_unref (channel);
|
|
||||||
|
|
||||||
act.sa_handler = &signal_handler;
|
|
||||||
if (sigaction (SIGTERM, &act, NULL) < 0)
|
|
||||||
g_printerr ("Failed to register SIGTERM handler: %s\n",
|
|
||||||
g_strerror (errno));
|
|
||||||
|
|
||||||
if (meta_is_wayland_compositor ())
|
if (meta_is_wayland_compositor ())
|
||||||
{
|
{
|
||||||
if (sigaction (SIGCHLD, &act, NULL) < 0)
|
act.sa_handler = crash_handler;
|
||||||
g_printerr ("Failed to register SIGCHLD handler: %s\n",
|
|
||||||
g_strerror (errno));
|
/* Ignore if we can't register signal handlers, worse
|
||||||
|
that can happen one needs the sysrq to get out of the VT */
|
||||||
|
sigaction (SIGABRT, &act, NULL);
|
||||||
|
sigaction (SIGSEGV, &act, NULL);
|
||||||
|
sigaction (SIGBUS, &act, NULL);
|
||||||
|
sigaction (SIGFPE, &act, NULL);
|
||||||
|
sigaction (SIGTRAP, &act, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_unix_signal_add (SIGTERM, on_sigterm, NULL);
|
||||||
|
|
||||||
if (g_getenv ("MUTTER_VERBOSE"))
|
if (g_getenv ("MUTTER_VERBOSE"))
|
||||||
meta_set_verbose (TRUE);
|
meta_set_verbose (TRUE);
|
||||||
if (g_getenv ("MUTTER_DEBUG"))
|
if (g_getenv ("MUTTER_DEBUG"))
|
||||||
|
46
src/core/meta-cursor-tracker-private.h
Normal file
46
src/core/meta-cursor-tracker-private.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 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.
|
||||||
|
*
|
||||||
|
* Author: Giovanni Campagna <gcampagn@redhat.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef META_CURSOR_TRACKER_PRIVATE_H
|
||||||
|
#define META_CURSOR_TRACKER_PRIVATE_H
|
||||||
|
|
||||||
|
#include <meta/meta-cursor-tracker.h>
|
||||||
|
|
||||||
|
gboolean meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker,
|
||||||
|
XEvent *xevent);
|
||||||
|
|
||||||
|
void meta_cursor_tracker_set_root_cursor (MetaCursorTracker *tracker,
|
||||||
|
MetaCursor cursor);
|
||||||
|
void meta_cursor_tracker_revert_root (MetaCursorTracker *tracker);
|
||||||
|
void meta_cursor_tracker_set_sprite (MetaCursorTracker *tracker,
|
||||||
|
CoglTexture2D *texture,
|
||||||
|
int hot_x,
|
||||||
|
int hot_y);
|
||||||
|
|
||||||
|
void meta_cursor_tracker_update_position (MetaCursorTracker *tracker,
|
||||||
|
int new_x,
|
||||||
|
int new_y);
|
||||||
|
void meta_cursor_tracker_paint (MetaCursorTracker *tracker);
|
||||||
|
void meta_cursor_tracker_queue_redraw (MetaCursorTracker *tracker,
|
||||||
|
ClutterActor *stage);
|
||||||
|
#endif
|
451
src/core/meta-cursor-tracker.c
Normal file
451
src/core/meta-cursor-tracker.c
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 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.
|
||||||
|
*
|
||||||
|
* Author: Giovanni Campagna <gcampagn@redhat.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:cursor-tracker
|
||||||
|
* @title: MetaCursorTracker
|
||||||
|
* @short_description: Mutter cursor tracking helper
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <meta/main.h>
|
||||||
|
#include <meta/util.h>
|
||||||
|
#include <meta/errors.h>
|
||||||
|
|
||||||
|
#include <cogl/cogl.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
#include <X11/extensions/Xfixes.h>
|
||||||
|
|
||||||
|
#include "meta-cursor-tracker-private.h"
|
||||||
|
#include "screen-private.h"
|
||||||
|
#include "meta-wayland-private.h"
|
||||||
|
|
||||||
|
#define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X 7
|
||||||
|
#define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y 4
|
||||||
|
|
||||||
|
struct _MetaCursorTracker {
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
MetaScreen *screen;
|
||||||
|
|
||||||
|
gboolean is_showing;
|
||||||
|
|
||||||
|
CoglTexture2D *sprite;
|
||||||
|
int hot_x, hot_y;
|
||||||
|
|
||||||
|
CoglTexture2D *root_cursor;
|
||||||
|
int root_hot_x, root_hot_y;
|
||||||
|
|
||||||
|
CoglTexture2D *default_cursor;
|
||||||
|
|
||||||
|
int current_x, current_y;
|
||||||
|
cairo_rectangle_int_t current_rect;
|
||||||
|
cairo_rectangle_int_t previous_rect;
|
||||||
|
gboolean previous_is_valid;
|
||||||
|
|
||||||
|
CoglPipeline *pipeline;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaCursorTrackerClass {
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CURSOR_CHANGED,
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint signals[LAST_SIGNAL];
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_cursor_tracker_init (MetaCursorTracker *self)
|
||||||
|
{
|
||||||
|
/* (JS) Best (?) that can be assumed since XFixes doesn't provide a way of
|
||||||
|
detecting if the system mouse cursor is showing or not.
|
||||||
|
|
||||||
|
On wayland we start with the cursor showing
|
||||||
|
*/
|
||||||
|
self->is_showing = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_cursor_tracker_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
MetaCursorTracker *self = META_CURSOR_TRACKER (object);
|
||||||
|
|
||||||
|
if (self->sprite)
|
||||||
|
cogl_object_unref (self->sprite);
|
||||||
|
if (self->root_cursor)
|
||||||
|
cogl_object_unref (self->root_cursor);
|
||||||
|
if (self->default_cursor)
|
||||||
|
cogl_object_unref (self->default_cursor);
|
||||||
|
if (self->pipeline)
|
||||||
|
cogl_object_unref (self->pipeline);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (meta_cursor_tracker_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_cursor_tracker_class_init (MetaCursorTrackerClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->finalize = meta_cursor_tracker_finalize;
|
||||||
|
|
||||||
|
signals[CURSOR_CHANGED] = g_signal_new ("cursor-changed",
|
||||||
|
G_TYPE_FROM_CLASS (klass),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
0,
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
G_TYPE_NONE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MetaCursorTracker *
|
||||||
|
make_wayland_cursor_tracker (MetaScreen *screen)
|
||||||
|
{
|
||||||
|
MetaWaylandCompositor *compositor;
|
||||||
|
CoglContext *ctx;
|
||||||
|
MetaCursorTracker *self = g_object_new (META_TYPE_CURSOR_TRACKER, NULL);
|
||||||
|
self->screen = screen;
|
||||||
|
|
||||||
|
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
||||||
|
self->pipeline = cogl_pipeline_new (ctx);
|
||||||
|
|
||||||
|
compositor = meta_wayland_compositor_get_default ();
|
||||||
|
compositor->seat->cursor_tracker = self;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MetaCursorTracker *
|
||||||
|
make_x11_cursor_tracker (MetaScreen *screen)
|
||||||
|
{
|
||||||
|
MetaCursorTracker *self = g_object_new (META_TYPE_CURSOR_TRACKER, NULL);
|
||||||
|
self->screen = screen;
|
||||||
|
|
||||||
|
XFixesSelectCursorInput (screen->display->xdisplay,
|
||||||
|
screen->xroot,
|
||||||
|
XFixesDisplayCursorNotifyMask);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_cursor_tracker_get_for_screen:
|
||||||
|
* @screen: the #MetaScreen
|
||||||
|
*
|
||||||
|
* Retrieves the cursor tracker object for @screen.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none):
|
||||||
|
*/
|
||||||
|
MetaCursorTracker *
|
||||||
|
meta_cursor_tracker_get_for_screen (MetaScreen *screen)
|
||||||
|
{
|
||||||
|
MetaCursorTracker *self;
|
||||||
|
|
||||||
|
if (screen->cursor_tracker)
|
||||||
|
return screen->cursor_tracker;
|
||||||
|
|
||||||
|
if (meta_is_wayland_compositor ())
|
||||||
|
self = make_wayland_cursor_tracker (screen);
|
||||||
|
else
|
||||||
|
self = make_x11_cursor_tracker (screen);
|
||||||
|
|
||||||
|
screen->cursor_tracker = self;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker,
|
||||||
|
XEvent *xevent)
|
||||||
|
{
|
||||||
|
XFixesCursorNotifyEvent *notify_event;
|
||||||
|
|
||||||
|
if (meta_is_wayland_compositor ())
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (xevent->xany.type != tracker->screen->display->xfixes_event_base + XFixesCursorNotify)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
notify_event = (XFixesCursorNotifyEvent *)xevent;
|
||||||
|
if (notify_event->subtype != XFixesDisplayCursorNotify)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_clear_pointer (&tracker->sprite, cogl_object_unref);
|
||||||
|
g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ensure_xfixes_cursor (MetaCursorTracker *tracker)
|
||||||
|
{
|
||||||
|
XFixesCursorImage *cursor_image;
|
||||||
|
CoglTexture2D *sprite;
|
||||||
|
guint8 *cursor_data;
|
||||||
|
gboolean free_cursor_data;
|
||||||
|
CoglContext *ctx;
|
||||||
|
|
||||||
|
if (tracker->sprite)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cursor_image = XFixesGetCursorImage (tracker->screen->display->xdisplay);
|
||||||
|
if (!cursor_image)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Like all X APIs, XFixesGetCursorImage() returns arrays of 32-bit
|
||||||
|
* quantities as arrays of long; we need to convert on 64 bit */
|
||||||
|
if (sizeof(long) == 4)
|
||||||
|
{
|
||||||
|
cursor_data = (guint8 *)cursor_image->pixels;
|
||||||
|
free_cursor_data = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
guint32 *cursor_words;
|
||||||
|
gulong *p;
|
||||||
|
guint32 *q;
|
||||||
|
|
||||||
|
cursor_words = g_new (guint32, cursor_image->width * cursor_image->height);
|
||||||
|
cursor_data = (guint8 *)cursor_words;
|
||||||
|
|
||||||
|
p = cursor_image->pixels;
|
||||||
|
q = cursor_words;
|
||||||
|
for (j = 0; j < cursor_image->height; j++)
|
||||||
|
for (i = 0; i < cursor_image->width; i++)
|
||||||
|
*(q++) = *(p++);
|
||||||
|
|
||||||
|
free_cursor_data = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
||||||
|
sprite = cogl_texture_2d_new_from_data (ctx,
|
||||||
|
cursor_image->width,
|
||||||
|
cursor_image->height,
|
||||||
|
CLUTTER_CAIRO_FORMAT_ARGB32,
|
||||||
|
COGL_PIXEL_FORMAT_ANY,
|
||||||
|
cursor_image->width * 4, /* stride */
|
||||||
|
cursor_data,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (free_cursor_data)
|
||||||
|
g_free (cursor_data);
|
||||||
|
|
||||||
|
if (sprite != NULL)
|
||||||
|
{
|
||||||
|
tracker->sprite = sprite;
|
||||||
|
tracker->hot_x = cursor_image->xhot;
|
||||||
|
tracker->hot_y = cursor_image->yhot;
|
||||||
|
}
|
||||||
|
XFree (cursor_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_cursor_tracker_get_sprite:
|
||||||
|
*
|
||||||
|
* Returns: (transfer none):
|
||||||
|
*/
|
||||||
|
CoglTexture *
|
||||||
|
meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (META_IS_CURSOR_TRACKER (tracker), NULL);
|
||||||
|
|
||||||
|
if (!meta_is_wayland_compositor ())
|
||||||
|
ensure_xfixes_cursor (tracker);
|
||||||
|
|
||||||
|
return COGL_TEXTURE (tracker->sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_cursor_tracker_get_hot:
|
||||||
|
* @tracker:
|
||||||
|
* @x: (out):
|
||||||
|
* @y: (out):
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
meta_cursor_tracker_get_hot (MetaCursorTracker *tracker,
|
||||||
|
int *x,
|
||||||
|
int *y)
|
||||||
|
{
|
||||||
|
g_return_if_fail (META_IS_CURSOR_TRACKER (tracker));
|
||||||
|
|
||||||
|
if (!meta_is_wayland_compositor ())
|
||||||
|
ensure_xfixes_cursor (tracker);
|
||||||
|
|
||||||
|
if (x)
|
||||||
|
*x = tracker->hot_x;
|
||||||
|
if (y)
|
||||||
|
*y = tracker->hot_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ensure_wayland_cursor (MetaCursorTracker *tracker)
|
||||||
|
{
|
||||||
|
CoglBitmap *bitmap;
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
if (tracker->default_cursor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
filename = g_build_filename (MUTTER_DATADIR,
|
||||||
|
"mutter/cursors/left_ptr.png",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
bitmap = cogl_bitmap_new_from_file (filename, NULL);
|
||||||
|
tracker->default_cursor = cogl_texture_2d_new_from_bitmap (bitmap,
|
||||||
|
COGL_PIXEL_FORMAT_ANY,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
cogl_object_unref (bitmap);
|
||||||
|
g_free (filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_cursor_tracker_set_root_cursor (MetaCursorTracker *tracker,
|
||||||
|
MetaCursor cursor)
|
||||||
|
{
|
||||||
|
Cursor xcursor;
|
||||||
|
MetaDisplay *display = tracker->screen->display;
|
||||||
|
|
||||||
|
/* First create a cursor for X11 applications that don't specify their own */
|
||||||
|
xcursor = meta_display_create_x_cursor (display, cursor);
|
||||||
|
|
||||||
|
XDefineCursor (display->xdisplay, tracker->screen->xroot, xcursor);
|
||||||
|
XFlush (display->xdisplay);
|
||||||
|
XFreeCursor (display->xdisplay, xcursor);
|
||||||
|
|
||||||
|
/* Now update the real root cursor */
|
||||||
|
if (meta_is_wayland_compositor ())
|
||||||
|
{
|
||||||
|
/* FIXME! We need to load all the other cursors too */
|
||||||
|
ensure_wayland_cursor (tracker);
|
||||||
|
|
||||||
|
g_clear_pointer (&tracker->root_cursor, cogl_object_unref);
|
||||||
|
tracker->root_cursor = cogl_object_ref (tracker->default_cursor);
|
||||||
|
tracker->root_hot_x = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X;
|
||||||
|
tracker->root_hot_y = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_cursor_tracker_revert_root (MetaCursorTracker *tracker)
|
||||||
|
{
|
||||||
|
meta_cursor_tracker_set_sprite (tracker,
|
||||||
|
tracker->root_cursor,
|
||||||
|
tracker->root_hot_x,
|
||||||
|
tracker->root_hot_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_cursor_tracker_set_sprite (MetaCursorTracker *tracker,
|
||||||
|
CoglTexture2D *sprite,
|
||||||
|
int hot_x,
|
||||||
|
int hot_y)
|
||||||
|
{
|
||||||
|
g_assert (meta_is_wayland_compositor ());
|
||||||
|
|
||||||
|
g_clear_pointer (&tracker->sprite, cogl_object_unref);
|
||||||
|
|
||||||
|
if (sprite)
|
||||||
|
{
|
||||||
|
tracker->sprite = cogl_object_ref (sprite);
|
||||||
|
tracker->hot_x = hot_x;
|
||||||
|
tracker->hot_y = hot_y;
|
||||||
|
cogl_pipeline_set_layer_texture (tracker->pipeline, 0, COGL_TEXTURE (tracker->sprite));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cogl_pipeline_set_layer_texture (tracker->pipeline, 0, NULL);
|
||||||
|
|
||||||
|
g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
|
||||||
|
|
||||||
|
meta_cursor_tracker_update_position (tracker, tracker->current_x, tracker->current_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_cursor_tracker_update_position (MetaCursorTracker *tracker,
|
||||||
|
int new_x,
|
||||||
|
int new_y)
|
||||||
|
{
|
||||||
|
g_assert (meta_is_wayland_compositor ());
|
||||||
|
|
||||||
|
tracker->current_x = new_x;
|
||||||
|
tracker->current_y = new_y;
|
||||||
|
tracker->current_rect.x = tracker->current_x - tracker->hot_x;
|
||||||
|
tracker->current_rect.y = tracker->current_y - tracker->hot_y;
|
||||||
|
|
||||||
|
if (tracker->sprite)
|
||||||
|
{
|
||||||
|
tracker->current_rect.width = cogl_texture_get_width (COGL_TEXTURE (tracker->sprite));
|
||||||
|
tracker->current_rect.height = cogl_texture_get_height (COGL_TEXTURE (tracker->sprite));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tracker->current_rect.width = 0;
|
||||||
|
tracker->current_rect.height = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_cursor_tracker_paint (MetaCursorTracker *tracker)
|
||||||
|
{
|
||||||
|
g_assert (meta_is_wayland_compositor ());
|
||||||
|
|
||||||
|
if (tracker->sprite == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* FIXME: try to use a DRM cursor when possible */
|
||||||
|
cogl_framebuffer_draw_rectangle (cogl_get_draw_framebuffer (),
|
||||||
|
tracker->pipeline,
|
||||||
|
tracker->current_rect.x,
|
||||||
|
tracker->current_rect.y,
|
||||||
|
tracker->current_rect.x +
|
||||||
|
tracker->current_rect.width,
|
||||||
|
tracker->current_rect.y +
|
||||||
|
tracker->current_rect.height);
|
||||||
|
|
||||||
|
tracker->previous_rect = tracker->current_rect;
|
||||||
|
tracker->previous_is_valid = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_cursor_tracker_queue_redraw (MetaCursorTracker *tracker,
|
||||||
|
ClutterActor *stage)
|
||||||
|
{
|
||||||
|
g_assert (meta_is_wayland_compositor ());
|
||||||
|
|
||||||
|
if (tracker->previous_is_valid)
|
||||||
|
{
|
||||||
|
clutter_actor_queue_redraw_with_clip (stage, &tracker->previous_rect);
|
||||||
|
tracker->previous_is_valid = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tracker->sprite == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
clutter_actor_queue_redraw_with_clip (stage, &tracker->current_rect);
|
||||||
|
}
|
31
src/core/meta-idle-monitor-private.h
Normal file
31
src/core/meta-idle-monitor-private.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 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.
|
||||||
|
*
|
||||||
|
* Adapted from gnome-session/gnome-session/gs-idle-monitor.c and
|
||||||
|
* from gnome-desktop/libgnome-desktop/gnome-idle-monitor.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <meta/meta-idle-monitor.h>
|
||||||
|
|
||||||
|
void meta_idle_monitor_handle_xevent_all (XEvent *xevent);
|
||||||
|
|
||||||
|
void meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor);
|
||||||
|
|
||||||
|
void meta_idle_monitor_init_dbus (void);
|
932
src/core/meta-idle-monitor.c
Normal file
932
src/core/meta-idle-monitor.c
Normal file
@@ -0,0 +1,932 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 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.
|
||||||
|
*
|
||||||
|
* Adapted from gnome-session/gnome-session/gs-idle-monitor.c and
|
||||||
|
* from gnome-desktop/libgnome-desktop/gnome-idle-monitor.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:idle-monitor
|
||||||
|
* @title: MetaIdleMonitor
|
||||||
|
* @short_description: Mutter idle counter (similar to X's IDLETIME)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/extensions/sync.h>
|
||||||
|
|
||||||
|
#include <meta/util.h>
|
||||||
|
#include <meta/main.h>
|
||||||
|
#include <meta/meta-idle-monitor.h>
|
||||||
|
#include "display-private.h"
|
||||||
|
#include "meta-idle-monitor-private.h"
|
||||||
|
#include "meta-dbus-idle-monitor.h"
|
||||||
|
|
||||||
|
G_STATIC_ASSERT(sizeof(unsigned long) == sizeof(gpointer));
|
||||||
|
|
||||||
|
struct _MetaIdleMonitor
|
||||||
|
{
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
GHashTable *watches;
|
||||||
|
GHashTable *alarms;
|
||||||
|
int device_id;
|
||||||
|
|
||||||
|
/* X11 implementation */
|
||||||
|
Display *display;
|
||||||
|
int sync_event_base;
|
||||||
|
XSyncCounter counter;
|
||||||
|
XSyncAlarm user_active_alarm;
|
||||||
|
|
||||||
|
/* Wayland implementation */
|
||||||
|
guint64 last_event_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaIdleMonitorClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
MetaIdleMonitor *monitor;
|
||||||
|
guint id;
|
||||||
|
MetaIdleMonitorWatchFunc callback;
|
||||||
|
gpointer user_data;
|
||||||
|
GDestroyNotify notify;
|
||||||
|
guint64 timeout_msec;
|
||||||
|
|
||||||
|
/* x11 */
|
||||||
|
XSyncAlarm xalarm;
|
||||||
|
|
||||||
|
/* wayland */
|
||||||
|
GSource *timeout_source;
|
||||||
|
} MetaIdleMonitorWatch;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_DEVICE_ID,
|
||||||
|
PROP_LAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
static GParamSpec *obj_props[PROP_LAST];
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (MetaIdleMonitor, meta_idle_monitor, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static MetaIdleMonitor *device_monitors[256];
|
||||||
|
static int device_id_max;
|
||||||
|
|
||||||
|
static gint64
|
||||||
|
_xsyncvalue_to_int64 (XSyncValue value)
|
||||||
|
{
|
||||||
|
return ((guint64) XSyncValueHigh32 (value)) << 32
|
||||||
|
| (guint64) XSyncValueLow32 (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GINT64_TO_XSYNCVALUE(value, ret) XSyncIntsToValue (ret, value, ((guint64)value) >> 32)
|
||||||
|
|
||||||
|
static void
|
||||||
|
fire_watch (MetaIdleMonitorWatch *watch)
|
||||||
|
{
|
||||||
|
MetaIdleMonitor *monitor;
|
||||||
|
|
||||||
|
monitor = watch->monitor;
|
||||||
|
g_object_ref (monitor);
|
||||||
|
|
||||||
|
if (watch->callback)
|
||||||
|
{
|
||||||
|
watch->callback (watch->monitor,
|
||||||
|
watch->id,
|
||||||
|
watch->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watch->timeout_msec == 0)
|
||||||
|
meta_idle_monitor_remove_watch (watch->monitor, watch->id);
|
||||||
|
|
||||||
|
g_object_unref (monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static XSyncAlarm
|
||||||
|
_xsync_alarm_set (MetaIdleMonitor *monitor,
|
||||||
|
XSyncTestType test_type,
|
||||||
|
guint64 interval,
|
||||||
|
gboolean want_events)
|
||||||
|
{
|
||||||
|
XSyncAlarmAttributes attr;
|
||||||
|
XSyncValue delta;
|
||||||
|
guint flags;
|
||||||
|
|
||||||
|
flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType |
|
||||||
|
XSyncCAValue | XSyncCADelta | XSyncCAEvents;
|
||||||
|
|
||||||
|
XSyncIntToValue (&delta, 0);
|
||||||
|
attr.trigger.counter = monitor->counter;
|
||||||
|
attr.trigger.value_type = XSyncAbsolute;
|
||||||
|
attr.delta = delta;
|
||||||
|
attr.events = want_events;
|
||||||
|
|
||||||
|
GINT64_TO_XSYNCVALUE (interval, &attr.trigger.wait_value);
|
||||||
|
attr.trigger.test_type = test_type;
|
||||||
|
return XSyncCreateAlarm (monitor->display, flags, &attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ensure_alarm_rescheduled (Display *dpy,
|
||||||
|
XSyncAlarm alarm)
|
||||||
|
{
|
||||||
|
XSyncAlarmAttributes attr;
|
||||||
|
|
||||||
|
/* Some versions of Xorg have an issue where alarms aren't
|
||||||
|
* always rescheduled. Calling XSyncChangeAlarm, even
|
||||||
|
* without any attributes, will reschedule the alarm. */
|
||||||
|
XSyncChangeAlarm (dpy, alarm, 0, &attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_alarm_enabled (Display *dpy,
|
||||||
|
XSyncAlarm alarm,
|
||||||
|
gboolean enabled)
|
||||||
|
{
|
||||||
|
XSyncAlarmAttributes attr;
|
||||||
|
attr.events = enabled;
|
||||||
|
XSyncChangeAlarm (dpy, alarm, XSyncCAEvents, &attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_x11_watch (gpointer data,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaIdleMonitorWatch *watch = data;
|
||||||
|
XSyncAlarm alarm = (XSyncAlarm) user_data;
|
||||||
|
|
||||||
|
if (watch->xalarm != alarm)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fire_watch (watch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_idle_monitor_handle_xevent (MetaIdleMonitor *monitor,
|
||||||
|
XSyncAlarmNotifyEvent *alarm_event)
|
||||||
|
{
|
||||||
|
XSyncAlarm alarm;
|
||||||
|
GList *watches;
|
||||||
|
gboolean has_alarm;
|
||||||
|
|
||||||
|
if (alarm_event->state != XSyncAlarmActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
alarm = alarm_event->alarm;
|
||||||
|
|
||||||
|
has_alarm = FALSE;
|
||||||
|
|
||||||
|
if (alarm == monitor->user_active_alarm)
|
||||||
|
{
|
||||||
|
set_alarm_enabled (monitor->display,
|
||||||
|
alarm,
|
||||||
|
FALSE);
|
||||||
|
has_alarm = TRUE;
|
||||||
|
}
|
||||||
|
else if (g_hash_table_contains (monitor->alarms, (gpointer) alarm))
|
||||||
|
{
|
||||||
|
ensure_alarm_rescheduled (monitor->display,
|
||||||
|
alarm);
|
||||||
|
has_alarm = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_alarm)
|
||||||
|
{
|
||||||
|
watches = g_hash_table_get_values (monitor->watches);
|
||||||
|
|
||||||
|
g_list_foreach (watches, check_x11_watch, monitor);
|
||||||
|
g_list_free (watches);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_idle_monitor_handle_xevent_all (XEvent *xevent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < device_id_max; i++)
|
||||||
|
if (device_monitors[i])
|
||||||
|
meta_idle_monitor_handle_xevent (device_monitors[i], (XSyncAlarmNotifyEvent*)xevent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
counter_name_for_device (int device_id)
|
||||||
|
{
|
||||||
|
if (device_id > 0)
|
||||||
|
return g_strdup_printf ("DEVICEIDLETIME %d", device_id);
|
||||||
|
|
||||||
|
return g_strdup ("IDLETIME");
|
||||||
|
}
|
||||||
|
|
||||||
|
static XSyncCounter
|
||||||
|
find_idletime_counter (MetaIdleMonitor *monitor)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int ncounters;
|
||||||
|
XSyncSystemCounter *counters;
|
||||||
|
XSyncCounter counter = None;
|
||||||
|
char *counter_name;
|
||||||
|
|
||||||
|
counter_name = counter_name_for_device (monitor->device_id);
|
||||||
|
counters = XSyncListSystemCounters (monitor->display, &ncounters);
|
||||||
|
for (i = 0; i < ncounters; i++)
|
||||||
|
{
|
||||||
|
if (counters[i].name != NULL && strcmp (counters[i].name, counter_name) == 0)
|
||||||
|
{
|
||||||
|
counter = counters[i].counter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XSyncFreeSystemCounterList (counters);
|
||||||
|
g_free (counter_name);
|
||||||
|
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
get_next_watch_serial (void)
|
||||||
|
{
|
||||||
|
static guint32 serial = 0;
|
||||||
|
g_atomic_int_inc (&serial);
|
||||||
|
return serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
idle_monitor_watch_free (MetaIdleMonitorWatch *watch)
|
||||||
|
{
|
||||||
|
MetaIdleMonitor *monitor;
|
||||||
|
|
||||||
|
if (watch == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
monitor = watch->monitor;
|
||||||
|
|
||||||
|
if (watch->notify != NULL)
|
||||||
|
watch->notify (watch->user_data);
|
||||||
|
|
||||||
|
if (watch->xalarm != monitor->user_active_alarm &&
|
||||||
|
watch->xalarm != None)
|
||||||
|
{
|
||||||
|
XSyncDestroyAlarm (monitor->display, watch->xalarm);
|
||||||
|
g_hash_table_remove (monitor->alarms, (gpointer) watch->xalarm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watch->timeout_source != NULL)
|
||||||
|
g_source_destroy (watch->timeout_source);
|
||||||
|
|
||||||
|
g_slice_free (MetaIdleMonitorWatch, watch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_xsync (MetaIdleMonitor *monitor)
|
||||||
|
{
|
||||||
|
monitor->counter = find_idletime_counter (monitor);
|
||||||
|
/* IDLETIME counter not found? */
|
||||||
|
if (monitor->counter == None)
|
||||||
|
return;
|
||||||
|
|
||||||
|
monitor->user_active_alarm = _xsync_alarm_set (monitor, XSyncNegativeTransition, 1, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_idle_monitor_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
MetaIdleMonitor *monitor;
|
||||||
|
|
||||||
|
monitor = META_IDLE_MONITOR (object);
|
||||||
|
|
||||||
|
g_clear_pointer (&monitor->watches, g_hash_table_destroy);
|
||||||
|
g_clear_pointer (&monitor->alarms, g_hash_table_destroy);
|
||||||
|
|
||||||
|
if (monitor->user_active_alarm != None)
|
||||||
|
{
|
||||||
|
XSyncDestroyAlarm (monitor->display, monitor->user_active_alarm);
|
||||||
|
monitor->user_active_alarm = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (meta_idle_monitor_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_idle_monitor_get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
MetaIdleMonitor *monitor = META_IDLE_MONITOR (object);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_DEVICE_ID:
|
||||||
|
g_value_set_int (value, monitor->device_id);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_idle_monitor_set_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
MetaIdleMonitor *monitor = META_IDLE_MONITOR (object);
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_DEVICE_ID:
|
||||||
|
monitor->device_id = g_value_get_int (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_idle_monitor_constructed (GObject *object)
|
||||||
|
{
|
||||||
|
MetaIdleMonitor *monitor = META_IDLE_MONITOR (object);
|
||||||
|
|
||||||
|
if (!meta_is_wayland_compositor ())
|
||||||
|
{
|
||||||
|
monitor->display = meta_get_display ()->xdisplay;
|
||||||
|
init_xsync (monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_idle_monitor_class_init (MetaIdleMonitorClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = meta_idle_monitor_dispose;
|
||||||
|
object_class->constructed = meta_idle_monitor_constructed;
|
||||||
|
object_class->get_property = meta_idle_monitor_get_property;
|
||||||
|
object_class->set_property = meta_idle_monitor_set_property;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaIdleMonitor:device_id:
|
||||||
|
*
|
||||||
|
* The device to listen to idletime on.
|
||||||
|
*/
|
||||||
|
obj_props[PROP_DEVICE_ID] =
|
||||||
|
g_param_spec_int ("device-id",
|
||||||
|
"Device ID",
|
||||||
|
"The device to listen to idletime on",
|
||||||
|
0, 255, 0,
|
||||||
|
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||||
|
g_object_class_install_property (object_class, PROP_DEVICE_ID, obj_props[PROP_DEVICE_ID]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_idle_monitor_init (MetaIdleMonitor *monitor)
|
||||||
|
{
|
||||||
|
monitor->watches = g_hash_table_new_full (NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
(GDestroyNotify)idle_monitor_watch_free);
|
||||||
|
|
||||||
|
monitor->alarms = g_hash_table_new (NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ensure_device_monitor (int device_id)
|
||||||
|
{
|
||||||
|
if (device_monitors[device_id])
|
||||||
|
return;
|
||||||
|
|
||||||
|
device_monitors[device_id] = g_object_new (META_TYPE_IDLE_MONITOR, "device-id", device_id, NULL);
|
||||||
|
device_id_max = MAX (device_id_max, device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_idle_monitor_get_core:
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): the #MetaIdleMonitor that tracks the server-global
|
||||||
|
* idletime for all devices. To track device-specific idletime,
|
||||||
|
* use meta_idle_monitor_get_for_device().
|
||||||
|
*/
|
||||||
|
MetaIdleMonitor *
|
||||||
|
meta_idle_monitor_get_core (void)
|
||||||
|
{
|
||||||
|
ensure_device_monitor (0);
|
||||||
|
return device_monitors[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_idle_monitor_get_for_device:
|
||||||
|
* @device_id: the device to get the idle time for.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): a new #MetaIdleMonitor that tracks the
|
||||||
|
* device-specific idletime for @device. To track server-global idletime
|
||||||
|
* for all devices, use meta_idle_monitor_get_core().
|
||||||
|
*/
|
||||||
|
MetaIdleMonitor *
|
||||||
|
meta_idle_monitor_get_for_device (int device_id)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (device_id > 0 && device_id < 256, NULL);
|
||||||
|
|
||||||
|
ensure_device_monitor (device_id);
|
||||||
|
return device_monitors[device_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
wayland_dispatch_timeout (GSource *source,
|
||||||
|
GSourceFunc callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaIdleMonitorWatch *watch = user_data;
|
||||||
|
|
||||||
|
fire_watch (watch);
|
||||||
|
g_source_set_ready_time (watch->timeout_source, -1);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSourceFuncs wayland_source_funcs = {
|
||||||
|
NULL, /* prepare */
|
||||||
|
NULL, /* check */
|
||||||
|
wayland_dispatch_timeout,
|
||||||
|
NULL, /* finalize */
|
||||||
|
};
|
||||||
|
|
||||||
|
static MetaIdleMonitorWatch *
|
||||||
|
make_watch (MetaIdleMonitor *monitor,
|
||||||
|
guint64 timeout_msec,
|
||||||
|
MetaIdleMonitorWatchFunc callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify notify)
|
||||||
|
{
|
||||||
|
MetaIdleMonitorWatch *watch;
|
||||||
|
|
||||||
|
watch = g_slice_new0 (MetaIdleMonitorWatch);
|
||||||
|
watch->monitor = monitor;
|
||||||
|
watch->id = get_next_watch_serial ();
|
||||||
|
watch->callback = callback;
|
||||||
|
watch->user_data = user_data;
|
||||||
|
watch->notify = notify;
|
||||||
|
watch->timeout_msec = timeout_msec;
|
||||||
|
|
||||||
|
if (meta_is_wayland_compositor ())
|
||||||
|
{
|
||||||
|
if (timeout_msec != 0)
|
||||||
|
{
|
||||||
|
GSource *source = g_source_new (&wayland_source_funcs, sizeof (GSource));
|
||||||
|
|
||||||
|
g_source_set_callback (source, NULL, watch, NULL);
|
||||||
|
g_source_set_ready_time (source, monitor->last_event_time + timeout_msec * 1000);
|
||||||
|
g_source_attach (source, NULL);
|
||||||
|
g_source_unref (source);
|
||||||
|
|
||||||
|
watch->timeout_source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (timeout_msec != 0)
|
||||||
|
{
|
||||||
|
watch->xalarm = _xsync_alarm_set (monitor, XSyncPositiveTransition, timeout_msec, TRUE);
|
||||||
|
|
||||||
|
g_hash_table_add (monitor->alarms, (gpointer) watch->xalarm);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
watch->xalarm = monitor->user_active_alarm;
|
||||||
|
|
||||||
|
set_alarm_enabled (monitor->display, monitor->user_active_alarm, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hash_table_insert (monitor->watches,
|
||||||
|
GUINT_TO_POINTER (watch->id),
|
||||||
|
watch);
|
||||||
|
return watch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_idle_monitor_add_idle_watch:
|
||||||
|
* @monitor: A #MetaIdleMonitor
|
||||||
|
* @interval_msec: The idletime interval, in milliseconds
|
||||||
|
* @callback: (allow-none): The callback to call when the user has
|
||||||
|
* accumulated @interval_msec milliseconds of idle time.
|
||||||
|
* @user_data: (allow-none): The user data to pass to the callback
|
||||||
|
* @notify: A #GDestroyNotify
|
||||||
|
*
|
||||||
|
* Returns: a watch id
|
||||||
|
*
|
||||||
|
* Adds a watch for a specific idle time. The callback will be called
|
||||||
|
* when the user has accumulated @interval_msec milliseconds of idle time.
|
||||||
|
* This function will return an ID that can either be passed to
|
||||||
|
* meta_idle_monitor_remove_watch(), or can be used to tell idle time
|
||||||
|
* watches apart if you have more than one.
|
||||||
|
*
|
||||||
|
* Also note that this function will only care about positive transitions
|
||||||
|
* (user's idle time exceeding a certain time). If you want to know about
|
||||||
|
* when the user has become active, use
|
||||||
|
* meta_idle_monitor_add_user_active_watch().
|
||||||
|
*/
|
||||||
|
guint
|
||||||
|
meta_idle_monitor_add_idle_watch (MetaIdleMonitor *monitor,
|
||||||
|
guint64 interval_msec,
|
||||||
|
MetaIdleMonitorWatchFunc callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify notify)
|
||||||
|
{
|
||||||
|
MetaIdleMonitorWatch *watch;
|
||||||
|
|
||||||
|
g_return_val_if_fail (META_IS_IDLE_MONITOR (monitor), 0);
|
||||||
|
g_return_val_if_fail (interval_msec > 0, 0);
|
||||||
|
|
||||||
|
watch = make_watch (monitor,
|
||||||
|
interval_msec,
|
||||||
|
callback,
|
||||||
|
user_data,
|
||||||
|
notify);
|
||||||
|
|
||||||
|
return watch->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_idle_monitor_add_user_active_watch:
|
||||||
|
* @monitor: A #MetaIdleMonitor
|
||||||
|
* @callback: (allow-none): The callback to call when the user is
|
||||||
|
* active again.
|
||||||
|
* @user_data: (allow-none): The user data to pass to the callback
|
||||||
|
* @notify: A #GDestroyNotify
|
||||||
|
*
|
||||||
|
* Returns: a watch id
|
||||||
|
*
|
||||||
|
* Add a one-time watch to know when the user is active again.
|
||||||
|
* Note that this watch is one-time and will de-activate after the
|
||||||
|
* function is called, for efficiency purposes. It's most convenient
|
||||||
|
* to call this when an idle watch, as added by
|
||||||
|
* meta_idle_monitor_add_idle_watch(), has triggered.
|
||||||
|
*/
|
||||||
|
guint
|
||||||
|
meta_idle_monitor_add_user_active_watch (MetaIdleMonitor *monitor,
|
||||||
|
MetaIdleMonitorWatchFunc callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify notify)
|
||||||
|
{
|
||||||
|
MetaIdleMonitorWatch *watch;
|
||||||
|
|
||||||
|
g_return_val_if_fail (META_IS_IDLE_MONITOR (monitor), 0);
|
||||||
|
|
||||||
|
watch = make_watch (monitor,
|
||||||
|
0,
|
||||||
|
callback,
|
||||||
|
user_data,
|
||||||
|
notify);
|
||||||
|
|
||||||
|
return watch->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_idle_monitor_remove_watch:
|
||||||
|
* @monitor: A #MetaIdleMonitor
|
||||||
|
* @id: A watch ID
|
||||||
|
*
|
||||||
|
* Removes an idle time watcher, previously added by
|
||||||
|
* meta_idle_monitor_add_idle_watch() or
|
||||||
|
* meta_idle_monitor_add_user_active_watch().
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
meta_idle_monitor_remove_watch (MetaIdleMonitor *monitor,
|
||||||
|
guint id)
|
||||||
|
{
|
||||||
|
g_return_if_fail (META_IS_IDLE_MONITOR (monitor));
|
||||||
|
|
||||||
|
g_hash_table_remove (monitor->watches,
|
||||||
|
GUINT_TO_POINTER (id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_idle_monitor_get_idletime:
|
||||||
|
* @monitor: A #MetaIdleMonitor
|
||||||
|
*
|
||||||
|
* Returns: The current idle time, in milliseconds, or -1 for not supported
|
||||||
|
*/
|
||||||
|
guint64
|
||||||
|
meta_idle_monitor_get_idletime (MetaIdleMonitor *monitor)
|
||||||
|
{
|
||||||
|
XSyncValue value;
|
||||||
|
|
||||||
|
if (meta_is_wayland_compositor ())
|
||||||
|
{
|
||||||
|
return (g_get_monotonic_time () - monitor->last_event_time) / 1000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!XSyncQueryCounter (monitor->display, monitor->counter, &value))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return _xsyncvalue_to_int64 (value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
MetaIdleMonitor *monitor;
|
||||||
|
GList *fired_watches;
|
||||||
|
} CheckWaylandClosure;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_wayland_watch (gpointer key,
|
||||||
|
gpointer value,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaIdleMonitorWatch *watch = value;
|
||||||
|
CheckWaylandClosure *closure = user_data;
|
||||||
|
gboolean steal;
|
||||||
|
|
||||||
|
if (watch->timeout_msec == 0)
|
||||||
|
{
|
||||||
|
closure->fired_watches = g_list_prepend (closure->fired_watches, watch);
|
||||||
|
steal = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_source_set_ready_time (watch->timeout_source,
|
||||||
|
closure->monitor->last_event_time +
|
||||||
|
watch->timeout_msec * 1000);
|
||||||
|
steal = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return steal;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fire_wayland_watch (gpointer watch,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
fire_watch (watch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor)
|
||||||
|
{
|
||||||
|
CheckWaylandClosure closure;
|
||||||
|
|
||||||
|
monitor->last_event_time = g_get_monotonic_time ();
|
||||||
|
|
||||||
|
closure.monitor = monitor;
|
||||||
|
closure.fired_watches = NULL;
|
||||||
|
g_hash_table_foreach_steal (monitor->watches, check_wayland_watch, &closure);
|
||||||
|
|
||||||
|
g_list_foreach (closure.fired_watches, fire_wayland_watch, NULL);
|
||||||
|
g_list_free (closure.fired_watches);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
handle_get_idletime (MetaDBusIdleMonitor *skeleton,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
MetaIdleMonitor *monitor)
|
||||||
|
{
|
||||||
|
guint64 idletime;
|
||||||
|
|
||||||
|
idletime = meta_idle_monitor_get_idletime (monitor);
|
||||||
|
meta_dbus_idle_monitor_complete_get_idletime (skeleton, invocation, idletime);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
MetaDBusIdleMonitor *dbus_monitor;
|
||||||
|
MetaIdleMonitor *monitor;
|
||||||
|
char *dbus_name;
|
||||||
|
guint watch_id;
|
||||||
|
guint name_watcher_id;
|
||||||
|
} DBusWatch;
|
||||||
|
|
||||||
|
static void
|
||||||
|
destroy_dbus_watch (gpointer data)
|
||||||
|
{
|
||||||
|
DBusWatch *watch = data;
|
||||||
|
|
||||||
|
g_object_unref (watch->dbus_monitor);
|
||||||
|
g_object_unref (watch->monitor);
|
||||||
|
g_free (watch->dbus_name);
|
||||||
|
g_bus_unwatch_name (watch->name_watcher_id);
|
||||||
|
|
||||||
|
g_slice_free (DBusWatch, watch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dbus_idle_callback (MetaIdleMonitor *monitor,
|
||||||
|
guint watch_id,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
DBusWatch *watch = user_data;
|
||||||
|
GDBusInterfaceSkeleton *skeleton = G_DBUS_INTERFACE_SKELETON (watch->dbus_monitor);
|
||||||
|
|
||||||
|
g_dbus_connection_emit_signal (g_dbus_interface_skeleton_get_connection (skeleton),
|
||||||
|
watch->dbus_name,
|
||||||
|
g_dbus_interface_skeleton_get_object_path (skeleton),
|
||||||
|
"org.gnome.Mutter.IdleMonitor",
|
||||||
|
"WatchFired",
|
||||||
|
g_variant_new ("(u)", watch_id),
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
name_vanished_callback (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
DBusWatch *watch = user_data;
|
||||||
|
|
||||||
|
meta_idle_monitor_remove_watch (watch->monitor, watch->watch_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DBusWatch *
|
||||||
|
make_dbus_watch (MetaDBusIdleMonitor *skeleton,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
MetaIdleMonitor *monitor)
|
||||||
|
{
|
||||||
|
DBusWatch *watch;
|
||||||
|
|
||||||
|
watch = g_slice_new (DBusWatch);
|
||||||
|
watch->dbus_monitor = g_object_ref (skeleton);
|
||||||
|
watch->monitor = g_object_ref (monitor);
|
||||||
|
watch->dbus_name = g_strdup (g_dbus_method_invocation_get_sender (invocation));
|
||||||
|
watch->name_watcher_id = g_bus_watch_name_on_connection (g_dbus_method_invocation_get_connection (invocation),
|
||||||
|
watch->dbus_name,
|
||||||
|
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||||
|
NULL, /* appeared */
|
||||||
|
name_vanished_callback,
|
||||||
|
watch, NULL);
|
||||||
|
|
||||||
|
return watch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
handle_add_idle_watch (MetaDBusIdleMonitor *skeleton,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
guint64 interval,
|
||||||
|
MetaIdleMonitor *monitor)
|
||||||
|
{
|
||||||
|
DBusWatch *watch;
|
||||||
|
|
||||||
|
watch = make_dbus_watch (skeleton, invocation, monitor);
|
||||||
|
watch->watch_id = meta_idle_monitor_add_idle_watch (monitor, interval,
|
||||||
|
dbus_idle_callback, watch, destroy_dbus_watch);
|
||||||
|
|
||||||
|
meta_dbus_idle_monitor_complete_add_idle_watch (skeleton, invocation, watch->watch_id);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
handle_add_user_active_watch (MetaDBusIdleMonitor *skeleton,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
MetaIdleMonitor *monitor)
|
||||||
|
{
|
||||||
|
DBusWatch *watch;
|
||||||
|
|
||||||
|
watch = make_dbus_watch (skeleton, invocation, monitor);
|
||||||
|
watch->watch_id = meta_idle_monitor_add_user_active_watch (monitor,
|
||||||
|
dbus_idle_callback, watch,
|
||||||
|
destroy_dbus_watch);
|
||||||
|
|
||||||
|
meta_dbus_idle_monitor_complete_add_user_active_watch (skeleton, invocation, watch->watch_id);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
handle_remove_watch (MetaDBusIdleMonitor *skeleton,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
guint id,
|
||||||
|
MetaIdleMonitor *monitor)
|
||||||
|
{
|
||||||
|
meta_idle_monitor_remove_watch (monitor, id);
|
||||||
|
meta_dbus_idle_monitor_complete_remove_watch (skeleton, invocation);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_device_added (ClutterDeviceManager *device_manager,
|
||||||
|
ClutterInputDevice *device,
|
||||||
|
GDBusObjectManagerServer *manager)
|
||||||
|
{
|
||||||
|
MetaDBusIdleMonitor *skeleton;
|
||||||
|
MetaIdleMonitor *monitor;
|
||||||
|
MetaDBusObjectSkeleton *object;
|
||||||
|
int device_id;
|
||||||
|
gboolean is_core;
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
is_core = clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER;
|
||||||
|
|
||||||
|
if (is_core)
|
||||||
|
{
|
||||||
|
monitor = meta_idle_monitor_get_core ();
|
||||||
|
path = g_strdup ("/org/gnome/Mutter/IdleMonitor/Core");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
device_id = clutter_input_device_get_device_id (device);
|
||||||
|
monitor = meta_idle_monitor_get_for_device (device_id);
|
||||||
|
path = g_strdup_printf ("/org/gnome/Mutter/IdleMonitor/Device%d", device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
skeleton = meta_dbus_idle_monitor_skeleton_new ();
|
||||||
|
g_signal_connect_object (skeleton, "handle-add-idle-watch",
|
||||||
|
G_CALLBACK (handle_add_idle_watch), monitor, 0);
|
||||||
|
g_signal_connect_object (skeleton, "handle-add-user-active-watch",
|
||||||
|
G_CALLBACK (handle_add_user_active_watch), monitor, 0);
|
||||||
|
g_signal_connect_object (skeleton, "handle-remove-watch",
|
||||||
|
G_CALLBACK (handle_remove_watch), monitor, 0);
|
||||||
|
g_signal_connect_object (skeleton, "handle-get-idletime",
|
||||||
|
G_CALLBACK (handle_get_idletime), monitor, 0);
|
||||||
|
|
||||||
|
object = meta_dbus_object_skeleton_new (path);
|
||||||
|
meta_dbus_object_skeleton_set_idle_monitor (object, skeleton);
|
||||||
|
|
||||||
|
g_dbus_object_manager_server_export (manager, G_DBUS_OBJECT_SKELETON (object));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_bus_acquired (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GDBusObjectManagerServer *manager;
|
||||||
|
ClutterDeviceManager *device_manager;
|
||||||
|
GSList *devices, *iter;
|
||||||
|
|
||||||
|
manager = g_dbus_object_manager_server_new ("/org/gnome/Mutter/IdleMonitor");
|
||||||
|
|
||||||
|
device_manager = clutter_device_manager_get_default ();
|
||||||
|
devices = clutter_device_manager_list_devices (device_manager);
|
||||||
|
|
||||||
|
for (iter = devices; iter; iter = iter->next)
|
||||||
|
on_device_added (device_manager, iter->data, manager);
|
||||||
|
|
||||||
|
g_signal_connect_object (device_manager, "device-added",
|
||||||
|
G_CALLBACK (on_device_added), manager, 0);
|
||||||
|
|
||||||
|
g_dbus_object_manager_server_set_connection (manager, g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_name_acquired (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
meta_topic (META_DEBUG_DBUS, "Acquired name %s\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_name_lost (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
meta_topic (META_DEBUG_DBUS, "Lost or failed to acquire name %s\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_idle_monitor_init_dbus (void)
|
||||||
|
{
|
||||||
|
static int dbus_name_id;
|
||||||
|
|
||||||
|
if (dbus_name_id > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
|
||||||
|
"org.gnome.Mutter.IdleMonitor",
|
||||||
|
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
|
||||||
|
(meta_get_replace_current_wm () ?
|
||||||
|
G_BUS_NAME_OWNER_FLAGS_REPLACE : 0),
|
||||||
|
on_bus_acquired,
|
||||||
|
on_name_acquired,
|
||||||
|
on_name_lost,
|
||||||
|
NULL, NULL);
|
||||||
|
}
|
||||||
|
|
40
src/core/meta-xrandr-shared.h
Normal file
40
src/core/meta-xrandr-shared.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file is shared between mutter (src/core/meta-xrandr-shared.h)
|
||||||
|
and gnome-desktop (libgnome-desktop/meta-xrandr-shared.h).
|
||||||
|
|
||||||
|
The canonical place for all changes is mutter.
|
||||||
|
|
||||||
|
There should be no includes in this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef META_XRANDR_SHARED_H
|
||||||
|
#define META_XRANDR_SHARED_H
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
META_POWER_SAVE_UNKNOWN = -1,
|
||||||
|
META_POWER_SAVE_ON = 0,
|
||||||
|
META_POWER_SAVE_STANDBY,
|
||||||
|
META_POWER_SAVE_SUSPEND,
|
||||||
|
META_POWER_SAVE_OFF,
|
||||||
|
} MetaPowerSave;
|
||||||
|
|
||||||
|
#endif
|
1755
src/core/monitor-config.c
Normal file
1755
src/core/monitor-config.c
Normal file
File diff suppressed because it is too large
Load Diff
767
src/core/monitor-kms.c
Normal file
767
src/core/monitor-kms.c
Normal file
@@ -0,0 +1,767 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Author: Giovanni Campagna <gcampagn@redhat.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
#include <xf86drmMode.h>
|
||||||
|
|
||||||
|
#include <meta/main.h>
|
||||||
|
#include <meta/errors.h>
|
||||||
|
#include "monitor-private.h"
|
||||||
|
|
||||||
|
#define ALL_WL_TRANSFORMS ((1 << (WL_OUTPUT_TRANSFORM_FLIPPED_270 + 1)) - 1)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
drmModeConnector *connector;
|
||||||
|
|
||||||
|
unsigned n_encoders;
|
||||||
|
drmModeEncoderPtr *encoders;
|
||||||
|
drmModeEncoderPtr current_encoder;
|
||||||
|
|
||||||
|
/* bitmasks of encoder position in the resources array */
|
||||||
|
uint32_t encoder_mask;
|
||||||
|
uint32_t enc_clone_mask;
|
||||||
|
|
||||||
|
uint32_t dpms_prop_id;
|
||||||
|
} MetaOutputKms;
|
||||||
|
|
||||||
|
struct _MetaMonitorManagerKms
|
||||||
|
{
|
||||||
|
MetaMonitorManager parent_instance;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
drmModeConnector **connectors;
|
||||||
|
unsigned int n_connectors;
|
||||||
|
|
||||||
|
drmModeEncoder **encoders;
|
||||||
|
unsigned int n_encoders;
|
||||||
|
|
||||||
|
drmModeEncoder *current_encoder;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaMonitorManagerKmsClass
|
||||||
|
{
|
||||||
|
MetaMonitorManagerClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (MetaMonitorManagerKms, meta_monitor_manager_kms, META_TYPE_MONITOR_MANAGER);
|
||||||
|
|
||||||
|
static int
|
||||||
|
compare_outputs (const void *one,
|
||||||
|
const void *two)
|
||||||
|
{
|
||||||
|
const MetaOutput *o_one = one, *o_two = two;
|
||||||
|
|
||||||
|
return strcmp (o_one->name, o_two->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
make_output_name (drmModeConnector *connector)
|
||||||
|
{
|
||||||
|
static const char * const connector_type_names[] = {
|
||||||
|
"unknown", "VGA", "DVII", "DVID", "DVID", "Composite",
|
||||||
|
"SVIDEO", "LVDS", "Component", "9PinDIN", "DisplayPort",
|
||||||
|
"HDMIA", "HDMIB", "TV", "eDP"
|
||||||
|
};
|
||||||
|
const char *connector_type_name;
|
||||||
|
|
||||||
|
if (connector->connector_type >= 0 &&
|
||||||
|
connector->connector_type < G_N_ELEMENTS (connector_type_names))
|
||||||
|
connector_type_name = connector_type_names[connector->connector_type];
|
||||||
|
else
|
||||||
|
connector_type_name = "unknown";
|
||||||
|
|
||||||
|
return g_strdup_printf ("%s%d", connector_type_name, connector->connector_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_output_destroy_notify (MetaOutput *output)
|
||||||
|
{
|
||||||
|
MetaOutputKms *output_kms;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
output_kms = output->driver_private;
|
||||||
|
|
||||||
|
for (i = 0; i < output_kms->n_encoders; i++)
|
||||||
|
drmModeFreeEncoder (output_kms->encoders[i]);
|
||||||
|
|
||||||
|
g_slice_free (MetaOutputKms, output_kms);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_mode_destroy_notify (MetaMonitorMode *output)
|
||||||
|
{
|
||||||
|
g_slice_free (drmModeModeInfo, output->driver_private);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
drm_mode_equal (gconstpointer one,
|
||||||
|
gconstpointer two)
|
||||||
|
{
|
||||||
|
return memcmp (one, two, sizeof (drmModeModeInfo)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
drm_mode_hash (gconstpointer ptr)
|
||||||
|
{
|
||||||
|
const drmModeModeInfo *mode = ptr;
|
||||||
|
guint hash = 0;
|
||||||
|
|
||||||
|
hash ^= mode->clock;
|
||||||
|
hash ^= mode->hdisplay ^ mode->hsync_start ^ mode->hsync_end;
|
||||||
|
hash ^= mode->vdisplay ^ mode->vsync_start ^ mode->vsync_end;
|
||||||
|
hash ^= mode->vrefresh;
|
||||||
|
hash ^= mode->flags ^ mode->type;
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_kms_read_current (MetaMonitorManager *manager)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
|
||||||
|
drmModeRes *resources;
|
||||||
|
GHashTable *modes;
|
||||||
|
GHashTableIter iter;
|
||||||
|
drmModeModeInfo *mode;
|
||||||
|
unsigned int i, j, k;
|
||||||
|
unsigned int n_actual_outputs;
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
resources = drmModeGetResources(manager_kms->fd);
|
||||||
|
modes = g_hash_table_new (drm_mode_hash, drm_mode_equal);
|
||||||
|
|
||||||
|
manager->max_screen_width = resources->max_width;
|
||||||
|
manager->max_screen_height = resources->max_height;
|
||||||
|
|
||||||
|
manager->power_save_mode = META_POWER_SAVE_ON;
|
||||||
|
|
||||||
|
manager_kms->n_connectors = resources->count_connectors;
|
||||||
|
manager_kms->connectors = g_new (drmModeConnector *, manager_kms->n_connectors);
|
||||||
|
for (i = 0; i < manager_kms->n_connectors; i++)
|
||||||
|
{
|
||||||
|
drmModeConnector *connector;
|
||||||
|
|
||||||
|
connector = drmModeGetConnector (manager_kms->fd, resources->connectors[i]);
|
||||||
|
manager_kms->connectors[i] = connector;
|
||||||
|
|
||||||
|
if (connector->connection == DRM_MODE_CONNECTED)
|
||||||
|
{
|
||||||
|
/* Collect all modes for this connector */
|
||||||
|
for (j = 0; j < (unsigned)connector->count_modes; j++)
|
||||||
|
g_hash_table_add (modes, &connector->modes[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manager_kms->n_encoders = resources->count_encoders;
|
||||||
|
manager_kms->encoders = g_new (drmModeEncoder *, manager_kms->n_encoders);
|
||||||
|
for (i = 0; i < manager_kms->n_encoders; i++)
|
||||||
|
{
|
||||||
|
manager_kms->encoders[i] = drmModeGetEncoder (manager_kms->fd,
|
||||||
|
resources->encoders[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
manager->n_modes = g_hash_table_size (modes);
|
||||||
|
manager->modes = g_new0 (MetaMonitorMode, manager->n_modes);
|
||||||
|
g_hash_table_iter_init (&iter, modes);
|
||||||
|
i = 0;
|
||||||
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer)&mode))
|
||||||
|
{
|
||||||
|
MetaMonitorMode *meta_mode;
|
||||||
|
|
||||||
|
meta_mode = &manager->modes[i];
|
||||||
|
|
||||||
|
meta_mode->mode_id = i;
|
||||||
|
meta_mode->name = g_strndup (mode->name, DRM_DISPLAY_MODE_LEN);
|
||||||
|
meta_mode->width = mode->hdisplay;
|
||||||
|
meta_mode->height = mode->vdisplay;
|
||||||
|
meta_mode->refresh_rate = (1000 * mode->clock /
|
||||||
|
((float)mode->htotal * mode->vtotal));
|
||||||
|
|
||||||
|
meta_mode->driver_private = g_slice_dup (drmModeModeInfo, mode);
|
||||||
|
meta_mode->driver_notify = (GDestroyNotify)meta_monitor_mode_destroy_notify;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
g_hash_table_destroy (modes);
|
||||||
|
|
||||||
|
manager->n_crtcs = resources->count_crtcs;
|
||||||
|
manager->crtcs = g_new0 (MetaCRTC, manager->n_crtcs);
|
||||||
|
width = 0; height = 0;
|
||||||
|
for (i = 0; i < (unsigned)resources->count_crtcs; i++)
|
||||||
|
{
|
||||||
|
drmModeCrtc *crtc;
|
||||||
|
MetaCRTC *meta_crtc;
|
||||||
|
|
||||||
|
crtc = drmModeGetCrtc (manager_kms->fd, resources->crtcs[i]);
|
||||||
|
|
||||||
|
meta_crtc = &manager->crtcs[i];
|
||||||
|
|
||||||
|
meta_crtc->crtc_id = crtc->crtc_id;
|
||||||
|
meta_crtc->rect.x = crtc->x;
|
||||||
|
meta_crtc->rect.y = crtc->y;
|
||||||
|
meta_crtc->rect.width = crtc->width;
|
||||||
|
meta_crtc->rect.height = crtc->height;
|
||||||
|
meta_crtc->dirty = FALSE;
|
||||||
|
|
||||||
|
/* FIXME: we can handle some transforms, with a combination of
|
||||||
|
scaling and fitting, but it is very driver dependent */
|
||||||
|
meta_crtc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||||
|
meta_crtc->all_transforms = 1 << WL_OUTPUT_TRANSFORM_NORMAL;
|
||||||
|
|
||||||
|
if (crtc->mode_valid)
|
||||||
|
{
|
||||||
|
for (j = 0; j < manager->n_modes; j++)
|
||||||
|
{
|
||||||
|
if (drm_mode_equal (&crtc->mode, manager->modes[j].driver_private))
|
||||||
|
{
|
||||||
|
meta_crtc->current_mode = &manager->modes[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
width = MAX (width, meta_crtc->rect.x + meta_crtc->rect.width);
|
||||||
|
height = MAX (height, meta_crtc->rect.y + meta_crtc->rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
drmModeFreeCrtc (crtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
manager->screen_width = width;
|
||||||
|
manager->screen_height = height;
|
||||||
|
|
||||||
|
manager->outputs = g_new0 (MetaOutput, manager_kms->n_connectors);
|
||||||
|
n_actual_outputs = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < manager_kms->n_connectors; i++)
|
||||||
|
{
|
||||||
|
MetaOutput *meta_output;
|
||||||
|
MetaOutputKms *output_kms;
|
||||||
|
drmModeConnector *connector;
|
||||||
|
GArray *crtcs;
|
||||||
|
unsigned int crtc_mask;
|
||||||
|
|
||||||
|
connector = manager_kms->connectors[i];
|
||||||
|
meta_output = &manager->outputs[n_actual_outputs];
|
||||||
|
|
||||||
|
if (connector->connection == DRM_MODE_CONNECTED)
|
||||||
|
{
|
||||||
|
meta_output->output_id = connector->connector_id;
|
||||||
|
meta_output->name = make_output_name (connector);
|
||||||
|
meta_output->vendor = g_strdup ("unknown");
|
||||||
|
meta_output->product = g_strdup ("unknown");
|
||||||
|
meta_output->serial = g_strdup ("");
|
||||||
|
meta_output->width_mm = connector->mmWidth;
|
||||||
|
meta_output->height_mm = connector->mmHeight;
|
||||||
|
|
||||||
|
if (connector->subpixel == DRM_MODE_SUBPIXEL_UNKNOWN)
|
||||||
|
meta_output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
|
||||||
|
else if (connector->subpixel == DRM_MODE_SUBPIXEL_NONE)
|
||||||
|
meta_output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE;
|
||||||
|
else
|
||||||
|
meta_output->subpixel_order = connector->subpixel;
|
||||||
|
|
||||||
|
meta_output->n_modes = connector->count_modes;
|
||||||
|
meta_output->modes = g_new0 (MetaMonitorMode *, meta_output->n_modes);
|
||||||
|
for (j = 0; j < meta_output->n_modes; j++)
|
||||||
|
{
|
||||||
|
for (k = 0; k < manager->n_modes; k++)
|
||||||
|
{
|
||||||
|
if (drm_mode_equal (&connector->modes[j], manager->modes[k].driver_private))
|
||||||
|
{
|
||||||
|
meta_output->modes[j] = &manager->modes[k];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
meta_output->preferred_mode = meta_output->modes[0];
|
||||||
|
|
||||||
|
meta_output->driver_private = output_kms = g_slice_new0 (MetaOutputKms);
|
||||||
|
meta_output->driver_notify = (GDestroyNotify)meta_output_destroy_notify;
|
||||||
|
|
||||||
|
output_kms->connector = connector;
|
||||||
|
output_kms->n_encoders = connector->count_encoders;
|
||||||
|
output_kms->encoders = g_new0 (drmModeEncoderPtr, output_kms->n_encoders);
|
||||||
|
|
||||||
|
crtc_mask = 0x7F;
|
||||||
|
for (j = 0; j < output_kms->n_encoders; j++)
|
||||||
|
{
|
||||||
|
output_kms->encoders[j] = drmModeGetEncoder (manager_kms->fd, connector->encoders[j]);
|
||||||
|
|
||||||
|
crtc_mask &= output_kms->encoders[j]->possible_crtcs;
|
||||||
|
|
||||||
|
if (output_kms->encoders[j]->encoder_id == connector->encoder_id)
|
||||||
|
output_kms->current_encoder = output_kms->encoders[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
crtcs = g_array_new (FALSE, FALSE, sizeof (MetaCRTC*));
|
||||||
|
|
||||||
|
for (j = 0; j < manager->n_crtcs; j++)
|
||||||
|
{
|
||||||
|
if (crtc_mask & (1 << j))
|
||||||
|
{
|
||||||
|
MetaCRTC *crtc = &manager->crtcs[j];
|
||||||
|
g_array_append_val (crtcs, crtc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_output->n_possible_crtcs = crtcs->len;
|
||||||
|
meta_output->possible_crtcs = (void*)g_array_free (crtcs, FALSE);
|
||||||
|
|
||||||
|
if (output_kms->current_encoder && output_kms->current_encoder->crtc_id != 0)
|
||||||
|
{
|
||||||
|
for (j = 0; j < manager->n_crtcs; j++)
|
||||||
|
{
|
||||||
|
if (manager->crtcs[j].crtc_id == output_kms->current_encoder->crtc_id)
|
||||||
|
{
|
||||||
|
meta_output->crtc = &manager->crtcs[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
meta_output->crtc = NULL;
|
||||||
|
|
||||||
|
meta_output->is_primary = FALSE;
|
||||||
|
meta_output->is_presentation = FALSE;
|
||||||
|
|
||||||
|
for (j = 0; j < (unsigned)connector->count_props; j++)
|
||||||
|
{
|
||||||
|
drmModePropertyPtr prop;
|
||||||
|
|
||||||
|
prop = drmModeGetProperty(manager_kms->fd, connector->props[j]);
|
||||||
|
|
||||||
|
if (prop)
|
||||||
|
{
|
||||||
|
if ((prop->flags & DRM_MODE_PROP_ENUM) &&
|
||||||
|
strcmp(prop->name, "DPMS") == 0)
|
||||||
|
{
|
||||||
|
output_kms->dpms_prop_id = prop->prop_id;
|
||||||
|
drmModeFreeProperty(prop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmModeFreeProperty(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: backlight is a very driver specific thing unfortunately,
|
||||||
|
every DDX does its own thing, and the dumb KMS API does not include it.
|
||||||
|
|
||||||
|
For example, xf86-video-intel has a list of paths to probe in /sys/class/backlight
|
||||||
|
(one for each major HW maker, and then some).
|
||||||
|
We can't do the same because we're not root.
|
||||||
|
It might be best to leave backlight out of the story and rely on the setuid
|
||||||
|
helper in gnome-settings-daemon.
|
||||||
|
*/
|
||||||
|
meta_output->backlight_min = 0;
|
||||||
|
meta_output->backlight_max = 0;
|
||||||
|
meta_output->backlight = -1;
|
||||||
|
|
||||||
|
n_actual_outputs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manager->n_outputs = n_actual_outputs;
|
||||||
|
manager->outputs = g_renew (MetaOutput, manager->outputs, manager->n_outputs);
|
||||||
|
|
||||||
|
/* Sort the outputs for easier handling in MetaMonitorConfig */
|
||||||
|
qsort (manager->outputs, manager->n_outputs, sizeof (MetaOutput), compare_outputs);
|
||||||
|
|
||||||
|
/* Now fix the clones.
|
||||||
|
Code mostly inspired by xf86-video-modesetting. */
|
||||||
|
|
||||||
|
/* XXX: intel hardware doesn't usually have clones, but we only have intel
|
||||||
|
cards, so this code was never tested! */
|
||||||
|
for (i = 0; i < manager->n_outputs; i++)
|
||||||
|
{
|
||||||
|
MetaOutput *meta_output;
|
||||||
|
MetaOutputKms *output_kms;
|
||||||
|
|
||||||
|
meta_output = &manager->outputs[i];
|
||||||
|
output_kms = meta_output->driver_private;
|
||||||
|
|
||||||
|
output_kms->enc_clone_mask = 0xff;
|
||||||
|
output_kms->encoder_mask = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < output_kms->n_encoders; j++)
|
||||||
|
{
|
||||||
|
for (k = 0; k < manager_kms->n_encoders; k++)
|
||||||
|
{
|
||||||
|
if (output_kms->encoders[j]->encoder_id == manager_kms->encoders[k]->encoder_id)
|
||||||
|
{
|
||||||
|
output_kms->encoder_mask |= (1 << k);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output_kms->enc_clone_mask &= output_kms->encoders[j]->possible_clones;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < manager->n_outputs; i++)
|
||||||
|
{
|
||||||
|
MetaOutput *meta_output;
|
||||||
|
MetaOutputKms *output_kms;
|
||||||
|
|
||||||
|
meta_output = &manager->outputs[i];
|
||||||
|
output_kms = meta_output->driver_private;
|
||||||
|
|
||||||
|
if (output_kms->enc_clone_mask == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (j = 0; j < manager->n_outputs; j++)
|
||||||
|
{
|
||||||
|
MetaOutput *meta_clone;
|
||||||
|
MetaOutputKms *clone_kms;
|
||||||
|
|
||||||
|
meta_clone = &manager->outputs[i];
|
||||||
|
clone_kms = meta_clone->driver_private;
|
||||||
|
|
||||||
|
if (meta_clone == meta_output)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (clone_kms->encoder_mask == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (clone_kms->encoder_mask == output_kms->enc_clone_mask)
|
||||||
|
{
|
||||||
|
meta_output->n_possible_clones++;
|
||||||
|
meta_output->possible_clones = g_renew (MetaOutput *,
|
||||||
|
meta_output->possible_clones,
|
||||||
|
meta_output->n_possible_clones);
|
||||||
|
meta_output->possible_clones[meta_output->n_possible_clones - 1] = meta_clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drmModeFreeResources (resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager,
|
||||||
|
MetaPowerSave mode)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
|
||||||
|
uint64_t state;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case META_POWER_SAVE_ON:
|
||||||
|
state = DRM_MODE_DPMS_ON;
|
||||||
|
break;
|
||||||
|
case META_POWER_SAVE_STANDBY:
|
||||||
|
state = DRM_MODE_DPMS_STANDBY;
|
||||||
|
break;
|
||||||
|
case META_POWER_SAVE_SUSPEND:
|
||||||
|
state = DRM_MODE_DPMS_SUSPEND;
|
||||||
|
break;
|
||||||
|
case META_POWER_SAVE_OFF:
|
||||||
|
state = DRM_MODE_DPMS_SUSPEND;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < manager->n_outputs; i++)
|
||||||
|
{
|
||||||
|
MetaOutput *meta_output;
|
||||||
|
MetaOutputKms *output_kms;
|
||||||
|
|
||||||
|
meta_output = &manager->outputs[i];
|
||||||
|
output_kms = meta_output->driver_private;
|
||||||
|
|
||||||
|
if (output_kms->dpms_prop_id)
|
||||||
|
{
|
||||||
|
int ok = drmModeConnectorSetProperty(manager_kms->fd, meta_output->output_id,
|
||||||
|
output_kms->dpms_prop_id, state);
|
||||||
|
|
||||||
|
if (ok < 0)
|
||||||
|
meta_warning ("Failed to set power save mode for output %s: %s\n",
|
||||||
|
meta_output->name, strerror (errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
crtc_free (CoglKmsCrtc *crtc)
|
||||||
|
{
|
||||||
|
g_free (crtc->connectors);
|
||||||
|
g_slice_free (CoglKmsCrtc, crtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
|
||||||
|
MetaCRTCInfo **crtcs,
|
||||||
|
unsigned int n_crtcs,
|
||||||
|
MetaOutputInfo **outputs,
|
||||||
|
unsigned int n_outputs)
|
||||||
|
{
|
||||||
|
ClutterBackend *backend;
|
||||||
|
CoglContext *cogl_context;
|
||||||
|
CoglDisplay *cogl_display;
|
||||||
|
unsigned i;
|
||||||
|
GList *cogl_crtcs;
|
||||||
|
int width, height;
|
||||||
|
gboolean ok;
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
cogl_crtcs = NULL;
|
||||||
|
width = 0; height = 0;
|
||||||
|
for (i = 0; i < n_crtcs; i++)
|
||||||
|
{
|
||||||
|
MetaCRTCInfo *crtc_info = crtcs[i];
|
||||||
|
MetaCRTC *crtc = crtc_info->crtc;
|
||||||
|
CoglKmsCrtc *cogl_crtc;
|
||||||
|
|
||||||
|
crtc->dirty = TRUE;
|
||||||
|
|
||||||
|
cogl_crtc = g_slice_new0 (CoglKmsCrtc);
|
||||||
|
cogl_crtcs = g_list_prepend (cogl_crtcs, cogl_crtc);
|
||||||
|
|
||||||
|
if (crtc_info->mode == NULL)
|
||||||
|
{
|
||||||
|
cogl_crtc->id = crtc->crtc_id;
|
||||||
|
cogl_crtc->x = 0;
|
||||||
|
cogl_crtc->y = 0;
|
||||||
|
cogl_crtc->count = 0;
|
||||||
|
memset (&cogl_crtc->mode, 0, sizeof (drmModeModeInfo));
|
||||||
|
cogl_crtc->connectors = NULL;
|
||||||
|
cogl_crtc->count = 0;
|
||||||
|
|
||||||
|
crtc->rect.x = 0;
|
||||||
|
crtc->rect.y = 0;
|
||||||
|
crtc->rect.width = 0;
|
||||||
|
crtc->rect.height = 0;
|
||||||
|
crtc->current_mode = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MetaMonitorMode *mode;
|
||||||
|
uint32_t *outputs;
|
||||||
|
unsigned int j, n_outputs;
|
||||||
|
|
||||||
|
mode = crtc_info->mode;
|
||||||
|
|
||||||
|
cogl_crtc->id = crtc->crtc_id;
|
||||||
|
cogl_crtc->x = crtc_info->x;
|
||||||
|
cogl_crtc->y = crtc_info->y;
|
||||||
|
cogl_crtc->count = n_outputs = crtc_info->outputs->len;
|
||||||
|
cogl_crtc->connectors = outputs = g_new (uint32_t, n_outputs);
|
||||||
|
|
||||||
|
for (j = 0; j < n_outputs; j++)
|
||||||
|
{
|
||||||
|
MetaOutput *output = ((MetaOutput**)crtc_info->outputs->pdata)[j];
|
||||||
|
|
||||||
|
outputs[j] = output->output_id;
|
||||||
|
|
||||||
|
output->dirty = TRUE;
|
||||||
|
output->crtc = crtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy (&cogl_crtc->mode, crtc_info->mode->driver_private,
|
||||||
|
sizeof (drmModeModeInfo));
|
||||||
|
|
||||||
|
width = MAX (width, crtc_info->x + crtc_info->mode->width);
|
||||||
|
height = MAX (height, crtc_info->y + crtc_info->mode->height);
|
||||||
|
|
||||||
|
crtc->rect.x = crtc_info->x;
|
||||||
|
crtc->rect.y = crtc_info->y;
|
||||||
|
crtc->rect.width = mode->width;
|
||||||
|
crtc->rect.height = mode->height;
|
||||||
|
crtc->current_mode = mode;
|
||||||
|
crtc->transform = crtc_info->transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable CRTCs not mentioned in the list */
|
||||||
|
for (i = 0; i < manager->n_crtcs; i++)
|
||||||
|
{
|
||||||
|
MetaCRTC *crtc = &manager->crtcs[i];
|
||||||
|
CoglKmsCrtc *cogl_crtc;
|
||||||
|
|
||||||
|
crtc->logical_monitor = NULL;
|
||||||
|
|
||||||
|
if (crtc->dirty)
|
||||||
|
{
|
||||||
|
crtc->dirty = FALSE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cogl_crtc = g_slice_new0 (CoglKmsCrtc);
|
||||||
|
cogl_crtcs = g_list_prepend (cogl_crtcs, cogl_crtc);
|
||||||
|
|
||||||
|
cogl_crtc->id = crtc->crtc_id;
|
||||||
|
cogl_crtc->x = 0;
|
||||||
|
cogl_crtc->y = 0;
|
||||||
|
cogl_crtc->count = 0;
|
||||||
|
memset (&cogl_crtc->mode, 0, sizeof (drmModeModeInfo));
|
||||||
|
cogl_crtc->connectors = NULL;
|
||||||
|
cogl_crtc->count = 0;
|
||||||
|
|
||||||
|
crtc->rect.x = 0;
|
||||||
|
crtc->rect.y = 0;
|
||||||
|
crtc->rect.width = 0;
|
||||||
|
crtc->rect.height = 0;
|
||||||
|
crtc->current_mode = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
backend = clutter_get_default_backend ();
|
||||||
|
cogl_context = clutter_backend_get_cogl_context (backend);
|
||||||
|
cogl_display = cogl_context_get_display (cogl_context);
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
ok = cogl_kms_display_set_layout (cogl_display, width, height, cogl_crtcs, &error);
|
||||||
|
g_list_free_full (cogl_crtcs, (GDestroyNotify) crtc_free);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
meta_warning ("Applying display configuration failed: %s\n", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < n_outputs; i++)
|
||||||
|
{
|
||||||
|
MetaOutputInfo *output_info = outputs[i];
|
||||||
|
MetaOutput *output = output_info->output;
|
||||||
|
|
||||||
|
output->is_primary = output_info->is_primary;
|
||||||
|
output->is_presentation = output_info->is_presentation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable outputs not mentioned in the list */
|
||||||
|
for (i = 0; i < manager->n_outputs; i++)
|
||||||
|
{
|
||||||
|
MetaOutput *output = &manager->outputs[i];
|
||||||
|
|
||||||
|
if (output->dirty)
|
||||||
|
{
|
||||||
|
output->dirty = FALSE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
output->crtc = NULL;
|
||||||
|
output->is_primary = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager->screen_width = width;
|
||||||
|
manager->screen_height = height;
|
||||||
|
|
||||||
|
meta_monitor_manager_rebuild_derived (manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_kms_get_crtc_gamma (MetaMonitorManager *manager,
|
||||||
|
MetaCRTC *crtc,
|
||||||
|
gsize *size,
|
||||||
|
unsigned short **red,
|
||||||
|
unsigned short **green,
|
||||||
|
unsigned short **blue)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
|
||||||
|
drmModeCrtc *kms_crtc;
|
||||||
|
|
||||||
|
kms_crtc = drmModeGetCrtc (manager_kms->fd, crtc->crtc_id);
|
||||||
|
|
||||||
|
*size = kms_crtc->gamma_size;
|
||||||
|
*red = g_new (unsigned short, *size);
|
||||||
|
*green = g_new (unsigned short, *size);
|
||||||
|
*blue = g_new (unsigned short, *size);
|
||||||
|
|
||||||
|
drmModeCrtcGetGamma (manager_kms->fd, crtc->crtc_id, *size, *red, *green, *blue);
|
||||||
|
|
||||||
|
drmModeFreeCrtc (kms_crtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_kms_set_crtc_gamma (MetaMonitorManager *manager,
|
||||||
|
MetaCRTC *crtc,
|
||||||
|
gsize size,
|
||||||
|
unsigned short *red,
|
||||||
|
unsigned short *green,
|
||||||
|
unsigned short *blue)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
|
||||||
|
|
||||||
|
drmModeCrtcSetGamma (manager_kms->fd, crtc->crtc_id, size, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_kms_init (MetaMonitorManagerKms *manager_kms)
|
||||||
|
{
|
||||||
|
ClutterBackend *backend;
|
||||||
|
CoglContext *cogl_context;
|
||||||
|
CoglDisplay *cogl_display;
|
||||||
|
CoglRenderer *cogl_renderer;
|
||||||
|
|
||||||
|
backend = clutter_get_default_backend ();
|
||||||
|
cogl_context = clutter_backend_get_cogl_context (backend);
|
||||||
|
cogl_display = cogl_context_get_display (cogl_context);
|
||||||
|
cogl_renderer = cogl_display_get_renderer (cogl_display);
|
||||||
|
|
||||||
|
manager_kms->fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_kms_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (object);
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < manager_kms->n_encoders; i++)
|
||||||
|
drmModeFreeEncoder (manager_kms->encoders[i]);
|
||||||
|
for (i = 0; i < manager_kms->n_connectors; i++)
|
||||||
|
drmModeFreeConnector (manager_kms->connectors[i]);
|
||||||
|
|
||||||
|
g_free (manager_kms->encoders);
|
||||||
|
g_free (manager_kms->connectors);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (meta_monitor_manager_kms_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_kms_class_init (MetaMonitorManagerKmsClass *klass)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass);
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->finalize = meta_monitor_manager_kms_finalize;
|
||||||
|
|
||||||
|
manager_class->read_current = meta_monitor_manager_kms_read_current;
|
||||||
|
manager_class->apply_configuration = meta_monitor_manager_kms_apply_configuration;
|
||||||
|
manager_class->set_power_save_mode = meta_monitor_manager_kms_set_power_save_mode;
|
||||||
|
manager_class->get_crtc_gamma = meta_monitor_manager_kms_get_crtc_gamma;
|
||||||
|
manager_class->set_crtc_gamma = meta_monitor_manager_kms_set_crtc_gamma;
|
||||||
|
}
|
||||||
|
|
408
src/core/monitor-private.h
Normal file
408
src/core/monitor-private.h
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file screen-private.h Handling of monitor configuration
|
||||||
|
*
|
||||||
|
* Managing multiple monitors
|
||||||
|
* This file contains structures and functions that handle
|
||||||
|
* multiple monitors, including reading the current configuration
|
||||||
|
* and available hardware, and applying it.
|
||||||
|
*
|
||||||
|
* This interface is private to mutter, API users should look
|
||||||
|
* at MetaScreen instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001 Havoc Pennington
|
||||||
|
* Copyright (C) 2003 Rob Adams
|
||||||
|
* Copyright (C) 2004-2006 Elijah Newren
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef META_MONITOR_PRIVATE_H
|
||||||
|
#define META_MONITOR_PRIVATE_H
|
||||||
|
|
||||||
|
#include <cogl/cogl.h>
|
||||||
|
|
||||||
|
#include "display-private.h"
|
||||||
|
#include <meta/screen.h>
|
||||||
|
#include "stack-tracker.h"
|
||||||
|
#include "ui.h"
|
||||||
|
#ifdef HAVE_WAYLAND
|
||||||
|
#include <wayland-server.h>
|
||||||
|
#endif
|
||||||
|
#include "meta-xrandr-shared.h"
|
||||||
|
|
||||||
|
#include "meta-dbus-xrandr.h"
|
||||||
|
|
||||||
|
typedef struct _MetaMonitorManagerClass MetaMonitorManagerClass;
|
||||||
|
typedef struct _MetaMonitorManager MetaMonitorManager;
|
||||||
|
typedef struct _MetaMonitorConfigClass MetaMonitorConfigClass;
|
||||||
|
typedef struct _MetaMonitorConfig MetaMonitorConfig;
|
||||||
|
|
||||||
|
#ifndef HAVE_WAYLAND
|
||||||
|
enum wl_output_transform {
|
||||||
|
WL_OUTPUT_TRANSFORM_NORMAL,
|
||||||
|
WL_OUTPUT_TRANSFORM_90,
|
||||||
|
WL_OUTPUT_TRANSFORM_180,
|
||||||
|
WL_OUTPUT_TRANSFORM_270,
|
||||||
|
WL_OUTPUT_TRANSFORM_FLIPPED,
|
||||||
|
WL_OUTPUT_TRANSFORM_FLIPPED_90,
|
||||||
|
WL_OUTPUT_TRANSFORM_FLIPPED_180,
|
||||||
|
WL_OUTPUT_TRANSFORM_FLIPPED_270
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _MetaOutput MetaOutput;
|
||||||
|
typedef struct _MetaCRTC MetaCRTC;
|
||||||
|
typedef struct _MetaMonitorMode MetaMonitorMode;
|
||||||
|
typedef struct _MetaMonitorInfo MetaMonitorInfo;
|
||||||
|
typedef struct _MetaCRTCInfo MetaCRTCInfo;
|
||||||
|
typedef struct _MetaOutputInfo MetaOutputInfo;
|
||||||
|
|
||||||
|
struct _MetaOutput
|
||||||
|
{
|
||||||
|
/* The CRTC driving this output, NULL if the output is not enabled */
|
||||||
|
MetaCRTC *crtc;
|
||||||
|
/* The low-level ID of this output, used to apply back configuration */
|
||||||
|
glong output_id;
|
||||||
|
char *name;
|
||||||
|
char *vendor;
|
||||||
|
char *product;
|
||||||
|
char *serial;
|
||||||
|
int width_mm;
|
||||||
|
int height_mm;
|
||||||
|
CoglSubpixelOrder subpixel_order;
|
||||||
|
|
||||||
|
MetaMonitorMode *preferred_mode;
|
||||||
|
MetaMonitorMode **modes;
|
||||||
|
unsigned int n_modes;
|
||||||
|
|
||||||
|
MetaCRTC **possible_crtcs;
|
||||||
|
unsigned int n_possible_crtcs;
|
||||||
|
|
||||||
|
MetaOutput **possible_clones;
|
||||||
|
unsigned int n_possible_clones;
|
||||||
|
|
||||||
|
int backlight;
|
||||||
|
int backlight_min;
|
||||||
|
int backlight_max;
|
||||||
|
|
||||||
|
/* Used when changing configuration */
|
||||||
|
gboolean dirty;
|
||||||
|
|
||||||
|
/* The low-level bits used to build the high-level info
|
||||||
|
in MetaMonitorInfo
|
||||||
|
|
||||||
|
XXX: flags maybe?
|
||||||
|
There is a lot of code that uses MonitorInfo->is_primary,
|
||||||
|
but nobody uses MetaOutput yet
|
||||||
|
*/
|
||||||
|
gboolean is_primary;
|
||||||
|
gboolean is_presentation;
|
||||||
|
|
||||||
|
gpointer driver_private;
|
||||||
|
GDestroyNotify driver_notify;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaCRTC
|
||||||
|
{
|
||||||
|
glong crtc_id;
|
||||||
|
MetaRectangle rect;
|
||||||
|
MetaMonitorMode *current_mode;
|
||||||
|
enum wl_output_transform transform;
|
||||||
|
unsigned int all_transforms;
|
||||||
|
|
||||||
|
/* Only used to build the logical configuration
|
||||||
|
from the HW one
|
||||||
|
*/
|
||||||
|
MetaMonitorInfo *logical_monitor;
|
||||||
|
|
||||||
|
/* Used when changing configuration */
|
||||||
|
gboolean dirty;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaMonitorMode
|
||||||
|
{
|
||||||
|
/* The low-level ID of this mode, used to apply back configuration */
|
||||||
|
glong mode_id;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
float refresh_rate;
|
||||||
|
|
||||||
|
gpointer driver_private;
|
||||||
|
GDestroyNotify driver_notify;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaMonitorInfo:
|
||||||
|
*
|
||||||
|
* A structure with high-level information about monitors.
|
||||||
|
* This corresponds to a subset of the compositor coordinate space.
|
||||||
|
* Clones are only reported once, irrespective of the way
|
||||||
|
* they're implemented (two CRTCs configured for the same
|
||||||
|
* coordinates or one CRTCs driving two outputs). Inactive CRTCs
|
||||||
|
* are ignored, and so are disabled outputs.
|
||||||
|
*/
|
||||||
|
struct _MetaMonitorInfo
|
||||||
|
{
|
||||||
|
int number;
|
||||||
|
int xinerama_index;
|
||||||
|
MetaRectangle rect;
|
||||||
|
gboolean is_primary;
|
||||||
|
gboolean is_presentation; /* XXX: not yet used */
|
||||||
|
gboolean in_fullscreen;
|
||||||
|
|
||||||
|
/* The primary or first output for this monitor, 0 if we can't figure out.
|
||||||
|
This is a XID when using XRandR, otherwise a KMS id (not implemented).
|
||||||
|
In any case, it can be matched to an output_id of a MetaOutput.
|
||||||
|
|
||||||
|
This is used as an opaque token on reconfiguration when switching from
|
||||||
|
clone to extened, to decide on what output the windows should go next
|
||||||
|
(it's an attempt to keep windows on the same monitor, and preferably on
|
||||||
|
the primary one).
|
||||||
|
*/
|
||||||
|
glong output_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MetaCRTCInfo:
|
||||||
|
* This represents the writable part of a CRTC, as deserialized from DBus
|
||||||
|
* or built by MetaMonitorConfig
|
||||||
|
*
|
||||||
|
* Note: differently from the other structures in this file, MetaCRTCInfo
|
||||||
|
* is handled by pointer. This is to accomodate the usage in MetaMonitorConfig
|
||||||
|
*/
|
||||||
|
struct _MetaCRTCInfo {
|
||||||
|
MetaCRTC *crtc;
|
||||||
|
MetaMonitorMode *mode;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
enum wl_output_transform transform;
|
||||||
|
GPtrArray *outputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MetaOutputInfo:
|
||||||
|
* this is the same as MetaOutputInfo, but for CRTCs
|
||||||
|
*/
|
||||||
|
struct _MetaOutputInfo {
|
||||||
|
MetaOutput *output;
|
||||||
|
gboolean is_primary;
|
||||||
|
gboolean is_presentation;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define META_TYPE_MONITOR_MANAGER (meta_monitor_manager_get_type ())
|
||||||
|
#define META_MONITOR_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER, MetaMonitorManager))
|
||||||
|
#define META_MONITOR_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER, MetaMonitorManagerClass))
|
||||||
|
#define META_IS_MONITOR_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER))
|
||||||
|
#define META_IS_MONITOR_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER))
|
||||||
|
#define META_MONITOR_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER, MetaMonitorManagerClass))
|
||||||
|
|
||||||
|
struct _MetaMonitorManager
|
||||||
|
{
|
||||||
|
MetaDBusDisplayConfigSkeleton parent_instance;
|
||||||
|
|
||||||
|
/* XXX: this structure is very badly
|
||||||
|
packed, but I like the logical organization
|
||||||
|
of fields */
|
||||||
|
|
||||||
|
gboolean in_init;
|
||||||
|
unsigned int serial;
|
||||||
|
|
||||||
|
MetaPowerSave power_save_mode;
|
||||||
|
|
||||||
|
int max_screen_width;
|
||||||
|
int max_screen_height;
|
||||||
|
int screen_width;
|
||||||
|
int screen_height;
|
||||||
|
|
||||||
|
/* Outputs refer to physical screens,
|
||||||
|
CRTCs refer to stuff that can drive outputs
|
||||||
|
(like encoders, but less tied to the HW),
|
||||||
|
while monitor_infos refer to logical ones.
|
||||||
|
|
||||||
|
See also the comment in monitor-private.h
|
||||||
|
*/
|
||||||
|
MetaOutput *outputs;
|
||||||
|
unsigned int n_outputs;
|
||||||
|
|
||||||
|
MetaMonitorMode *modes;
|
||||||
|
unsigned int n_modes;
|
||||||
|
|
||||||
|
MetaCRTC *crtcs;
|
||||||
|
unsigned int n_crtcs;
|
||||||
|
|
||||||
|
MetaMonitorInfo *monitor_infos;
|
||||||
|
unsigned int n_monitor_infos;
|
||||||
|
int primary_monitor_index;
|
||||||
|
|
||||||
|
int dbus_name_id;
|
||||||
|
|
||||||
|
int persistent_timeout_id;
|
||||||
|
MetaMonitorConfig *config;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaMonitorManagerClass
|
||||||
|
{
|
||||||
|
MetaDBusDisplayConfigSkeletonClass parent_class;
|
||||||
|
|
||||||
|
void (*read_current) (MetaMonitorManager *);
|
||||||
|
|
||||||
|
char* (*get_edid_file) (MetaMonitorManager *,
|
||||||
|
MetaOutput *);
|
||||||
|
GBytes* (*read_edid) (MetaMonitorManager *,
|
||||||
|
MetaOutput *);
|
||||||
|
|
||||||
|
void (*apply_configuration) (MetaMonitorManager *,
|
||||||
|
MetaCRTCInfo **,
|
||||||
|
unsigned int ,
|
||||||
|
MetaOutputInfo **,
|
||||||
|
unsigned int);
|
||||||
|
|
||||||
|
void (*set_power_save_mode) (MetaMonitorManager *,
|
||||||
|
MetaPowerSave);
|
||||||
|
|
||||||
|
void (*change_backlight) (MetaMonitorManager *,
|
||||||
|
MetaOutput *,
|
||||||
|
int);
|
||||||
|
|
||||||
|
void (*get_crtc_gamma) (MetaMonitorManager *,
|
||||||
|
MetaCRTC *,
|
||||||
|
gsize *,
|
||||||
|
unsigned short **,
|
||||||
|
unsigned short **,
|
||||||
|
unsigned short **);
|
||||||
|
void (*set_crtc_gamma) (MetaMonitorManager *,
|
||||||
|
MetaCRTC *,
|
||||||
|
gsize ,
|
||||||
|
unsigned short *,
|
||||||
|
unsigned short *,
|
||||||
|
unsigned short *);
|
||||||
|
|
||||||
|
gboolean (*handle_xevent) (MetaMonitorManager *,
|
||||||
|
XEvent *);
|
||||||
|
};
|
||||||
|
|
||||||
|
GType meta_monitor_manager_get_type (void);
|
||||||
|
|
||||||
|
void meta_monitor_manager_initialize (void);
|
||||||
|
MetaMonitorManager *meta_monitor_manager_get (void);
|
||||||
|
|
||||||
|
void meta_monitor_manager_init_dbus (MetaMonitorManager *manager,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean meta_monitor_manager_init_dbus_finish (MetaMonitorManager *manager,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
void meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager);
|
||||||
|
|
||||||
|
MetaMonitorInfo *meta_monitor_manager_get_monitor_infos (MetaMonitorManager *manager,
|
||||||
|
unsigned int *n_infos);
|
||||||
|
|
||||||
|
MetaOutput *meta_monitor_manager_get_outputs (MetaMonitorManager *manager,
|
||||||
|
unsigned int *n_outputs);
|
||||||
|
|
||||||
|
void meta_monitor_manager_get_resources (MetaMonitorManager *manager,
|
||||||
|
MetaMonitorMode **modes,
|
||||||
|
unsigned int *n_modes,
|
||||||
|
MetaCRTC **crtcs,
|
||||||
|
unsigned int *n_crtcs,
|
||||||
|
MetaOutput **outputs,
|
||||||
|
unsigned int *n_outputs);
|
||||||
|
|
||||||
|
int meta_monitor_manager_get_primary_index (MetaMonitorManager *manager);
|
||||||
|
|
||||||
|
gboolean meta_monitor_manager_handle_xevent (MetaMonitorManager *manager,
|
||||||
|
XEvent *event);
|
||||||
|
|
||||||
|
void meta_monitor_manager_get_screen_size (MetaMonitorManager *manager,
|
||||||
|
int *width,
|
||||||
|
int *height);
|
||||||
|
|
||||||
|
void meta_monitor_manager_get_screen_limits (MetaMonitorManager *manager,
|
||||||
|
int *width,
|
||||||
|
int *height);
|
||||||
|
|
||||||
|
void meta_monitor_manager_apply_configuration (MetaMonitorManager *manager,
|
||||||
|
MetaCRTCInfo **crtcs,
|
||||||
|
unsigned int n_crtcs,
|
||||||
|
MetaOutputInfo **outputs,
|
||||||
|
unsigned int n_outputs);
|
||||||
|
|
||||||
|
void meta_monitor_manager_confirm_configuration (MetaMonitorManager *manager,
|
||||||
|
gboolean ok);
|
||||||
|
|
||||||
|
#define META_TYPE_MONITOR_MANAGER_XRANDR (meta_monitor_manager_xrandr_get_type ())
|
||||||
|
#define META_MONITOR_MANAGER_XRANDR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER_XRANDR, MetaMonitorManagerXrandr))
|
||||||
|
#define META_MONITOR_MANAGER_XRANDR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER_XRANDR, MetaMonitorManagerXrandrClass))
|
||||||
|
#define META_IS_MONITOR_MANAGER_XRANDR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER_XRANDR))
|
||||||
|
#define META_IS_MONITOR_MANAGER_XRANDR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER_XRANDR))
|
||||||
|
#define META_MONITOR_MANAGER_XRANDR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER_XRANDR, MetaMonitorManagerXrandrClass))
|
||||||
|
|
||||||
|
typedef struct _MetaMonitorManagerXrandrClass MetaMonitorManagerXrandrClass;
|
||||||
|
typedef struct _MetaMonitorManagerXrandr MetaMonitorManagerXrandr;
|
||||||
|
|
||||||
|
GType meta_monitor_manager_xrandr_get_type (void);
|
||||||
|
|
||||||
|
#define META_TYPE_MONITOR_MANAGER_KMS (meta_monitor_manager_kms_get_type ())
|
||||||
|
#define META_MONITOR_MANAGER_KMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER_KMS, MetaMonitorManagerKms))
|
||||||
|
#define META_MONITOR_MANAGER_KMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER_KMS, MetaMonitorManagerKmsClass))
|
||||||
|
#define META_IS_MONITOR_MANAGER_KMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER_KMS))
|
||||||
|
#define META_IS_MONITOR_MANAGER_KMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER_KMS))
|
||||||
|
#define META_MONITOR_MANAGER_KMS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER_KMS, MetaMonitorManagerKmsClass))
|
||||||
|
|
||||||
|
typedef struct _MetaMonitorManagerKmsClass MetaMonitorManagerKmsClass;
|
||||||
|
typedef struct _MetaMonitorManagerKms MetaMonitorManagerKms;
|
||||||
|
|
||||||
|
GType meta_monitor_manager_kms_get_type (void);
|
||||||
|
|
||||||
|
#define META_TYPE_MONITOR_CONFIG (meta_monitor_config_get_type ())
|
||||||
|
#define META_MONITOR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_CONFIG, MetaMonitorConfig))
|
||||||
|
#define META_MONITOR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_CONFIG, MetaMonitorConfigClass))
|
||||||
|
#define META_IS_MONITOR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_CONFIG))
|
||||||
|
#define META_IS_MONITOR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_CONFIG))
|
||||||
|
#define META_MONITOR_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_CONFIG, MetaMonitorConfigClass))
|
||||||
|
|
||||||
|
GType meta_monitor_config_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
MetaMonitorConfig *meta_monitor_config_new (void);
|
||||||
|
|
||||||
|
gboolean meta_monitor_config_match_current (MetaMonitorConfig *config,
|
||||||
|
MetaMonitorManager *manager);
|
||||||
|
|
||||||
|
gboolean meta_monitor_config_apply_stored (MetaMonitorConfig *config,
|
||||||
|
MetaMonitorManager *manager);
|
||||||
|
|
||||||
|
void meta_monitor_config_make_default (MetaMonitorConfig *config,
|
||||||
|
MetaMonitorManager *manager);
|
||||||
|
|
||||||
|
void meta_monitor_config_update_current (MetaMonitorConfig *config,
|
||||||
|
MetaMonitorManager *manager);
|
||||||
|
void meta_monitor_config_make_persistent (MetaMonitorConfig *config);
|
||||||
|
|
||||||
|
void meta_monitor_config_restore_previous (MetaMonitorConfig *config,
|
||||||
|
MetaMonitorManager *manager);
|
||||||
|
|
||||||
|
void meta_crtc_info_free (MetaCRTCInfo *info);
|
||||||
|
void meta_output_info_free (MetaOutputInfo *info);
|
||||||
|
|
||||||
|
#endif
|
846
src/core/monitor-xrandr.c
Normal file
846
src/core/monitor-xrandr.c
Normal file
@@ -0,0 +1,846 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001, 2002 Havoc Pennington
|
||||||
|
* Copyright (C) 2002, 2003 Red Hat Inc.
|
||||||
|
* Some ICCCM manager selection code derived from fvwm2,
|
||||||
|
* Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team
|
||||||
|
* Copyright (C) 2003 Rob Adams
|
||||||
|
* Copyright (C) 2004-2006 Elijah Newren
|
||||||
|
* 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 <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
#include <X11/extensions/Xrandr.h>
|
||||||
|
#include <X11/extensions/dpms.h>
|
||||||
|
|
||||||
|
#include <meta/main.h>
|
||||||
|
#include <meta/errors.h>
|
||||||
|
#include "monitor-private.h"
|
||||||
|
|
||||||
|
#define ALL_WL_TRANSFORMS ((1 << (WL_OUTPUT_TRANSFORM_FLIPPED_270 + 1)) - 1)
|
||||||
|
|
||||||
|
struct _MetaMonitorManagerXrandr
|
||||||
|
{
|
||||||
|
MetaMonitorManager parent_instance;
|
||||||
|
|
||||||
|
Display *xdisplay;
|
||||||
|
XRRScreenResources *resources;
|
||||||
|
int time;
|
||||||
|
int rr_event_base;
|
||||||
|
int rr_error_base;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaMonitorManagerXrandrClass
|
||||||
|
{
|
||||||
|
MetaMonitorManagerClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (MetaMonitorManagerXrandr, meta_monitor_manager_xrandr, META_TYPE_MONITOR_MANAGER);
|
||||||
|
|
||||||
|
static enum wl_output_transform
|
||||||
|
wl_transform_from_xrandr (Rotation rotation)
|
||||||
|
{
|
||||||
|
static const enum wl_output_transform y_reflected_map[4] = {
|
||||||
|
WL_OUTPUT_TRANSFORM_FLIPPED_180,
|
||||||
|
WL_OUTPUT_TRANSFORM_FLIPPED_90,
|
||||||
|
WL_OUTPUT_TRANSFORM_FLIPPED,
|
||||||
|
WL_OUTPUT_TRANSFORM_FLIPPED_270
|
||||||
|
};
|
||||||
|
enum wl_output_transform ret;
|
||||||
|
|
||||||
|
switch (rotation & 0x7F)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case RR_Rotate_0:
|
||||||
|
ret = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||||
|
break;
|
||||||
|
case RR_Rotate_90:
|
||||||
|
ret = WL_OUTPUT_TRANSFORM_90;
|
||||||
|
break;
|
||||||
|
case RR_Rotate_180:
|
||||||
|
ret = WL_OUTPUT_TRANSFORM_180;
|
||||||
|
break;
|
||||||
|
case RR_Rotate_270:
|
||||||
|
ret = WL_OUTPUT_TRANSFORM_270;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotation & RR_Reflect_X)
|
||||||
|
return ret + 4;
|
||||||
|
else if (rotation & RR_Reflect_Y)
|
||||||
|
return y_reflected_map[ret];
|
||||||
|
else
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ALL_ROTATIONS (RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270)
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
wl_transform_from_xrandr_all (Rotation rotation)
|
||||||
|
{
|
||||||
|
unsigned ret;
|
||||||
|
|
||||||
|
/* Handle the common cases first (none or all) */
|
||||||
|
if (rotation == 0 || rotation == RR_Rotate_0)
|
||||||
|
return (1 << WL_OUTPUT_TRANSFORM_NORMAL);
|
||||||
|
|
||||||
|
/* All rotations and one reflection -> all of them by composition */
|
||||||
|
if ((rotation & ALL_ROTATIONS) &&
|
||||||
|
((rotation & RR_Reflect_X) || (rotation & RR_Reflect_Y)))
|
||||||
|
return ALL_WL_TRANSFORMS;
|
||||||
|
|
||||||
|
ret = 1 << WL_OUTPUT_TRANSFORM_NORMAL;
|
||||||
|
if (rotation & RR_Rotate_90)
|
||||||
|
ret |= 1 << WL_OUTPUT_TRANSFORM_90;
|
||||||
|
if (rotation & RR_Rotate_180)
|
||||||
|
ret |= 1 << WL_OUTPUT_TRANSFORM_180;
|
||||||
|
if (rotation & RR_Rotate_270)
|
||||||
|
ret |= 1 << WL_OUTPUT_TRANSFORM_270;
|
||||||
|
if (rotation & (RR_Rotate_0 | RR_Reflect_X))
|
||||||
|
ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED;
|
||||||
|
if (rotation & (RR_Rotate_90 | RR_Reflect_X))
|
||||||
|
ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED_90;
|
||||||
|
if (rotation & (RR_Rotate_180 | RR_Reflect_X))
|
||||||
|
ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED_180;
|
||||||
|
if (rotation & (RR_Rotate_270 | RR_Reflect_X))
|
||||||
|
ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED_270;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
output_get_presentation_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
|
||||||
|
MetaOutput *output)
|
||||||
|
{
|
||||||
|
MetaDisplay *display = meta_get_display ();
|
||||||
|
gboolean value;
|
||||||
|
Atom actual_type;
|
||||||
|
int actual_format;
|
||||||
|
unsigned long nitems, bytes_after;
|
||||||
|
unsigned char *buffer;
|
||||||
|
|
||||||
|
XRRGetOutputProperty (manager_xrandr->xdisplay,
|
||||||
|
(XID)output->output_id,
|
||||||
|
display->atom__MUTTER_PRESENTATION_OUTPUT,
|
||||||
|
0, G_MAXLONG, False, False, XA_CARDINAL,
|
||||||
|
&actual_type, &actual_format,
|
||||||
|
&nitems, &bytes_after, &buffer);
|
||||||
|
|
||||||
|
if (actual_type != XA_CARDINAL || actual_format != 32 ||
|
||||||
|
nitems < 1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
value = ((int*)buffer)[0];
|
||||||
|
|
||||||
|
XFree (buffer);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
normalize_backlight (MetaOutput *output,
|
||||||
|
int hw_value)
|
||||||
|
{
|
||||||
|
return round((double)(hw_value - output->backlight_min) /
|
||||||
|
(output->backlight_max - output->backlight_min) * 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
output_get_backlight_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
|
||||||
|
MetaOutput *output)
|
||||||
|
{
|
||||||
|
MetaDisplay *display = meta_get_display ();
|
||||||
|
gboolean value;
|
||||||
|
Atom actual_type;
|
||||||
|
int actual_format;
|
||||||
|
unsigned long nitems, bytes_after;
|
||||||
|
unsigned char *buffer;
|
||||||
|
|
||||||
|
XRRGetOutputProperty (manager_xrandr->xdisplay,
|
||||||
|
(XID)output->output_id,
|
||||||
|
display->atom_BACKLIGHT,
|
||||||
|
0, G_MAXLONG, False, False, XA_INTEGER,
|
||||||
|
&actual_type, &actual_format,
|
||||||
|
&nitems, &bytes_after, &buffer);
|
||||||
|
|
||||||
|
if (actual_type != XA_INTEGER || actual_format != 32 ||
|
||||||
|
nitems < 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
value = ((int*)buffer)[0];
|
||||||
|
|
||||||
|
XFree (buffer);
|
||||||
|
return normalize_backlight (output, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
output_get_backlight_limits_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
|
||||||
|
MetaOutput *output)
|
||||||
|
{
|
||||||
|
MetaDisplay *display = meta_get_display ();
|
||||||
|
XRRPropertyInfo *info;
|
||||||
|
|
||||||
|
meta_error_trap_push (display);
|
||||||
|
info = XRRQueryOutputProperty (manager_xrandr->xdisplay,
|
||||||
|
(XID)output->output_id,
|
||||||
|
display->atom_BACKLIGHT);
|
||||||
|
meta_error_trap_pop (display);
|
||||||
|
|
||||||
|
if (info == NULL)
|
||||||
|
{
|
||||||
|
meta_verbose ("could not get output property for %s\n", output->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info->range || info->num_values != 2)
|
||||||
|
{
|
||||||
|
meta_verbose ("backlight %s was not range\n", output->name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
output->backlight_min = info->values[0];
|
||||||
|
output->backlight_max = info->values[1];
|
||||||
|
|
||||||
|
out:
|
||||||
|
XFree (info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
compare_outputs (const void *one,
|
||||||
|
const void *two)
|
||||||
|
{
|
||||||
|
const MetaOutput *o_one = one, *o_two = two;
|
||||||
|
|
||||||
|
return strcmp (o_one->name, o_two->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_xrandr_read_current (MetaMonitorManager *manager)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
|
||||||
|
XRRScreenResources *resources;
|
||||||
|
RROutput primary_output;
|
||||||
|
unsigned int i, j, k;
|
||||||
|
unsigned int n_actual_outputs;
|
||||||
|
int min_width, min_height;
|
||||||
|
Screen *screen;
|
||||||
|
BOOL dpms_capable, dpms_enabled;
|
||||||
|
CARD16 dpms_state;
|
||||||
|
|
||||||
|
if (manager_xrandr->resources)
|
||||||
|
XRRFreeScreenResources (manager_xrandr->resources);
|
||||||
|
manager_xrandr->resources = NULL;
|
||||||
|
|
||||||
|
meta_error_trap_push (meta_get_display ());
|
||||||
|
dpms_capable = DPMSCapable (manager_xrandr->xdisplay);
|
||||||
|
meta_error_trap_pop (meta_get_display ());
|
||||||
|
|
||||||
|
if (dpms_capable &&
|
||||||
|
DPMSInfo (manager_xrandr->xdisplay, &dpms_state, &dpms_enabled) &&
|
||||||
|
dpms_enabled)
|
||||||
|
{
|
||||||
|
switch (dpms_state)
|
||||||
|
{
|
||||||
|
case DPMSModeOn:
|
||||||
|
manager->power_save_mode = META_POWER_SAVE_ON;
|
||||||
|
case DPMSModeStandby:
|
||||||
|
manager->power_save_mode = META_POWER_SAVE_STANDBY;
|
||||||
|
case DPMSModeSuspend:
|
||||||
|
manager->power_save_mode = META_POWER_SAVE_SUSPEND;
|
||||||
|
case DPMSModeOff:
|
||||||
|
manager->power_save_mode = META_POWER_SAVE_OFF;
|
||||||
|
default:
|
||||||
|
manager->power_save_mode = META_POWER_SAVE_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
manager->power_save_mode = META_POWER_SAVE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
XRRGetScreenSizeRange (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay),
|
||||||
|
&min_width,
|
||||||
|
&min_height,
|
||||||
|
&manager->max_screen_width,
|
||||||
|
&manager->max_screen_height);
|
||||||
|
|
||||||
|
screen = ScreenOfDisplay (manager_xrandr->xdisplay,
|
||||||
|
DefaultScreen (manager_xrandr->xdisplay));
|
||||||
|
/* This is updated because we called RRUpdateConfiguration below */
|
||||||
|
manager->screen_width = WidthOfScreen (screen);
|
||||||
|
manager->screen_height = HeightOfScreen (screen);
|
||||||
|
|
||||||
|
resources = XRRGetScreenResourcesCurrent (manager_xrandr->xdisplay,
|
||||||
|
DefaultRootWindow (manager_xrandr->xdisplay));
|
||||||
|
if (!resources)
|
||||||
|
return;
|
||||||
|
|
||||||
|
manager_xrandr->resources = resources;
|
||||||
|
manager_xrandr->time = resources->configTimestamp;
|
||||||
|
manager->n_outputs = resources->noutput;
|
||||||
|
manager->n_crtcs = resources->ncrtc;
|
||||||
|
manager->n_modes = resources->nmode;
|
||||||
|
manager->outputs = g_new0 (MetaOutput, manager->n_outputs);
|
||||||
|
manager->modes = g_new0 (MetaMonitorMode, manager->n_modes);
|
||||||
|
manager->crtcs = g_new0 (MetaCRTC, manager->n_crtcs);
|
||||||
|
|
||||||
|
for (i = 0; i < (unsigned)resources->nmode; i++)
|
||||||
|
{
|
||||||
|
XRRModeInfo *xmode = &resources->modes[i];
|
||||||
|
MetaMonitorMode *mode;
|
||||||
|
|
||||||
|
mode = &manager->modes[i];
|
||||||
|
|
||||||
|
mode->mode_id = xmode->id;
|
||||||
|
mode->width = xmode->width;
|
||||||
|
mode->height = xmode->height;
|
||||||
|
mode->refresh_rate = (xmode->dotClock /
|
||||||
|
((float)xmode->hTotal * xmode->vTotal));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < (unsigned)resources->ncrtc; i++)
|
||||||
|
{
|
||||||
|
XRRCrtcInfo *crtc;
|
||||||
|
MetaCRTC *meta_crtc;
|
||||||
|
|
||||||
|
crtc = XRRGetCrtcInfo (manager_xrandr->xdisplay, resources, resources->crtcs[i]);
|
||||||
|
|
||||||
|
meta_crtc = &manager->crtcs[i];
|
||||||
|
|
||||||
|
meta_crtc->crtc_id = resources->crtcs[i];
|
||||||
|
meta_crtc->rect.x = crtc->x;
|
||||||
|
meta_crtc->rect.y = crtc->y;
|
||||||
|
meta_crtc->rect.width = crtc->width;
|
||||||
|
meta_crtc->rect.height = crtc->height;
|
||||||
|
meta_crtc->dirty = FALSE;
|
||||||
|
meta_crtc->transform = wl_transform_from_xrandr (crtc->rotation);
|
||||||
|
meta_crtc->all_transforms = wl_transform_from_xrandr_all (crtc->rotations);
|
||||||
|
|
||||||
|
for (j = 0; j < (unsigned)resources->nmode; j++)
|
||||||
|
{
|
||||||
|
if (resources->modes[j].id == crtc->mode)
|
||||||
|
{
|
||||||
|
meta_crtc->current_mode = &manager->modes[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XRRFreeCrtcInfo (crtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
primary_output = XRRGetOutputPrimary (manager_xrandr->xdisplay,
|
||||||
|
DefaultRootWindow (manager_xrandr->xdisplay));
|
||||||
|
|
||||||
|
n_actual_outputs = 0;
|
||||||
|
for (i = 0; i < (unsigned)resources->noutput; i++)
|
||||||
|
{
|
||||||
|
XRROutputInfo *output;
|
||||||
|
MetaOutput *meta_output;
|
||||||
|
|
||||||
|
output = XRRGetOutputInfo (manager_xrandr->xdisplay, resources, resources->outputs[i]);
|
||||||
|
|
||||||
|
meta_output = &manager->outputs[n_actual_outputs];
|
||||||
|
|
||||||
|
if (output->connection != RR_Disconnected)
|
||||||
|
{
|
||||||
|
meta_output->output_id = resources->outputs[i];
|
||||||
|
meta_output->name = g_strdup (output->name);
|
||||||
|
meta_output->vendor = g_strdup ("unknown");
|
||||||
|
meta_output->product = g_strdup ("unknown");
|
||||||
|
meta_output->serial = g_strdup ("");
|
||||||
|
meta_output->width_mm = output->mm_width;
|
||||||
|
meta_output->height_mm = output->mm_height;
|
||||||
|
meta_output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
|
||||||
|
|
||||||
|
meta_output->n_modes = output->nmode;
|
||||||
|
meta_output->modes = g_new0 (MetaMonitorMode *, meta_output->n_modes);
|
||||||
|
for (j = 0; j < meta_output->n_modes; j++)
|
||||||
|
{
|
||||||
|
for (k = 0; k < manager->n_modes; k++)
|
||||||
|
{
|
||||||
|
if (output->modes[j] == (XID)manager->modes[k].mode_id)
|
||||||
|
{
|
||||||
|
meta_output->modes[j] = &manager->modes[k];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
meta_output->preferred_mode = meta_output->modes[0];
|
||||||
|
|
||||||
|
meta_output->n_possible_crtcs = output->ncrtc;
|
||||||
|
meta_output->possible_crtcs = g_new0 (MetaCRTC *, meta_output->n_possible_crtcs);
|
||||||
|
for (j = 0; j < (unsigned)output->ncrtc; j++)
|
||||||
|
{
|
||||||
|
for (k = 0; k < manager->n_crtcs; k++)
|
||||||
|
{
|
||||||
|
if ((XID)manager->crtcs[k].crtc_id == output->crtcs[j])
|
||||||
|
{
|
||||||
|
meta_output->possible_crtcs[j] = &manager->crtcs[k];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_output->crtc = NULL;
|
||||||
|
for (j = 0; j < manager->n_crtcs; j++)
|
||||||
|
{
|
||||||
|
if ((XID)manager->crtcs[j].crtc_id == output->crtc)
|
||||||
|
{
|
||||||
|
meta_output->crtc = &manager->crtcs[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_output->n_possible_clones = output->nclone;
|
||||||
|
meta_output->possible_clones = g_new0 (MetaOutput *, meta_output->n_possible_clones);
|
||||||
|
/* We can build the list of clones now, because we don't have the list of outputs
|
||||||
|
yet, so temporarily set the pointers to the bare XIDs, and then we'll fix them
|
||||||
|
in a second pass
|
||||||
|
*/
|
||||||
|
for (j = 0; j < (unsigned)output->nclone; j++)
|
||||||
|
{
|
||||||
|
meta_output->possible_clones = GINT_TO_POINTER (output->clones[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_output->is_primary = ((XID)meta_output->output_id == primary_output);
|
||||||
|
meta_output->is_presentation = output_get_presentation_xrandr (manager_xrandr, meta_output);
|
||||||
|
output_get_backlight_limits_xrandr (manager_xrandr, meta_output);
|
||||||
|
|
||||||
|
if (!(meta_output->backlight_min == 0 && meta_output->backlight_max == 0))
|
||||||
|
meta_output->backlight = output_get_backlight_xrandr (manager_xrandr, meta_output);
|
||||||
|
else
|
||||||
|
meta_output->backlight = -1;
|
||||||
|
|
||||||
|
n_actual_outputs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
XRRFreeOutputInfo (output);
|
||||||
|
}
|
||||||
|
|
||||||
|
manager->n_outputs = n_actual_outputs;
|
||||||
|
|
||||||
|
/* Sort the outputs for easier handling in MetaMonitorConfig */
|
||||||
|
qsort (manager->outputs, manager->n_outputs, sizeof (MetaOutput), compare_outputs);
|
||||||
|
|
||||||
|
/* Now fix the clones */
|
||||||
|
for (i = 0; i < manager->n_outputs; i++)
|
||||||
|
{
|
||||||
|
MetaOutput *meta_output;
|
||||||
|
|
||||||
|
meta_output = &manager->outputs[i];
|
||||||
|
|
||||||
|
for (j = 0; j < meta_output->n_possible_clones; j++)
|
||||||
|
{
|
||||||
|
RROutput clone = GPOINTER_TO_INT (meta_output->possible_clones[j]);
|
||||||
|
|
||||||
|
for (k = 0; k < manager->n_outputs; k++)
|
||||||
|
{
|
||||||
|
if (clone == (XID)manager->outputs[k].output_id)
|
||||||
|
{
|
||||||
|
meta_output->possible_clones[j] = &manager->outputs[k];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint8 *
|
||||||
|
get_edid_property (Display *dpy,
|
||||||
|
RROutput output,
|
||||||
|
Atom atom,
|
||||||
|
gsize *len)
|
||||||
|
{
|
||||||
|
unsigned char *prop;
|
||||||
|
int actual_format;
|
||||||
|
unsigned long nitems, bytes_after;
|
||||||
|
Atom actual_type;
|
||||||
|
guint8 *result;
|
||||||
|
|
||||||
|
XRRGetOutputProperty (dpy, output, atom,
|
||||||
|
0, 100, False, False,
|
||||||
|
AnyPropertyType,
|
||||||
|
&actual_type, &actual_format,
|
||||||
|
&nitems, &bytes_after, &prop);
|
||||||
|
|
||||||
|
if (actual_type == XA_INTEGER && actual_format == 8)
|
||||||
|
{
|
||||||
|
result = g_memdup (prop, nitems);
|
||||||
|
if (len)
|
||||||
|
*len = nitems;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
XFree (prop);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GBytes *
|
||||||
|
meta_monitor_manager_xrandr_read_edid (MetaMonitorManager *manager,
|
||||||
|
MetaOutput *output)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
|
||||||
|
Atom edid_atom;
|
||||||
|
guint8 *result;
|
||||||
|
gsize len;
|
||||||
|
|
||||||
|
edid_atom = XInternAtom (manager_xrandr->xdisplay, "EDID", FALSE);
|
||||||
|
result = get_edid_property (manager_xrandr->xdisplay, output->output_id, edid_atom, &len);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
edid_atom = XInternAtom (manager_xrandr->xdisplay, "EDID_DATA", FALSE);
|
||||||
|
result = get_edid_property (manager_xrandr->xdisplay, output->output_id, edid_atom, &len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
edid_atom = XInternAtom (manager_xrandr->xdisplay, "XFree86_DDC_EDID1_RAWDATA", FALSE);
|
||||||
|
result = get_edid_property (manager_xrandr->xdisplay, output->output_id, edid_atom, &len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
if (len > 0 && len % 128 == 0)
|
||||||
|
return g_bytes_new_take (result, len);
|
||||||
|
else
|
||||||
|
g_free (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_xrandr_set_power_save_mode (MetaMonitorManager *manager,
|
||||||
|
MetaPowerSave mode)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
|
||||||
|
CARD16 state;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case META_POWER_SAVE_ON:
|
||||||
|
state = DPMSModeOn;
|
||||||
|
break;
|
||||||
|
case META_POWER_SAVE_STANDBY:
|
||||||
|
state = DPMSModeStandby;
|
||||||
|
break;
|
||||||
|
case META_POWER_SAVE_SUSPEND:
|
||||||
|
state = DPMSModeSuspend;
|
||||||
|
break;
|
||||||
|
case META_POWER_SAVE_OFF:
|
||||||
|
state = DPMSModeOff;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_error_trap_push (meta_get_display ());
|
||||||
|
DPMSForceLevel (manager_xrandr->xdisplay, state);
|
||||||
|
DPMSSetTimeouts (manager_xrandr->xdisplay, 0, 0, 0);
|
||||||
|
meta_error_trap_pop (meta_get_display ());
|
||||||
|
}
|
||||||
|
|
||||||
|
static Rotation
|
||||||
|
wl_transform_to_xrandr (enum wl_output_transform transform)
|
||||||
|
{
|
||||||
|
switch (transform)
|
||||||
|
{
|
||||||
|
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||||
|
return RR_Rotate_0;
|
||||||
|
case WL_OUTPUT_TRANSFORM_90:
|
||||||
|
return RR_Rotate_90;
|
||||||
|
case WL_OUTPUT_TRANSFORM_180:
|
||||||
|
return RR_Rotate_180;
|
||||||
|
case WL_OUTPUT_TRANSFORM_270:
|
||||||
|
return RR_Rotate_270;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED:
|
||||||
|
return RR_Reflect_X | RR_Rotate_0;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||||
|
return RR_Reflect_X | RR_Rotate_90;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||||
|
return RR_Reflect_X | RR_Rotate_180;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||||
|
return RR_Reflect_X | RR_Rotate_270;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
output_set_presentation_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
|
||||||
|
MetaOutput *output,
|
||||||
|
gboolean presentation)
|
||||||
|
{
|
||||||
|
MetaDisplay *display = meta_get_display ();
|
||||||
|
int value = presentation;
|
||||||
|
|
||||||
|
XRRChangeOutputProperty (manager_xrandr->xdisplay,
|
||||||
|
(XID)output->output_id,
|
||||||
|
display->atom__MUTTER_PRESENTATION_OUTPUT,
|
||||||
|
XA_CARDINAL, 32, PropModeReplace,
|
||||||
|
(unsigned char*) &value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_xrandr_apply_configuration (MetaMonitorManager *manager,
|
||||||
|
MetaCRTCInfo **crtcs,
|
||||||
|
unsigned int n_crtcs,
|
||||||
|
MetaOutputInfo **outputs,
|
||||||
|
unsigned int n_outputs)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
meta_display_grab (meta_get_display ());
|
||||||
|
|
||||||
|
for (i = 0; i < n_crtcs; i++)
|
||||||
|
{
|
||||||
|
MetaCRTCInfo *crtc_info = crtcs[i];
|
||||||
|
MetaCRTC *crtc = crtc_info->crtc;
|
||||||
|
crtc->dirty = TRUE;
|
||||||
|
|
||||||
|
if (crtc_info->mode == NULL)
|
||||||
|
{
|
||||||
|
XRRSetCrtcConfig (manager_xrandr->xdisplay,
|
||||||
|
manager_xrandr->resources,
|
||||||
|
(XID)crtc->crtc_id,
|
||||||
|
manager_xrandr->time,
|
||||||
|
0, 0,
|
||||||
|
None,
|
||||||
|
RR_Rotate_0,
|
||||||
|
NULL, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MetaMonitorMode *mode;
|
||||||
|
XID *outputs;
|
||||||
|
int j, n_outputs;
|
||||||
|
Status ok;
|
||||||
|
|
||||||
|
mode = crtc_info->mode;
|
||||||
|
|
||||||
|
n_outputs = crtc_info->outputs->len;
|
||||||
|
outputs = g_new (XID, n_outputs);
|
||||||
|
|
||||||
|
for (j = 0; j < n_outputs; j++)
|
||||||
|
outputs[j] = ((MetaOutput**)crtc_info->outputs->pdata)[j]->output_id;
|
||||||
|
|
||||||
|
meta_error_trap_push (meta_get_display ());
|
||||||
|
ok = XRRSetCrtcConfig (manager_xrandr->xdisplay,
|
||||||
|
manager_xrandr->resources,
|
||||||
|
(XID)crtc->crtc_id,
|
||||||
|
manager_xrandr->time,
|
||||||
|
crtc_info->x, crtc_info->y,
|
||||||
|
(XID)mode->mode_id,
|
||||||
|
wl_transform_to_xrandr (crtc_info->transform),
|
||||||
|
outputs, n_outputs);
|
||||||
|
meta_error_trap_pop (meta_get_display ());
|
||||||
|
|
||||||
|
if (ok != Success)
|
||||||
|
meta_warning ("Configuring CRTC %d with mode %d (%d x %d @ %f) at position %d, %d and transfrom %u failed\n",
|
||||||
|
(unsigned)(crtc - manager->crtcs), (unsigned)(mode - manager->modes),
|
||||||
|
mode->width, mode->height, (float)mode->refresh_rate,
|
||||||
|
crtc_info->x, crtc_info->y, crtc_info->transform);
|
||||||
|
|
||||||
|
g_free (outputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < n_outputs; i++)
|
||||||
|
{
|
||||||
|
MetaOutputInfo *output_info = outputs[i];
|
||||||
|
|
||||||
|
if (output_info->is_primary)
|
||||||
|
{
|
||||||
|
XRRSetOutputPrimary (manager_xrandr->xdisplay,
|
||||||
|
DefaultRootWindow (manager_xrandr->xdisplay),
|
||||||
|
(XID)output_info->output->output_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
output_set_presentation_xrandr (manager_xrandr,
|
||||||
|
output_info->output,
|
||||||
|
output_info->is_presentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable CRTCs not mentioned in the list */
|
||||||
|
for (i = 0; i < manager->n_crtcs; i++)
|
||||||
|
{
|
||||||
|
MetaCRTC *crtc = &manager->crtcs[i];
|
||||||
|
|
||||||
|
if (crtc->dirty)
|
||||||
|
{
|
||||||
|
crtc->dirty = FALSE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (crtc->current_mode == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
XRRSetCrtcConfig (manager_xrandr->xdisplay,
|
||||||
|
manager_xrandr->resources,
|
||||||
|
(XID)crtc->crtc_id,
|
||||||
|
manager_xrandr->time,
|
||||||
|
0, 0,
|
||||||
|
None,
|
||||||
|
RR_Rotate_0,
|
||||||
|
NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_display_ungrab (meta_get_display ());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_xrandr_change_backlight (MetaMonitorManager *manager,
|
||||||
|
MetaOutput *output,
|
||||||
|
gint value)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
|
||||||
|
MetaDisplay *display = meta_get_display ();
|
||||||
|
int hw_value;
|
||||||
|
|
||||||
|
hw_value = round((double)value / 100.0 * output->backlight_max + output->backlight_min);
|
||||||
|
|
||||||
|
meta_error_trap_push (display);
|
||||||
|
XRRChangeOutputProperty (manager_xrandr->xdisplay,
|
||||||
|
(XID)output->output_id,
|
||||||
|
display->atom_BACKLIGHT,
|
||||||
|
XA_INTEGER, 32, PropModeReplace,
|
||||||
|
(unsigned char *) &hw_value, 1);
|
||||||
|
meta_error_trap_pop (display);
|
||||||
|
|
||||||
|
/* We're not selecting for property notifies, so update the value immediately */
|
||||||
|
output->backlight = normalize_backlight (output, hw_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_xrandr_get_crtc_gamma (MetaMonitorManager *manager,
|
||||||
|
MetaCRTC *crtc,
|
||||||
|
gsize *size,
|
||||||
|
unsigned short **red,
|
||||||
|
unsigned short **green,
|
||||||
|
unsigned short **blue)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
|
||||||
|
XRRCrtcGamma *gamma;
|
||||||
|
|
||||||
|
gamma = XRRGetCrtcGamma (manager_xrandr->xdisplay, (XID)crtc->crtc_id);
|
||||||
|
|
||||||
|
*size = gamma->size;
|
||||||
|
*red = g_memdup (gamma->red, sizeof (unsigned short) * gamma->size);
|
||||||
|
*green = g_memdup (gamma->green, sizeof (unsigned short) * gamma->size);
|
||||||
|
*blue = g_memdup (gamma->blue, sizeof (unsigned short) * gamma->size);
|
||||||
|
|
||||||
|
XRRFreeGamma (gamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_xrandr_set_crtc_gamma (MetaMonitorManager *manager,
|
||||||
|
MetaCRTC *crtc,
|
||||||
|
gsize size,
|
||||||
|
unsigned short *red,
|
||||||
|
unsigned short *green,
|
||||||
|
unsigned short *blue)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
|
||||||
|
XRRCrtcGamma gamma;
|
||||||
|
|
||||||
|
gamma.size = size;
|
||||||
|
gamma.red = red;
|
||||||
|
gamma.green = green;
|
||||||
|
gamma.blue = blue;
|
||||||
|
|
||||||
|
XRRSetCrtcGamma (manager_xrandr->xdisplay, (XID)crtc->crtc_id, &gamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManager *manager,
|
||||||
|
XEvent *event)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
|
||||||
|
|
||||||
|
if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
XRRUpdateConfiguration (event);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_xrandr_init (MetaMonitorManagerXrandr *manager_xrandr)
|
||||||
|
{
|
||||||
|
MetaDisplay *display = meta_get_display ();
|
||||||
|
|
||||||
|
manager_xrandr->xdisplay = display->xdisplay;
|
||||||
|
|
||||||
|
if (!XRRQueryExtension (manager_xrandr->xdisplay,
|
||||||
|
&manager_xrandr->rr_event_base,
|
||||||
|
&manager_xrandr->rr_error_base))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We only use ScreenChangeNotify, but GDK uses the others,
|
||||||
|
and we don't want to step on its toes */
|
||||||
|
XRRSelectInput (manager_xrandr->xdisplay,
|
||||||
|
DefaultRootWindow (manager_xrandr->xdisplay),
|
||||||
|
RRScreenChangeNotifyMask
|
||||||
|
| RRCrtcChangeNotifyMask
|
||||||
|
| RROutputPropertyNotifyMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_xrandr_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (object);
|
||||||
|
|
||||||
|
if (manager_xrandr->resources)
|
||||||
|
XRRFreeScreenResources (manager_xrandr->resources);
|
||||||
|
manager_xrandr->resources = NULL;
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (meta_monitor_manager_xrandr_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_monitor_manager_xrandr_class_init (MetaMonitorManagerXrandrClass *klass)
|
||||||
|
{
|
||||||
|
MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass);
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->finalize = meta_monitor_manager_xrandr_finalize;
|
||||||
|
|
||||||
|
manager_class->read_current = meta_monitor_manager_xrandr_read_current;
|
||||||
|
manager_class->read_edid = meta_monitor_manager_xrandr_read_edid;
|
||||||
|
manager_class->apply_configuration = meta_monitor_manager_xrandr_apply_configuration;
|
||||||
|
manager_class->set_power_save_mode = meta_monitor_manager_xrandr_set_power_save_mode;
|
||||||
|
manager_class->change_backlight = meta_monitor_manager_xrandr_change_backlight;
|
||||||
|
manager_class->get_crtc_gamma = meta_monitor_manager_xrandr_get_crtc_gamma;
|
||||||
|
manager_class->set_crtc_gamma = meta_monitor_manager_xrandr_set_crtc_gamma;
|
||||||
|
manager_class->handle_xevent = meta_monitor_manager_xrandr_handle_xevent;
|
||||||
|
}
|
||||||
|
|
1554
src/core/monitor.c
Normal file
1554
src/core/monitor.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -38,17 +38,7 @@
|
|||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
#include "stack-tracker.h"
|
#include "stack-tracker.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
#include "monitor-private.h"
|
||||||
typedef struct _MetaMonitorInfo MetaMonitorInfo;
|
|
||||||
|
|
||||||
struct _MetaMonitorInfo
|
|
||||||
{
|
|
||||||
int number;
|
|
||||||
MetaRectangle rect;
|
|
||||||
gboolean is_primary;
|
|
||||||
gboolean in_fullscreen;
|
|
||||||
XID output; /* The primary or first output for this crtc, None if no xrandr */
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void (* MetaScreenWindowFunc) (MetaScreen *screen, MetaWindow *window,
|
typedef void (* MetaScreenWindowFunc) (MetaScreen *screen, MetaWindow *window,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
@@ -93,6 +83,7 @@ struct _MetaScreen
|
|||||||
MetaStack *stack;
|
MetaStack *stack;
|
||||||
MetaStackTracker *stack_tracker;
|
MetaStackTracker *stack_tracker;
|
||||||
|
|
||||||
|
MetaCursorTracker *cursor_tracker;
|
||||||
MetaCursor current_cursor;
|
MetaCursor current_cursor;
|
||||||
|
|
||||||
Window flash_window;
|
Window flash_window;
|
||||||
@@ -100,10 +91,11 @@ struct _MetaScreen
|
|||||||
Window wm_sn_selection_window;
|
Window wm_sn_selection_window;
|
||||||
Atom wm_sn_atom;
|
Atom wm_sn_atom;
|
||||||
guint32 wm_sn_timestamp;
|
guint32 wm_sn_timestamp;
|
||||||
|
|
||||||
MetaMonitorInfo *monitor_infos;
|
MetaMonitorInfo *monitor_infos;
|
||||||
int primary_monitor_index;
|
|
||||||
int n_monitor_infos;
|
int n_monitor_infos;
|
||||||
|
int primary_monitor_index;
|
||||||
|
gboolean has_xinerama_indices;
|
||||||
|
|
||||||
/* Cache the current monitor */
|
/* Cache the current monitor */
|
||||||
int last_monitor_index;
|
int last_monitor_index;
|
||||||
@@ -231,10 +223,6 @@ void meta_screen_calc_workspace_layout (MetaScreen *screen,
|
|||||||
MetaWorkspaceLayout *layout);
|
MetaWorkspaceLayout *layout);
|
||||||
void meta_screen_free_workspace_layout (MetaWorkspaceLayout *layout);
|
void meta_screen_free_workspace_layout (MetaWorkspaceLayout *layout);
|
||||||
|
|
||||||
void meta_screen_resize (MetaScreen *screen,
|
|
||||||
int width,
|
|
||||||
int height);
|
|
||||||
|
|
||||||
void meta_screen_minimize_all_on_active_workspace_except (MetaScreen *screen,
|
void meta_screen_minimize_all_on_active_workspace_except (MetaScreen *screen,
|
||||||
MetaWindow *keep);
|
MetaWindow *keep);
|
||||||
|
|
||||||
@@ -259,4 +247,12 @@ void meta_screen_set_active_workspace_hint (MetaScreen *screen);
|
|||||||
|
|
||||||
Window meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen);
|
Window meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen);
|
||||||
|
|
||||||
|
int meta_screen_xinerama_index_to_monitor_index (MetaScreen *screen,
|
||||||
|
int index);
|
||||||
|
int meta_screen_monitor_index_to_xinerama_index (MetaScreen *screen,
|
||||||
|
int index);
|
||||||
|
|
||||||
|
gboolean meta_screen_handle_xevent (MetaScreen *screen,
|
||||||
|
XEvent *xevent);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -48,13 +48,11 @@
|
|||||||
#ifdef HAVE_WAYLAND
|
#ifdef HAVE_WAYLAND
|
||||||
#include "meta-wayland-private.h"
|
#include "meta-wayland-private.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "meta-cursor-tracker-private.h"
|
||||||
|
#include "meta-idle-monitor-private.h"
|
||||||
|
|
||||||
#include <X11/extensions/Xinerama.h>
|
#include <X11/extensions/Xinerama.h>
|
||||||
|
|
||||||
#ifdef HAVE_RANDR
|
|
||||||
#include <X11/extensions/Xrandr.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -79,6 +77,9 @@ static void meta_screen_sn_event (SnMonitorEvent *event,
|
|||||||
void *user_data);
|
void *user_data);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void on_monitors_changed (MetaMonitorManager *manager,
|
||||||
|
MetaScreen *screen);
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_N_WORKSPACES = 1,
|
PROP_N_WORKSPACES = 1,
|
||||||
@@ -353,250 +354,93 @@ set_wm_icon_size_hint (MetaScreen *screen)
|
|||||||
#undef N_VALS
|
#undef N_VALS
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The list of monitors reported by the windowing system might include
|
|
||||||
* mirrored monitors with identical bounds. Since mirrored monitors
|
|
||||||
* shouldn't be treated as separate monitors for most purposes, we
|
|
||||||
* filter them out here. (We ignore the possibility of partially
|
|
||||||
* overlapping monitors because they are rare and it's hard to come
|
|
||||||
* up with any sensible interpretation.)
|
|
||||||
*/
|
|
||||||
static void
|
static void
|
||||||
filter_mirrored_monitors (MetaScreen *screen)
|
meta_screen_ensure_xinerama_indices (MetaScreen *screen)
|
||||||
{
|
{
|
||||||
int i, j;
|
XineramaScreenInfo *infos;
|
||||||
|
int n_infos, i, j;
|
||||||
|
|
||||||
/* Currently always true and simplifies things */
|
if (screen->has_xinerama_indices)
|
||||||
g_assert (screen->primary_monitor_index == 0);
|
return;
|
||||||
|
|
||||||
for (i = 1; i < screen->n_monitor_infos; i++)
|
screen->has_xinerama_indices = TRUE;
|
||||||
|
|
||||||
|
if (!XineramaIsActive (screen->display->xdisplay))
|
||||||
|
return;
|
||||||
|
|
||||||
|
infos = XineramaQueryScreens (screen->display->xdisplay, &n_infos);
|
||||||
|
if (n_infos <= 0 || infos == NULL)
|
||||||
{
|
{
|
||||||
/* In case we've filtered previous monitors */
|
meta_XFree (infos);
|
||||||
screen->monitor_infos[i].number = i;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 0; j < i; j++)
|
for (i = 0; i < screen->n_monitor_infos; ++i)
|
||||||
|
{
|
||||||
|
for (j = 0; j < n_infos; ++j)
|
||||||
{
|
{
|
||||||
if (meta_rectangle_equal (&screen->monitor_infos[i].rect,
|
if (screen->monitor_infos[i].rect.x == infos[j].x_org &&
|
||||||
&screen->monitor_infos[j].rect))
|
screen->monitor_infos[i].rect.y == infos[j].y_org &&
|
||||||
{
|
screen->monitor_infos[i].rect.width == infos[j].width &&
|
||||||
memmove (&screen->monitor_infos[i],
|
screen->monitor_infos[i].rect.height == infos[j].height)
|
||||||
&screen->monitor_infos[i + 1],
|
screen->monitor_infos[i].xinerama_index = j;
|
||||||
(screen->n_monitor_infos - i - 1) * sizeof (MetaMonitorInfo));
|
|
||||||
screen->n_monitor_infos--;
|
|
||||||
i--;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meta_XFree (infos);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_RANDR
|
int
|
||||||
static MetaMonitorInfo *
|
meta_screen_monitor_index_to_xinerama_index (MetaScreen *screen,
|
||||||
find_monitor_with_rect (MetaScreen *screen, int x, int y, int w, int h)
|
int index)
|
||||||
|
{
|
||||||
|
meta_screen_ensure_xinerama_indices (screen);
|
||||||
|
|
||||||
|
return screen->monitor_infos[index].xinerama_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
meta_screen_xinerama_index_to_monitor_index (MetaScreen *screen,
|
||||||
|
int index)
|
||||||
{
|
{
|
||||||
MetaMonitorInfo *info;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
meta_screen_ensure_xinerama_indices (screen);
|
||||||
|
|
||||||
for (i = 0; i < screen->n_monitor_infos; i++)
|
for (i = 0; i < screen->n_monitor_infos; i++)
|
||||||
{
|
if (screen->monitor_infos[i].xinerama_index == index)
|
||||||
info = &screen->monitor_infos[i];
|
return i;
|
||||||
if (x == info->rect.x &&
|
|
||||||
y == info->rect.y &&
|
return -1;
|
||||||
w == info->rect.width &&
|
|
||||||
h == info->rect.height)
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In the case of multiple outputs of a single crtc (mirroring), we consider one of the
|
|
||||||
* outputs the "main". This is the one we consider "owning" the windows, so if
|
|
||||||
* the mirroring is changed to a dual monitor setup then the windows are moved to the
|
|
||||||
* crtc that now has that main output. If one of the outputs is the primary that is
|
|
||||||
* always the main, otherwise we just use the first.
|
|
||||||
*/
|
|
||||||
static XID
|
|
||||||
find_main_output_for_crtc (MetaScreen *screen, XRRScreenResources *resources, XRRCrtcInfo *crtc)
|
|
||||||
{
|
|
||||||
XRROutputInfo *output;
|
|
||||||
RROutput primary_output;
|
|
||||||
int i;
|
|
||||||
XID res;
|
|
||||||
|
|
||||||
primary_output = XRRGetOutputPrimary (screen->display->xdisplay, screen->xroot);
|
|
||||||
|
|
||||||
res = None;
|
|
||||||
for (i = 0; i < crtc->noutput; i++)
|
|
||||||
{
|
|
||||||
output = XRRGetOutputInfo (screen->display->xdisplay, resources, crtc->outputs[i]);
|
|
||||||
if (output->connection != RR_Disconnected &&
|
|
||||||
(res == None || crtc->outputs[i] == primary_output))
|
|
||||||
res = crtc->outputs[i];
|
|
||||||
XRRFreeOutputInfo (output);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
reload_monitor_infos (MetaScreen *screen)
|
reload_monitor_infos (MetaScreen *screen)
|
||||||
{
|
{
|
||||||
MetaDisplay *display;
|
GList *tmp;
|
||||||
|
MetaMonitorManager *manager;
|
||||||
|
|
||||||
{
|
tmp = screen->workspaces;
|
||||||
GList *tmp;
|
while (tmp != NULL)
|
||||||
|
{
|
||||||
|
MetaWorkspace *space = tmp->data;
|
||||||
|
|
||||||
tmp = screen->workspaces;
|
meta_workspace_invalidate_work_area (space);
|
||||||
while (tmp != NULL)
|
|
||||||
{
|
tmp = tmp->next;
|
||||||
MetaWorkspace *space = tmp->data;
|
}
|
||||||
|
|
||||||
meta_workspace_invalidate_work_area (space);
|
/* Any previous screen->monitor_infos or screen->outputs is freed by the caller */
|
||||||
|
|
||||||
tmp = tmp->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
display = screen->display;
|
|
||||||
|
|
||||||
/* Any previous screen->monitor_infos is freed by the caller */
|
|
||||||
|
|
||||||
screen->monitor_infos = NULL;
|
|
||||||
screen->n_monitor_infos = 0;
|
|
||||||
screen->last_monitor_index = 0;
|
screen->last_monitor_index = 0;
|
||||||
|
screen->has_xinerama_indices = FALSE;
|
||||||
/* Xinerama doesn't have a concept of primary monitor, however XRandR
|
|
||||||
* does. However, the XRandR xinerama compat code always sorts the
|
|
||||||
* primary output first, so we rely on that here. We could use the
|
|
||||||
* native XRandR calls instead of xinerama, but that would be
|
|
||||||
* slightly problematic for _NET_WM_FULLSCREEN_MONITORS support, as
|
|
||||||
* that is defined in terms of xinerama monitor indexes.
|
|
||||||
* So, since we don't need anything in xrandr except the primary
|
|
||||||
* we can keep using xinerama and use the first monitor as the
|
|
||||||
* primary.
|
|
||||||
*/
|
|
||||||
screen->primary_monitor_index = 0;
|
|
||||||
|
|
||||||
screen->display->monitor_cache_invalidated = TRUE;
|
screen->display->monitor_cache_invalidated = TRUE;
|
||||||
|
|
||||||
if (g_getenv ("MUTTER_DEBUG_XINERAMA"))
|
manager = meta_monitor_manager_get ();
|
||||||
{
|
|
||||||
meta_topic (META_DEBUG_XINERAMA,
|
|
||||||
"Pretending a single monitor has two Xinerama screens\n");
|
|
||||||
|
|
||||||
screen->monitor_infos = g_new0 (MetaMonitorInfo, 2);
|
screen->monitor_infos = meta_monitor_manager_get_monitor_infos (manager,
|
||||||
screen->n_monitor_infos = 2;
|
(unsigned*)&screen->n_monitor_infos);
|
||||||
|
screen->primary_monitor_index = meta_monitor_manager_get_primary_index (manager);
|
||||||
screen->monitor_infos[0].number = 0;
|
|
||||||
screen->monitor_infos[0].rect = screen->rect;
|
|
||||||
screen->monitor_infos[0].rect.width = screen->rect.width / 2;
|
|
||||||
screen->monitor_infos[0].in_fullscreen = -1;
|
|
||||||
|
|
||||||
screen->monitor_infos[1].number = 1;
|
|
||||||
screen->monitor_infos[1].rect = screen->rect;
|
|
||||||
screen->monitor_infos[1].rect.x = screen->rect.width / 2;
|
|
||||||
screen->monitor_infos[1].rect.width = screen->rect.width / 2;
|
|
||||||
screen->monitor_infos[0].in_fullscreen = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (screen->n_monitor_infos == 0 &&
|
|
||||||
XineramaIsActive (display->xdisplay))
|
|
||||||
{
|
|
||||||
XineramaScreenInfo *infos;
|
|
||||||
int n_infos;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
n_infos = 0;
|
|
||||||
infos = XineramaQueryScreens (display->xdisplay, &n_infos);
|
|
||||||
|
|
||||||
meta_topic (META_DEBUG_XINERAMA,
|
|
||||||
"Found %d Xinerama screens on display %s\n",
|
|
||||||
n_infos, display->name);
|
|
||||||
|
|
||||||
if (n_infos > 0)
|
|
||||||
{
|
|
||||||
screen->monitor_infos = g_new0 (MetaMonitorInfo, n_infos);
|
|
||||||
screen->n_monitor_infos = n_infos;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (i < n_infos)
|
|
||||||
{
|
|
||||||
screen->monitor_infos[i].number = infos[i].screen_number;
|
|
||||||
screen->monitor_infos[i].rect.x = infos[i].x_org;
|
|
||||||
screen->monitor_infos[i].rect.y = infos[i].y_org;
|
|
||||||
screen->monitor_infos[i].rect.width = infos[i].width;
|
|
||||||
screen->monitor_infos[i].rect.height = infos[i].height;
|
|
||||||
screen->monitor_infos[i].in_fullscreen = -1;
|
|
||||||
|
|
||||||
meta_topic (META_DEBUG_XINERAMA,
|
|
||||||
"Monitor %d is %d,%d %d x %d\n",
|
|
||||||
screen->monitor_infos[i].number,
|
|
||||||
screen->monitor_infos[i].rect.x,
|
|
||||||
screen->monitor_infos[i].rect.y,
|
|
||||||
screen->monitor_infos[i].rect.width,
|
|
||||||
screen->monitor_infos[i].rect.height);
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meta_XFree (infos);
|
|
||||||
|
|
||||||
#ifdef HAVE_RANDR
|
|
||||||
{
|
|
||||||
XRRScreenResources *resources;
|
|
||||||
|
|
||||||
resources = XRRGetScreenResourcesCurrent (display->xdisplay, screen->xroot);
|
|
||||||
if (resources)
|
|
||||||
{
|
|
||||||
for (i = 0; i < resources->ncrtc; i++)
|
|
||||||
{
|
|
||||||
XRRCrtcInfo *crtc;
|
|
||||||
MetaMonitorInfo *info;
|
|
||||||
|
|
||||||
crtc = XRRGetCrtcInfo (display->xdisplay, resources, resources->crtcs[i]);
|
|
||||||
info = find_monitor_with_rect (screen, crtc->x, crtc->y, (int)crtc->width, (int)crtc->height);
|
|
||||||
if (info)
|
|
||||||
info->output = find_main_output_for_crtc (screen, resources, crtc);
|
|
||||||
|
|
||||||
XRRFreeCrtcInfo (crtc);
|
|
||||||
}
|
|
||||||
XRRFreeScreenResources (resources);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (screen->n_monitor_infos > 0)
|
|
||||||
{
|
|
||||||
meta_topic (META_DEBUG_XINERAMA,
|
|
||||||
"No Xinerama extension or Xinerama inactive on display %s\n",
|
|
||||||
display->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If no Xinerama, fill in the single screen info so
|
|
||||||
* we can use the field unconditionally
|
|
||||||
*/
|
|
||||||
if (screen->n_monitor_infos == 0)
|
|
||||||
{
|
|
||||||
meta_topic (META_DEBUG_XINERAMA,
|
|
||||||
"No Xinerama screens, using default screen info\n");
|
|
||||||
|
|
||||||
screen->monitor_infos = g_new0 (MetaMonitorInfo, 1);
|
|
||||||
screen->n_monitor_infos = 1;
|
|
||||||
|
|
||||||
screen->monitor_infos[0].number = 0;
|
|
||||||
screen->monitor_infos[0].rect = screen->rect;
|
|
||||||
screen->monitor_infos[0].in_fullscreen = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
filter_mirrored_monitors (screen);
|
|
||||||
|
|
||||||
screen->monitor_infos[screen->primary_monitor_index].is_primary = TRUE;
|
|
||||||
|
|
||||||
g_assert (screen->n_monitor_infos > 0);
|
|
||||||
g_assert (screen->monitor_infos != NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The guard window allows us to leave minimized windows mapped so
|
/* The guard window allows us to leave minimized windows mapped so
|
||||||
@@ -674,9 +518,7 @@ meta_screen_new (MetaDisplay *display,
|
|||||||
char buf[128];
|
char buf[128];
|
||||||
guint32 manager_timestamp;
|
guint32 manager_timestamp;
|
||||||
gulong current_workspace;
|
gulong current_workspace;
|
||||||
#ifdef HAVE_WAYLAND
|
MetaMonitorManager *manager;
|
||||||
MetaWaylandCompositor *compositor;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
replace_current_wm = meta_get_replace_current_wm ();
|
replace_current_wm = meta_get_replace_current_wm ();
|
||||||
|
|
||||||
@@ -837,18 +679,23 @@ meta_screen_new (MetaDisplay *display,
|
|||||||
screen->rect.x = screen->rect.y = 0;
|
screen->rect.x = screen->rect.y = 0;
|
||||||
|
|
||||||
#ifdef HAVE_WAYLAND
|
#ifdef HAVE_WAYLAND
|
||||||
if (meta_is_wayland_compositor ())
|
if (!meta_is_wayland_compositor ())
|
||||||
{
|
|
||||||
compositor = meta_wayland_compositor_get_default ();
|
|
||||||
screen->rect.width = clutter_actor_get_width (compositor->stage);
|
|
||||||
screen->rect.height = clutter_actor_get_height (compositor->stage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
#endif
|
||||||
{
|
meta_monitor_manager_initialize ();
|
||||||
screen->rect.width = WidthOfScreen (screen->xscreen);
|
|
||||||
screen->rect.height = HeightOfScreen (screen->xscreen);
|
manager = meta_monitor_manager_get ();
|
||||||
}
|
g_signal_connect (manager, "monitors-changed",
|
||||||
|
G_CALLBACK (on_monitors_changed), screen);
|
||||||
|
|
||||||
|
meta_monitor_manager_get_screen_size (manager,
|
||||||
|
&screen->rect.width,
|
||||||
|
&screen->rect.height);
|
||||||
|
|
||||||
|
#ifdef HAVE_WAYLAND
|
||||||
|
if (!meta_is_wayland_compositor ())
|
||||||
|
#endif
|
||||||
|
meta_monitor_manager_init_dbus (manager, NULL, NULL);
|
||||||
|
meta_idle_monitor_init_dbus ();
|
||||||
|
|
||||||
screen->current_cursor = -1; /* invalid/unset */
|
screen->current_cursor = -1; /* invalid/unset */
|
||||||
screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
|
screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
|
||||||
@@ -874,12 +721,9 @@ meta_screen_new (MetaDisplay *display,
|
|||||||
screen->compositor_data = NULL;
|
screen->compositor_data = NULL;
|
||||||
screen->guard_window = None;
|
screen->guard_window = None;
|
||||||
|
|
||||||
screen->monitor_infos = NULL;
|
|
||||||
screen->n_monitor_infos = 0;
|
|
||||||
screen->last_monitor_index = 0;
|
|
||||||
|
|
||||||
reload_monitor_infos (screen);
|
reload_monitor_infos (screen);
|
||||||
|
|
||||||
|
meta_cursor_tracker_get_for_screen (screen);
|
||||||
meta_screen_set_cursor (screen, META_CURSOR_DEFAULT);
|
meta_screen_set_cursor (screen, META_CURSOR_DEFAULT);
|
||||||
|
|
||||||
/* Handle creating a no_focus_window for this screen */
|
/* Handle creating a no_focus_window for this screen */
|
||||||
@@ -963,7 +807,7 @@ meta_screen_new (MetaDisplay *display,
|
|||||||
|
|
||||||
meta_verbose ("Added screen %d ('%s') root 0x%lx\n",
|
meta_verbose ("Added screen %d ('%s') root 0x%lx\n",
|
||||||
screen->number, screen->screen_name, screen->xroot);
|
screen->number, screen->screen_name, screen->xroot);
|
||||||
|
|
||||||
return screen;
|
return screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1635,29 +1479,18 @@ void
|
|||||||
meta_screen_set_cursor (MetaScreen *screen,
|
meta_screen_set_cursor (MetaScreen *screen,
|
||||||
MetaCursor cursor)
|
MetaCursor cursor)
|
||||||
{
|
{
|
||||||
Cursor xcursor;
|
|
||||||
|
|
||||||
if (cursor == screen->current_cursor)
|
if (cursor == screen->current_cursor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
screen->current_cursor = cursor;
|
screen->current_cursor = cursor;
|
||||||
|
meta_cursor_tracker_set_root_cursor (screen->cursor_tracker, cursor);
|
||||||
xcursor = meta_display_create_x_cursor (screen->display, cursor);
|
|
||||||
XDefineCursor (screen->display->xdisplay, screen->xroot, xcursor);
|
|
||||||
XFlush (screen->display->xdisplay);
|
|
||||||
XFreeCursor (screen->display->xdisplay, xcursor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_screen_update_cursor (MetaScreen *screen)
|
meta_screen_update_cursor (MetaScreen *screen)
|
||||||
{
|
{
|
||||||
Cursor xcursor;
|
meta_cursor_tracker_set_root_cursor (screen->cursor_tracker,
|
||||||
|
screen->current_cursor);
|
||||||
xcursor = meta_display_create_x_cursor (screen->display,
|
|
||||||
screen->current_cursor);
|
|
||||||
XDefineCursor (screen->display->xdisplay, screen->xroot, xcursor);
|
|
||||||
XFlush (screen->display->xdisplay);
|
|
||||||
XFreeCursor (screen->display->xdisplay, xcursor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -3022,19 +2855,15 @@ meta_screen_resize_func (MetaScreen *screen,
|
|||||||
meta_window_recalc_features (window);
|
meta_window_recalc_features (window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
meta_screen_resize (MetaScreen *screen,
|
on_monitors_changed (MetaMonitorManager *manager,
|
||||||
int width,
|
MetaScreen *screen)
|
||||||
int height)
|
|
||||||
{
|
{
|
||||||
GSList *windows, *tmp;
|
GSList *tmp, *windows;
|
||||||
MetaMonitorInfo *old_monitor_infos;
|
|
||||||
|
|
||||||
screen->rect.width = width;
|
meta_monitor_manager_get_screen_size (manager,
|
||||||
screen->rect.height = height;
|
&screen->rect.width,
|
||||||
|
&screen->rect.height);
|
||||||
/* Save the old monitor infos, so they stay valid during the update */
|
|
||||||
old_monitor_infos = screen->monitor_infos;
|
|
||||||
|
|
||||||
reload_monitor_infos (screen);
|
reload_monitor_infos (screen);
|
||||||
set_desktop_geometry_hint (screen);
|
set_desktop_geometry_hint (screen);
|
||||||
@@ -3046,8 +2875,8 @@ meta_screen_resize (MetaScreen *screen,
|
|||||||
|
|
||||||
changes.x = 0;
|
changes.x = 0;
|
||||||
changes.y = 0;
|
changes.y = 0;
|
||||||
changes.width = width;
|
changes.width = screen->rect.width;
|
||||||
changes.height = height;
|
changes.height = screen->rect.height;
|
||||||
|
|
||||||
XConfigureWindow(screen->display->xdisplay,
|
XConfigureWindow(screen->display->xdisplay,
|
||||||
screen->guard_window,
|
screen->guard_window,
|
||||||
@@ -3055,9 +2884,9 @@ meta_screen_resize (MetaScreen *screen,
|
|||||||
&changes);
|
&changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (screen->display->compositor)
|
meta_compositor_sync_screen_size (screen->display->compositor,
|
||||||
meta_compositor_sync_screen_size (screen->display->compositor,
|
screen,
|
||||||
screen, width, height);
|
screen->rect.width, screen->rect.height);
|
||||||
|
|
||||||
/* Queue a resize on all the windows */
|
/* Queue a resize on all the windows */
|
||||||
meta_screen_foreach_window (screen, meta_screen_resize_func, 0);
|
meta_screen_foreach_window (screen, meta_screen_resize_func, 0);
|
||||||
@@ -3073,7 +2902,6 @@ meta_screen_resize (MetaScreen *screen,
|
|||||||
meta_window_update_for_monitors_changed (window);
|
meta_window_update_for_monitors_changed (window);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free (old_monitor_infos);
|
|
||||||
g_slist_free (windows);
|
g_slist_free (windows);
|
||||||
|
|
||||||
meta_screen_queue_check_fullscreen (screen);
|
meta_screen_queue_check_fullscreen (screen);
|
||||||
@@ -3869,3 +3697,13 @@ meta_screen_get_monitor_in_fullscreen (MetaScreen *screen,
|
|||||||
/* We use -1 as a flag to mean "not known yet" for notification purposes */
|
/* We use -1 as a flag to mean "not known yet" for notification purposes */
|
||||||
return screen->monitor_infos[monitor].in_fullscreen == TRUE;
|
return screen->monitor_infos[monitor].in_fullscreen == TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_screen_handle_xevent (MetaScreen *screen,
|
||||||
|
XEvent *xevent)
|
||||||
|
{
|
||||||
|
if (meta_cursor_tracker_handle_xevent (screen->cursor_tracker, xevent))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
@@ -345,6 +345,8 @@ topic_name (MetaDebugTopic topic)
|
|||||||
return "COMPOSITOR";
|
return "COMPOSITOR";
|
||||||
case META_DEBUG_EDGE_RESISTANCE:
|
case META_DEBUG_EDGE_RESISTANCE:
|
||||||
return "EDGE_RESISTANCE";
|
return "EDGE_RESISTANCE";
|
||||||
|
case META_DEBUG_DBUS:
|
||||||
|
return "DBUS";
|
||||||
case META_DEBUG_VERBOSE:
|
case META_DEBUG_VERBOSE:
|
||||||
return "VERBOSE";
|
return "VERBOSE";
|
||||||
}
|
}
|
||||||
@@ -650,8 +652,13 @@ meta_show_dialog (const char *type,
|
|||||||
|
|
||||||
append_argument (args, "zenity");
|
append_argument (args, "zenity");
|
||||||
append_argument (args, type);
|
append_argument (args, type);
|
||||||
append_argument (args, "--display");
|
|
||||||
append_argument (args, display);
|
if (display)
|
||||||
|
{
|
||||||
|
append_argument (args, "--display");
|
||||||
|
append_argument (args, display);
|
||||||
|
}
|
||||||
|
|
||||||
append_argument (args, "--class");
|
append_argument (args, "--class");
|
||||||
append_argument (args, "mutter-dialog");
|
append_argument (args, "mutter-dialog");
|
||||||
append_argument (args, "--title");
|
append_argument (args, "--title");
|
||||||
|
@@ -181,7 +181,7 @@ struct _MetaWindow
|
|||||||
* been overridden (via a client message), the window will cover the union of
|
* been overridden (via a client message), the window will cover the union of
|
||||||
* these monitors. If not, this is the single monitor which the window's
|
* these monitors. If not, this is the single monitor which the window's
|
||||||
* origin is on. */
|
* origin is on. */
|
||||||
long fullscreen_monitors[4];
|
gint fullscreen_monitors[4];
|
||||||
|
|
||||||
/* Whether we're trying to constrain the window to be fully onscreen */
|
/* Whether we're trying to constrain the window to be fully onscreen */
|
||||||
guint require_fully_onscreen : 1;
|
guint require_fully_onscreen : 1;
|
||||||
|
@@ -2289,10 +2289,14 @@ set_net_wm_state (MetaWindow *window)
|
|||||||
|
|
||||||
if (window->fullscreen)
|
if (window->fullscreen)
|
||||||
{
|
{
|
||||||
data[0] = window->fullscreen_monitors[0];
|
data[0] = meta_screen_monitor_index_to_xinerama_index (window->screen,
|
||||||
data[1] = window->fullscreen_monitors[1];
|
window->fullscreen_monitors[0]);
|
||||||
data[2] = window->fullscreen_monitors[2];
|
data[1] = meta_screen_monitor_index_to_xinerama_index (window->screen,
|
||||||
data[3] = window->fullscreen_monitors[3];
|
window->fullscreen_monitors[1]);
|
||||||
|
data[2] = meta_screen_monitor_index_to_xinerama_index (window->screen,
|
||||||
|
window->fullscreen_monitors[2]);
|
||||||
|
data[3] = meta_screen_monitor_index_to_xinerama_index (window->screen,
|
||||||
|
window->fullscreen_monitors[3]);
|
||||||
|
|
||||||
meta_verbose ("Setting _NET_WM_FULLSCREEN_MONITORS\n");
|
meta_verbose ("Setting _NET_WM_FULLSCREEN_MONITORS\n");
|
||||||
meta_error_trap_push (window->display);
|
meta_error_trap_push (window->display);
|
||||||
@@ -4966,7 +4970,8 @@ meta_window_update_for_monitors_changed (MetaWindow *window)
|
|||||||
{
|
{
|
||||||
MetaMonitorInfo *info = &window->screen->monitor_infos[i];
|
MetaMonitorInfo *info = &window->screen->monitor_infos[i];
|
||||||
|
|
||||||
if (info->output == old->output)
|
if (info->output_id != 0 &&
|
||||||
|
info->output_id == old->output_id)
|
||||||
{
|
{
|
||||||
new = info;
|
new = info;
|
||||||
break;
|
break;
|
||||||
@@ -7311,10 +7316,14 @@ meta_window_client_message (MetaWindow *window,
|
|||||||
meta_verbose ("_NET_WM_FULLSCREEN_MONITORS request for window '%s'\n",
|
meta_verbose ("_NET_WM_FULLSCREEN_MONITORS request for window '%s'\n",
|
||||||
window->desc);
|
window->desc);
|
||||||
|
|
||||||
top = event->xclient.data.l[0];
|
top = meta_screen_xinerama_index_to_monitor_index (window->screen,
|
||||||
bottom = event->xclient.data.l[1];
|
event->xclient.data.l[0]);
|
||||||
left = event->xclient.data.l[2];
|
bottom = meta_screen_xinerama_index_to_monitor_index (window->screen,
|
||||||
right = event->xclient.data.l[3];
|
event->xclient.data.l[1]);
|
||||||
|
left = meta_screen_xinerama_index_to_monitor_index (window->screen,
|
||||||
|
event->xclient.data.l[2]);
|
||||||
|
right = meta_screen_xinerama_index_to_monitor_index (window->screen,
|
||||||
|
event->xclient.data.l[3]);
|
||||||
/* source_indication = event->xclient.data.l[4]; */
|
/* source_indication = event->xclient.data.l[4]; */
|
||||||
|
|
||||||
meta_window_update_fullscreen_monitors (window, top, bottom, left, right);
|
meta_window_update_fullscreen_monitors (window, top, bottom, left, right);
|
||||||
@@ -7390,6 +7399,9 @@ void
|
|||||||
meta_window_set_focused_internal (MetaWindow *window,
|
meta_window_set_focused_internal (MetaWindow *window,
|
||||||
gboolean focused)
|
gboolean focused)
|
||||||
{
|
{
|
||||||
|
if (window->unmanaging)
|
||||||
|
return;
|
||||||
|
|
||||||
if (focused)
|
if (focused)
|
||||||
{
|
{
|
||||||
window->has_focus = TRUE;
|
window->has_focus = TRUE;
|
||||||
|
@@ -72,6 +72,7 @@ item(_MUTTER_TIMESTAMP_PING)
|
|||||||
item(_MUTTER_FOCUS_SET)
|
item(_MUTTER_FOCUS_SET)
|
||||||
item(_MUTTER_SENTINEL)
|
item(_MUTTER_SENTINEL)
|
||||||
item(_MUTTER_VERSION)
|
item(_MUTTER_VERSION)
|
||||||
|
item(_MUTTER_PRESENTATION_OUTPUT)
|
||||||
item(WM_CLIENT_MACHINE)
|
item(WM_CLIENT_MACHINE)
|
||||||
item(MANAGER)
|
item(MANAGER)
|
||||||
item(TARGETS)
|
item(TARGETS)
|
||||||
@@ -79,6 +80,7 @@ item(MULTIPLE)
|
|||||||
item(TIMESTAMP)
|
item(TIMESTAMP)
|
||||||
item(VERSION)
|
item(VERSION)
|
||||||
item(ATOM_PAIR)
|
item(ATOM_PAIR)
|
||||||
|
item(BACKLIGHT)
|
||||||
|
|
||||||
/* Oddities: These are used, and we need atoms for them,
|
/* Oddities: These are used, and we need atoms for them,
|
||||||
* but when we need all _NET_WM hints (i.e. when we're making
|
* but when we need all _NET_WM hints (i.e. when we're making
|
||||||
|
49
src/meta/meta-cursor-tracker.h
Normal file
49
src/meta/meta-cursor-tracker.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Author: Giovanni Campagna <gcampagn@redhat.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef META_CURSOR_TRACKER_H
|
||||||
|
#define META_CURSOR_TRACKER_H
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <meta/types.h>
|
||||||
|
#include <meta/workspace.h>
|
||||||
|
|
||||||
|
#define META_TYPE_CURSOR_TRACKER (meta_cursor_tracker_get_type ())
|
||||||
|
#define META_CURSOR_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_CURSOR_TRACKER, MetaCursorTracker))
|
||||||
|
#define META_CURSOR_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_CURSOR_TRACKER, MetaCursorTrackerClass))
|
||||||
|
#define META_IS_CURSOR_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_CURSOR_TRACKER))
|
||||||
|
#define META_IS_CURSOR_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_CURSOR_TRACKER))
|
||||||
|
#define META_CURSOR_TRACKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_CURSOR_TRACKER, MetaCursorTrackerClass))
|
||||||
|
|
||||||
|
typedef struct _MetaCursorTrackerClass MetaCursorTrackerClass;
|
||||||
|
|
||||||
|
GType meta_cursor_tracker_get_type (void);
|
||||||
|
|
||||||
|
MetaCursorTracker *meta_cursor_tracker_get_for_screen (MetaScreen *screen);
|
||||||
|
|
||||||
|
void meta_cursor_tracker_get_hot (MetaCursorTracker *tracker,
|
||||||
|
int *x,
|
||||||
|
int *y);
|
||||||
|
CoglTexture *meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker);
|
||||||
|
|
||||||
|
#endif
|
62
src/meta/meta-idle-monitor.h
Normal file
62
src/meta/meta-idle-monitor.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef META_IDLE_MONITOR_H
|
||||||
|
#define META_IDLE_MONITOR_H
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <meta/types.h>
|
||||||
|
|
||||||
|
#define META_TYPE_IDLE_MONITOR (meta_idle_monitor_get_type ())
|
||||||
|
#define META_IDLE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_IDLE_MONITOR, MetaIdleMonitor))
|
||||||
|
#define META_IDLE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_IDLE_MONITOR, MetaIdleMonitorClass))
|
||||||
|
#define META_IS_IDLE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_IDLE_MONITOR))
|
||||||
|
#define META_IS_IDLE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_IDLE_MONITOR))
|
||||||
|
#define META_IDLE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_IDLE_MONITOR, MetaIdleMonitorClass))
|
||||||
|
|
||||||
|
typedef struct _MetaIdleMonitor MetaIdleMonitor;
|
||||||
|
typedef struct _MetaIdleMonitorClass MetaIdleMonitorClass;
|
||||||
|
|
||||||
|
GType meta_idle_monitor_get_type (void);
|
||||||
|
|
||||||
|
typedef void (*MetaIdleMonitorWatchFunc) (MetaIdleMonitor *monitor,
|
||||||
|
guint watch_id,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
MetaIdleMonitor *meta_idle_monitor_get_core (void);
|
||||||
|
MetaIdleMonitor *meta_idle_monitor_get_for_device (int device_id);
|
||||||
|
|
||||||
|
guint meta_idle_monitor_add_idle_watch (MetaIdleMonitor *monitor,
|
||||||
|
guint64 interval_msec,
|
||||||
|
MetaIdleMonitorWatchFunc callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify notify);
|
||||||
|
|
||||||
|
guint meta_idle_monitor_add_user_active_watch (MetaIdleMonitor *monitor,
|
||||||
|
MetaIdleMonitorWatchFunc callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify notify);
|
||||||
|
|
||||||
|
void meta_idle_monitor_remove_watch (MetaIdleMonitor *monitor,
|
||||||
|
guint id);
|
||||||
|
guint64 meta_idle_monitor_get_idletime (MetaIdleMonitor *monitor);
|
||||||
|
|
||||||
|
#endif
|
@@ -205,6 +205,21 @@ struct _MetaPluginClass
|
|||||||
gboolean (*keybinding_filter) (MetaPlugin *plugin,
|
gboolean (*keybinding_filter) (MetaPlugin *plugin,
|
||||||
MetaKeyBinding *binding);
|
MetaKeyBinding *binding);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaPluginClass::confirm_display_config:
|
||||||
|
* @plugin: a #MetaPlugin
|
||||||
|
*
|
||||||
|
* Virtual function called when the display configuration changes.
|
||||||
|
* The common way to implement this function is to show some form
|
||||||
|
* of modal dialog that should ask the user if everything was ok.
|
||||||
|
*
|
||||||
|
* When confirmed by the user, the plugin must call meta_plugin_complete_display_change()
|
||||||
|
* to make the configuration permanent. If that function is not
|
||||||
|
* called within the timeout, the previous configuration will be
|
||||||
|
* reapplied.
|
||||||
|
*/
|
||||||
|
void (*confirm_display_change) (MetaPlugin *plugin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MetaPluginClass::plugin_info:
|
* MetaPluginClass::plugin_info:
|
||||||
* @plugin: a #MetaPlugin
|
* @plugin: a #MetaPlugin
|
||||||
@@ -214,6 +229,7 @@ struct _MetaPluginClass
|
|||||||
* Returns: a #MetaPluginInfo.
|
* Returns: a #MetaPluginInfo.
|
||||||
*/
|
*/
|
||||||
const MetaPluginInfo * (*plugin_info) (MetaPlugin *plugin);
|
const MetaPluginInfo * (*plugin_info) (MetaPlugin *plugin);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -360,6 +376,10 @@ void
|
|||||||
meta_plugin_destroy_completed (MetaPlugin *plugin,
|
meta_plugin_destroy_completed (MetaPlugin *plugin,
|
||||||
MetaWindowActor *actor);
|
MetaWindowActor *actor);
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_plugin_complete_display_change (MetaPlugin *plugin,
|
||||||
|
gboolean ok);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MetaModalOptions:
|
* MetaModalOptions:
|
||||||
* @META_MODAL_POINTER_ALREADY_GRABBED: if set the pointer is already
|
* @META_MODAL_POINTER_ALREADY_GRABBED: if set the pointer is already
|
||||||
@@ -376,8 +396,6 @@ typedef enum {
|
|||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
meta_plugin_begin_modal (MetaPlugin *plugin,
|
meta_plugin_begin_modal (MetaPlugin *plugin,
|
||||||
Window grab_window,
|
|
||||||
Cursor cursor,
|
|
||||||
MetaModalOptions options,
|
MetaModalOptions options,
|
||||||
guint32 timestamp);
|
guint32 timestamp);
|
||||||
|
|
||||||
|
@@ -38,5 +38,6 @@ typedef struct _MetaWorkspace MetaWorkspace;
|
|||||||
*/
|
*/
|
||||||
typedef struct _MetaGroup MetaGroup;
|
typedef struct _MetaGroup MetaGroup;
|
||||||
typedef struct _MetaKeyBinding MetaKeyBinding;
|
typedef struct _MetaKeyBinding MetaKeyBinding;
|
||||||
|
typedef struct _MetaCursorTracker MetaCursorTracker;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -102,7 +102,8 @@ typedef enum
|
|||||||
META_DEBUG_RESIZING = 1 << 18,
|
META_DEBUG_RESIZING = 1 << 18,
|
||||||
META_DEBUG_SHAPES = 1 << 19,
|
META_DEBUG_SHAPES = 1 << 19,
|
||||||
META_DEBUG_COMPOSITOR = 1 << 20,
|
META_DEBUG_COMPOSITOR = 1 << 20,
|
||||||
META_DEBUG_EDGE_RESISTANCE = 1 << 21
|
META_DEBUG_EDGE_RESISTANCE = 1 << 21,
|
||||||
|
META_DEBUG_DBUS = 1 << 22
|
||||||
} MetaDebugTopic;
|
} MetaDebugTopic;
|
||||||
|
|
||||||
void meta_topic_real (MetaDebugTopic topic,
|
void meta_topic_real (MetaDebugTopic topic,
|
||||||
|
@@ -116,5 +116,34 @@
|
|||||||
<_summary>Cancel tab popup</_summary>
|
<_summary>Cancel tab popup</_summary>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
|
<key name="switch-to-session-1" type="as">
|
||||||
|
<default><![CDATA[['<Primary><Alt>F1']]]></default>
|
||||||
|
<_summary>Switch to VT 1</_summary>
|
||||||
|
</key>
|
||||||
|
<key name="switch-to-session-2" type="as">
|
||||||
|
<default><![CDATA[['<Primary><Alt>F2']]]></default>
|
||||||
|
<_summary>Switch to VT 2</_summary>
|
||||||
|
</key>
|
||||||
|
<key name="switch-to-session-3" type="as">
|
||||||
|
<default><![CDATA[['<Primary><Alt>F3']]]></default>
|
||||||
|
<_summary>Switch to VT 3</_summary>
|
||||||
|
</key>
|
||||||
|
<key name="switch-to-session-4" type="as">
|
||||||
|
<default><![CDATA[['<Primary><Alt>F4']]]></default>
|
||||||
|
<_summary>Switch to VT 4</_summary>
|
||||||
|
</key>
|
||||||
|
<key name="switch-to-session-5" type="as">
|
||||||
|
<default><![CDATA[['<Primary><Alt>F5']]]></default>
|
||||||
|
<_summary>Switch to VT 5</_summary>
|
||||||
|
</key>
|
||||||
|
<key name="switch-to-session-6" type="as">
|
||||||
|
<default><![CDATA[['<Primary><Alt>F6']]]></default>
|
||||||
|
<_summary>Switch to VT 6</_summary>
|
||||||
|
</key>
|
||||||
|
<key name="switch-to-session-7" type="as">
|
||||||
|
<default><![CDATA[['<Primary><Alt>F7']]]></default>
|
||||||
|
<_summary>Switch to VT 7</_summary>
|
||||||
|
</key>
|
||||||
|
|
||||||
</schema>
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
||||||
|
436
src/wayland/meta-tty.c
Normal file
436
src/wayland/meta-tty.c
Normal file
@@ -0,0 +1,436 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2010 Intel Corporation
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <termios.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <linux/kd.h>
|
||||||
|
#include <linux/vt.h>
|
||||||
|
#include <linux/major.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "meta-tty.h"
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
|
|
||||||
|
/* Introduced in 2.6.38 */
|
||||||
|
#ifndef K_OFF
|
||||||
|
#define K_OFF 0x04
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct _MetaTTYClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaTTY
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
struct termios terminal_attributes;
|
||||||
|
|
||||||
|
GMainContext *nested_context;
|
||||||
|
GMainLoop *nested_loop;
|
||||||
|
|
||||||
|
int input_source;
|
||||||
|
GSource *vt_enter_source, *vt_leave_source;
|
||||||
|
GSource *nested_term;
|
||||||
|
int vt, starting_vt;
|
||||||
|
int kb_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SIGNAL_ENTER,
|
||||||
|
SIGNAL_LEAVE,
|
||||||
|
SIGNAL_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
static int signals[SIGNAL_LAST];
|
||||||
|
|
||||||
|
static void meta_tty_initable_iface_init (GInitableIface *);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_CODE (MetaTTY, meta_tty, G_TYPE_OBJECT,
|
||||||
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
|
||||||
|
meta_tty_initable_iface_init));
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
quit_nested_loop (gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaTTY *tty = user_data;
|
||||||
|
|
||||||
|
g_main_loop_quit (tty->nested_loop);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
vt_release_handler (gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaTTY *tty = user_data;
|
||||||
|
|
||||||
|
g_signal_emit (tty, signals[SIGNAL_LEAVE], 0);
|
||||||
|
|
||||||
|
ioctl (tty->fd, VT_RELDISP, 1);
|
||||||
|
|
||||||
|
/* 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 (tty->nested_loop);
|
||||||
|
|
||||||
|
ioctl (tty->fd, VT_RELDISP, VT_ACKACQ);
|
||||||
|
|
||||||
|
g_signal_emit (tty, signals[SIGNAL_ENTER], 0);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
on_tty_input (int fd,
|
||||||
|
GIOCondition mask,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
MetaTTY *tty = user_data;
|
||||||
|
|
||||||
|
/* Ignore input to tty. We get keyboard events from evdev */
|
||||||
|
tcflush(tty->fd, TCIFLUSH);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
try_open_vt (MetaTTY *tty,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
int tty0, fd;
|
||||||
|
char filename[16];
|
||||||
|
|
||||||
|
tty0 = open ("/dev/tty0", O_WRONLY | O_CLOEXEC);
|
||||||
|
if (tty0 < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR,
|
||||||
|
g_io_error_from_errno (errno),
|
||||||
|
"Could not open tty0: %s", strerror (errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl (tty0, VT_OPENQRY, &tty->vt) < 0 || tty->vt == -1) {
|
||||||
|
g_set_error (error, G_IO_ERROR,
|
||||||
|
g_io_error_from_errno (errno),
|
||||||
|
"Could not open tty0: %s", strerror (errno));
|
||||||
|
close (tty0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
close (tty0);
|
||||||
|
snprintf (filename, sizeof filename, "/dev/tty%d", tty->vt);
|
||||||
|
g_debug("compositor: using new vt %s\n", filename);
|
||||||
|
fd = open (filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_tty_activate_vt (MetaTTY *tty,
|
||||||
|
int vt,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
if (ioctl(tty->fd, VT_ACTIVATE, vt) < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
|
||||||
|
strerror (errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_tty_initable_init(GInitable *initable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
MetaTTY *tty = META_TTY (initable);
|
||||||
|
struct termios raw_attributes;
|
||||||
|
struct vt_mode mode = { 0 };
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
struct stat buf;
|
||||||
|
struct vt_stat vts;
|
||||||
|
|
||||||
|
tty->fd = env_get_fd ("WESTON_TTY_FD");
|
||||||
|
if (tty->fd < 0)
|
||||||
|
tty->fd = STDIN_FILENO;
|
||||||
|
|
||||||
|
if (fstat(tty->fd, &buf) == 0 &&
|
||||||
|
major(buf.st_rdev) == TTY_MAJOR &&
|
||||||
|
minor(buf.st_rdev) > 0)
|
||||||
|
{
|
||||||
|
if (tty->fd == STDIN_FILENO)
|
||||||
|
tty->fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0);
|
||||||
|
tty->vt = minor(buf.st_rdev);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Fall back to try opening a new VT. This typically
|
||||||
|
* requires root. */
|
||||||
|
tty->fd = try_open_vt(tty, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tty->fd <= 0 && (!error || !*error))
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR,
|
||||||
|
g_io_error_from_errno (errno),
|
||||||
|
"Could not open tty0: %s", strerror (errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(tty->fd, VT_GETSTATE, &vts) == 0)
|
||||||
|
tty->starting_vt = vts.v_active;
|
||||||
|
else
|
||||||
|
tty->starting_vt = tty->vt;
|
||||||
|
|
||||||
|
if (tty->starting_vt != tty->vt)
|
||||||
|
{
|
||||||
|
if (ioctl(tty->fd, VT_ACTIVATE, tty->vt) < 0 ||
|
||||||
|
ioctl(tty->fd, VT_WAITACTIVE, tty->vt) < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR,
|
||||||
|
g_io_error_from_errno (errno),
|
||||||
|
"Failed to switch to new vt: %s", strerror (errno));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcgetattr(tty->fd, &tty->terminal_attributes) < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR,
|
||||||
|
g_io_error_from_errno (errno),
|
||||||
|
"Could not get terminal attributes: %s", strerror (errno));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore control characters and disable echo */
|
||||||
|
raw_attributes = tty->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(tty->fd, TCSANOW, &raw_attributes) < 0)
|
||||||
|
g_warning("Could not put terminal into raw mode: %s", strerror (errno));
|
||||||
|
|
||||||
|
ioctl(tty->fd, KDGKBMODE, &tty->kb_mode);
|
||||||
|
ret = ioctl(tty->fd, KDSKBMODE, K_OFF);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
ret = ioctl(tty->fd, KDSKBMODE, K_RAW);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR,
|
||||||
|
g_io_error_from_errno (errno),
|
||||||
|
"Failed to set keyboard mode: %s", strerror (errno));
|
||||||
|
goto err_attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
tty->input_source = g_unix_fd_add (tty->fd,
|
||||||
|
G_IO_IN,
|
||||||
|
on_tty_input, tty);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ioctl(tty->fd, KDSETMODE, KD_GRAPHICS);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR,
|
||||||
|
g_io_error_from_errno (errno),
|
||||||
|
"Failed to set KD_GRAPHICS mode: %s", strerror (errno));
|
||||||
|
goto err_kdkbmode;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode.mode = VT_PROCESS;
|
||||||
|
mode.relsig = SIGUSR1;
|
||||||
|
mode.acqsig = SIGUSR2;
|
||||||
|
if (ioctl(tty->fd, VT_SETMODE, &mode) < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR,
|
||||||
|
g_io_error_from_errno (errno),
|
||||||
|
"Failed to take control of vt handling: %s", strerror (errno));
|
||||||
|
goto err_kdmode;
|
||||||
|
}
|
||||||
|
|
||||||
|
tty->vt_leave_source = g_unix_signal_source_new (SIGUSR1);
|
||||||
|
g_source_set_callback (tty->vt_leave_source, vt_release_handler, tty, NULL);
|
||||||
|
|
||||||
|
tty->vt_enter_source = g_unix_signal_source_new (SIGUSR2);
|
||||||
|
g_source_set_callback (tty->vt_enter_source, quit_nested_loop, tty, NULL);
|
||||||
|
tty->nested_term = g_unix_signal_source_new (SIGTERM);
|
||||||
|
g_source_set_callback (tty->nested_term, quit_nested_loop, tty, NULL);
|
||||||
|
|
||||||
|
tty->nested_context = g_main_context_new ();
|
||||||
|
tty->nested_loop = g_main_loop_new (tty->nested_context, FALSE);
|
||||||
|
|
||||||
|
g_source_attach (tty->vt_leave_source, NULL);
|
||||||
|
g_source_attach (tty->vt_enter_source, tty->nested_context);
|
||||||
|
g_source_attach (tty->nested_term, tty->nested_context);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
err_kdmode:
|
||||||
|
ioctl (tty->fd, KDSETMODE, KD_TEXT);
|
||||||
|
|
||||||
|
err_kdkbmode:
|
||||||
|
if (tty->input_source)
|
||||||
|
g_source_remove (tty->input_source);
|
||||||
|
ioctl (tty->fd, KDSKBMODE, tty->kb_mode);
|
||||||
|
|
||||||
|
err_attr:
|
||||||
|
tcsetattr (tty->fd, TCSANOW, &tty->terminal_attributes);
|
||||||
|
|
||||||
|
err:
|
||||||
|
close (tty->fd);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_tty_reset (MetaTTY *tty,
|
||||||
|
gboolean warn_if_fail)
|
||||||
|
{
|
||||||
|
struct vt_mode mode = { 0 };
|
||||||
|
|
||||||
|
if (ioctl (tty->fd, KDSKBMODE, tty->kb_mode) && warn_if_fail)
|
||||||
|
g_warning ("failed to restore keyboard mode: %s", strerror (errno));
|
||||||
|
|
||||||
|
if (ioctl (tty->fd, KDSETMODE, KD_TEXT) && warn_if_fail)
|
||||||
|
g_warning ("failed to set KD_TEXT mode on tty: %s", strerror (errno));
|
||||||
|
|
||||||
|
if (tcsetattr (tty->fd, TCSANOW, &tty->terminal_attributes) < 0 && warn_if_fail)
|
||||||
|
g_warning ("could not restore terminal to canonical mode");
|
||||||
|
|
||||||
|
mode.mode = VT_AUTO;
|
||||||
|
if (ioctl (tty->fd, VT_SETMODE, &mode) < 0 && warn_if_fail)
|
||||||
|
g_warning ("could not reset vt handling\n");
|
||||||
|
|
||||||
|
if (tty->vt != tty->starting_vt)
|
||||||
|
{
|
||||||
|
ioctl(tty->fd, VT_ACTIVATE, tty->starting_vt);
|
||||||
|
ioctl(tty->fd, VT_WAITACTIVE, tty->starting_vt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_tty_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
MetaTTY *tty = META_TTY (object);
|
||||||
|
|
||||||
|
if (tty->input_source)
|
||||||
|
g_source_remove (tty->input_source);
|
||||||
|
|
||||||
|
g_source_destroy (tty->vt_enter_source);
|
||||||
|
g_source_destroy (tty->vt_leave_source);
|
||||||
|
g_source_destroy (tty->nested_term);
|
||||||
|
|
||||||
|
g_main_loop_unref (tty->nested_loop);
|
||||||
|
g_main_context_unref (tty->nested_context);
|
||||||
|
|
||||||
|
meta_tty_reset (tty, TRUE);
|
||||||
|
|
||||||
|
close (tty->fd);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (meta_tty_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_tty_init (MetaTTY *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_tty_class_init (MetaTTYClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->finalize = meta_tty_finalize;
|
||||||
|
|
||||||
|
signals[SIGNAL_ENTER] = g_signal_new ("enter",
|
||||||
|
G_TYPE_FROM_CLASS (klass),
|
||||||
|
G_SIGNAL_RUN_FIRST,
|
||||||
|
0, /* class offset */
|
||||||
|
NULL, NULL, /* accumulator */
|
||||||
|
g_cclosure_marshal_VOID__VOID,
|
||||||
|
G_TYPE_NONE, 0);
|
||||||
|
|
||||||
|
signals[SIGNAL_LEAVE] = g_signal_new ("leave",
|
||||||
|
G_TYPE_FROM_CLASS (klass),
|
||||||
|
G_SIGNAL_RUN_FIRST,
|
||||||
|
0, /* class offset */
|
||||||
|
NULL, NULL, /* accumulator */
|
||||||
|
g_cclosure_marshal_VOID__VOID,
|
||||||
|
G_TYPE_NONE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_tty_initable_iface_init (GInitableIface *iface)
|
||||||
|
{
|
||||||
|
iface->init = meta_tty_initable_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaTTY *
|
||||||
|
meta_tty_new (void)
|
||||||
|
{
|
||||||
|
GError *error;
|
||||||
|
MetaTTY *tty;
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
tty = g_initable_new (META_TYPE_TTY, NULL, &error, NULL);
|
||||||
|
|
||||||
|
if (tty == NULL)
|
||||||
|
{
|
||||||
|
g_warning ("Failed to initalize TTY handling: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tty;
|
||||||
|
}
|
50
src/wayland/meta-tty.h
Normal file
50
src/wayland/meta-tty.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef META_TTY_H
|
||||||
|
#define META_TTY_H
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define META_TYPE_TTY (meta_tty_get_type())
|
||||||
|
#define META_TTY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_TTY, MetaTTY))
|
||||||
|
#define META_TTY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_TTY, MetaTTYClass))
|
||||||
|
#define META_IS_TTY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_TTY))
|
||||||
|
#define META_IS_TTY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_TTY))
|
||||||
|
#define META_TTY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TTY, MetaTTYClass))
|
||||||
|
|
||||||
|
typedef struct _MetaTTY MetaTTY;
|
||||||
|
typedef struct _MetaTTYClass MetaTTYClass;
|
||||||
|
|
||||||
|
GType meta_tty_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
MetaTTY *meta_tty_new (void);
|
||||||
|
|
||||||
|
gboolean meta_tty_activate_vt (MetaTTY *self,
|
||||||
|
int number,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
void meta_tty_reset (MetaTTY *self,
|
||||||
|
gboolean warn_if_fail);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* META_TTY_H */
|
@@ -56,6 +56,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <clutter/evdev/clutter-evdev.h>
|
||||||
|
|
||||||
#include "meta-wayland-keyboard.h"
|
#include "meta-wayland-keyboard.h"
|
||||||
|
|
||||||
@@ -258,12 +259,11 @@ default_grab_modifiers (MetaWaylandKeyboardGrab *grab, uint32_t serial,
|
|||||||
pointer->focus);
|
pointer->focus);
|
||||||
if (pr)
|
if (pr)
|
||||||
{
|
{
|
||||||
wl_keyboard_send_modifiers (pr,
|
wl_keyboard_send_modifiers (pr, serial,
|
||||||
serial,
|
mods_depressed,
|
||||||
keyboard->modifiers.mods_depressed,
|
mods_latched,
|
||||||
keyboard->modifiers.mods_latched,
|
mods_locked,
|
||||||
keyboard->modifiers.mods_locked,
|
group);
|
||||||
keyboard->modifiers.group);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,8 +276,11 @@ static const MetaWaylandKeyboardGrabInterface
|
|||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
|
meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
|
||||||
struct wl_display *display)
|
struct wl_display *display,
|
||||||
|
gboolean is_evdev)
|
||||||
{
|
{
|
||||||
|
ClutterDeviceManager *manager;
|
||||||
|
|
||||||
memset (keyboard, 0, sizeof *keyboard);
|
memset (keyboard, 0, sizeof *keyboard);
|
||||||
|
|
||||||
wl_list_init (&keyboard->resource_list);
|
wl_list_init (&keyboard->resource_list);
|
||||||
@@ -293,8 +296,22 @@ meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
|
|||||||
keyboard->xkb_context = xkb_context_new (0 /* flags */);
|
keyboard->xkb_context = xkb_context_new (0 /* flags */);
|
||||||
|
|
||||||
meta_wayland_keyboard_build_global_keymap (keyboard->xkb_context,
|
meta_wayland_keyboard_build_global_keymap (keyboard->xkb_context,
|
||||||
&keyboard->xkb_names,
|
&keyboard->xkb_names,
|
||||||
&keyboard->xkb_info);
|
&keyboard->xkb_info);
|
||||||
|
|
||||||
|
keyboard->is_evdev = is_evdev;
|
||||||
|
if (is_evdev)
|
||||||
|
{
|
||||||
|
manager = clutter_device_manager_get_default ();
|
||||||
|
|
||||||
|
clutter_evdev_set_keyboard_map (manager, keyboard->xkb_info.keymap);
|
||||||
|
keyboard->xkb_state = clutter_evdev_get_keyboard_state (manager);
|
||||||
|
xkb_state_ref (keyboard->xkb_state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
keyboard->xkb_state = xkb_state_new (keyboard->xkb_info.keymap);
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -312,17 +329,12 @@ meta_wayland_xkb_info_destroy (MetaWaylandXkbInfo *xkb_info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_modifiers (MetaWaylandKeyboard *keyboard,
|
update_state_from_clutter (MetaWaylandKeyboard *keyboard,
|
||||||
guint32 serial,
|
ClutterModifierType modifier_state)
|
||||||
ClutterModifierType modifier_state)
|
|
||||||
{
|
{
|
||||||
MetaWaylandKeyboardGrab *grab = keyboard->grab;
|
|
||||||
uint32_t depressed_mods = 0;
|
uint32_t depressed_mods = 0;
|
||||||
uint32_t locked_mods = 0;
|
uint32_t locked_mods = 0;
|
||||||
|
|
||||||
if (keyboard->last_modifier_state == modifier_state)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ((modifier_state & CLUTTER_SHIFT_MASK) &&
|
if ((modifier_state & CLUTTER_SHIFT_MASK) &&
|
||||||
keyboard->xkb_info.shift_mod != XKB_MOD_INVALID)
|
keyboard->xkb_info.shift_mod != XKB_MOD_INVALID)
|
||||||
depressed_mods |= (1 << keyboard->xkb_info.shift_mod);
|
depressed_mods |= (1 << keyboard->xkb_info.shift_mod);
|
||||||
@@ -355,14 +367,56 @@ set_modifiers (MetaWaylandKeyboard *keyboard,
|
|||||||
keyboard->xkb_info.mod5_mod != XKB_MOD_INVALID)
|
keyboard->xkb_info.mod5_mod != XKB_MOD_INVALID)
|
||||||
depressed_mods |= (1 << keyboard->xkb_info.mod5_mod);
|
depressed_mods |= (1 << keyboard->xkb_info.mod5_mod);
|
||||||
|
|
||||||
keyboard->last_modifier_state = modifier_state;
|
xkb_state_update_mask (keyboard->xkb_state,
|
||||||
|
depressed_mods,
|
||||||
|
0,
|
||||||
|
locked_mods,
|
||||||
|
0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
state_equal (MetaWaylandXkbState *one,
|
||||||
|
MetaWaylandXkbState *two)
|
||||||
|
{
|
||||||
|
return one->mods_depressed == two->mods_depressed &&
|
||||||
|
one->mods_latched == two->mods_latched &&
|
||||||
|
one->mods_locked == two->mods_locked &&
|
||||||
|
one->group == two->group;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_modifiers (MetaWaylandKeyboard *keyboard,
|
||||||
|
guint32 serial,
|
||||||
|
ClutterModifierType modifier_state)
|
||||||
|
{
|
||||||
|
MetaWaylandKeyboardGrab *grab = keyboard->grab;
|
||||||
|
MetaWaylandXkbState new_state;
|
||||||
|
|
||||||
|
/* In the evdev case, the state is shared with the clutter backend, so
|
||||||
|
we don't need to update it */
|
||||||
|
if (!keyboard->is_evdev)
|
||||||
|
update_state_from_clutter (keyboard, modifier_state);
|
||||||
|
|
||||||
|
new_state.mods_depressed = xkb_state_serialize_mods (keyboard->xkb_state,
|
||||||
|
XKB_STATE_MODS_DEPRESSED);
|
||||||
|
new_state.mods_latched = xkb_state_serialize_mods (keyboard->xkb_state,
|
||||||
|
XKB_STATE_MODS_LATCHED);
|
||||||
|
new_state.mods_locked = xkb_state_serialize_mods (keyboard->xkb_state,
|
||||||
|
XKB_STATE_MODS_LOCKED);
|
||||||
|
new_state.group = xkb_state_serialize_layout (keyboard->xkb_state,
|
||||||
|
XKB_STATE_LAYOUT_EFFECTIVE);
|
||||||
|
|
||||||
|
if (state_equal (&keyboard->modifier_state, &new_state))
|
||||||
|
return;
|
||||||
|
|
||||||
|
keyboard->modifier_state = new_state;
|
||||||
|
|
||||||
grab->interface->modifiers (grab,
|
grab->interface->modifiers (grab,
|
||||||
serial,
|
serial,
|
||||||
depressed_mods,
|
new_state.mods_depressed,
|
||||||
0, /* latched_modes */
|
new_state.mods_latched,
|
||||||
locked_mods,
|
new_state.mods_locked,
|
||||||
0 /* group */);
|
new_state.group);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -416,7 +470,7 @@ meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
|
|||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_warning ("unexpected key release event for key 0x%x", evdev_code);
|
g_warning ("unexpected key release event for key 0x%x (%d)", evdev_code, event->keyval);
|
||||||
|
|
||||||
found:
|
found:
|
||||||
(void) 0;
|
(void) 0;
|
||||||
@@ -462,10 +516,10 @@ meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard,
|
|||||||
display = wl_client_get_display (client);
|
display = wl_client_get_display (client);
|
||||||
serial = wl_display_next_serial (display);
|
serial = wl_display_next_serial (display);
|
||||||
wl_keyboard_send_modifiers (resource, serial,
|
wl_keyboard_send_modifiers (resource, serial,
|
||||||
keyboard->modifiers.mods_depressed,
|
keyboard->modifier_state.mods_depressed,
|
||||||
keyboard->modifiers.mods_latched,
|
keyboard->modifier_state.mods_latched,
|
||||||
keyboard->modifiers.mods_locked,
|
keyboard->modifier_state.mods_locked,
|
||||||
keyboard->modifiers.group);
|
keyboard->modifier_state.group);
|
||||||
wl_keyboard_send_enter (resource, serial, surface->resource,
|
wl_keyboard_send_enter (resource, serial, surface->resource,
|
||||||
&keyboard->keys);
|
&keyboard->keys);
|
||||||
wl_resource_add_destroy_listener (resource, &keyboard->focus_listener);
|
wl_resource_add_destroy_listener (resource, &keyboard->focus_listener);
|
||||||
@@ -504,9 +558,61 @@ meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard)
|
|||||||
|
|
||||||
meta_wayland_xkb_info_destroy (&keyboard->xkb_info);
|
meta_wayland_xkb_info_destroy (&keyboard->xkb_info);
|
||||||
xkb_context_unref (keyboard->xkb_context);
|
xkb_context_unref (keyboard->xkb_context);
|
||||||
|
xkb_state_unref (keyboard->xkb_state);
|
||||||
|
|
||||||
/* XXX: What about keyboard->resource_list? */
|
/* XXX: What about keyboard->resource_list? */
|
||||||
if (keyboard->focus_resource)
|
if (keyboard->focus_resource)
|
||||||
wl_list_remove (&keyboard->focus_listener.link);
|
wl_list_remove (&keyboard->focus_listener.link);
|
||||||
wl_array_release (&keyboard->keys);
|
wl_array_release (&keyboard->keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
modal_key (MetaWaylandKeyboardGrab *grab,
|
||||||
|
uint32_t time,
|
||||||
|
uint32_t key,
|
||||||
|
uint32_t state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
modal_modifiers (MetaWaylandKeyboardGrab *grab,
|
||||||
|
uint32_t serial,
|
||||||
|
uint32_t mods_depressed,
|
||||||
|
uint32_t mods_latched,
|
||||||
|
uint32_t mods_locked,
|
||||||
|
uint32_t group)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static MetaWaylandKeyboardGrabInterface modal_grab = {
|
||||||
|
modal_key,
|
||||||
|
modal_modifiers,
|
||||||
|
};
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_wayland_keyboard_begin_modal (MetaWaylandKeyboard *keyboard)
|
||||||
|
{
|
||||||
|
MetaWaylandKeyboardGrab *grab;
|
||||||
|
|
||||||
|
if (keyboard->grab != &keyboard->default_grab)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
grab = g_slice_new0 (MetaWaylandKeyboardGrab);
|
||||||
|
grab->interface = &modal_grab;
|
||||||
|
meta_wayland_keyboard_start_grab (keyboard, grab);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_wayland_keyboard_end_modal (MetaWaylandKeyboard *keyboard)
|
||||||
|
{
|
||||||
|
MetaWaylandKeyboardGrab *grab;
|
||||||
|
|
||||||
|
grab = keyboard->grab;
|
||||||
|
|
||||||
|
g_assert (grab->interface == &modal_grab);
|
||||||
|
|
||||||
|
meta_wayland_keyboard_end_grab (keyboard);
|
||||||
|
g_slice_free (MetaWaylandKeyboardGrab, grab);
|
||||||
|
}
|
||||||
|
@@ -52,7 +52,8 @@
|
|||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
|
meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
|
||||||
struct wl_display *display);
|
struct wl_display *display,
|
||||||
|
gboolean is_evdev);
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
|
meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
|
||||||
@@ -69,6 +70,11 @@ meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *device,
|
|||||||
void
|
void
|
||||||
meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard);
|
meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_wayland_keyboard_begin_modal (MetaWaylandKeyboard *keyboard);
|
||||||
|
void
|
||||||
|
meta_wayland_keyboard_end_modal (MetaWaylandKeyboard *keyboard);
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard);
|
meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard);
|
||||||
|
|
||||||
|
@@ -45,6 +45,8 @@
|
|||||||
|
|
||||||
#include "meta-wayland-pointer.h"
|
#include "meta-wayland-pointer.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
static MetaWaylandSeat *
|
static MetaWaylandSeat *
|
||||||
meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer)
|
meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer)
|
||||||
{
|
{
|
||||||
@@ -192,10 +194,10 @@ meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer,
|
|||||||
{
|
{
|
||||||
wl_keyboard_send_modifiers (kr,
|
wl_keyboard_send_modifiers (kr,
|
||||||
serial,
|
serial,
|
||||||
kbd->modifiers.mods_depressed,
|
kbd->modifier_state.mods_depressed,
|
||||||
kbd->modifiers.mods_latched,
|
kbd->modifier_state.mods_latched,
|
||||||
kbd->modifiers.mods_locked,
|
kbd->modifier_state.mods_locked,
|
||||||
kbd->modifiers.group);
|
kbd->modifier_state.group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wl_pointer_send_enter (resource, serial, surface->resource, sx, sy);
|
wl_pointer_send_enter (resource, serial, surface->resource, sx, sy);
|
||||||
@@ -260,3 +262,65 @@ meta_wayland_pointer_set_current (MetaWaylandPointer *pointer,
|
|||||||
&pointer->current_listener);
|
&pointer->current_listener);
|
||||||
pointer->current_listener.notify = current_surface_destroy;
|
pointer->current_listener.notify = current_surface_destroy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
modal_focus (MetaWaylandPointerGrab *grab,
|
||||||
|
MetaWaylandSurface *surface,
|
||||||
|
wl_fixed_t x,
|
||||||
|
wl_fixed_t y)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
modal_motion (MetaWaylandPointerGrab *grab,
|
||||||
|
uint32_t time,
|
||||||
|
wl_fixed_t x,
|
||||||
|
wl_fixed_t y)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
modal_button (MetaWaylandPointerGrab *grab,
|
||||||
|
uint32_t time,
|
||||||
|
uint32_t button,
|
||||||
|
uint32_t state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static MetaWaylandPointerGrabInterface modal_grab = {
|
||||||
|
modal_focus,
|
||||||
|
modal_motion,
|
||||||
|
modal_button
|
||||||
|
};
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_wayland_pointer_begin_modal (MetaWaylandPointer *pointer)
|
||||||
|
{
|
||||||
|
MetaWaylandPointerGrab *grab;
|
||||||
|
|
||||||
|
if (pointer->grab != &pointer->default_grab)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
meta_wayland_pointer_set_focus (pointer, NULL,
|
||||||
|
wl_fixed_from_int (0),
|
||||||
|
wl_fixed_from_int (0));
|
||||||
|
|
||||||
|
grab = g_slice_new0 (MetaWaylandPointerGrab);
|
||||||
|
grab->interface = &modal_grab;
|
||||||
|
meta_wayland_pointer_start_grab (pointer, grab);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_wayland_pointer_end_modal (MetaWaylandPointer *pointer)
|
||||||
|
{
|
||||||
|
MetaWaylandPointerGrab *grab;
|
||||||
|
|
||||||
|
grab = pointer->grab;
|
||||||
|
|
||||||
|
g_assert (grab->interface == &modal_grab);
|
||||||
|
|
||||||
|
meta_wayland_pointer_end_grab (pointer);
|
||||||
|
g_slice_free (MetaWaylandPointerGrab, grab);
|
||||||
|
}
|
||||||
|
@@ -42,6 +42,11 @@ meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer,
|
|||||||
void
|
void
|
||||||
meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer);
|
meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_wayland_pointer_begin_modal (MetaWaylandPointer *pointer);
|
||||||
|
void
|
||||||
|
meta_wayland_pointer_end_modal (MetaWaylandPointer *pointer);
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_wayland_pointer_set_current (MetaWaylandPointer *pointer,
|
meta_wayland_pointer_set_current (MetaWaylandPointer *pointer,
|
||||||
MetaWaylandSurface *surface);
|
MetaWaylandSurface *surface);
|
||||||
|
@@ -28,6 +28,8 @@
|
|||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
|
|
||||||
#include "window-private.h"
|
#include "window-private.h"
|
||||||
|
#include "meta-tty.h"
|
||||||
|
#include <meta/meta-cursor-tracker.h>
|
||||||
|
|
||||||
typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
|
typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
|
||||||
|
|
||||||
@@ -109,26 +111,6 @@ typedef struct
|
|||||||
struct wl_listener surface_destroy_listener;
|
struct wl_listener surface_destroy_listener;
|
||||||
} MetaWaylandShellSurface;
|
} MetaWaylandShellSurface;
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
guint32 flags;
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
int refresh;
|
|
||||||
} MetaWaylandMode;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
struct wl_object wayland_output;
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
int width_mm;
|
|
||||||
int height_mm;
|
|
||||||
/* XXX: with sliced stages we'd reference a CoglFramebuffer here. */
|
|
||||||
|
|
||||||
GList *modes;
|
|
||||||
} MetaWaylandOutput;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
GSource source;
|
GSource source;
|
||||||
@@ -148,11 +130,12 @@ typedef struct
|
|||||||
|
|
||||||
struct _MetaWaylandCompositor
|
struct _MetaWaylandCompositor
|
||||||
{
|
{
|
||||||
|
GHashTable *outputs;
|
||||||
|
|
||||||
struct wl_display *wayland_display;
|
struct wl_display *wayland_display;
|
||||||
struct wl_event_loop *wayland_loop;
|
struct wl_event_loop *wayland_loop;
|
||||||
GMainLoop *init_loop;
|
GMainLoop *init_loop;
|
||||||
ClutterActor *stage;
|
ClutterActor *stage;
|
||||||
GList *outputs;
|
|
||||||
GSource *wayland_event_source;
|
GSource *wayland_event_source;
|
||||||
GList *surfaces;
|
GList *surfaces;
|
||||||
struct wl_list frame_callbacks;
|
struct wl_list frame_callbacks;
|
||||||
@@ -165,6 +148,10 @@ struct _MetaWaylandCompositor
|
|||||||
struct wl_client *xwayland_client;
|
struct wl_client *xwayland_client;
|
||||||
struct wl_resource *xserver_resource;
|
struct wl_resource *xserver_resource;
|
||||||
|
|
||||||
|
MetaTTY *tty;
|
||||||
|
int drm_fd;
|
||||||
|
GSocket *weston_launch;
|
||||||
|
|
||||||
MetaWaylandSeat *seat;
|
MetaWaylandSeat *seat;
|
||||||
|
|
||||||
/* This surface is only used to keep drag of the implicit grab when
|
/* This surface is only used to keep drag of the implicit grab when
|
||||||
@@ -251,6 +238,14 @@ typedef struct
|
|||||||
xkb_mod_index_t mod5_mod;
|
xkb_mod_index_t mod5_mod;
|
||||||
} MetaWaylandXkbInfo;
|
} MetaWaylandXkbInfo;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t mods_depressed;
|
||||||
|
uint32_t mods_latched;
|
||||||
|
uint32_t mods_locked;
|
||||||
|
uint32_t group;
|
||||||
|
} MetaWaylandXkbState;
|
||||||
|
|
||||||
struct _MetaWaylandKeyboard
|
struct _MetaWaylandKeyboard
|
||||||
{
|
{
|
||||||
struct wl_list resource_list;
|
struct wl_list resource_list;
|
||||||
@@ -268,25 +263,19 @@ struct _MetaWaylandKeyboard
|
|||||||
|
|
||||||
struct wl_array keys;
|
struct wl_array keys;
|
||||||
|
|
||||||
struct
|
MetaWaylandXkbState modifier_state;
|
||||||
{
|
|
||||||
uint32_t mods_depressed;
|
|
||||||
uint32_t mods_latched;
|
|
||||||
uint32_t mods_locked;
|
|
||||||
uint32_t group;
|
|
||||||
} modifiers;
|
|
||||||
|
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
|
|
||||||
struct xkb_context *xkb_context;
|
struct xkb_context *xkb_context;
|
||||||
|
struct xkb_state *xkb_state;
|
||||||
|
gboolean is_evdev;
|
||||||
|
|
||||||
MetaWaylandXkbInfo xkb_info;
|
MetaWaylandXkbInfo xkb_info;
|
||||||
struct xkb_rule_names xkb_names;
|
struct xkb_rule_names xkb_names;
|
||||||
|
|
||||||
MetaWaylandKeyboardGrab input_method_grab;
|
MetaWaylandKeyboardGrab input_method_grab;
|
||||||
struct wl_resource *input_method_resource;
|
struct wl_resource *input_method_resource;
|
||||||
|
|
||||||
ClutterModifierType last_modifier_state;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _MetaWaylandDataOffer
|
struct _MetaWaylandDataOffer
|
||||||
@@ -335,6 +324,7 @@ struct _MetaWaylandSeat
|
|||||||
|
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
|
|
||||||
|
MetaCursorTracker *cursor_tracker;
|
||||||
MetaWaylandSurface *sprite;
|
MetaWaylandSurface *sprite;
|
||||||
int hotspot_x, hotspot_y;
|
int hotspot_x, hotspot_y;
|
||||||
struct wl_listener sprite_destroy_listener;
|
struct wl_listener sprite_destroy_listener;
|
||||||
@@ -349,13 +339,14 @@ void meta_wayland_finalize (void);
|
|||||||
* API after meta_wayland_init() has been called. */
|
* API after meta_wayland_init() has been called. */
|
||||||
MetaWaylandCompositor *meta_wayland_compositor_get_default (void);
|
MetaWaylandCompositor *meta_wayland_compositor_get_default (void);
|
||||||
|
|
||||||
void meta_wayland_handle_sig_child (void);
|
|
||||||
|
|
||||||
void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor);
|
void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor);
|
||||||
|
|
||||||
void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
|
void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
|
||||||
MetaWindow *window);
|
MetaWindow *window);
|
||||||
|
|
||||||
|
MetaTTY *meta_wayland_compositor_get_tty (MetaWaylandCompositor *compositor);
|
||||||
|
gboolean meta_wayland_compositor_is_native (MetaWaylandCompositor *compositor);
|
||||||
|
|
||||||
void meta_wayland_surface_free (MetaWaylandSurface *surface);
|
void meta_wayland_surface_free (MetaWaylandSurface *surface);
|
||||||
|
|
||||||
#endif /* META_WAYLAND_PRIVATE_H */
|
#endif /* META_WAYLAND_PRIVATE_H */
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
#include "meta-window-actor-private.h"
|
#include "meta-window-actor-private.h"
|
||||||
#include "meta/meta-shaped-texture.h"
|
#include "meta/meta-shaped-texture.h"
|
||||||
#include "meta-wayland-stage.h"
|
#include "meta-wayland-stage.h"
|
||||||
|
#include "meta-cursor-tracker-private.h"
|
||||||
|
|
||||||
#define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int (10)
|
#define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int (10)
|
||||||
|
|
||||||
@@ -73,10 +74,14 @@ transform_stage_point_fixed (MetaWaylandSurface *surface,
|
|||||||
static void
|
static void
|
||||||
pointer_unmap_sprite (MetaWaylandSeat *seat)
|
pointer_unmap_sprite (MetaWaylandSeat *seat)
|
||||||
{
|
{
|
||||||
if (seat->current_stage)
|
if (seat->cursor_tracker)
|
||||||
{
|
{
|
||||||
MetaWaylandStage *stage = META_WAYLAND_STAGE (seat->current_stage);
|
meta_cursor_tracker_set_sprite (seat->cursor_tracker,
|
||||||
meta_wayland_stage_set_invisible_cursor (stage);
|
NULL, 0, 0);
|
||||||
|
|
||||||
|
if (seat->current_stage)
|
||||||
|
meta_cursor_tracker_queue_redraw (seat->cursor_tracker,
|
||||||
|
CLUTTER_ACTOR (seat->current_stage));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seat->sprite)
|
if (seat->sprite)
|
||||||
@@ -89,22 +94,29 @@ pointer_unmap_sprite (MetaWaylandSeat *seat)
|
|||||||
void
|
void
|
||||||
meta_wayland_seat_update_sprite (MetaWaylandSeat *seat)
|
meta_wayland_seat_update_sprite (MetaWaylandSeat *seat)
|
||||||
{
|
{
|
||||||
|
ClutterBackend *backend;
|
||||||
|
CoglContext *context;
|
||||||
|
struct wl_resource *buffer;
|
||||||
|
CoglTexture2D *texture;
|
||||||
|
|
||||||
|
if (seat->cursor_tracker == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
backend = clutter_get_default_backend ();
|
||||||
|
context = clutter_backend_get_cogl_context (backend);
|
||||||
|
buffer = seat->sprite->buffer_ref.buffer->resource;
|
||||||
|
texture = cogl_wayland_texture_2d_new_from_buffer (context, buffer, NULL);
|
||||||
|
|
||||||
|
meta_cursor_tracker_set_sprite (seat->cursor_tracker,
|
||||||
|
texture,
|
||||||
|
seat->hotspot_x,
|
||||||
|
seat->hotspot_y);
|
||||||
|
|
||||||
if (seat->current_stage)
|
if (seat->current_stage)
|
||||||
{
|
meta_cursor_tracker_queue_redraw (seat->cursor_tracker,
|
||||||
MetaWaylandStage *stage = META_WAYLAND_STAGE (seat->current_stage);
|
CLUTTER_ACTOR (seat->current_stage));
|
||||||
ClutterBackend *backend = clutter_get_default_backend ();
|
|
||||||
CoglContext *context = clutter_backend_get_cogl_context (backend);
|
|
||||||
struct wl_resource *buffer = seat->sprite->buffer_ref.buffer->resource;
|
|
||||||
CoglTexture2D *texture =
|
|
||||||
cogl_wayland_texture_2d_new_from_buffer (context, buffer, NULL);
|
|
||||||
|
|
||||||
meta_wayland_stage_set_cursor_from_texture (stage,
|
cogl_object_unref (texture);
|
||||||
COGL_TEXTURE (texture),
|
|
||||||
seat->hotspot_x,
|
|
||||||
seat->hotspot_y);
|
|
||||||
|
|
||||||
cogl_object_unref (texture);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -258,7 +270,8 @@ pointer_handle_sprite_destroy (struct wl_listener *listener, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MetaWaylandSeat *
|
MetaWaylandSeat *
|
||||||
meta_wayland_seat_new (struct wl_display *display)
|
meta_wayland_seat_new (struct wl_display *display,
|
||||||
|
gboolean is_native)
|
||||||
{
|
{
|
||||||
MetaWaylandSeat *seat = g_new0 (MetaWaylandSeat, 1);
|
MetaWaylandSeat *seat = g_new0 (MetaWaylandSeat, 1);
|
||||||
|
|
||||||
@@ -272,7 +285,7 @@ meta_wayland_seat_new (struct wl_display *display)
|
|||||||
|
|
||||||
meta_wayland_pointer_init (&seat->pointer);
|
meta_wayland_pointer_init (&seat->pointer);
|
||||||
|
|
||||||
meta_wayland_keyboard_init (&seat->keyboard, display);
|
meta_wayland_keyboard_init (&seat->keyboard, display, is_native);
|
||||||
|
|
||||||
seat->display = display;
|
seat->display = display;
|
||||||
|
|
||||||
@@ -507,14 +520,14 @@ meta_wayland_seat_repick (MetaWaylandSeat *seat,
|
|||||||
surface = meta_shaped_texture_get_wayland_surface (shaped_texture);
|
surface = meta_shaped_texture_get_wayland_surface (shaped_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (surface != pointer->current)
|
pointer->current = surface;
|
||||||
|
if (surface != pointer->focus)
|
||||||
{
|
{
|
||||||
const MetaWaylandPointerGrabInterface *interface =
|
const MetaWaylandPointerGrabInterface *interface =
|
||||||
pointer->grab->interface;
|
pointer->grab->interface;
|
||||||
interface->focus (pointer->grab,
|
interface->focus (pointer->grab,
|
||||||
surface,
|
surface,
|
||||||
pointer->current_x, pointer->current_y);
|
pointer->current_x, pointer->current_y);
|
||||||
pointer->current = surface;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pointer->grab->focus)
|
if (pointer->grab->focus)
|
||||||
|
@@ -30,7 +30,8 @@
|
|||||||
#include "meta-wayland-private.h"
|
#include "meta-wayland-private.h"
|
||||||
|
|
||||||
MetaWaylandSeat *
|
MetaWaylandSeat *
|
||||||
meta_wayland_seat_new (struct wl_display *display);
|
meta_wayland_seat_new (struct wl_display *display,
|
||||||
|
gboolean is_native);
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_wayland_seat_handle_event (MetaWaylandSeat *seat,
|
meta_wayland_seat_handle_event (MetaWaylandSeat *seat,
|
||||||
|
@@ -28,157 +28,34 @@
|
|||||||
#include "meta-wayland-stage.h"
|
#include "meta-wayland-stage.h"
|
||||||
#include "meta/meta-window-actor.h"
|
#include "meta/meta-window-actor.h"
|
||||||
#include "meta/meta-shaped-texture.h"
|
#include "meta/meta-shaped-texture.h"
|
||||||
|
#include "meta-cursor-tracker-private.h"
|
||||||
#define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X 7
|
|
||||||
#define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y 4
|
|
||||||
|
|
||||||
G_DEFINE_TYPE (MetaWaylandStage, meta_wayland_stage, CLUTTER_TYPE_STAGE);
|
G_DEFINE_TYPE (MetaWaylandStage, meta_wayland_stage, CLUTTER_TYPE_STAGE);
|
||||||
|
|
||||||
static void
|
|
||||||
meta_wayland_stage_finalize (GObject *object)
|
|
||||||
{
|
|
||||||
MetaWaylandStage *self = (MetaWaylandStage *) object;
|
|
||||||
|
|
||||||
cogl_object_unref (self->default_cursor_pipeline);
|
|
||||||
cogl_object_unref (self->texture_cursor_pipeline);
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (meta_wayland_stage_parent_class)->finalize (object);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
get_cursor_draw_position (MetaWaylandStage *self,
|
|
||||||
cairo_rectangle_int_t *rect)
|
|
||||||
{
|
|
||||||
rect->x = self->cursor_x - self->cursor_hotspot_x;
|
|
||||||
rect->y = self->cursor_y - self->cursor_hotspot_y;
|
|
||||||
rect->width = self->cursor_width;
|
|
||||||
rect->height = self->cursor_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
draw_cursor_pipeline (MetaWaylandStage *self,
|
|
||||||
CoglPipeline *pipeline)
|
|
||||||
{
|
|
||||||
cairo_rectangle_int_t rect;
|
|
||||||
|
|
||||||
get_cursor_draw_position (self, &rect);
|
|
||||||
|
|
||||||
cogl_framebuffer_draw_rectangle (cogl_get_draw_framebuffer (),
|
|
||||||
pipeline,
|
|
||||||
rect.x, rect.y,
|
|
||||||
rect.x + rect.width,
|
|
||||||
rect.y + rect.height);
|
|
||||||
|
|
||||||
self->has_last_cursor_position = TRUE;
|
|
||||||
self->last_cursor_position = rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
meta_wayland_stage_paint (ClutterActor *actor)
|
meta_wayland_stage_paint (ClutterActor *actor)
|
||||||
{
|
{
|
||||||
MetaWaylandStage *self = META_WAYLAND_STAGE (actor);
|
MetaWaylandCompositor *compositor;
|
||||||
|
|
||||||
CLUTTER_ACTOR_CLASS (meta_wayland_stage_parent_class)->paint (actor);
|
CLUTTER_ACTOR_CLASS (meta_wayland_stage_parent_class)->paint (actor);
|
||||||
|
|
||||||
/* Make sure the cursor is always painted on top of all of the other
|
compositor = meta_wayland_compositor_get_default ();
|
||||||
actors */
|
if (compositor->seat->cursor_tracker)
|
||||||
|
meta_cursor_tracker_paint (compositor->seat->cursor_tracker);
|
||||||
switch (self->cursor_type)
|
|
||||||
{
|
|
||||||
case META_WAYLAND_STAGE_CURSOR_INVISIBLE:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case META_WAYLAND_STAGE_CURSOR_DEFAULT:
|
|
||||||
draw_cursor_pipeline (self, self->default_cursor_pipeline);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case META_WAYLAND_STAGE_CURSOR_TEXTURE:
|
|
||||||
draw_cursor_pipeline (self, self->texture_cursor_pipeline);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
update_cursor_position (MetaWaylandStage *self)
|
|
||||||
{
|
|
||||||
cairo_rectangle_int_t rect;
|
|
||||||
|
|
||||||
if (self->has_last_cursor_position)
|
|
||||||
{
|
|
||||||
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (self),
|
|
||||||
&self->last_cursor_position);
|
|
||||||
self->has_last_cursor_position = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_cursor_draw_position (self, &rect);
|
|
||||||
if (rect.width != 0 && rect.height != 0)
|
|
||||||
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (self), &rect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
meta_wayland_stage_class_init (MetaWaylandStageClass *klass)
|
meta_wayland_stage_class_init (MetaWaylandStageClass *klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
|
||||||
ClutterActorClass *actor_class = (ClutterActorClass *) klass;
|
ClutterActorClass *actor_class = (ClutterActorClass *) klass;
|
||||||
|
|
||||||
gobject_class->finalize = meta_wayland_stage_finalize;
|
|
||||||
|
|
||||||
actor_class->paint = meta_wayland_stage_paint;
|
actor_class->paint = meta_wayland_stage_paint;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
load_default_cursor_pipeline (MetaWaylandStage *self)
|
|
||||||
{
|
|
||||||
CoglContext *context =
|
|
||||||
clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
|
||||||
CoglTexture *texture;
|
|
||||||
CoglError *error = NULL;
|
|
||||||
char *filename;
|
|
||||||
|
|
||||||
filename = g_build_filename (MUTTER_DATADIR,
|
|
||||||
"mutter/cursors/left_ptr.png",
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
texture = cogl_texture_new_from_file (filename,
|
|
||||||
COGL_TEXTURE_NONE,
|
|
||||||
COGL_PIXEL_FORMAT_ANY,
|
|
||||||
&error);
|
|
||||||
|
|
||||||
g_free (filename);
|
|
||||||
|
|
||||||
self->default_cursor_pipeline = cogl_pipeline_new (context);
|
|
||||||
cogl_pipeline_set_layer_filters (self->default_cursor_pipeline,
|
|
||||||
0, /* layer */
|
|
||||||
COGL_PIPELINE_FILTER_NEAREST,
|
|
||||||
COGL_PIPELINE_FILTER_NEAREST);
|
|
||||||
|
|
||||||
if (texture == NULL)
|
|
||||||
{
|
|
||||||
g_warning ("Failed to load default cursor: %s",
|
|
||||||
error->message);
|
|
||||||
cogl_error_free (error);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self->default_cursor_width = cogl_texture_get_width (texture);
|
|
||||||
self->default_cursor_height = cogl_texture_get_height (texture);
|
|
||||||
|
|
||||||
cogl_pipeline_set_layer_texture (self->default_cursor_pipeline,
|
|
||||||
0, /* layer */
|
|
||||||
texture);
|
|
||||||
cogl_object_unref (texture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
meta_wayland_stage_init (MetaWaylandStage *self)
|
meta_wayland_stage_init (MetaWaylandStage *self)
|
||||||
{
|
{
|
||||||
load_default_cursor_pipeline (self);
|
clutter_stage_set_user_resizable (CLUTTER_STAGE (self), FALSE);
|
||||||
|
|
||||||
self->texture_cursor_pipeline =
|
|
||||||
cogl_pipeline_copy (self->default_cursor_pipeline);
|
|
||||||
|
|
||||||
meta_wayland_stage_set_default_cursor (self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClutterActor *
|
ClutterActor *
|
||||||
@@ -189,55 +66,3 @@ meta_wayland_stage_new (void)
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
meta_wayland_stage_set_cursor_position (MetaWaylandStage *self,
|
|
||||||
int x,
|
|
||||||
int y)
|
|
||||||
{
|
|
||||||
self->cursor_x = x;
|
|
||||||
self->cursor_y = y;
|
|
||||||
update_cursor_position (self);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
meta_wayland_stage_set_cursor_from_texture (MetaWaylandStage *self,
|
|
||||||
CoglTexture *texture,
|
|
||||||
int hotspot_x,
|
|
||||||
int hotspot_y)
|
|
||||||
{
|
|
||||||
CoglPipeline *pipeline;
|
|
||||||
|
|
||||||
self->cursor_hotspot_x = hotspot_x;
|
|
||||||
self->cursor_hotspot_y = hotspot_y;
|
|
||||||
self->cursor_type = META_WAYLAND_STAGE_CURSOR_TEXTURE;
|
|
||||||
|
|
||||||
pipeline = cogl_pipeline_copy (self->texture_cursor_pipeline);
|
|
||||||
cogl_pipeline_set_layer_texture (pipeline, 0, texture);
|
|
||||||
cogl_object_unref (self->texture_cursor_pipeline);
|
|
||||||
self->texture_cursor_pipeline = pipeline;
|
|
||||||
|
|
||||||
self->cursor_width = cogl_texture_get_width (texture);
|
|
||||||
self->cursor_height = cogl_texture_get_height (texture);
|
|
||||||
|
|
||||||
update_cursor_position (self);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
meta_wayland_stage_set_invisible_cursor (MetaWaylandStage *self)
|
|
||||||
{
|
|
||||||
self->cursor_type = META_WAYLAND_STAGE_CURSOR_INVISIBLE;
|
|
||||||
self->cursor_width = 0;
|
|
||||||
self->cursor_height = 0;
|
|
||||||
update_cursor_position (self);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
meta_wayland_stage_set_default_cursor (MetaWaylandStage *self)
|
|
||||||
{
|
|
||||||
self->cursor_type = META_WAYLAND_STAGE_CURSOR_DEFAULT;
|
|
||||||
self->cursor_hotspot_x = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X;
|
|
||||||
self->cursor_hotspot_y = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y;
|
|
||||||
self->cursor_width = self->default_cursor_width;
|
|
||||||
self->cursor_height = self->default_cursor_height;
|
|
||||||
update_cursor_position (self);
|
|
||||||
}
|
|
||||||
|
@@ -59,53 +59,12 @@ struct _MetaWaylandStageClass
|
|||||||
struct _MetaWaylandStage
|
struct _MetaWaylandStage
|
||||||
{
|
{
|
||||||
ClutterStage parent;
|
ClutterStage parent;
|
||||||
|
|
||||||
/* A pipeline containing the cursor texture that will be used when
|
|
||||||
the cursor is not over a surface */
|
|
||||||
CoglPipeline *default_cursor_pipeline;
|
|
||||||
int default_cursor_width;
|
|
||||||
int default_cursor_height;
|
|
||||||
|
|
||||||
CoglPipeline *texture_cursor_pipeline;
|
|
||||||
|
|
||||||
int cursor_x;
|
|
||||||
int cursor_y;
|
|
||||||
int cursor_width;
|
|
||||||
int cursor_height;
|
|
||||||
int cursor_hotspot_x;
|
|
||||||
int cursor_hotspot_y;
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
/* No cursor will be drawn */
|
|
||||||
META_WAYLAND_STAGE_CURSOR_INVISIBLE,
|
|
||||||
/* The cursor will be drawn from our default cursor image */
|
|
||||||
META_WAYLAND_STAGE_CURSOR_DEFAULT,
|
|
||||||
/* The cursor will be drawn using a custom texture */
|
|
||||||
META_WAYLAND_STAGE_CURSOR_TEXTURE
|
|
||||||
} cursor_type;
|
|
||||||
|
|
||||||
gboolean has_last_cursor_position;
|
|
||||||
cairo_rectangle_int_t last_cursor_position;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
GType meta_wayland_stage_get_type (void) G_GNUC_CONST;
|
GType meta_wayland_stage_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
ClutterActor *meta_wayland_stage_new (void);
|
ClutterActor *meta_wayland_stage_new (void);
|
||||||
|
|
||||||
void meta_wayland_stage_set_cursor_position (MetaWaylandStage *stage,
|
|
||||||
int x,
|
|
||||||
int y);
|
|
||||||
|
|
||||||
void meta_wayland_stage_set_default_cursor (MetaWaylandStage *self);
|
|
||||||
|
|
||||||
void meta_wayland_stage_set_cursor_from_texture (MetaWaylandStage *self,
|
|
||||||
CoglTexture *texture,
|
|
||||||
int hotspot_x,
|
|
||||||
int hotspot_y);
|
|
||||||
|
|
||||||
void meta_wayland_stage_set_invisible_cursor (MetaWaylandStage *self);
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* META_WAYLAND_STAGE_H */
|
#endif /* META_WAYLAND_STAGE_H */
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
#include <clutter/clutter.h>
|
#include <clutter/clutter.h>
|
||||||
#include <clutter/wayland/clutter-wayland-compositor.h>
|
#include <clutter/wayland/clutter-wayland-compositor.h>
|
||||||
#include <clutter/wayland/clutter-wayland-surface.h>
|
#include <clutter/wayland/clutter-wayland-surface.h>
|
||||||
|
#include <clutter/evdev/clutter-evdev.h>
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
@@ -31,12 +32,12 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include <wayland-server.h>
|
#include <wayland-server.h>
|
||||||
|
|
||||||
#include "xserver-server-protocol.h"
|
|
||||||
|
|
||||||
#include "meta-wayland-private.h"
|
#include "meta-wayland-private.h"
|
||||||
#include "meta-xwayland-private.h"
|
#include "meta-xwayland-private.h"
|
||||||
#include "meta-wayland-stage.h"
|
#include "meta-wayland-stage.h"
|
||||||
@@ -45,11 +46,14 @@
|
|||||||
#include "meta-wayland-keyboard.h"
|
#include "meta-wayland-keyboard.h"
|
||||||
#include "meta-wayland-pointer.h"
|
#include "meta-wayland-pointer.h"
|
||||||
#include "meta-wayland-data-device.h"
|
#include "meta-wayland-data-device.h"
|
||||||
|
#include "meta-cursor-tracker-private.h"
|
||||||
#include "display-private.h"
|
#include "display-private.h"
|
||||||
#include "window-private.h"
|
#include "window-private.h"
|
||||||
#include <meta/types.h>
|
#include <meta/types.h>
|
||||||
#include <meta/main.h>
|
#include <meta/main.h>
|
||||||
#include "frame.h"
|
#include "frame.h"
|
||||||
|
#include "meta-weston-launch.h"
|
||||||
|
#include "meta-idle-monitor-private.h"
|
||||||
|
|
||||||
static MetaWaylandCompositor _meta_wayland_compositor;
|
static MetaWaylandCompositor _meta_wayland_compositor;
|
||||||
|
|
||||||
@@ -210,6 +214,20 @@ surface_process_damage (MetaWaylandSurface *surface,
|
|||||||
MetaWindowActor *window_actor =
|
MetaWindowActor *window_actor =
|
||||||
META_WINDOW_ACTOR (meta_window_get_compositor_private (surface->window));
|
META_WINDOW_ACTOR (meta_window_get_compositor_private (surface->window));
|
||||||
|
|
||||||
|
if (!surface->window->override_redirect)
|
||||||
|
{
|
||||||
|
MetaRectangle rect;
|
||||||
|
cairo_rectangle_int_t cairo_rect;
|
||||||
|
|
||||||
|
meta_window_get_input_rect (surface->window, &rect);
|
||||||
|
cairo_rect.x = 0;
|
||||||
|
cairo_rect.y = 0;
|
||||||
|
cairo_rect.width = rect.width;
|
||||||
|
cairo_rect.height = rect.height;
|
||||||
|
|
||||||
|
cairo_region_intersect_rectangle (region, &cairo_rect);
|
||||||
|
}
|
||||||
|
|
||||||
if (window_actor)
|
if (window_actor)
|
||||||
{
|
{
|
||||||
int i, n_rectangles = cairo_region_num_rectangles (region);
|
int i, n_rectangles = cairo_region_num_rectangles (region);
|
||||||
@@ -629,84 +647,182 @@ meta_wayland_compositor_create_region (struct wl_client *wayland_client,
|
|||||||
region->region = cairo_region_create ();
|
region->region = cairo_region_create ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
MetaOutput *output;
|
||||||
|
struct wl_global *global;
|
||||||
|
int x, y;
|
||||||
|
enum wl_output_transform transform;
|
||||||
|
|
||||||
|
GList *resources;
|
||||||
|
} MetaWaylandOutput;
|
||||||
|
|
||||||
|
static void
|
||||||
|
output_resource_destroy (struct wl_resource *res)
|
||||||
|
{
|
||||||
|
MetaWaylandOutput *wayland_output;
|
||||||
|
|
||||||
|
wayland_output = wl_resource_get_user_data (res);
|
||||||
|
wayland_output->resources = g_list_remove (wayland_output->resources, res);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
bind_output (struct wl_client *client,
|
bind_output (struct wl_client *client,
|
||||||
void *data,
|
void *data,
|
||||||
guint32 version,
|
guint32 version,
|
||||||
guint32 id)
|
guint32 id)
|
||||||
{
|
{
|
||||||
MetaWaylandOutput *output = data;
|
MetaWaylandOutput *wayland_output = data;
|
||||||
struct wl_resource *resource =
|
MetaOutput *output = wayland_output->output;
|
||||||
wl_resource_create (client, &wl_output_interface, version, id);
|
struct wl_resource *resource;
|
||||||
GList *l;
|
guint mode_flags;
|
||||||
|
|
||||||
|
resource = wl_resource_create (client, &wl_output_interface, version, id);
|
||||||
|
wayland_output->resources = g_list_prepend (wayland_output->resources, resource);
|
||||||
|
|
||||||
|
wl_resource_set_user_data (resource, wayland_output);
|
||||||
|
wl_resource_set_destructor (resource, output_resource_destroy);
|
||||||
|
|
||||||
|
meta_verbose ("Binding output %p/%s (%u, %u, %u, %u) x %f\n",
|
||||||
|
output, output->name,
|
||||||
|
output->crtc->rect.x, output->crtc->rect.y,
|
||||||
|
output->crtc->rect.width, output->crtc->rect.height,
|
||||||
|
output->crtc->current_mode->refresh_rate);
|
||||||
|
|
||||||
wl_resource_post_event (resource,
|
wl_resource_post_event (resource,
|
||||||
WL_OUTPUT_GEOMETRY,
|
WL_OUTPUT_GEOMETRY,
|
||||||
output->x, output->y,
|
(int)output->crtc->rect.x,
|
||||||
output->width_mm,
|
(int)output->crtc->rect.y,
|
||||||
|
output->width_mm,
|
||||||
output->height_mm,
|
output->height_mm,
|
||||||
0, /* subpixel: unknown */
|
/* Cogl values reflect XRandR values,
|
||||||
"unknown", /* make */
|
and so does wayland */
|
||||||
"unknown"); /* model */
|
output->subpixel_order,
|
||||||
|
output->vendor,
|
||||||
|
output->product,
|
||||||
|
output->crtc->transform);
|
||||||
|
|
||||||
for (l = output->modes; l; l = l->next)
|
mode_flags = WL_OUTPUT_MODE_CURRENT;
|
||||||
{
|
if (output->crtc->current_mode == output->preferred_mode)
|
||||||
MetaWaylandMode *mode = l->data;
|
mode_flags |= WL_OUTPUT_MODE_PREFERRED;
|
||||||
wl_resource_post_event (resource,
|
|
||||||
WL_OUTPUT_MODE,
|
wl_resource_post_event (resource,
|
||||||
mode->flags,
|
WL_OUTPUT_MODE,
|
||||||
mode->width,
|
mode_flags,
|
||||||
mode->height,
|
(int)output->crtc->rect.width,
|
||||||
mode->refresh);
|
(int)output->crtc->rect.height,
|
||||||
}
|
(int)output->crtc->current_mode->refresh_rate);
|
||||||
|
|
||||||
|
if (version >= 2)
|
||||||
|
wl_resource_post_event (resource,
|
||||||
|
WL_OUTPUT_DONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
meta_wayland_compositor_create_output (MetaWaylandCompositor *compositor,
|
wayland_output_destroy_notify (gpointer data)
|
||||||
int x,
|
|
||||||
int y,
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
int width_mm,
|
|
||||||
int height_mm)
|
|
||||||
{
|
{
|
||||||
MetaWaylandOutput *output = g_slice_new0 (MetaWaylandOutput);
|
MetaWaylandOutput *wayland_output = data;
|
||||||
MetaWaylandMode *mode;
|
GList *resources;
|
||||||
float final_width, final_height;
|
|
||||||
|
|
||||||
/* XXX: eventually we will support sliced stages and an output should
|
/* Make sure the destructors don't mess with the list */
|
||||||
* correspond to a slice/CoglFramebuffer, but for now we only support
|
resources = wayland_output->resources;
|
||||||
* one output so we make sure it always matches the size of the stage
|
wayland_output->resources = NULL;
|
||||||
*/
|
|
||||||
clutter_actor_set_size (compositor->stage, width, height);
|
|
||||||
|
|
||||||
/* Read back the actual size we were given.
|
wl_global_destroy (wayland_output->global);
|
||||||
* XXX: This really needs re-thinking later though so we know the
|
g_list_free (resources);
|
||||||
* correct output geometry to use. */
|
|
||||||
clutter_actor_get_size (compositor->stage, &final_width, &final_height);
|
|
||||||
width = final_width;
|
|
||||||
height = final_height;
|
|
||||||
|
|
||||||
output->wayland_output.interface = &wl_output_interface;
|
g_slice_free (MetaWaylandOutput, wayland_output);
|
||||||
|
}
|
||||||
|
|
||||||
output->x = x;
|
static void
|
||||||
output->y = y;
|
wayland_output_update_for_output (MetaWaylandOutput *wayland_output,
|
||||||
output->width_mm = width_mm;
|
MetaOutput *output)
|
||||||
output->height_mm = height_mm;
|
{
|
||||||
|
GList *iter;
|
||||||
|
guint mode_flags;
|
||||||
|
|
||||||
wl_global_create (compositor->wayland_display,
|
mode_flags = WL_OUTPUT_MODE_CURRENT;
|
||||||
&wl_output_interface, 2,
|
if (output->crtc->current_mode == output->preferred_mode)
|
||||||
output, bind_output);
|
mode_flags |= WL_OUTPUT_MODE_PREFERRED;
|
||||||
|
|
||||||
mode = g_slice_new0 (MetaWaylandMode);
|
for (iter = wayland_output->resources; iter; iter = iter->next)
|
||||||
mode->flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
|
{
|
||||||
mode->width = width;
|
struct wl_resource *resource = iter->data;
|
||||||
mode->height = height;
|
|
||||||
mode->refresh = 60;
|
|
||||||
|
|
||||||
output->modes = g_list_prepend (output->modes, mode);
|
if (wayland_output->x != output->crtc->rect.x ||
|
||||||
|
wayland_output->y != output->crtc->rect.y ||
|
||||||
|
wayland_output->transform != output->crtc->transform)
|
||||||
|
{
|
||||||
|
wl_resource_post_event (resource,
|
||||||
|
WL_OUTPUT_GEOMETRY,
|
||||||
|
(int)output->crtc->rect.x,
|
||||||
|
(int)output->crtc->rect.y,
|
||||||
|
output->width_mm,
|
||||||
|
output->height_mm,
|
||||||
|
output->subpixel_order,
|
||||||
|
output->vendor,
|
||||||
|
output->product,
|
||||||
|
output->crtc->transform);
|
||||||
|
}
|
||||||
|
|
||||||
compositor->outputs = g_list_prepend (compositor->outputs, output);
|
wl_resource_post_event (resource,
|
||||||
|
WL_OUTPUT_MODE,
|
||||||
|
mode_flags,
|
||||||
|
(int)output->crtc->rect.width,
|
||||||
|
(int)output->crtc->rect.height,
|
||||||
|
(int)output->crtc->current_mode->refresh_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It's very important that we change the output pointer here, as
|
||||||
|
the old structure is about to be freed by MetaMonitorManager */
|
||||||
|
wayland_output->output = output;
|
||||||
|
wayland_output->x = output->crtc->rect.x;
|
||||||
|
wayland_output->y = output->crtc->rect.y;
|
||||||
|
wayland_output->transform = output->crtc->transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GHashTable *
|
||||||
|
meta_wayland_compositor_update_outputs (MetaWaylandCompositor *compositor,
|
||||||
|
MetaMonitorManager *monitors)
|
||||||
|
{
|
||||||
|
MetaOutput *outputs;
|
||||||
|
unsigned int i, n_outputs;
|
||||||
|
GHashTable *new_table;
|
||||||
|
|
||||||
|
outputs = meta_monitor_manager_get_outputs (monitors, &n_outputs);
|
||||||
|
new_table = g_hash_table_new_full (NULL, NULL, NULL, wayland_output_destroy_notify);
|
||||||
|
|
||||||
|
for (i = 0; i < n_outputs; i++)
|
||||||
|
{
|
||||||
|
MetaOutput *output = &outputs[i];
|
||||||
|
MetaWaylandOutput *wayland_output;
|
||||||
|
|
||||||
|
/* wayland does not expose disabled outputs */
|
||||||
|
if (output->crtc == NULL)
|
||||||
|
{
|
||||||
|
g_hash_table_remove (compositor->outputs, GSIZE_TO_POINTER (output->output_id));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
wayland_output = g_hash_table_lookup (compositor->outputs, GSIZE_TO_POINTER (output->output_id));
|
||||||
|
|
||||||
|
if (wayland_output)
|
||||||
|
{
|
||||||
|
g_hash_table_steal (compositor->outputs, GSIZE_TO_POINTER (output->output_id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wayland_output = g_slice_new0 (MetaWaylandOutput);
|
||||||
|
wayland_output->global = wl_global_create (compositor->wayland_display,
|
||||||
|
&wl_output_interface, 2,
|
||||||
|
wayland_output, bind_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
wayland_output_update_for_output (wayland_output, output);
|
||||||
|
g_hash_table_insert (new_table, GSIZE_TO_POINTER (output->output_id), wayland_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hash_table_destroy (compositor->outputs);
|
||||||
|
return new_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
const static struct wl_compositor_interface meta_wayland_compositor_interface = {
|
const static struct wl_compositor_interface meta_wayland_compositor_interface = {
|
||||||
@@ -1143,84 +1259,52 @@ bind_shell (struct wl_client *client,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xserver_set_window_id (struct wl_client *client,
|
gnome_session_died (GPid pid,
|
||||||
struct wl_resource *compositor_resource,
|
gint status,
|
||||||
struct wl_resource *surface_resource,
|
gpointer user_data)
|
||||||
guint32 xid)
|
|
||||||
{
|
{
|
||||||
MetaWaylandCompositor *compositor =
|
if (!WIFEXITED (status))
|
||||||
wl_resource_get_user_data (compositor_resource);
|
g_error ("gnome-session crashed; aborting");
|
||||||
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
else
|
||||||
MetaDisplay *display = meta_get_display ();
|
|
||||||
MetaWindow *window;
|
|
||||||
|
|
||||||
g_return_if_fail (surface->xid == None);
|
|
||||||
|
|
||||||
surface->xid = xid;
|
|
||||||
|
|
||||||
window = meta_display_lookup_x_window (display, xid);
|
|
||||||
if (window)
|
|
||||||
{
|
{
|
||||||
MetaWindowActor *window_actor =
|
/* A clean exit of gnome-session implies a logout, exit cleanly */
|
||||||
META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
|
meta_quit (META_EXIT_SUCCESS);
|
||||||
|
|
||||||
meta_window_actor_set_wayland_surface (window_actor, surface);
|
|
||||||
|
|
||||||
surface->window = window;
|
|
||||||
window->surface = surface;
|
|
||||||
|
|
||||||
/* If the window is already meant to have focus then the
|
|
||||||
* original attempt to call this in response to the FocusIn
|
|
||||||
* event will have been lost because there was no surface
|
|
||||||
* yet. */
|
|
||||||
if (window->has_focus)
|
|
||||||
meta_wayland_compositor_set_input_focus (compositor, window);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct xserver_interface xserver_implementation = {
|
|
||||||
xserver_set_window_id
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
bind_xserver (struct wl_client *client,
|
start_gnome_session (MetaWaylandCompositor *compositor)
|
||||||
void *data,
|
|
||||||
guint32 version,
|
|
||||||
guint32 id)
|
|
||||||
{
|
{
|
||||||
MetaWaylandCompositor *compositor = data;
|
GPid pid;
|
||||||
|
char *args[6];
|
||||||
|
GError *error;
|
||||||
|
|
||||||
/* If it's a different client than the xserver we launched,
|
args[0] = "setsid";
|
||||||
* don't start the wm. */
|
args[1] = "gnome-session";
|
||||||
if (client != compositor->xwayland_client)
|
args[2] = "--session";
|
||||||
return;
|
args[3] = "gnome-wayland";
|
||||||
|
args[4] = "--debug";
|
||||||
|
args[5] = NULL;
|
||||||
|
|
||||||
compositor->xserver_resource =
|
error = NULL;
|
||||||
wl_resource_create (client, &xserver_interface, version, id);
|
if (g_spawn_async (NULL, /* cwd */
|
||||||
wl_resource_set_implementation (compositor->xserver_resource,
|
args,
|
||||||
&xserver_implementation, compositor, NULL);
|
NULL,
|
||||||
|
G_SPAWN_SEARCH_PATH |
|
||||||
|
G_SPAWN_DO_NOT_REAP_CHILD,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&pid,
|
||||||
|
&error))
|
||||||
|
{
|
||||||
|
g_message ("forked gnome-session, pid %d\n", pid);
|
||||||
|
|
||||||
wl_resource_post_event (compositor->xserver_resource,
|
g_child_watch_add (pid, gnome_session_died, NULL);
|
||||||
XSERVER_LISTEN_SOCKET,
|
}
|
||||||
compositor->xwayland_abstract_fd);
|
else
|
||||||
|
{
|
||||||
wl_resource_post_event (compositor->xserver_resource,
|
g_error ("Failed to fork gnome-session server: %s", error->message);
|
||||||
XSERVER_LISTEN_SOCKET,
|
}
|
||||||
compositor->xwayland_unix_fd);
|
|
||||||
|
|
||||||
/* Make sure xwayland will recieve the above sockets in a finite
|
|
||||||
* time before unblocking the initialization mainloop since we are
|
|
||||||
* then going to immediately try and connect to those as the window
|
|
||||||
* manager. */
|
|
||||||
wl_client_flush (client);
|
|
||||||
|
|
||||||
/* At this point xwayland is all setup to start accepting
|
|
||||||
* connections so we can quit the transient initialization mainloop
|
|
||||||
* and unblock meta_wayland_init() to continue initializing mutter.
|
|
||||||
* */
|
|
||||||
g_main_loop_quit (compositor->init_loop);
|
|
||||||
compositor->init_loop = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -1342,6 +1426,23 @@ synthesize_motion_event (MetaWaylandCompositor *compositor,
|
|||||||
meta_display_handle_event (display, (XEvent *) &generic_event);
|
meta_display_handle_event (display, (XEvent *) &generic_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
reset_idletimes (const ClutterEvent *event)
|
||||||
|
{
|
||||||
|
ClutterInputDevice *device;
|
||||||
|
MetaIdleMonitor *core_monitor, *device_monitor;
|
||||||
|
int device_id;
|
||||||
|
|
||||||
|
device = clutter_event_get_source_device (event);
|
||||||
|
device_id = clutter_input_device_get_device_id (device);
|
||||||
|
|
||||||
|
core_monitor = meta_idle_monitor_get_core ();
|
||||||
|
device_monitor = meta_idle_monitor_get_for_device (device_id);
|
||||||
|
|
||||||
|
meta_idle_monitor_reset_idletime (core_monitor);
|
||||||
|
meta_idle_monitor_reset_idletime (device_monitor);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
event_cb (ClutterActor *stage,
|
event_cb (ClutterActor *stage,
|
||||||
const ClutterEvent *event,
|
const ClutterEvent *event,
|
||||||
@@ -1352,6 +1453,8 @@ event_cb (ClutterActor *stage,
|
|||||||
MetaWaylandSurface *surface;
|
MetaWaylandSurface *surface;
|
||||||
MetaDisplay *display;
|
MetaDisplay *display;
|
||||||
|
|
||||||
|
reset_idletimes (event);
|
||||||
|
|
||||||
meta_wayland_seat_handle_event (compositor->seat, event);
|
meta_wayland_seat_handle_event (compositor->seat, event);
|
||||||
|
|
||||||
/* HACK: for now, the surfaces from Wayland clients aren't
|
/* HACK: for now, the surfaces from Wayland clients aren't
|
||||||
@@ -1373,12 +1476,17 @@ event_cb (ClutterActor *stage,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
meta_wayland_stage_set_cursor_position (META_WAYLAND_STAGE (stage),
|
if (seat->cursor_tracker)
|
||||||
wl_fixed_to_int (pointer->x),
|
{
|
||||||
wl_fixed_to_int (pointer->y));
|
meta_cursor_tracker_update_position (seat->cursor_tracker,
|
||||||
|
wl_fixed_to_int (pointer->x),
|
||||||
|
wl_fixed_to_int (pointer->y));
|
||||||
|
|
||||||
if (pointer->current == NULL)
|
if (pointer->current == NULL)
|
||||||
meta_wayland_stage_set_default_cursor (META_WAYLAND_STAGE (stage));
|
meta_cursor_tracker_revert_root (seat->cursor_tracker);
|
||||||
|
|
||||||
|
meta_cursor_tracker_queue_redraw (seat->cursor_tracker, stage);
|
||||||
|
}
|
||||||
|
|
||||||
display = meta_get_display ();
|
display = meta_get_display ();
|
||||||
if (!display)
|
if (!display)
|
||||||
@@ -1405,6 +1513,20 @@ event_cb (ClutterActor *stage,
|
|||||||
synthesize_motion_event (compositor, event);
|
synthesize_motion_event (compositor, event);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
case CLUTTER_KEY_PRESS:
|
||||||
|
case CLUTTER_KEY_RELEASE:
|
||||||
|
meta_verbose ("Clutter key event %s for key %d (%d), state %d\n",
|
||||||
|
event->type == CLUTTER_KEY_PRESS ? "press" : "release",
|
||||||
|
clutter_event_get_key_symbol (event),
|
||||||
|
clutter_event_get_key_code (event),
|
||||||
|
clutter_event_get_state (event));
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case CLUTTER_SCROLL:
|
||||||
|
meta_verbose ("Clutter scroll event %s\n",
|
||||||
|
clutter_event_get_scroll_direction (event) == CLUTTER_SCROLL_DOWN ? "down" : "up");
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@@ -1462,11 +1584,101 @@ event_emission_hook_cb (GSignalInvocationHint *ihint,
|
|||||||
return TRUE /* stay connected */;
|
return TRUE /* stay connected */;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_our_vt_enter (MetaTTY *tty,
|
||||||
|
MetaWaylandCompositor *compositor)
|
||||||
|
{
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
if (!meta_weston_launch_set_master (compositor->weston_launch,
|
||||||
|
compositor->drm_fd, TRUE, &error))
|
||||||
|
{
|
||||||
|
g_warning ("Failed to become DRM master: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clutter_evdev_reclaim_devices ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_our_vt_leave (MetaTTY *tty,
|
||||||
|
MetaWaylandCompositor *compositor)
|
||||||
|
{
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
if (!meta_weston_launch_set_master (compositor->weston_launch,
|
||||||
|
compositor->drm_fd, FALSE, &error))
|
||||||
|
{
|
||||||
|
g_warning ("Failed to release DRM master: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clutter_evdev_release_devices ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
on_evdev_device_open (const char *path,
|
||||||
|
int flags,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
MetaWaylandCompositor *compositor = user_data;
|
||||||
|
|
||||||
|
return meta_weston_launch_open_input_device (compositor->weston_launch,
|
||||||
|
path, flags, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_monitors_changed (MetaMonitorManager *monitors,
|
||||||
|
MetaWaylandCompositor *compositor)
|
||||||
|
{
|
||||||
|
compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_display_config_ready (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
gboolean ok;
|
||||||
|
|
||||||
|
ok = meta_monitor_manager_init_dbus_finish (META_MONITOR_MANAGER (object), result, NULL);
|
||||||
|
g_assert (ok);
|
||||||
|
|
||||||
|
meta_idle_monitor_init_dbus ();
|
||||||
|
|
||||||
|
/* Now we have X and DBus, and our stuff is on the bus.
|
||||||
|
The only thing missing is gnome-session! */
|
||||||
|
start_gnome_session (user_data);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_wayland_init (void)
|
meta_wayland_init (void)
|
||||||
{
|
{
|
||||||
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
|
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
|
||||||
guint event_signal;
|
guint event_signal;
|
||||||
|
ClutterBackend *backend;
|
||||||
|
CoglContext *cogl_context;
|
||||||
|
CoglRenderer *cogl_renderer;
|
||||||
|
int weston_launch_fd;
|
||||||
|
MetaMonitorManager *monitors;
|
||||||
|
GDBusConnection *session_bus;
|
||||||
|
char *session_bus_address;
|
||||||
|
|
||||||
memset (compositor, 0, sizeof (MetaWaylandCompositor));
|
memset (compositor, 0, sizeof (MetaWaylandCompositor));
|
||||||
|
|
||||||
@@ -1503,11 +1715,76 @@ meta_wayland_init (void)
|
|||||||
|
|
||||||
clutter_wayland_set_compositor_display (compositor->wayland_display);
|
clutter_wayland_set_compositor_display (compositor->wayland_display);
|
||||||
|
|
||||||
|
/* We need to set this before clutter_init(), so we do it unconditionally.
|
||||||
|
It doesn't harm anyway to do it under X11 */
|
||||||
|
weston_launch_fd = env_get_fd ("WESTON_LAUNCHER_SOCK");
|
||||||
|
if (weston_launch_fd >= 0)
|
||||||
|
compositor->weston_launch = g_socket_new_from_fd (weston_launch_fd, NULL);
|
||||||
|
clutter_evdev_set_open_callback (on_evdev_device_open, compositor);
|
||||||
|
|
||||||
if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS)
|
if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS)
|
||||||
g_error ("Failed to initialize Clutter");
|
g_error ("Failed to initialize Clutter");
|
||||||
|
|
||||||
|
backend = clutter_get_default_backend ();
|
||||||
|
cogl_context = clutter_backend_get_cogl_context (backend);
|
||||||
|
cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (cogl_context));
|
||||||
|
|
||||||
|
if (cogl_renderer_get_winsys_id (cogl_renderer) == COGL_WINSYS_ID_EGL_KMS)
|
||||||
|
compositor->drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
|
||||||
|
else
|
||||||
|
compositor->drm_fd = -1;
|
||||||
|
|
||||||
|
if (compositor->drm_fd >= 0)
|
||||||
|
{
|
||||||
|
GError *error;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
/* Running on bare metal, let's initalize DRM master and VT handling */
|
||||||
|
compositor->tty = meta_tty_new ();
|
||||||
|
if (compositor->tty)
|
||||||
|
{
|
||||||
|
g_signal_connect (compositor->tty, "enter", G_CALLBACK (on_our_vt_enter), compositor);
|
||||||
|
g_signal_connect (compositor->tty, "leave", G_CALLBACK (on_our_vt_leave), compositor);
|
||||||
|
}
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
if (!meta_weston_launch_set_master (compositor->weston_launch,
|
||||||
|
compositor->drm_fd, TRUE, &error))
|
||||||
|
{
|
||||||
|
g_error ("Failed to become DRM master: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open a log in the home directory. This is necessary because otherwise
|
||||||
|
all background processes (such as gnome-session and children) get SIGTTOU
|
||||||
|
trying to write to the terminal.
|
||||||
|
|
||||||
|
Then close (</dev/null) stdin, so we don't get SIGTTIN or other crazy stuff.
|
||||||
|
*/
|
||||||
|
snprintf(path, PATH_MAX, "%s/gnome-wayland.log", g_get_user_cache_dir ());
|
||||||
|
fd = open (path, O_WRONLY | O_APPEND | O_CREAT | O_TRUNC, 0600);
|
||||||
|
if (fd < 0)
|
||||||
|
fd = open ("/dev/null", O_WRONLY | O_NOCTTY, 0600);
|
||||||
|
|
||||||
|
dup2 (fd, STDOUT_FILENO);
|
||||||
|
dup2 (fd, STDERR_FILENO);
|
||||||
|
close (fd);
|
||||||
|
|
||||||
|
fd = open ("/dev/null", O_WRONLY | O_NOCTTY, 0600);
|
||||||
|
dup2 (fd, STDIN_FILENO);
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_monitor_manager_initialize ();
|
||||||
|
monitors = meta_monitor_manager_get ();
|
||||||
|
g_signal_connect (monitors, "monitors-changed",
|
||||||
|
G_CALLBACK (on_monitors_changed), compositor);
|
||||||
|
|
||||||
|
compositor->outputs = g_hash_table_new_full (NULL, NULL, NULL, wayland_output_destroy_notify);
|
||||||
|
compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors);
|
||||||
|
|
||||||
compositor->stage = meta_wayland_stage_new ();
|
compositor->stage = meta_wayland_stage_new ();
|
||||||
clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor->stage), FALSE);
|
|
||||||
g_signal_connect_after (compositor->stage, "paint",
|
g_signal_connect_after (compositor->stage, "paint",
|
||||||
G_CALLBACK (paint_finished_cb), compositor);
|
G_CALLBACK (paint_finished_cb), compositor);
|
||||||
g_signal_connect (compositor->stage, "destroy",
|
g_signal_connect (compositor->stage, "destroy",
|
||||||
@@ -1515,7 +1792,8 @@ meta_wayland_init (void)
|
|||||||
|
|
||||||
meta_wayland_data_device_manager_init (compositor->wayland_display);
|
meta_wayland_data_device_manager_init (compositor->wayland_display);
|
||||||
|
|
||||||
compositor->seat = meta_wayland_seat_new (compositor->wayland_display);
|
compositor->seat = meta_wayland_seat_new (compositor->wayland_display,
|
||||||
|
compositor->drm_fd >= 0);
|
||||||
|
|
||||||
g_signal_connect (compositor->stage,
|
g_signal_connect (compositor->stage,
|
||||||
"captured-event",
|
"captured-event",
|
||||||
@@ -1532,22 +1810,14 @@ meta_wayland_init (void)
|
|||||||
compositor, /* hook_data */
|
compositor, /* hook_data */
|
||||||
NULL /* data_destroy */);
|
NULL /* data_destroy */);
|
||||||
|
|
||||||
meta_wayland_compositor_create_output (compositor, 0, 0, 1024, 600, 222, 125);
|
|
||||||
|
|
||||||
if (wl_global_create (compositor->wayland_display,
|
if (wl_global_create (compositor->wayland_display,
|
||||||
&wl_shell_interface, 1,
|
&wl_shell_interface, 1,
|
||||||
compositor, bind_shell) == NULL)
|
compositor, bind_shell) == NULL)
|
||||||
g_error ("Failed to register a global shell object");
|
g_error ("Failed to register a global shell object");
|
||||||
|
|
||||||
clutter_actor_show (compositor->stage);
|
|
||||||
|
|
||||||
if (wl_display_add_socket (compositor->wayland_display, "wayland-0"))
|
if (wl_display_add_socket (compositor->wayland_display, "wayland-0"))
|
||||||
g_error ("Failed to create socket");
|
g_error ("Failed to create socket");
|
||||||
|
|
||||||
wl_global_create (compositor->wayland_display,
|
|
||||||
&xserver_interface, 1,
|
|
||||||
compositor, bind_xserver);
|
|
||||||
|
|
||||||
/* XXX: It's important that we only try and start xwayland after we
|
/* XXX: It's important that we only try and start xwayland after we
|
||||||
* have initialized EGL because EGL implements the "wl_drm"
|
* have initialized EGL because EGL implements the "wl_drm"
|
||||||
* interface which xwayland requires to determine what drm device
|
* interface which xwayland requires to determine what drm device
|
||||||
@@ -1563,40 +1833,43 @@ meta_wayland_init (void)
|
|||||||
|
|
||||||
putenv (g_strdup_printf ("DISPLAY=:%d", compositor->xwayland_display_index));
|
putenv (g_strdup_printf ("DISPLAY=:%d", compositor->xwayland_display_index));
|
||||||
|
|
||||||
/* We need to run a mainloop until we know xwayland has a binding
|
/* Now xwayland is ready. Get ourselves a dbus daemon. This will autolaunch
|
||||||
* for our xserver interface at which point we can assume it's
|
if no bus is found.
|
||||||
* ready to start accepting connections. */
|
*/
|
||||||
compositor->init_loop = g_main_loop_new (NULL, FALSE);
|
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
|
||||||
|
if (!session_bus)
|
||||||
|
meta_fatal ("Could not connect to the session bus\n");
|
||||||
|
|
||||||
g_main_loop_run (compositor->init_loop);
|
session_bus_address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, NULL);
|
||||||
|
putenv (g_strdup_printf ("DBUS_SESSION_BUS_ADDRESS=%s", session_bus_address));
|
||||||
|
g_free (session_bus_address);
|
||||||
|
|
||||||
|
/* Now get our interface on the dbus (or gnome-settings-daemon will refuse
|
||||||
|
to start, which in turn will stall gnome-session from launching the rest
|
||||||
|
of the session)
|
||||||
|
*/
|
||||||
|
meta_monitor_manager_init_dbus (monitors, on_display_config_ready, compositor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_wayland_finalize (void)
|
meta_wayland_finalize (void)
|
||||||
{
|
{
|
||||||
meta_xwayland_stop (meta_wayland_compositor_get_default ());
|
MetaWaylandCompositor *compositor;
|
||||||
|
|
||||||
|
compositor = meta_wayland_compositor_get_default ();
|
||||||
|
|
||||||
|
meta_xwayland_stop (compositor);
|
||||||
|
g_clear_object (&compositor->tty);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
MetaTTY *
|
||||||
meta_wayland_handle_sig_child (void)
|
meta_wayland_compositor_get_tty (MetaWaylandCompositor *compositor)
|
||||||
{
|
{
|
||||||
int status;
|
return compositor->tty;
|
||||||
pid_t pid = waitpid (-1, &status, WNOHANG);
|
}
|
||||||
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
|
|
||||||
|
gboolean
|
||||||
/* The simplest measure to avoid infinitely re-spawning a crashing
|
meta_wayland_compositor_is_native (MetaWaylandCompositor *compositor)
|
||||||
* X server */
|
{
|
||||||
if (pid == compositor->xwayland_pid)
|
return compositor->drm_fd >= 0;
|
||||||
{
|
|
||||||
if (!WIFEXITED (status))
|
|
||||||
g_critical ("X Wayland crashed; aborting");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* For now we simply abort if we see the server exit.
|
|
||||||
*
|
|
||||||
* In the future X will only be loaded lazily for legacy X support
|
|
||||||
* but for now it's a hard requirement. */
|
|
||||||
g_critical ("Spurious exit of X Wayland server");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
203
src/wayland/meta-weston-launch.c
Normal file
203
src/wayland/meta-weston-launch.c
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* 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 <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-weston-launch.h"
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
send_message_to_wl (GSocket *weston_launch,
|
||||||
|
void *message,
|
||||||
|
gsize size,
|
||||||
|
GSocketControlMessage *out_cmsg,
|
||||||
|
GSocketControlMessage **in_cmsg,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
int ok;
|
||||||
|
GInputVector in_iov = { &ok, sizeof (int) };
|
||||||
|
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 (weston_launch, NULL,
|
||||||
|
&out_iov, 1,
|
||||||
|
out_all_cmsg, -1,
|
||||||
|
flags, NULL, error) != (gssize)size)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (g_socket_receive_message (weston_launch, NULL,
|
||||||
|
&in_iov, 1,
|
||||||
|
&in_all_cmsg, NULL,
|
||||||
|
&flags, NULL, error) != sizeof (int))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (ok != 0)
|
||||||
|
{
|
||||||
|
if (ok == -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 (-ok),
|
||||||
|
"Got failure from weston-launch: %s", strerror (-ok));
|
||||||
|
|
||||||
|
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_weston_launch_set_master (GSocket *weston_launch,
|
||||||
|
int drm_fd,
|
||||||
|
gboolean master,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
if (weston_launch)
|
||||||
|
{
|
||||||
|
struct weston_launcher_set_master message;
|
||||||
|
GSocketControlMessage *cmsg;
|
||||||
|
gboolean ok;
|
||||||
|
|
||||||
|
message.header.opcode = WESTON_LAUNCHER_DRM_SET_MASTER;
|
||||||
|
message.set_master = master;
|
||||||
|
|
||||||
|
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 (weston_launch, &message, sizeof message, cmsg, NULL, error);
|
||||||
|
|
||||||
|
g_object_unref (cmsg);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (master)
|
||||||
|
ret = drmSetMaster (drm_fd);
|
||||||
|
else
|
||||||
|
ret = drmDropMaster (drm_fd);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
|
||||||
|
"Failed to set DRM master directly: %s", strerror (-ret));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
meta_weston_launch_open_input_device (GSocket *weston_launch,
|
||||||
|
const char *name,
|
||||||
|
int flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
if (weston_launch)
|
||||||
|
{
|
||||||
|
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 (weston_launch, 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;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = open (name, flags, 0);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
|
||||||
|
"Failed to open input device directly: %s", strerror (errno));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
56
src/wayland/meta-weston-launch.h
Normal file
56
src/wayland/meta-weston-launch.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef META_WESTON_LAUNCH_H
|
||||||
|
#define META_WESTON_LAUNCH_H
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
/* Keep this in sync with weston-launch */
|
||||||
|
|
||||||
|
enum weston_launcher_opcode {
|
||||||
|
WESTON_LAUNCHER_OPEN,
|
||||||
|
WESTON_LAUNCHER_DRM_SET_MASTER
|
||||||
|
};
|
||||||
|
|
||||||
|
struct weston_launcher_message {
|
||||||
|
int opcode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct weston_launcher_open {
|
||||||
|
struct weston_launcher_message header;
|
||||||
|
int flags;
|
||||||
|
char path[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct weston_launcher_set_master {
|
||||||
|
struct weston_launcher_message header;
|
||||||
|
int set_master;
|
||||||
|
};
|
||||||
|
|
||||||
|
gboolean meta_weston_launch_set_master (GSocket *weston_launch,
|
||||||
|
int drm_fd,
|
||||||
|
gboolean master,
|
||||||
|
GError **error);
|
||||||
|
int meta_weston_launch_open_input_device (GSocket *weston_launch,
|
||||||
|
const char *name,
|
||||||
|
int flags,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
#endif
|
@@ -19,8 +19,6 @@
|
|||||||
* 02111-1307, USA.
|
* 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "meta-xwayland-private.h"
|
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -28,6 +26,93 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "meta-xwayland-private.h"
|
||||||
|
#include "meta-window-actor-private.h"
|
||||||
|
#include "xserver-server-protocol.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
xserver_set_window_id (struct wl_client *client,
|
||||||
|
struct wl_resource *compositor_resource,
|
||||||
|
struct wl_resource *surface_resource,
|
||||||
|
guint32 xid)
|
||||||
|
{
|
||||||
|
MetaWaylandCompositor *compositor =
|
||||||
|
wl_resource_get_user_data (compositor_resource);
|
||||||
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
||||||
|
MetaDisplay *display = meta_get_display ();
|
||||||
|
MetaWindow *window;
|
||||||
|
|
||||||
|
g_return_if_fail (surface->xid == None);
|
||||||
|
|
||||||
|
surface->xid = xid;
|
||||||
|
|
||||||
|
window = meta_display_lookup_x_window (display, xid);
|
||||||
|
if (window)
|
||||||
|
{
|
||||||
|
MetaWindowActor *window_actor =
|
||||||
|
META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
|
||||||
|
|
||||||
|
meta_window_actor_set_wayland_surface (window_actor, surface);
|
||||||
|
|
||||||
|
surface->window = window;
|
||||||
|
window->surface = surface;
|
||||||
|
|
||||||
|
/* If the window is already meant to have focus then the
|
||||||
|
* original attempt to call this in response to the FocusIn
|
||||||
|
* event will have been lost because there was no surface
|
||||||
|
* yet. */
|
||||||
|
if (window->has_focus)
|
||||||
|
meta_wayland_compositor_set_input_focus (compositor, window);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xserver_interface xserver_implementation = {
|
||||||
|
xserver_set_window_id
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
bind_xserver (struct wl_client *client,
|
||||||
|
void *data,
|
||||||
|
guint32 version,
|
||||||
|
guint32 id)
|
||||||
|
{
|
||||||
|
MetaWaylandCompositor *compositor = data;
|
||||||
|
|
||||||
|
/* If it's a different client than the xserver we launched,
|
||||||
|
* don't start the wm. */
|
||||||
|
if (client != compositor->xwayland_client)
|
||||||
|
return;
|
||||||
|
|
||||||
|
compositor->xserver_resource =
|
||||||
|
wl_resource_create (client, &xserver_interface, version, id);
|
||||||
|
wl_resource_set_implementation (compositor->xserver_resource,
|
||||||
|
&xserver_implementation, compositor, NULL);
|
||||||
|
|
||||||
|
wl_resource_post_event (compositor->xserver_resource,
|
||||||
|
XSERVER_LISTEN_SOCKET,
|
||||||
|
compositor->xwayland_abstract_fd);
|
||||||
|
|
||||||
|
wl_resource_post_event (compositor->xserver_resource,
|
||||||
|
XSERVER_LISTEN_SOCKET,
|
||||||
|
compositor->xwayland_unix_fd);
|
||||||
|
|
||||||
|
/* Make sure xwayland will recieve the above sockets in a finite
|
||||||
|
* time before unblocking the initialization mainloop since we are
|
||||||
|
* then going to immediately try and connect to those as the window
|
||||||
|
* manager. */
|
||||||
|
wl_client_flush (client);
|
||||||
|
|
||||||
|
/* At this point xwayland is all setup to start accepting
|
||||||
|
* connections so we can quit the transient initialization mainloop
|
||||||
|
* and unblock meta_wayland_init() to continue initializing mutter.
|
||||||
|
* */
|
||||||
|
g_main_loop_quit (compositor->init_loop);
|
||||||
|
compositor->init_loop = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
create_lockfile (int display, int *display_out)
|
create_lockfile (int display, int *display_out)
|
||||||
@@ -183,6 +268,47 @@ bind_to_unix_socket (int display)
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
uncloexec_and_setpgid (gpointer user_data)
|
||||||
|
{
|
||||||
|
int fd = GPOINTER_TO_INT (user_data);
|
||||||
|
|
||||||
|
/* Make sure the client end of the socket pair doesn't get closed
|
||||||
|
* when we exec xwayland. */
|
||||||
|
int flags = fcntl (fd, F_GETFD);
|
||||||
|
if (flags != -1)
|
||||||
|
fcntl (fd, F_SETFD, flags & ~FD_CLOEXEC);
|
||||||
|
|
||||||
|
/* Put this process in a background process group, so that Ctrl-C
|
||||||
|
goes to mutter only */
|
||||||
|
setpgid (0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xserver_died (GPid pid,
|
||||||
|
gint status,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
if (!WIFEXITED (status))
|
||||||
|
g_error ("X Wayland crashed; aborting");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* For now we simply abort if we see the server exit.
|
||||||
|
*
|
||||||
|
* In the future X will only be loaded lazily for legacy X support
|
||||||
|
* but for now it's a hard requirement. */
|
||||||
|
g_error ("Spurious exit of X Wayland server");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
x_io_error (Display *display)
|
||||||
|
{
|
||||||
|
g_error ("Connection to xwayland lost");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
meta_xwayland_start (MetaWaylandCompositor *compositor)
|
meta_xwayland_start (MetaWaylandCompositor *compositor)
|
||||||
{
|
{
|
||||||
@@ -190,6 +316,15 @@ meta_xwayland_start (MetaWaylandCompositor *compositor)
|
|||||||
char *lockfile = NULL;
|
char *lockfile = NULL;
|
||||||
int sp[2];
|
int sp[2];
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
char **env;
|
||||||
|
char *fd_string;
|
||||||
|
char *display_name;
|
||||||
|
char *args[11];
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
wl_global_create (compositor->wayland_display,
|
||||||
|
&xserver_interface, 1,
|
||||||
|
compositor, bind_xserver);
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@@ -238,45 +373,39 @@ meta_xwayland_start (MetaWaylandCompositor *compositor)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ((pid = fork()))
|
env = g_get_environ ();
|
||||||
|
fd_string = g_strdup_printf ("%d", sp[1]);
|
||||||
|
env = g_environ_setenv (env, "WAYLAND_SOCKET", fd_string, TRUE);
|
||||||
|
g_free (fd_string);
|
||||||
|
|
||||||
|
display_name = g_strdup_printf (":%d",
|
||||||
|
compositor->xwayland_display_index);
|
||||||
|
|
||||||
|
args[0] = XWAYLAND_PATH;
|
||||||
|
args[1] = display_name;
|
||||||
|
args[2] = "-wayland";
|
||||||
|
args[3] = "-rootless";
|
||||||
|
args[4] = "-retro";
|
||||||
|
args[5] = "-noreset";
|
||||||
|
args[6] = "-logfile";
|
||||||
|
args[7] = g_build_filename (g_get_user_cache_dir (), "xwayland.log", NULL);
|
||||||
|
args[8] = "-nolisten";
|
||||||
|
args[9] = "all";
|
||||||
|
args[10] = NULL;
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
if (g_spawn_async (NULL, /* cwd */
|
||||||
|
args,
|
||||||
|
env,
|
||||||
|
G_SPAWN_LEAVE_DESCRIPTORS_OPEN |
|
||||||
|
G_SPAWN_DO_NOT_REAP_CHILD |
|
||||||
|
G_SPAWN_STDOUT_TO_DEV_NULL |
|
||||||
|
G_SPAWN_STDERR_TO_DEV_NULL,
|
||||||
|
uncloexec_and_setpgid,
|
||||||
|
GINT_TO_POINTER (sp[1]),
|
||||||
|
&pid,
|
||||||
|
&error))
|
||||||
{
|
{
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
char *fd_string;
|
|
||||||
char *display_name;
|
|
||||||
/* Make sure the client end of the socket pair doesn't get closed
|
|
||||||
* when we exec xwayland. */
|
|
||||||
int flags = fcntl (sp[1], F_GETFD);
|
|
||||||
if (flags != -1)
|
|
||||||
fcntl (sp[1], F_SETFD, flags & ~FD_CLOEXEC);
|
|
||||||
|
|
||||||
fd_string = g_strdup_printf ("%d", sp[1]);
|
|
||||||
setenv ("WAYLAND_SOCKET", fd_string, 1);
|
|
||||||
g_free (fd_string);
|
|
||||||
|
|
||||||
display_name = g_strdup_printf (":%d",
|
|
||||||
compositor->xwayland_display_index);
|
|
||||||
|
|
||||||
if (execl (XWAYLAND_PATH,
|
|
||||||
XWAYLAND_PATH,
|
|
||||||
display_name,
|
|
||||||
"-wayland",
|
|
||||||
"-rootless",
|
|
||||||
"-retro",
|
|
||||||
"-noreset",
|
|
||||||
/* FIXME: does it make sense to log to the filesystem by
|
|
||||||
* default? */
|
|
||||||
"-logfile", "/tmp/xwayland.log",
|
|
||||||
"-nolisten", "all",
|
|
||||||
NULL) < 0)
|
|
||||||
{
|
|
||||||
char *msg = strerror (errno);
|
|
||||||
g_warning ("xwayland exec failed: %s", msg);
|
|
||||||
}
|
|
||||||
exit (-1);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
g_message ("forked X server, pid %d\n", pid);
|
g_message ("forked X server, pid %d\n", pid);
|
||||||
|
|
||||||
close (sp[1]);
|
close (sp[1]);
|
||||||
@@ -284,12 +413,29 @@ meta_xwayland_start (MetaWaylandCompositor *compositor)
|
|||||||
wl_client_create (compositor->wayland_display, sp[0]);
|
wl_client_create (compositor->wayland_display, sp[0]);
|
||||||
|
|
||||||
compositor->xwayland_pid = pid;
|
compositor->xwayland_pid = pid;
|
||||||
break;
|
g_child_watch_add (pid, xserver_died, NULL);
|
||||||
|
|
||||||
case -1:
|
|
||||||
g_error ("Failed to fork for xwayland server");
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_error ("Failed to fork for xwayland server: %s", error->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev (env);
|
||||||
|
g_free (display_name);
|
||||||
|
|
||||||
|
/* We need to run a mainloop until we know xwayland has a binding
|
||||||
|
* for our xserver interface at which point we can assume it's
|
||||||
|
* ready to start accepting connections. */
|
||||||
|
compositor->init_loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
|
||||||
|
g_main_loop_run (compositor->init_loop);
|
||||||
|
|
||||||
|
/* We install an X IO error handler in addition to the child watch,
|
||||||
|
because after Xlib connects our child watch may not be called soon
|
||||||
|
enough, and therefore we won't crash when X exits (and most important
|
||||||
|
we won't reset the tty).
|
||||||
|
*/
|
||||||
|
XSetIOErrorHandler (x_io_error);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
703
src/wayland/weston-launch.c
Normal file
703
src/wayland/weston-launch.c
Normal file
@@ -0,0 +1,703 @@
|
|||||||
|
/*
|
||||||
|
* 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 <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <security/pam_appl.h>
|
||||||
|
|
||||||
|
#include <xf86drm.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_SYSTEMD_LOGIN
|
||||||
|
#include <systemd/sd-login.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "weston-launch.h"
|
||||||
|
|
||||||
|
#define MAX_ARGV_SIZE 256
|
||||||
|
|
||||||
|
struct weston_launch {
|
||||||
|
struct pam_conv pc;
|
||||||
|
pam_handle_t *ph;
|
||||||
|
int tty;
|
||||||
|
int ttynr;
|
||||||
|
int sock[2];
|
||||||
|
struct passwd *pw;
|
||||||
|
|
||||||
|
int signalfd;
|
||||||
|
|
||||||
|
pid_t child;
|
||||||
|
int verbose;
|
||||||
|
char *new_user;
|
||||||
|
};
|
||||||
|
|
||||||
|
union cmsg_data { unsigned char b[4]; int fd; };
|
||||||
|
|
||||||
|
static gid_t *
|
||||||
|
read_groups(void)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
gid_t *groups;
|
||||||
|
|
||||||
|
n = getgroups(0, NULL);
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
|
fprintf(stderr, "Unable to retrieve groups: %m\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
groups = malloc(n * sizeof(gid_t));
|
||||||
|
if (!groups)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (getgroups(n, groups) < 0) {
|
||||||
|
fprintf(stderr, "Unable to retrieve groups: %m\n");
|
||||||
|
free(groups);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
weston_launch_allowed(struct weston_launch *wl)
|
||||||
|
{
|
||||||
|
struct group *gr;
|
||||||
|
gid_t *groups;
|
||||||
|
int i;
|
||||||
|
#ifdef HAVE_SYSTEMD_LOGIN
|
||||||
|
char *session, *seat;
|
||||||
|
int err;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (getuid() == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
gr = getgrnam("weston-launch");
|
||||||
|
if (gr) {
|
||||||
|
groups = read_groups();
|
||||||
|
if (groups) {
|
||||||
|
for (i = 0; groups[i]; ++i) {
|
||||||
|
if (groups[i] == gr->gr_gid) {
|
||||||
|
free(groups);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(groups);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_SYSTEMD_LOGIN
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
pam_conversation_fn(int msg_count,
|
||||||
|
const struct pam_message **messages,
|
||||||
|
struct pam_response **responses,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
return PAM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
setup_pam(struct weston_launch *wl)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
wl->pc.conv = pam_conversation_fn;
|
||||||
|
wl->pc.appdata_ptr = wl;
|
||||||
|
|
||||||
|
err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph);
|
||||||
|
if (err != PAM_SUCCESS) {
|
||||||
|
fprintf(stderr, "failed to start pam transaction: %d: %s\n",
|
||||||
|
err, pam_strerror(wl->ph, err));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty));
|
||||||
|
if (err != PAM_SUCCESS) {
|
||||||
|
fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n",
|
||||||
|
err, pam_strerror(wl->ph, err));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pam_open_session(wl->ph, 0);
|
||||||
|
if (err != PAM_SUCCESS) {
|
||||||
|
fprintf(stderr, "failed to open pam session: %d: %s\n",
|
||||||
|
err, pam_strerror(wl->ph, err));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
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_setmaster(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
struct cmsghdr *cmsg;
|
||||||
|
struct weston_launcher_set_master *message;
|
||||||
|
union cmsg_data *data;
|
||||||
|
|
||||||
|
if (len != sizeof(*message)) {
|
||||||
|
error(0, 0, "missing value in setmaster request");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = msg->msg_iov->iov_base;
|
||||||
|
|
||||||
|
cmsg = CMSG_FIRSTHDR(msg);
|
||||||
|
if (!cmsg ||
|
||||||
|
cmsg->cmsg_level != SOL_SOCKET ||
|
||||||
|
cmsg->cmsg_type != SCM_RIGHTS) {
|
||||||
|
error(0, 0, "invalid control message");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = (union cmsg_data *) CMSG_DATA(cmsg);
|
||||||
|
if (data->fd == -1) {
|
||||||
|
error(0, 0, "missing drm fd in socket request");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message->set_master)
|
||||||
|
ret = drmSetMaster(data->fd);
|
||||||
|
else
|
||||||
|
ret = drmDropMaster(data->fd);
|
||||||
|
|
||||||
|
close(data->fd);
|
||||||
|
|
||||||
|
if (wl->verbose)
|
||||||
|
fprintf(stderr, "weston-launch: %sMaster, ret: %d, fd: %d\n",
|
||||||
|
message->set_master ? "set" : "drop", ret, data->fd);
|
||||||
|
|
||||||
|
out:
|
||||||
|
do {
|
||||||
|
len = send(wl->sock[0], &ret, sizeof ret, 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)
|
||||||
|
{
|
||||||
|
int fd = -1, ret = -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;
|
||||||
|
|
||||||
|
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)
|
||||||
|
goto err0;
|
||||||
|
|
||||||
|
fd = open(message->path, message->flags);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "Error opening device %s: %m\n",
|
||||||
|
message->path);
|
||||||
|
goto err0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (major(s.st_rdev) != INPUT_MAJOR) {
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
fprintf(stderr, "Device %s is not an input device\n",
|
||||||
|
message->path);
|
||||||
|
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;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
iov.iov_base = &ret;
|
||||||
|
iov.iov_len = sizeof ret;
|
||||||
|
|
||||||
|
if (wl->verbose)
|
||||||
|
fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n",
|
||||||
|
message->path, 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_MASTER:
|
||||||
|
ret = handle_setmaster(wl, &msg, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
quit(struct weston_launch *wl, int status)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
close(wl->signalfd);
|
||||||
|
close(wl->sock[0]);
|
||||||
|
|
||||||
|
if (wl->new_user) {
|
||||||
|
err = pam_close_session(wl->ph, 0);
|
||||||
|
if (err)
|
||||||
|
fprintf(stderr, "pam_close_session failed: %d: %s\n",
|
||||||
|
err, pam_strerror(wl->ph, err));
|
||||||
|
pam_end(wl->ph, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
setup_tty(struct weston_launch *wl, const char *tty)
|
||||||
|
{
|
||||||
|
struct stat buf;
|
||||||
|
char *t;
|
||||||
|
|
||||||
|
if (!wl->new_user) {
|
||||||
|
wl->tty = STDIN_FILENO;
|
||||||
|
} else if (tty) {
|
||||||
|
t = ttyname(STDIN_FILENO);
|
||||||
|
if (t && strcmp(t, tty) == 0)
|
||||||
|
wl->tty = STDIN_FILENO;
|
||||||
|
else
|
||||||
|
wl->tty = open(tty, O_RDWR | O_NOCTTY);
|
||||||
|
} else {
|
||||||
|
int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
|
||||||
|
char filename[16];
|
||||||
|
|
||||||
|
if (tty0 < 0)
|
||||||
|
error(1, errno, "could not open tty0");
|
||||||
|
|
||||||
|
if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1)
|
||||||
|
error(1, errno, "failed to find non-opened console");
|
||||||
|
|
||||||
|
snprintf(filename, sizeof filename, "/dev/tty%d", wl->ttynr);
|
||||||
|
wl->tty = open(filename, O_RDWR | O_NOCTTY);
|
||||||
|
close(tty0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wl->tty < 0)
|
||||||
|
error(1, errno, "failed to open tty");
|
||||||
|
|
||||||
|
if (tty) {
|
||||||
|
if (fstat(wl->tty, &buf) < 0)
|
||||||
|
error(1, errno, "stat %s failed", tty);
|
||||||
|
|
||||||
|
if (major(buf.st_rdev) != TTY_MAJOR)
|
||||||
|
error(1, 0, "invalid tty device: %s", tty);
|
||||||
|
|
||||||
|
wl->ttynr = minor(buf.st_rdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
setup_session(struct weston_launch *wl)
|
||||||
|
{
|
||||||
|
char **env;
|
||||||
|
char *term;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (wl->tty != STDIN_FILENO) {
|
||||||
|
if (setsid() < 0)
|
||||||
|
error(1, errno, "setsid failed");
|
||||||
|
if (ioctl(wl->tty, TIOCSCTTY, 0) < 0)
|
||||||
|
error(1, errno, "TIOCSCTTY failed - tty is in use");
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
term = getenv("TERM");
|
||||||
|
clearenv();
|
||||||
|
setenv("TERM", term, 1);
|
||||||
|
setenv("USER", wl->pw->pw_name, 1);
|
||||||
|
setenv("LOGNAME", wl->pw->pw_name, 1);
|
||||||
|
setenv("HOME", wl->pw->pw_dir, 1);
|
||||||
|
setenv("SHELL", wl->pw->pw_shell, 1);
|
||||||
|
|
||||||
|
env = pam_getenvlist(wl->ph);
|
||||||
|
if (env) {
|
||||||
|
for (i = 0; env[i]; ++i) {
|
||||||
|
if (putenv(env[i]) < 0)
|
||||||
|
error(0, 0, "putenv %s failed", env[i]);
|
||||||
|
}
|
||||||
|
free(env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
if (wl->new_user)
|
||||||
|
setup_session(wl);
|
||||||
|
|
||||||
|
if (wl->tty != STDIN_FILENO)
|
||||||
|
setenv_fd("WESTON_TTY_FD", wl->tty);
|
||||||
|
|
||||||
|
setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]);
|
||||||
|
|
||||||
|
unsetenv("DISPLAY");
|
||||||
|
|
||||||
|
/* Do not give our signal mask to the new process. */
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, SIGTERM);
|
||||||
|
sigaddset(&mask, SIGCHLD);
|
||||||
|
sigaddset(&mask, SIGINT);
|
||||||
|
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, " -t, --tty Start session on alternative tty\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;
|
||||||
|
char *tty = NULL;
|
||||||
|
struct option opts[] = {
|
||||||
|
{ "user", required_argument, NULL, 'u' },
|
||||||
|
{ "tty", required_argument, NULL, 't' },
|
||||||
|
{ "verbose", no_argument, NULL, 'v' },
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ 0, 0, NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
memset(&wl, 0, sizeof wl);
|
||||||
|
|
||||||
|
while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'u':
|
||||||
|
wl.new_user = optarg;
|
||||||
|
if (getuid() != 0)
|
||||||
|
error(1, 0, "Permission denied. -u allowed for root only");
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
tty = optarg;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
wl.verbose = 1;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
help("weston-launch");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((argc - optind) > (MAX_ARGV_SIZE - 6))
|
||||||
|
error(1, E2BIG, "Too many arguments to pass to weston");
|
||||||
|
|
||||||
|
if (strcmp (argv[optind], "mutter") &&
|
||||||
|
strcmp (argv[optind], "gnome-shell") &&
|
||||||
|
strcmp (argv[optind], "gnome-shell-real") && 0)
|
||||||
|
error(1, 0, "mutter-launch can only be used to launch mutter or gnome-shell");
|
||||||
|
|
||||||
|
if (wl.new_user)
|
||||||
|
wl.pw = getpwnam(wl.new_user);
|
||||||
|
else
|
||||||
|
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 should either:\n"
|
||||||
|
#ifdef HAVE_SYSTEMD_LOGIN
|
||||||
|
" - run from an active and local (systemd) session.\n"
|
||||||
|
#else
|
||||||
|
" - enable systemd session support for weston-launch.\n"
|
||||||
|
#endif
|
||||||
|
" - or add yourself to the 'weston-launch' group.");
|
||||||
|
|
||||||
|
if (setup_tty(&wl, tty) < 0)
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
if (wl.new_user && setup_pam(&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]);
|
||||||
|
if (wl.tty != STDIN_FILENO)
|
||||||
|
close(wl.tty);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
46
src/wayland/weston-launch.h
Normal file
46
src/wayland/weston-launch.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _WESTON_LAUNCH_H_
|
||||||
|
#define _WESTON_LAUNCH_H_
|
||||||
|
|
||||||
|
enum weston_launcher_opcode {
|
||||||
|
WESTON_LAUNCHER_OPEN,
|
||||||
|
WESTON_LAUNCHER_DRM_SET_MASTER
|
||||||
|
};
|
||||||
|
|
||||||
|
struct weston_launcher_message {
|
||||||
|
int opcode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct weston_launcher_open {
|
||||||
|
struct weston_launcher_message header;
|
||||||
|
int flags;
|
||||||
|
char path[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct weston_launcher_set_master {
|
||||||
|
struct weston_launcher_message header;
|
||||||
|
int set_master;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
283
src/xrandr.xml
Normal file
283
src/xrandr.xml
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
<!DOCTYPE node PUBLIC
|
||||||
|
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||||
|
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||||
|
<node>
|
||||||
|
<!--
|
||||||
|
org.gnome.Mutter.DisplayConfig:
|
||||||
|
@short_description: display configuration interface
|
||||||
|
|
||||||
|
This interface is used by mutter and gnome-settings-daemon
|
||||||
|
to apply multiple monitor configuration.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<interface name="org.gnome.Mutter.DisplayConfig">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
GetResources:
|
||||||
|
@serial: configuration serial
|
||||||
|
@crtcs: available CRTCs
|
||||||
|
@outputs: available outputs
|
||||||
|
@modes: available modes
|
||||||
|
@max_screen_width:
|
||||||
|
@max_screen_height:
|
||||||
|
|
||||||
|
Retrieves the current layout of the hardware.
|
||||||
|
|
||||||
|
@serial is an unique identifier representing the current state
|
||||||
|
of the screen. It must be passed back to ApplyConfiguration()
|
||||||
|
and will be increased for every configuration change (so that
|
||||||
|
mutter can detect that the new configuration is based on old
|
||||||
|
state).
|
||||||
|
|
||||||
|
A CRTC (CRT controller) is a logical monitor, ie a portion
|
||||||
|
of the compositor coordinate space. It might correspond
|
||||||
|
to multiple monitors, when in clone mode, but not that
|
||||||
|
it is possible to implement clone mode also by setting different
|
||||||
|
CRTCs to the same coordinates.
|
||||||
|
|
||||||
|
The number of CRTCs represent the maximum number of monitors
|
||||||
|
that can be set to expand and it is a HW constraint; if more
|
||||||
|
monitors are connected, then necessarily some will clone. This
|
||||||
|
is complementary to the concept of the encoder (not exposed in
|
||||||
|
the API), which groups outputs that necessarily will show the
|
||||||
|
same image (again a HW constraint).
|
||||||
|
|
||||||
|
A CRTC is represented by a DBus structure with the following
|
||||||
|
layout:
|
||||||
|
* u ID: the ID in the API of this CRTC
|
||||||
|
* x winsys_id: the low-level ID of this CRTC (which might
|
||||||
|
be a XID, a KMS handle or something entirely
|
||||||
|
different)
|
||||||
|
* i x, y, width, height: the geometry of this CRTC
|
||||||
|
(might be invalid if the CRTC is not in
|
||||||
|
use)
|
||||||
|
* i current_mode: the current mode of the CRTC, or -1 if this
|
||||||
|
CRTC is not used
|
||||||
|
Note: the size of the mode will always correspond
|
||||||
|
to the width and height of the CRTC
|
||||||
|
* u current_transform: the current transform (espressed according
|
||||||
|
to the wayland protocol)
|
||||||
|
* au transforms: all possible transforms
|
||||||
|
* a{sv} properties: other high-level properties that affect this
|
||||||
|
CRTC; they are not necessarily reflected in
|
||||||
|
the hardware.
|
||||||
|
No property is specified in this version of the API.
|
||||||
|
|
||||||
|
Note: all geometry information refers to the untransformed
|
||||||
|
display.
|
||||||
|
|
||||||
|
An output represents a physical screen, connected somewhere to
|
||||||
|
the computer. Floating connectors are not exposed in the API.
|
||||||
|
An output is a DBus struct with the following fields:
|
||||||
|
* u ID: the ID in the API
|
||||||
|
* x winsys_id: the low-level ID of this output (XID or KMS handle)
|
||||||
|
* i current_crtc: the CRTC that is currently driving this output,
|
||||||
|
or -1 if the output is disabled
|
||||||
|
* au possible_crtcs: all CRTCs that can control this output
|
||||||
|
* s name: the name of the connector to which the output is attached
|
||||||
|
(like VGA1 or HDMI)
|
||||||
|
* au modes: valid modes for this output
|
||||||
|
* au clones: valid clones for this output, ie other outputs that
|
||||||
|
can be assigned the same CRTC as this one; if you
|
||||||
|
want to mirror two outputs that don't have each other
|
||||||
|
in the clone list, you must configure two different
|
||||||
|
CRTCs for the same geometry
|
||||||
|
* a{sv} properties: other high-level properties that affect this
|
||||||
|
output; they are not necessarily reflected in
|
||||||
|
the hardware.
|
||||||
|
Known properties:
|
||||||
|
- "vendor" (s): (readonly) the human readable name
|
||||||
|
of the manufacturer
|
||||||
|
- "product" (s): (readonly) the human readable name
|
||||||
|
of the display model
|
||||||
|
- "serial" (s): (readonly) the serial number of this
|
||||||
|
particular hardware part
|
||||||
|
- "display-name" (s): (readonly) a human readable name
|
||||||
|
of this output, to be shown in the UI
|
||||||
|
- "backlight" (i): (readonly, use the specific interface)
|
||||||
|
the backlight value as a percentage
|
||||||
|
(-1 if not supported)
|
||||||
|
- "primary" (b): whether this output is primary
|
||||||
|
or not
|
||||||
|
- "presentation" (b): whether this output is
|
||||||
|
for presentation only
|
||||||
|
Note: properties might be ignored if not consistenly
|
||||||
|
applied to all outputs in the same clone group. In
|
||||||
|
general, it's expected that presentation or primary
|
||||||
|
outputs will not be cloned.
|
||||||
|
|
||||||
|
A mode represents a set of parameters that are applied to
|
||||||
|
each output, such as resolution and refresh rate. It is a separate
|
||||||
|
object so that it can be referenced by CRTCs and outputs.
|
||||||
|
Multiple outputs in the same CRTCs must all have the same mode.
|
||||||
|
A mode is exposed as:
|
||||||
|
* u ID: the ID in the API
|
||||||
|
* x winsys_id: the low-level ID of this mode
|
||||||
|
* u width, height: the resolution
|
||||||
|
* d frequency: refresh rate
|
||||||
|
|
||||||
|
Output and modes are read-only objects (except for output properties),
|
||||||
|
they can change only in accordance to HW changes (such as hotplugging
|
||||||
|
a monitor), while CRTCs can be changed with ApplyConfiguration().
|
||||||
|
|
||||||
|
XXX: actually, if you insist enough, you can add new modes
|
||||||
|
through xrandr command line or the KMS API, overriding what the
|
||||||
|
kernel driver and the EDID say.
|
||||||
|
Usually, it only matters with old cards with broken drivers, or
|
||||||
|
old monitors with broken EDIDs, but it happens more often with
|
||||||
|
projectors (if for example the kernel driver doesn't add the
|
||||||
|
640x480 - 800x600 - 1024x768 default modes). Probably something
|
||||||
|
that we need to handle in mutter anyway.
|
||||||
|
-->
|
||||||
|
<method name="GetResources">
|
||||||
|
<arg name="serial" direction="out" type="u" />
|
||||||
|
<arg name="crtcs" direction="out" type="a(uxiiiiiuaua{sv})" />
|
||||||
|
<arg name="outputs" direction="out" type="a(uxiausauaua{sv})" />
|
||||||
|
<arg name="modes" direction="out" type="a(uxuud)" />
|
||||||
|
<arg name="max_screen_width" direction="out" type="i" />
|
||||||
|
<arg name="max_screen_height" direction="out" type="i" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ApplyConfiguration:
|
||||||
|
@serial: configuration serial
|
||||||
|
@persistent: whether this configuration should be saved on disk
|
||||||
|
@crtcs: new data for CRTCs
|
||||||
|
@outputs: new data for outputs
|
||||||
|
|
||||||
|
Applies the requested configuration changes.
|
||||||
|
|
||||||
|
@serial must match the serial from the last GetResources() call,
|
||||||
|
or org.freedesktop.DBus.AccessDenied will be generated.
|
||||||
|
(XXX: a better error maybe?)
|
||||||
|
|
||||||
|
If @persistent is true, mutter will attempt to replicate this
|
||||||
|
configuration the next time this HW layout appears.
|
||||||
|
(XXX: or is this gnome-settings-daemon role?)
|
||||||
|
|
||||||
|
@crtcs represents the new logical configuration, as a list
|
||||||
|
of structures containing:
|
||||||
|
- u ID: the API ID from the corresponding GetResources() call
|
||||||
|
- i new_mode: the API ID of the new mode to configure the CRTC
|
||||||
|
with, or -1 if the CRTC should be disabled
|
||||||
|
- i x, y: the new coordinates of the top left corner
|
||||||
|
the geometry will be completed with the size information
|
||||||
|
from @new_mode
|
||||||
|
- u transform: the desired transform
|
||||||
|
- au outputs: the API ID of outputs that should be assigned to
|
||||||
|
this CRTC
|
||||||
|
- a{sv} properties: properties whose value should be changed
|
||||||
|
|
||||||
|
Note: CRTCs not referenced in the array will be disabled.
|
||||||
|
|
||||||
|
@outputs represent the output property changes as:
|
||||||
|
- u ID: the API ID of the output to change
|
||||||
|
- a{sv} properties: properties whose value should be changed
|
||||||
|
|
||||||
|
Note: both for CRTCs and outputs, properties not included in
|
||||||
|
the dictionary will not be changed.
|
||||||
|
|
||||||
|
Note: unrecognized properties will have no effect, but if the
|
||||||
|
configuration change succeeds the property will be reported
|
||||||
|
by the next GetResources() call, and if @persistent is true,
|
||||||
|
it will also be saved to disk.
|
||||||
|
|
||||||
|
If the configuration is invalid according to the previous
|
||||||
|
GetResources() call, for example because a CRTC references
|
||||||
|
an output it cannot drive, or not all outputs support the
|
||||||
|
chosen mode, the error org.freedesktop.DBus.InvalidArgs will
|
||||||
|
be generated.
|
||||||
|
|
||||||
|
If the configuration cannot be applied for any other reason
|
||||||
|
(eg. the screen size would exceed texture limits), the error
|
||||||
|
org.freedesktop.DBus.Error.LimitsExceeded will be generated.
|
||||||
|
-->
|
||||||
|
<method name="ApplyConfiguration">
|
||||||
|
<arg name="serial" direction="in" type="u" />
|
||||||
|
<arg name="persistent" direction="in" type="b" />
|
||||||
|
<arg name="crtcs" direction="in" type="a(uiiiuaua{sv})" />
|
||||||
|
<arg name="outputs" direction="in" type="a(ua{sv})" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ChangeBacklight:
|
||||||
|
@serial: configuration serial
|
||||||
|
@output: the API id of the output
|
||||||
|
@value: the new backlight value
|
||||||
|
|
||||||
|
Changes the backlight of @output to @value, which is
|
||||||
|
expressed as a percentage and rounded to the HW limits.
|
||||||
|
-->
|
||||||
|
<method name="ChangeBacklight">
|
||||||
|
<arg name="serial" direction="in" type="u" />
|
||||||
|
<arg name="output" direction="in" type="u" />
|
||||||
|
<arg name="value" direction="in" type="i" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
GetCrtcGamma:
|
||||||
|
@serial: configuration serial
|
||||||
|
@crtc: API id of the crtc
|
||||||
|
@red: red gamma ramp
|
||||||
|
@green: green gamma ramp
|
||||||
|
@blue: blue gamma ramp
|
||||||
|
|
||||||
|
Requests the current gamma ramps of @crtc.
|
||||||
|
-->
|
||||||
|
<method name="GetCrtcGamma">
|
||||||
|
<arg name="serial" direction="in" type="u" />
|
||||||
|
<arg name="crtc" direction="in" type="u" />
|
||||||
|
<arg name="red" direction="out" type="aq" />
|
||||||
|
<arg name="green" direction="out" type="aq" />
|
||||||
|
<arg name="blue" direction="out" type="aq" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SetCrtcGamma:
|
||||||
|
@serial: configuration serial
|
||||||
|
@crtc: API id of the crtc
|
||||||
|
@red: red gamma ramp
|
||||||
|
@green: green gamma ramp
|
||||||
|
@blue: blue gamma ramp
|
||||||
|
|
||||||
|
Changes the gamma ramps of @crtc.
|
||||||
|
-->
|
||||||
|
<method name="SetCrtcGamma">
|
||||||
|
<arg name="serial" direction="in" type="u" />
|
||||||
|
<arg name="crtc" direction="in" type="u" />
|
||||||
|
<arg name="red" direction="in" type="aq" />
|
||||||
|
<arg name="green" direction="in" type="aq" />
|
||||||
|
<arg name="blue" direction="in" type="aq" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
PowerSaveMode:
|
||||||
|
|
||||||
|
Contains the current power saving mode for the screen, and
|
||||||
|
allows changing it.
|
||||||
|
|
||||||
|
Possible values:
|
||||||
|
- 0: on
|
||||||
|
- 1: standby
|
||||||
|
- 2: suspend
|
||||||
|
- 3: off
|
||||||
|
- -1: unknown (unsupported)
|
||||||
|
|
||||||
|
A client should not attempt to change the powersave mode
|
||||||
|
from -1 (unknown) to any other value, and viceversa.
|
||||||
|
Note that the actual effects of the different values
|
||||||
|
depend on the hardware and the kernel driver in use, and
|
||||||
|
it's perfectly possible that all values different than on
|
||||||
|
have the same effect.
|
||||||
|
Also, setting the PowerSaveMode to 3 (off) may or may
|
||||||
|
not have the same effect as disabling all outputs by
|
||||||
|
setting no CRTC on them with ApplyConfiguration(), and
|
||||||
|
may or may not cause a configuration change.
|
||||||
|
|
||||||
|
Also note that this property might become out of date
|
||||||
|
if changed through different means (for example using the
|
||||||
|
XRandR interface directly).
|
||||||
|
-->
|
||||||
|
<property name="PowerSaveMode" type="i" access="readwrite" />
|
||||||
|
</interface>
|
||||||
|
</node>
|
Reference in New Issue
Block a user