-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <linux/vt.h>
-#include <linux/kd.h>
-#include <linux/major.h>
#include "ecore_drm_private.h"
-#include "ecore_drm_logind.h"
#ifndef KDSKBMUTE
# define KDSKBMUTE 0x4B51
#endif
-static Ecore_Event_Handler *active_hdl;
-static char *sid;
+static Ecore_Event_Handler *active_hdlr;
-static Eina_Bool
+#ifdef HAVE_SYSTEMD
+static inline Eina_Bool
+_ecore_drm_logind_vt_get(Ecore_Drm_Device *dev)
+{
+ int ret;
+ char *tty;
+
+ ret = sd_session_get_tty(dev->session, &tty);
+ if (ret < 0)
+ {
+ ERR("Could not get systemd tty: %m");
+ return EINA_FALSE;
+ }
+
+ ret = sscanf(tty, "tty%u", &dev->vt);
+ free(tty);
+
+ if (ret != 1) return EINA_FALSE;
+
+ return EINA_TRUE;
+}
+#endif
+
+static Eina_Bool
+_ecore_drm_logind_vt_setup(Ecore_Drm_Device *dev)
+{
+ struct stat st;
+ char buff[64];
+ struct vt_mode vtmode = { 0 };
+
+ snprintf(buff, sizeof(buff), "/dev/tty%d", dev->vt);
+ buff[sizeof(buff) - 1] = 0;
+
+ dev->tty.fd = open(buff, (O_RDWR | O_CLOEXEC | O_NONBLOCK));
+ if (dev->tty.fd < 0)
+ {
+ ERR("Could not open VT %s %m", buff);
+ return EINA_FALSE;
+ }
+
+ if ((fstat(dev->tty.fd, &st) == -1) ||
+ (major(st.st_rdev) != TTY_MAJOR) ||
+ (minor(st.st_rdev) <= 0) || (minor(st.st_rdev) >= 64))
+ {
+ ERR("TTY %s is not a virtual terminal", buff);
+ goto stat_err;
+ }
+
+ if (ioctl(dev->tty.fd, KDGKBMODE, &dev->tty.kbd_mode) < 0)
+ {
+ ERR("Could not read keyboard mode of %s: %m", buff);
+ dev->tty.kbd_mode = K_UNICODE;
+ }
+ else if (dev->tty.kbd_mode == K_OFF)
+ dev->tty.kbd_mode = K_UNICODE;
+
+ if ((ioctl(dev->tty.fd, KDSKBMUTE, 1) < 0) &&
+ (ioctl(dev->tty.fd, KDSKBMODE, K_OFF) < 0))
+ {
+ ERR("Could not set K_OFF keyboard mode on %s: %m", buff);
+ goto stat_err;
+ }
+
+ if (ioctl(dev->tty.fd, KDSETMODE, KD_GRAPHICS) < 0)
+ {
+ ERR("Could not set KD_GRAPHICS mode on %s: %m", buff);
+ goto kbdmode_err;
+ }
+
+ vtmode.mode = VT_PROCESS;
+ vtmode.waitv =0 ;
+ vtmode.relsig = SIGUSR1;
+ vtmode.acqsig = SIGUSR2;
+
+ if (ioctl(dev->tty.fd, VT_SETMODE, &vtmode) < 0)
+ {
+ ERR("Could not take over virtual terminal: %m");
+ goto mode_err;
+ }
+
+ return EINA_TRUE;
+
+mode_err:
+ ioctl(dev->tty.fd, KDSETMODE, KD_TEXT);
+kbdmode_err:
+ ioctl(dev->tty.fd, KDSKBMUTE, 0);
+ ioctl(dev->tty.fd, KDSKBMODE, dev->tty.kbd_mode);
+stat_err:
+ close(dev->tty.fd);
+ return EINA_FALSE;
+}
+
+static void
+_ecore_drm_logind_vt_destroy(Ecore_Drm_Device *dev)
+{
+ _ecore_drm_logind_restore(dev);
+ close(dev->tty.fd);
+}
+
+static Eina_Bool
+_ecore_drm_logind_cb_vt_signal(void *data, int type EINA_UNUSED, void *event)
+{
+ Ecore_Drm_Device *dev;
+ Ecore_Event_Signal_User *ev;
+ siginfo_t sig;
+
+ ev = event;
+ sig = ev->data;
+
+ if (sig.si_code != SI_KERNEL) return ECORE_CALLBACK_RENEW;
+ if (!(dev = data)) return ECORE_CALLBACK_RENEW;
+
+ switch (ev->number)
+ {
+ case 1:
+ _ecore_drm_event_activate_send(EINA_FALSE);
+ ioctl(dev->tty.fd, VT_RELDISP, 1);
+ break;
+ case 2:
+ ioctl(dev->tty.fd, VT_RELDISP, VT_ACKACQ);
+ _ecore_drm_event_activate_send(EINA_TRUE);
+ break;
+ default:
+ break;
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
_ecore_drm_logind_cb_activate(void *data, int type EINA_UNUSED, void *event)
{
- Ecore_Drm_Event_Activate *e;
+ Ecore_Drm_Event_Activate *ev;
Ecore_Drm_Device *dev;
Ecore_Drm_Output *output;
Eina_List *l;
if ((!event) || (!data)) return ECORE_CALLBACK_RENEW;
- e = event;
+ ev = event;
dev = data;
- if (e->active)
+ if (ev->active)
{
/* set output mode */
EINA_LIST_FOREACH(dev->outputs, l, output)
return ECORE_CALLBACK_PASS_ON;
}
-Eina_Bool
+Eina_Bool
_ecore_drm_logind_connect(Ecore_Drm_Device *dev)
{
#ifdef HAVE_SYSTEMD
- /* get sd-login properties we need */
- if (sd_pid_get_session(getpid(), &sid) < 0) return EINA_FALSE;
-#endif
+ char *seat;
+
+ /* get session id */
+ if (sd_pid_get_session(getpid(), &dev->session) < 0)
+ {
+ ERR("Could not get systemd session: %m");
+ return EINA_FALSE;
+ }
- /* try to init dbus */
- if (!_ecore_drm_dbus_init(sid))
+ if (sd_session_get_seat(dev->session, &seat) < 0)
+ {
+ ERR("Could not get systemd seat: %m");
+ free(seat);
+ return EINA_FALSE;
+ }
+ else if (strcmp(dev->seat, seat))
{
- free(sid);
+ ERR("Session seat '%s' differs from device seat '%s'", seat, dev->seat);
+ free(seat);
return EINA_FALSE;
}
- active_hdl =
- ecore_event_handler_add(ECORE_DRM_EVENT_ACTIVATE,
- _ecore_drm_logind_cb_activate, dev);
+ free(seat);
+
+ if (!_ecore_drm_logind_vt_get(dev)) return EINA_FALSE;
+#endif
+
+ if (!_ecore_drm_dbus_init(dev)) return EINA_FALSE;
+
+ /* take control of session */
+ if (!_ecore_drm_dbus_session_take(dev->session))
+ {
+ ERR("Could not take control of session");
+ goto take_err;
+ }
+
+ /* setup vt */
+ if (!_ecore_drm_logind_vt_setup(dev))
+ {
+ ERR("Could not setup vt '%d'", dev->vt);
+ goto vt_err;
+ }
+
+ /* setup handler for vt signals */
+ dev->tty.event_hdlr =
+ ecore_event_handler_add(ECORE_EVENT_SIGNAL_USER,
+ _ecore_drm_logind_cb_vt_signal, dev);
+
+ active_hdlr =
+ ecore_event_handler_add(ECORE_DRM_EVENT_ACTIVATE,
+ _ecore_drm_logind_cb_activate, dev);
return EINA_TRUE;
+
+vt_err:
+ _ecore_drm_dbus_session_release(dev->session);
+take_err:
+ _ecore_drm_dbus_shutdown();
+ return EINA_FALSE;
}
-void
-_ecore_drm_logind_disconnect(Ecore_Drm_Device *dev EINA_UNUSED)
+void
+_ecore_drm_logind_disconnect(Ecore_Drm_Device *dev)
{
+ if (active_hdlr) ecore_event_handler_del(active_hdlr);
+ active_hdlr = NULL;
+
+ _ecore_drm_logind_vt_destroy(dev);
+ _ecore_drm_dbus_session_release(dev->session);
_ecore_drm_dbus_shutdown();
+}
- if (active_hdl)
- {
- ecore_event_handler_del(active_hdl);
- active_hdl = NULL;
- }
+void
+_ecore_drm_logind_restore(Ecore_Drm_Device *dev)
+{
+ struct vt_mode vtmode = { 0 };
+
+ if ((!dev) || (dev->tty.fd < 0)) return;
+
+ ioctl(dev->tty.fd, KDSETMODE, KD_TEXT);
+ ioctl(dev->tty.fd, KDSKBMUTE, 0);
+ ioctl(dev->tty.fd, KDSKBMODE, dev->tty.kbd_mode);
+ vtmode.mode = VT_AUTO;
+ ioctl(dev->tty.fd, VT_SETMODE, &vtmode);
}
Eina_Bool