summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcin Kurczewski <mkurczew@gmail.com>2015-04-17 19:59:31 +0200
committerwm4 <wm4@nowhere>2015-04-18 20:53:17 +0200
commitf3c8c613ba93f622a8a15299b2a05838b470b923 (patch)
tree554077ad2b41a2c47a338e0d877ea32e3dc72bb5
parentf51952441eb62744afeee60a553b70c91eaeef34 (diff)
downloadmpv-f3c8c613ba93f622a8a15299b2a05838b470b923.tar.bz2
mpv-f3c8c613ba93f622a8a15299b2a05838b470b923.tar.xz
vo_drm: fix VT switching
Fixes #1827
-rw-r--r--video/out/vo_drm.c284
1 files changed, 229 insertions, 55 deletions
diff --git a/video/out/vo_drm.c b/video/out/vo_drm.c
index f05e6eb940..2d820ba566 100644
--- a/video/out/vo_drm.c
+++ b/video/out/vo_drm.c
@@ -22,12 +22,19 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <linux/vt.h>
+#include <poll.h>
+#include <signal.h>
#include <stdbool.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
#include <libswscale/swscale.h>
#include <sys/mman.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
+#include "osdep/io.h"
+#include "osdep/timer.h"
#include "common/msg.h"
#include "sub/osd.h"
#include "video/fmt-conversion.h"
@@ -35,8 +42,15 @@
#include "video/sws_utils.h"
#include "vo.h"
+#define USE_MASTER 0
+#define EVT_RELEASE 1
+#define EVT_ACQUIRE 2
+#define EVT_INTERRUPT 255
#define BUF_COUNT 2
+static int setup_vo_crtc(struct vo *vo);
+static void release_vo_crtc(struct vo *vo);
+
struct modeset_buf {
uint32_t width;
uint32_t height;
@@ -55,11 +69,18 @@ struct modeset_dev {
int front_buf;
};
+struct vt_switcher {
+ int tty_fd;
+ struct vo *vo;
+};
+
struct priv {
int fd;
+ struct vt_switcher vt_switcher;
struct modeset_dev *dev;
drmModeCrtc *old_crtc;
+ bool active;
char *device_path;
int connector_id;
@@ -76,6 +97,8 @@ struct priv {
static int modeset_open(struct vo *vo, int *out, const char *node)
{
+ *out = -1;
+
int fd = open(node, O_RDWR | O_CLOEXEC);
if (fd < 0) {
MP_ERR(vo, "Cannot open \"%s\": %s.\n", node, mp_strerror(errno));
@@ -325,6 +348,162 @@ end:
+static int vt_switcher_pipe[2];
+
+static void vt_switcher_sighandler(int sig)
+{
+ unsigned char event = sig == SIGUSR1 ? EVT_RELEASE : EVT_ACQUIRE;
+ write(vt_switcher_pipe[1], &event, sizeof(event));
+}
+
+static void vt_switcher_interrupt(struct vt_switcher *s)
+{
+ unsigned char event = EVT_INTERRUPT;
+ write(vt_switcher_pipe[1], &event, sizeof(event));
+}
+
+static int vt_switcher_init(struct vt_switcher *s, struct vo *vo)
+{
+ s->tty_fd = -1;
+ vt_switcher_pipe[0] = -1;
+ vt_switcher_pipe[1] = -1;
+
+ s->vo = vo;
+ if (mp_make_cloexec_pipe(vt_switcher_pipe)) {
+ MP_ERR(vo, "Creating pipe failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ s->tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC);
+ if (s->tty_fd < 0) {
+ MP_ERR(vo, "Can't open TTY for VT control: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ struct sigaction act;
+ act.sa_handler = vt_switcher_sighandler;
+ act.sa_flags = SA_RESTART;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGUSR1, &act, 0);
+ sigaction(SIGUSR2, &act, 0);
+
+ struct vt_mode vt_mode;
+ if (ioctl(s->tty_fd, VT_GETMODE, &vt_mode) < 0) {
+ MP_ERR(vo, "VT_GETMODE failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ vt_mode.mode = VT_PROCESS;
+ vt_mode.relsig = SIGUSR1;
+ vt_mode.acqsig = SIGUSR2;
+ if (ioctl(s->tty_fd, VT_SETMODE, &vt_mode) < 0) {
+ MP_ERR(vo, "VT_SETMODE failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void vt_switcher_destroy(struct vt_switcher *s)
+{
+ close(s->tty_fd);
+ close(vt_switcher_pipe[0]);
+ close(vt_switcher_pipe[1]);
+}
+
+static void vt_switcher_poll(struct vt_switcher *s, int timeout_ms)
+{
+ struct pollfd fds[1] = {
+ { .events = POLLIN, .fd = vt_switcher_pipe[0] },
+ };
+ poll(fds, 1, timeout_ms);
+ if (!fds[0].revents) return;
+
+ unsigned char event;
+ if (read(fds[0].fd, &event, sizeof(event)) != sizeof(event)) return;
+
+ switch (event) {
+ case EVT_RELEASE:
+ release_vo_crtc(s->vo);
+ if (USE_MASTER) {
+ //this function enables support for switching to x, weston etc.
+ //however, for whatever reason, it can be called only by root users.
+ //until things change, this is commented.
+ struct priv *p = s->vo->priv;
+ if (drmDropMaster(p->fd)) {
+ MP_WARN(s->vo, "Failed to drop DRM master: %s\n", mp_strerror(errno));
+ }
+ }
+
+ if (ioctl(s->tty_fd, VT_RELDISP, 1) < 0) {
+ MP_ERR(s->vo, "Failed to release virtual terminal\n");
+ }
+ break;
+
+ case EVT_ACQUIRE:
+ if (USE_MASTER) {
+ struct priv *p = s->vo->priv;
+ if (drmSetMaster(p->fd)) {
+ MP_WARN(s->vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno));
+ }
+ }
+
+ setup_vo_crtc(s->vo);
+ if (ioctl(s->tty_fd, VT_RELDISP, VT_ACKACQ) < 0) {
+ MP_ERR(s->vo, "Failed to acquire virtual terminal\n");
+ }
+ break;
+
+ case EVT_INTERRUPT:
+ break;
+ }
+}
+
+
+
+static int setup_vo_crtc(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc);
+ int ret = drmModeSetCrtc(p->fd, p->dev->crtc,
+ p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb,
+ 0, 0, &p->dev->conn, 1, &p->dev->mode);
+ p->active = true;
+ return ret;
+}
+
+static void release_vo_crtc(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ p->active = false;
+ if (p->old_crtc) {
+ drmModeSetCrtc(p->fd,
+ p->old_crtc->crtc_id,
+ p->old_crtc->buffer_id,
+ p->old_crtc->x,
+ p->old_crtc->y,
+ &p->dev->conn,
+ 1,
+ &p->dev->mode);
+ drmModeFreeCrtc(p->old_crtc);
+ }
+}
+
+static int wait_events(struct vo *vo, int64_t until_time_us)
+{
+ struct priv *p = vo->priv;
+ int64_t wait_us = until_time_us - mp_time_us();
+ int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000);
+ vt_switcher_poll(&p->vt_switcher, timeout_ms);
+ return 0;
+}
+
+static void wakeup(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ vt_switcher_interrupt(&p->vt_switcher);
+}
+
static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
{
struct priv *p = vo->priv;
@@ -375,23 +554,23 @@ static void draw_image(struct vo *vo, mp_image_t *mpi)
{
struct priv *p = vo->priv;
- struct mp_rect src_rc = p->src;
- src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);
- src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y);
- mp_image_crop_rc(mpi, src_rc);
-
- mp_sws_scale(p->sws, p->cur_frame, mpi);
-
- osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, p->cur_frame);
-
- struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf];
- int32_t shift = (p->device_w * p->y + p->x) * 4;
- memcpy_pic(front_buf->map + shift,
- p->cur_frame->planes[0],
- (p->dst.x1 - p->dst.x0) * 4,
- p->dst.y1 - p->dst.y0,
- p->device_w * 4,
- p->cur_frame->stride[0]);
+ if (p->active) {
+ struct mp_rect src_rc = p->src;
+ src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);
+ src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y);
+ mp_image_crop_rc(mpi, src_rc);
+ mp_sws_scale(p->sws, p->cur_frame, mpi);
+ osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, p->cur_frame);
+
+ struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf];
+ int32_t shift = (p->device_w * p->y + p->x) * 4;
+ memcpy_pic(front_buf->map + shift,
+ p->cur_frame->planes[0],
+ (p->dst.x1 - p->dst.x0) * 4,
+ p->dst.y1 - p->dst.y0,
+ p->device_w * 4,
+ p->cur_frame->stride[0]);
+ }
if (mpi != p->last_input) {
talloc_free(p->last_input);
@@ -402,6 +581,7 @@ static void draw_image(struct vo *vo, mp_image_t *mpi)
static void flip_page(struct vo *vo)
{
struct priv *p = vo->priv;
+ if (!p->active) return;
int ret = drmModeSetCrtc(p->fd, p->dev->crtc,
p->dev->bufs[p->dev->front_buf].fb,
@@ -414,62 +594,54 @@ static void flip_page(struct vo *vo)
}
}
+static void uninit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ if (p->dev) {
+ release_vo_crtc(vo);
+
+ modeset_destroy_fb(p->fd, &p->dev->bufs[1]);
+ modeset_destroy_fb(p->fd, &p->dev->bufs[0]);
+ }
+
+ vt_switcher_destroy(&p->vt_switcher);
+ talloc_free(p->last_input);
+ talloc_free(p->cur_frame);
+ talloc_free(p->dev);
+ close(p->fd);
+}
+
static int preinit(struct vo *vo)
{
struct priv *p = vo->priv;
p->sws = mp_sws_alloc(vo);
+ p->fd = -1;
- int ret;
+ if (vt_switcher_init(&p->vt_switcher, vo))
+ goto err;
- ret = modeset_open(vo, &p->fd, p->device_path);
- if (ret)
- return -1;
+ if (modeset_open(vo, &p->fd, p->device_path))
+ goto err;
- ret = modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev);
- if (ret)
- return -1;
+ if (modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev))
+ goto err;
assert(p->dev);
p->device_w = p->dev->bufs[0].width;
p->device_h = p->dev->bufs[0].height;
- p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc);
- ret = drmModeSetCrtc(p->fd, p->dev->crtc,
- p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb,
- 0, 0, &p->dev->conn, 1, &p->dev->mode);
- if (ret) {
+ if (setup_vo_crtc(vo)) {
MP_ERR(vo, "Cannot set CRTC for connector %u: %s\n", p->connector_id,
mp_strerror(errno));
- return -1;
+ goto err;
}
return 0;
-}
-static void uninit(struct vo *vo)
-{
- struct priv *p = vo->priv;
-
- if (p->dev) {
- if (p->old_crtc) {
- drmModeSetCrtc(p->fd,
- p->old_crtc->crtc_id,
- p->old_crtc->buffer_id,
- p->old_crtc->x,
- p->old_crtc->y,
- &p->dev->conn,
- 1,
- &p->dev->mode);
- drmModeFreeCrtc(p->old_crtc);
- }
-
- modeset_destroy_fb(p->fd, &p->dev->bufs[1]);
- modeset_destroy_fb(p->fd, &p->dev->bufs[0]);
- }
-
- talloc_free(p->last_input);
- talloc_free(p->cur_frame);
- talloc_free(p->dev);
+err:
+ uninit(vo);
+ return -1;
}
static int query_format(struct vo *vo, int format)
@@ -500,6 +672,8 @@ const struct vo_driver video_out_drm = {
.draw_image = draw_image,
.flip_page = flip_page,
.uninit = uninit,
+ .wait_events = wait_events,
+ .wakeup = wakeup,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {
OPT_STRING("devpath", device_path, 0),