[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