kmscon/src/uterm_video.c
David Herrmann 335182556c uterm_video: add udev device parameter to video_new()
When triggered by seat monitor we need to be able to create uterm_video
objects on a concrete device so enable passing it in.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
2012-05-03 19:24:39 +02:00

577 lines
12 KiB
C

/*
* uterm - Linux User-Space Terminal
*
* Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* Video Control
* Core Implementation of the uterm_video, uterm_display and uterm_screen
* objects.
*/
#include <errno.h>
#include <inttypes.h>
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "eloop.h"
#include "log.h"
#include "misc.h"
#include "uterm.h"
#include "uterm_internal.h"
#define LOG_SUBSYSTEM "video"
const char *uterm_dpms_to_name(int dpms)
{
switch (dpms) {
case UTERM_DPMS_ON:
return "ON";
case UTERM_DPMS_STANDBY:
return "STANDBY";
case UTERM_DPMS_SUSPEND:
return "SUSPEND";
case UTERM_DPMS_OFF:
return "OFF";
default:
return "UNKNOWN";
}
}
/* Until we allow multiple displays in one screen, we use this constructor which
* is basically just a wrapper around "struct uterm_dispaly".
* The idea behind screens is having one single drawing-target which is spread
* across several displays which can be placed anywhere in the virtual screen.
*/
int uterm_screen_new_single(struct uterm_screen **out,
struct uterm_display *disp)
{
struct uterm_screen *screen;
if (!out || !disp)
return -EINVAL;
screen = malloc(sizeof(*screen));
if (!screen)
return -ENOMEM;
memset(screen, 0, sizeof(*screen));
screen->ref = 1;
screen->disp = disp;
uterm_display_ref(screen->disp);
*out = screen;
return 0;
}
void uterm_screen_ref(struct uterm_screen *screen)
{
if (!screen || !screen->ref)
return;
++screen->ref;
}
void uterm_screen_unref(struct uterm_screen *screen)
{
if (!screen || !screen->ref || --screen->ref)
return;
uterm_display_unref(screen->disp);
free(screen);
}
unsigned int uterm_screen_width(struct uterm_screen *screen)
{
if (!screen)
return 0;
return uterm_mode_get_width(uterm_display_get_current(screen->disp));
}
unsigned int uterm_screen_height(struct uterm_screen *screen)
{
if (!screen)
return 0;
return uterm_mode_get_height(uterm_display_get_current(screen->disp));
}
int uterm_screen_use(struct uterm_screen *screen)
{
if (!screen || !display_is_online(screen->disp))
return -EINVAL;
return VIDEO_CALL(screen->disp->ops->use, 0, screen->disp);
}
int uterm_screen_swap(struct uterm_screen *screen)
{
if (!screen || !display_is_online(screen->disp))
return -EINVAL;
return VIDEO_CALL(screen->disp->ops->swap, 0, screen->disp);
}
int mode_new(struct uterm_mode **out, const struct mode_ops *ops)
{
struct uterm_mode *mode;
int ret;
if (!out || !ops)
return -EINVAL;
mode = malloc(sizeof(*mode));
if (!mode)
return -ENOMEM;
memset(mode, 0, sizeof(*mode));
mode->ref = 1;
mode->ops = ops;
ret = VIDEO_CALL(mode->ops->init, 0, mode);
if (ret)
goto err_free;
*out = mode;
return 0;
err_free:
free(mode);
return ret;
}
void uterm_mode_ref(struct uterm_mode *mode)
{
if (!mode || !mode->ref)
return;
++mode->ref;
}
void uterm_mode_unref(struct uterm_mode *mode)
{
if (!mode || !mode->ref || --mode->ref)
return;
VIDEO_CALL(mode->ops->destroy, 0, mode);
free(mode);
}
struct uterm_mode *uterm_mode_next(struct uterm_mode *mode)
{
if (!mode)
return NULL;
return mode->next;
}
const char *uterm_mode_get_name(const struct uterm_mode *mode)
{
if (!mode)
return NULL;
return VIDEO_CALL(mode->ops->get_name, NULL, mode);
}
unsigned int uterm_mode_get_width(const struct uterm_mode *mode)
{
if (!mode)
return 0;
return VIDEO_CALL(mode->ops->get_width, 0, mode);
}
unsigned int uterm_mode_get_height(const struct uterm_mode *mode)
{
if (!mode)
return 0;
return VIDEO_CALL(mode->ops->get_height, 0, mode);
}
int display_new(struct uterm_display **out, const struct display_ops *ops)
{
struct uterm_display *disp;
int ret;
if (!out || !ops)
return -EINVAL;
disp = malloc(sizeof(*disp));
if (!disp)
return -ENOMEM;
memset(disp, 0, sizeof(*disp));
disp->ref = 1;
disp->ops = ops;
ret = VIDEO_CALL(disp->ops->init, 0, disp);
if (ret)
goto err_free;
log_info("new display %p", disp);
*out = disp;
return 0;
err_free:
free(disp);
return ret;
}
void uterm_display_ref(struct uterm_display *disp)
{
if (!disp || !disp->ref)
return;
++disp->ref;
}
void uterm_display_unref(struct uterm_display *disp)
{
struct uterm_mode *mode;
if (!disp || !disp->ref || --disp->ref)
return;
log_info("free display %p", disp);
VIDEO_CALL(disp->ops->destroy, 0, disp);
while ((mode = disp->modes)) {
disp->modes = mode->next;
mode->next = NULL;
uterm_mode_unref(mode);
}
free(disp);
}
struct uterm_display *uterm_display_next(struct uterm_display *disp)
{
if (!disp)
return NULL;
return disp->next;
}
struct uterm_mode *uterm_display_get_modes(struct uterm_display *disp)
{
if (!disp)
return NULL;
return disp->modes;
}
struct uterm_mode *uterm_display_get_current(struct uterm_display *disp)
{
if (!disp)
return NULL;
return disp->current_mode;
}
struct uterm_mode *uterm_display_get_default(struct uterm_display *disp)
{
if (!disp)
return NULL;
return disp->default_mode;
}
int uterm_display_get_state(struct uterm_display *disp)
{
if (!disp)
return UTERM_DISPLAY_GONE;
if (disp->video) {
if (disp->flags & DISPLAY_ONLINE) {
if (disp->video->flags & VIDEO_AWAKE)
return UTERM_DISPLAY_ACTIVE;
else
return UTERM_DISPLAY_ASLEEP;
} else {
return UTERM_DISPLAY_INACTIVE;
}
} else {
return UTERM_DISPLAY_GONE;
}
}
int uterm_display_activate(struct uterm_display *disp, struct uterm_mode *mode)
{
if (!disp || !display_is_conn(disp) || display_is_online(disp))
return -EINVAL;
if (!mode)
mode = disp->default_mode;
return VIDEO_CALL(disp->ops->activate, 0, disp, mode);
}
void uterm_display_deactivate(struct uterm_display *disp)
{
if (!disp || !display_is_online(disp))
return;
VIDEO_CALL(disp->ops->deactivate, 0, disp);
}
int uterm_display_set_dpms(struct uterm_display *disp, int state)
{
if (!disp || !display_is_conn(disp))
return -EINVAL;
return VIDEO_CALL(disp->ops->set_dpms, 0, disp, state);
}
int uterm_display_get_dpms(const struct uterm_display *disp)
{
if (!disp || !display_is_conn(disp))
return UTERM_DPMS_OFF;
return disp->dpms;
}
static void video_poll(struct ev_fd *fd, int mask, void *data)
{
struct uterm_video *video = data;
int ret;
ret = VIDEO_CALL(video->ops->poll, 0, video, mask);
if (ret) {
ev_eloop_rm_fd(video->umon_fd);
video->umon_fd = NULL;
}
}
static volatile int video_protect = 0;
int uterm_video_new(struct uterm_video **out,
int type,
struct ev_eloop *eloop)
{
struct uterm_video *video;
int ret, ufd;
const struct video_ops *ops;
/*
* We allow only one global video object. The reason behind this is that
* the OpenGL API has thread-awareness and we want to hide this behind
* our API. Otherwise, the caller would need to manage GL-contexts
* himself and this makes the API complex. Furthermore, there is really
* no reason why someone wants two video objects in the same process.
* That would mean that you want to control two graphic-cards in one
* process and we have never heard of a situation where this was needed.
* If you think you need two video objects, feel free to contact us and
* we might add a proper GL-context management API.
* But for now we protect this by a GCC-atomic. *_unref() decrements the
* counter again.
*/
ret = __sync_fetch_and_add(&video_protect, 1);
if (ret) {
__sync_fetch_and_sub(&video_protect, 1);
log_err("cannot create multiple instances");
return -ENOTSUP;
}
if (!out || !eloop)
return -EINVAL;
switch (type) {
case UTERM_VIDEO_DRM:
if (!drm_available) {
log_err("DRM backend is not available");
return -ENOTSUP;
}
ops = &drm_video_ops;
break;
case UTERM_VIDEO_FBDEV:
if (!fbdev_available) {
log_err("FBDEV backend is not available");
return -ENOTSUP;
}
ops = &fbdev_video_ops;
break;
default:
log_err("invalid video backend %d", type);
return -EINVAL;
}
video = malloc(sizeof(*video));
if (!video)
return -ENOMEM;
memset(video, 0, sizeof(*video));
video->ref = 1;
video->ops = ops;
video->eloop = eloop;
ret = kmscon_hook_new(&video->hook);
if (ret)
goto err_free;
video->udev = udev_new();
if (!video->udev) {
log_err("cannot create udev object");
ret = -EFAULT;
goto err_hook;
}
video->umon = udev_monitor_new_from_netlink(video->udev, "udev");
if (!video->umon) {
log_err("cannot create udev monitor");
ret = -EFAULT;
goto err_udev;
}
ufd = udev_monitor_get_fd(video->umon);
if (ufd < 0) {
log_err("cannot get udev-monitor fd");
ret = -EFAULT;
goto err_umon;
}
ret = ev_eloop_new_fd(video->eloop, &video->umon_fd, ufd,
EV_READABLE, video_poll, video);
if (ret)
goto err_umon;
ret = VIDEO_CALL(video->ops->init, 0, video, NULL);
if (ret)
goto err_umon_add;
ev_eloop_ref(video->eloop);
log_info("new device %p", video);
*out = video;
return 0;
err_umon_add:
ev_eloop_rm_fd(video->umon_fd);
err_umon:
udev_monitor_unref(video->umon);
err_udev:
udev_unref(video->udev);
err_hook:
kmscon_hook_free(video->hook);
err_free:
free(video);
return ret;
}
void uterm_video_ref(struct uterm_video *video)
{
if (!video || !video->ref)
return;
++video->ref;
}
void uterm_video_unref(struct uterm_video *video)
{
struct uterm_display *disp;
if (!video || !video->ref || --video->ref)
return;
log_info("free device %p", video);
VIDEO_CALL(video->ops->destroy, 0, video);
while ((disp = video->displays)) {
video->displays = disp->next;
disp->next = NULL;
uterm_display_unref(disp);
}
ev_eloop_rm_fd(video->umon_fd);
udev_monitor_unref(video->umon);
udev_unref(video->udev);
kmscon_hook_free(video->hook);
ev_eloop_unref(video->eloop);
free(video);
__sync_fetch_and_sub(&video_protect, 1);
}
void uterm_video_segfault(struct uterm_video *video)
{
if (!video)
return;
VIDEO_CALL(video->ops->segfault, 0, video);
}
int uterm_video_use(struct uterm_video *video)
{
if (!video)
return -EINVAL;
return video_do_use(video);
}
struct uterm_display *uterm_video_get_displays(struct uterm_video *video)
{
if (!video)
return NULL;
return video->displays;
}
int uterm_video_register_cb(struct uterm_video *video, uterm_video_cb cb,
void *data)
{
if (!video || !cb)
return -EINVAL;
return kmscon_hook_add_cast(video->hook, cb, data);
}
void uterm_video_unregister_cb(struct uterm_video *video, uterm_video_cb cb,
void *data)
{
if (!video || !cb)
return;
kmscon_hook_rm_cast(video->hook, cb, data);
}
void uterm_video_sleep(struct uterm_video *video)
{
if (!video || !video_is_awake(video))
return;
VIDEO_CALL(video->ops->sleep, 0, video);
}
int uterm_video_wake_up(struct uterm_video *video)
{
if (!video)
return -EINVAL;
if (video_is_awake(video))
return 0;
return VIDEO_CALL(video->ops->wake_up, 0, video);
}
bool uterm_video_is_awake(struct uterm_video *video)
{
return video && video_is_awake(video);
}