From fbe779b195539b292ca9fce85e6b361249c85712 Mon Sep 17 00:00:00 2001 From: Vincent Wei Date: Fri, 14 Jun 2019 10:12:17 +0800 Subject: [PATCH] operators for Linux TTY --- src/ial/linux-tty.c | 266 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 src/ial/linux-tty.c diff --git a/src/ial/linux-tty.c b/src/ial/linux-tty.c new file mode 100644 index 00000000..d574f270 --- /dev/null +++ b/src/ial/linux-tty.c @@ -0,0 +1,266 @@ +/* + * This file is part of MiniGUI, a mature cross-platform windowing + * and Graphics User Interface (GUI) support system for embedded systems + * and smart IoT devices. + * + * Copyright (C) 2002~2019, Beijing FMSoft Technologies Co., Ltd. + * Copyright (C) 1998~2002, WEI Yongming + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Or, + * + * As this program is a library, any link to this program must follow + * GNU General Public License version 3 (GPLv3). If you cannot accept + * GPLv3, you need to be licensed from FMSoft. + * + * If you have got a commercial license of this program, please use it + * under the terms and conditions of the commercial license. + * + * For more information about the commercial license, please refer to + * . + */ +/* +** linux-tty.c: handle linux tty device, e.g., VT switching. +** +** Derived from native/vt-switch.c by WEI Yongming at 2019-06-14 +*/ + +#include +#include +#include + +#include "common.h" +#include "minigui.h" + +#ifdef __LINUX__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gdi.h" +#include "window.h" +#include "cliprect.h" +#include "gal.h" +#include "internals.h" +#include "ctrlclass.h" +#include "dc.h" +#include "ial.h" +#include "linux-tty.h" + +#define SIGRELVT SIGUSR1 +#define SIGACQVT SIGUSR2 + +static int ttyfd = -1; +static int vtswitch_initialized = 0; +static int old_kd_mode = -1; +static struct vt_mode saved_vtmode; + +static int current_vt; +static volatile int console_active = 1; /* are we active? */ +static volatile int console_should_be_active = 1; /* should we be? */ + +/* vt_switch_requested: + * This is our signal handler; it gets called whenever a switch is + * requested, because either SIGRELVT or SIGACQVT is raised. + */ +static void vt_switch_requested(int signo) +{ + switch (signo) { + case SIGRELVT: + console_should_be_active = 0; + break; + case SIGACQVT: + console_should_be_active = 1; + break; + default: + return; + } + + /* Checks whether a switch is needed and not blocked */ + if (console_active == console_should_be_active) + return; + + if (console_should_be_active) { + /* Performs a switch back. */ + int new_fd; + + IAL_ResumeMouse(); + if ((new_fd = IAL_ResumeKeyboard()) >= 0) + ttyfd = new_fd; + + ioctl (ttyfd, VT_RELDISP, VT_ACKACQ); + + console_active = 1; + +#ifndef _MGRM_THREADS + __mg_switch_away = FALSE; +#endif + +#ifdef _MGRM_PROCESSES + UpdateTopmostLayer (NULL); +#else + SendNotifyMessage (HWND_DESKTOP, MSG_PAINT, 0, 0); +#endif + } + else { + /* Performs a switch away. */ + +#ifdef _MGRM_PROCESSES + DisableClientsOutput (); +#endif + + if (ioctl (ttyfd, VT_RELDISP, 1) == -1) { + fprintf (stderr, "Error can't switch away from VT: %m\n"); + return; + } + + console_active = 0; +#ifndef _MGRM_THREADS + __mg_switch_away = TRUE; +#endif + IAL_SuspendKeyboard(); + IAL_SuspendMouse(); + } +} + +/* Init linux tty module, and returns the tty fd */ +int mg_linux_tty_init(BOOL graf_mode, BOOL vt_switch) +{ + const char* tty_dev; + if (geteuid() == 0) + tty_dev = "/dev/tty0"; + else /* not a super user, so try to open the control terminal */ + tty_dev = "/dev/tty"; + + /* open tty */ + ttyfd = open (tty_dev, O_RDWR); + if (ttyfd < 0) { + _ERR_PRINTF("Linux>TTY: Cannot open %s: %m\n", tty_dev); + goto fail; + } + + if (graf_mode) { + if (ioctl (ttyfd, KDGETMODE, &old_kd_mode) == -1) { + _ERR_PRINTF("Linux>TTY: Cannot get kd mode: %m\n"); + goto fail; + } + + /* enter graphics mode */ + if (ioctl (ttyfd, KDSETMODE, KD_GRAPHICS) == -1) { + _WRN_PRINTF("Failed when setting the terminal to graphics mode: %m."); + _WRN_PRINTF("It might is not a console."); + } + } + + if (vt_switch) { + struct sigaction sa; + struct vt_mode vtm; + struct vt_stat stat; + + if (ioctl (ttyfd, VT_GETSTATE, &stat) == -1) { + _ERR_PRINTF("Linux>TTY: failed to get vt state: %m.\n"); + goto fail; + } + + current_vt = stat.v_active; + console_active = console_should_be_active = 1; + + /* Hook the signals */ + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGIO); /* block async IO during the VT switch */ + sa.sa_flags = 0; + sa.sa_handler = vt_switch_requested; + if ((sigaction(SIGRELVT, &sa, NULL) < 0) || (sigaction(SIGACQVT, &sa, NULL) < 0)) { + _ERR_PRINTF("Linux>TTY: Unable to control VT switch.\n"); + goto fail; + } + + /* Save old mode, take control, and arrange for the signals + * to be raised. */ + if (ioctl(ttyfd, VT_GETMODE, &saved_vtmode) == -1) { + _ERR_PRINTF("Linux>TTY: failed to get vt mode: %m.\n"); + goto fail; + } + + vtm = saved_vtmode; + vtm.mode = VT_PROCESS; + vtm.relsig = SIGRELVT; + vtm.acqsig = SIGACQVT; + + if (ioctl(ttyfd, VT_SETMODE, &vtm) == -1) { + _ERR_PRINTF("Linux>TTY: failed to set vt mode: %m.\n"); + goto fail; + } + + vtswitch_initialized = 1; + } + + return ttyfd; + +fail: + if (ttyfd >= 0) { + close(ttyfd); + ttyfd = -1; + } + + return -1; +} + +int mg_linux_tty_fini(void) +{ + if (ttyfd < 0) + return -1; + + if (vtswitch_initialized) { + struct sigaction sa; + + /* Must turn off the signals before unhooking them... */ + ioctl (ttyfd, VT_SETMODE, &saved_vtmode); + + sigemptyset (&sa.sa_mask); + sa.sa_handler = SIG_DFL; + sa.sa_flags = SA_RESTART; + sigaction (SIGRELVT, &sa, NULL); + sigaction (SIGACQVT, &sa, NULL); + + vtswitch_initialized = 0; + } + + if (old_kd_mode >= 0) { + ioctl (ttyfd, KDSETMODE, old_kd_mode); + } + + close (ttyfd); + ttyfd = -1; + return 0; +} + +int mg_linux_tty_switch_vt(int vt) +{ + if (ttyfd < 0 || !vtswitch_initialized || vt == current_vt) + return 1; + + ioctl (ttyfd, VT_ACTIVATE, vt); + return 0; +} + +#endif /* __LINUX__ */ +