[MPlayer-dev-eng] [PATCH] 5/5 The main body of vtswitch code
Alan Curry
pacman at theworld.com
Thu Apr 20 03:47:31 CEST 2006
Now that the preliminary stuff is done, here's VT-switching.
--- libvo/vo_fbdev.c.orig 2006-04-18 14:09:43.000000000 -0500
+++ libvo/vo_fbdev.c 2006-04-18 13:46:19.000000000 -0500
@@ -14,11 +14,13 @@
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
+#include <signal.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/kd.h>
+#include <sys/vt.h>
#include <linux/fb.h>
#include "config.h"
@@ -546,7 +548,16 @@
static int vt_doit = 1;
/* vt-switching variables */
+static int vtswitch_ok; /* true if doing process-managed VT switching */
+static volatile sig_atomic_t switched; /* true if mplayer's VT is not active */
+static volatile sig_atomic_t canswitch; /* true unless writing to fb */
+static volatile sig_atomic_t needswitch; /* true if signaled while !canswitch */
+static volatile sig_atomic_t canreinit; /* true if mode variables are ready */
+static volatile sig_atomic_t needreinit; /* true if signaled while !canreinit */
static int orig_kdmode;
+static struct vt_mode vtmode, orig_vtmode;
+static struct sigaction orig_usr1, orig_usr2;
+static sigset_t vtswitch_sigset;
/* vo_fbdev related variables */
static int fb_dev_fd;
@@ -736,8 +747,138 @@
return map.framebuffer == my_fb();
}
+/* Return true if mplayer's tty is currently visible */
+static int my_vt_is_active(void)
+{
+ struct vt_stat vt;
+
+ if(fb_tty_fd<0 || ioctl(fb_tty_fd, VT_GETSTATE, &vt))
+ /* Can't find the truth; let's use this baseless assumption: */
+ return 1;
+
+ if(my_vt()==-1)
+ /* Here's the same baseless assumption again */
+ return 1;
+
+ return my_vt()==vt.v_active;
+}
+
+static void reinit_vt(void)
+{
+ ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_vinfo);
+ if(fb_newcmap)
+ ioctl(fb_dev_fd, FBIOPUTCMAP, fb_newcmap);
+ if (fs || vm) {
+ memset(frame_buffer, '\0', fb_line_len * fb_yres);
+ }
+
+ /* It would be nice to redraw the last displayed frame if
+ playback is currently paused. Problem: where to find a
+ copy of the last displayed frame */
+
+ needreinit = 0;
+ switched = 0;
+}
+
+static void handle_vtswitch(int sig)
+{
+ if(sig==SIGUSR1) {
+ if(canswitch) {
+ ioctl(fb_tty_fd, VT_RELDISP, 1);
+ switched = 1;
+ } else {
+ needswitch = 1;
+ }
+ } else { /* SIGUSR2 */
+ ioctl(fb_tty_fd, VT_RELDISP, VT_ACKACQ);
+ if(canreinit) {
+ reinit_vt();
+ } else {
+ needreinit = 1;
+ needswitch = 0;
+ }
+ }
+}
+
+/* Before writing to framebuffer memory, call this function to disable
+ switching away from the graphics VT in the middle of the drawing operation.
+ If it returns 0, we're already switched away, so don't draw anything.
+ If it returns non-zero, draw whatever you want and then call
+ vtswitch_enable(). */
+static int vtswitch_disable(void)
+{
+ if (!vtswitch_ok)
+ return 1;
+
+ canswitch = 0;
+
+ if (needreinit) {
+ sigprocmask(SIG_BLOCK, &vtswitch_sigset, NULL);
+ if (needreinit)
+ reinit_vt();
+ sigprocmask(SIG_UNBLOCK, &vtswitch_sigset, NULL);
+ }
+
+ if (switched) {
+ canswitch = 1;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* This is a version of vtswitch_disable which is guaranteed to return
+ non-zero, by waiting for the user to switch back to mplayer's VT if
+ necessary. */
+static int vtswitch_disable_wait(void)
+{
+ if (!vtswitch_ok)
+ return 1;
+
+ canswitch = 0;
+
+ if (switched && !needreinit) {
+ sigset_t mask, oldmask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+
+ sigprocmask(SIG_BLOCK, &mask, &oldmask);
+ while (switched && !needreinit)
+ sigsuspend(&oldmask);
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ }
+
+ if (needreinit)
+ reinit_vt();
+
+ return 1;
+}
+
+static void vtswitch_enable(void)
+{
+ if (!vtswitch_ok)
+ return;
+
+ canswitch = 1;
+
+ if (needswitch) {
+ sigprocmask(SIG_BLOCK, &vtswitch_sigset, NULL);
+
+ if (needswitch) {
+ ioctl(fb_tty_fd, VT_RELDISP, 1);
+ switched = 1;
+ needswitch = 0;
+ }
+
+ sigprocmask(SIG_UNBLOCK, &vtswitch_sigset, NULL);
+ }
+}
+
static void vtswitch_init(void)
{
+ struct sigaction act;
+
if (fb_tty_fd < 0)
return;
@@ -749,19 +890,87 @@
fb_tty_fd = -1;
return;
}
+
+ canswitch = 0;
+ needswitch = 0;
+ canreinit = 0;
+ needreinit = 0;
+ memset(&act, 0, sizeof(act));
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGUSR1);
+ sigaddset(&act.sa_mask, SIGUSR2);
+ act.sa_handler = handle_vtswitch;
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGUSR1, &act, &orig_usr1);
+ sigaction(SIGUSR2, &act, &orig_usr2);
+
+ if(orig_usr1.sa_handler != SIG_DFL)
+ mp_msg(MSGT_VO, MSGL_WARN, "SIGUSR1 already in use?\n");
+ if(orig_usr1.sa_handler != SIG_DFL)
+ mp_msg(MSGT_VO, MSGL_WARN, "SIGUSR2 already in use?\n");
+
+ sigemptyset(&vtswitch_sigset);
+ sigaddset(&vtswitch_sigset, SIGUSR1);
+ sigaddset(&vtswitch_sigset, SIGUSR2);
}
if (ioctl(fb_tty_fd, KDGETMODE, &orig_kdmode) < 0) {
mp_msg(MSGT_VO, MSGL_V, "Can't get graphics mode: %s\n", strerror(errno));
close(fb_tty_fd);
fb_tty_fd = -1;
- return;
+ goto fail;
}
if (ioctl(fb_tty_fd, KDSETMODE, KD_GRAPHICS) < 0) {
mp_msg(MSGT_VO, MSGL_V, "Can't set graphics mode: %s\n", strerror(errno));
close(fb_tty_fd);
fb_tty_fd = -1;
+ goto fail;
+ }
+
+#ifdef CONFIG_VIDIX
+ if (vidix_name)
return;
+#endif
+
+ if (ioctl(fb_tty_fd, VT_GETMODE, &vtmode) < 0) {
+ mp_msg(MSGT_VO, MSGL_V, "Can't get VT mode: %s\n", strerror(errno));
+ goto fail;
+ }
+ if (vtmode.mode == VT_PROCESS) {
+ /* It is not possible to save the current VT state and
+ restore it later, because an important piece of that state
+ (the PID which will receive the VT-switching signals) is
+ not kept in the vt_mode struct, so it can neither be
+ retrieved by VT_GETMODE nor restored by VT_SETMODE, which
+ always sets it to the current process when
+ mode==VT_PROCESS. */
+ mp_msg(MSGT_VO, MSGL_V,
+ "VT switching is already managed by another process\n");
+ goto fail;
+ }
+ orig_vtmode = vtmode;
+ vtmode.mode = VT_PROCESS;
+ vtmode.relsig = SIGUSR1;
+ vtmode.acqsig = SIGUSR2;
+ if (ioctl(fb_tty_fd, VT_SETMODE, &vtmode) < 0) {
+ mp_msg(MSGT_VO, MSGL_V, "Can't set VT mode: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ vtswitch_ok = 1;
+
+ switched = !my_vt_is_active();
+
+ vtswitch_enable();
+ return;
+
+fail:
+#ifdef CONFIG_VIDIX
+ if (!vidix_name)
+#endif
+ {
+ sigaction(SIGUSR1, &orig_usr1, NULL);
+ sigaction(SIGUSR2, &orig_usr2, NULL);
}
}
@@ -771,6 +980,20 @@
if (ioctl(fb_tty_fd, KDSETMODE, orig_kdmode) < 0)
mp_msg(MSGT_VO, MSGL_WARN, "Can't restore text mode: %s\n", strerror(errno));
}
+
+ if (!vtswitch_ok)
+ return;
+
+ if (ioctl(fb_tty_fd, VT_SETMODE, &orig_vtmode) < 0) {
+ mp_msg(MSGT_VO, MSGL_WARN, "Can't restore VT mode: %s\n", strerror(errno));
+ }
+
+ sigaction(SIGUSR1, &orig_usr1, NULL);
+ sigaction(SIGUSR2, &orig_usr2, NULL);
+
+ vtswitch_enable();
+
+ vtswitch_ok = 0;
}
static int fb_preinit(int reset)
@@ -932,6 +1155,8 @@
int zoom = flags & VOFLAG_SWSCALE;
int vt_fd;
+ canreinit = 0;
+
vm = flags & VOFLAG_MODESWITCHING;
fs = flags & VOFLAG_FULLSCREEN;
@@ -990,13 +1215,13 @@
fb_vinfo.xres_virtual = fb_vinfo.xres;
fb_vinfo.yres_virtual = fb_vinfo.yres;
+ vtswitch_disable_wait();
if (ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_vinfo)) {
mp_msg(MSGT_VO, MSGL_ERR, "Can't put VSCREENINFO: %s\n", strerror(errno));
- if (fb_tty_fd >= 0 && ioctl(fb_tty_fd, KDSETMODE, KD_TEXT) < 0) {
- mp_msg(MSGT_VO, MSGL_ERR, "Can't restore text mode: %s\n", strerror(errno));
- }
+ vtswitch_enable();
return 1;
}
+ vtswitch_enable();
fb_pixel_size = fb_vinfo.bits_per_pixel / 8;
fb_bpp = fb_vinfo.red.length + fb_vinfo.green.length +
@@ -1028,10 +1253,13 @@
first_row = (out_height - in_height) / 2;
last_row = (out_height + in_height) / 2;
+ vtswitch_disable_wait();
if (ioctl(fb_dev_fd, FBIOGET_FSCREENINFO, &fb_finfo)) {
mp_msg(MSGT_VO, MSGL_ERR, "Can't get FSCREENINFO: %s\n", strerror(errno));
+ vtswitch_enable();
return 1;
}
+ vtswitch_enable();
lots_of_printf();
@@ -1045,6 +1273,8 @@
break;
case FB_VISUAL_DIRECTCOLOR:
mp_msg(MSGT_VO, MSGL_V, "creating cmap for directcolor\n");
+
+ vtswitch_disable_wait();
if(fb_newcmap) {
freecmap(fb_newcmap);
fb_newcmap = NULL;
@@ -1052,16 +1282,21 @@
if (ioctl(fb_dev_fd, FBIOGETCMAP, &fb_oldcmap)) {
mp_msg(MSGT_VO, MSGL_ERR, "can't get cmap: %s\n",
strerror(errno));
+ vtswitch_enable();
return 1;
}
- if (!(fb_newcmap = make_directcolor_cmap(&fb_vinfo)))
+ if (!(fb_newcmap = make_directcolor_cmap(&fb_vinfo))) {
+ vtswitch_enable();
return 1;
+ }
if (ioctl(fb_dev_fd, FBIOPUTCMAP, fb_newcmap)) {
mp_msg(MSGT_VO, MSGL_ERR, "can't put cmap: %s\n",
strerror(errno));
freecmap(fb_newcmap);
+ vtswitch_enable();
return 1;
}
+ vtswitch_enable();
break;
default:
mp_msg(MSGT_VO, MSGL_ERR, "visual: %d not yet supported\n",
@@ -1147,8 +1382,12 @@
mp_msg(MSGT_VO, MSGL_DBG2, "center @ %p\n", center);
mp_msg(MSGT_VO, MSGL_V, "pixel per line: %d\n", fb_line_len / fb_pixel_size);
- if (fs || vm)
- memset(frame_buffer, '\0', fb_line_len * fb_yres);
+ if (fs || vm) {
+ if(vtswitch_disable()) {
+ memset(frame_buffer, '\0', fb_line_len * fb_yres);
+ vtswitch_enable();
+ }
+ }
}
if (vt_doit && (vt_fd = open("/dev/tty", O_WRONLY)) == -1) {
mp_msg(MSGT_VO, MSGL_ERR, "can't open /dev/tty: %s\n", strerror(errno));
@@ -1162,6 +1401,8 @@
if (vt_doit)
vt_set_textarea(last_row, fb_yres);
+ canreinit = 1;
+
return 0;
}
@@ -1187,9 +1428,14 @@
{
unsigned char *dst;
+ if (!vtswitch_disable())
+ return;
+
dst = center + fb_line_len * y0 + fb_pixel_size * x0;
(*draw_alpha_p)(w, h, src, srca, stride, dst, fb_line_len);
+
+ vtswitch_enable();
}
static int draw_frame(uint8_t *src[]) { return 1; }
@@ -1200,6 +1446,9 @@
uint8_t *d;
uint8_t *s;
+ if (!vtswitch_disable())
+ return;
+
d = center + fb_line_len * y + fb_pixel_size * x;
s = src[0];
@@ -1210,6 +1459,8 @@
h--;
}
+ vtswitch_enable();
+
return 0;
}
@@ -1228,6 +1479,8 @@
static void uninit(void)
{
+ canreinit = 0;
+ vtswitch_disable_wait();
if (fb_newcmap) {
if (ioctl(fb_dev_fd, FBIOPUTCMAP, &fb_oldcmap))
mp_msg(MSGT_VO, MSGL_WARN, "Can't restore original cmap\n");
@@ -1240,6 +1493,7 @@
fb_orig_vinfo.yoffset = fb_vinfo.yoffset;
if (ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_orig_vinfo))
mp_msg(MSGT_VO, MSGL_WARN, "Can't reset original fb_var_screeninfo: %s\n", strerror(errno));
+ vtswitch_enable();
vtswitch_uninit();
if (vt_doit)
vt_set_textarea(0, fb_orig_vinfo.yres);
@@ -1277,6 +1531,12 @@
static uint32_t get_image(mp_image_t *mpi)
{
+ /* Since we don't know exactly when the caller will be done with mpi,
+ just keep vt switching disabled all the time, except right here: */
+ vtswitch_enable();
+ if(vtswitch_disable())
+ return VO_FALSE;
+
if (
!IMGFMT_IS_BGR(mpi->imgfmt) ||
(IMGFMT_BGR_DEPTH(mpi->imgfmt) != fb_bpp) ||
More information about the MPlayer-dev-eng
mailing list