uterm: new video backend
Our old backend was hacked together and hadn't have any structure. This is a new approach to create the uterm-library inside kmscon. The uterm-library will contain everything that is needed to run an application on Linux DRM devices without X11 or Wayland support. The idea is to move the input subsystem to uterm, too. No other stuff is currently planned to be included in uterm. Although uterm is supposed to be a separate library, we do not build it as such library. We currently include the log-subsystem and the eloop-handlers in the library so we cannot build it as stand-alone library. However, we try to keep it separate so if we ever need to export it, then it should be a one-hour job to do it so. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
parent
7eafca0e9f
commit
8e2af97038
@ -51,7 +51,9 @@ libkmscon_core_la_SOURCES = \
|
||||
src/input.c src/input.h \
|
||||
src/vte.c src/vte.h \
|
||||
src/terminal.c src/terminal.h \
|
||||
src/pty.c src/pty.h
|
||||
src/pty.c src/pty.h \
|
||||
src/uterm.h src/uterm_internal.h \
|
||||
src/uterm_video.c
|
||||
|
||||
if USE_XKBCOMMON
|
||||
libkmscon_core_la_SOURCES += \
|
||||
|
175
src/uterm.h
Normal file
175
src/uterm.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Linux User-Space Terminal
|
||||
* Historically, terminals were implemented in kernel-space on linux. With the
|
||||
* development of KMS and the linux input-API it is now possible to implement
|
||||
* all we need in user-space. This allows to disable the in-kernel CONFIG_VT and
|
||||
* similar options and reduce the kernel-overhead.
|
||||
* This library provides an API to implement terminals in user-space. This is
|
||||
* not limited to classic text-terminals but rather to all kind of applications
|
||||
* that need graphical output (with OpenGL) or direct keyboard/mouse/etc. input
|
||||
* from the kernel.
|
||||
* This API is divided into categories. Each sub-API implements one aspect.
|
||||
* Currently, only video output is available. Other sub-APIs will follow.
|
||||
*/
|
||||
|
||||
#ifndef UTERM_UTERM_H
|
||||
#define UTERM_UTERM_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "eloop.h"
|
||||
|
||||
/*
|
||||
* Video Control
|
||||
* Linux provides 2 famous ways to access the video hardware: fbdev and drm
|
||||
* fbdev is the older one of both and is simply a mmap() of the framebuffer into
|
||||
* main memory. It does not allow 3D acceleration and if you need 2D
|
||||
* acceleration you should use libraries like cairo to draw into the framebuffer
|
||||
* provided by this library.
|
||||
* DRM is the new approach which provides 3D acceleration with mesa. It allows
|
||||
* much more configuration as fbdev and is the recommended way to access video
|
||||
* hardware on modern computers.
|
||||
* Modern mesa provides 3D acceleration on fbdev, too. This is used in systems
|
||||
* like Android. This will allow us to provide an fbdev backend here, however,
|
||||
* we haven't implemented this yet, as this requires the Android software stack
|
||||
* on your system which is really not what we want.
|
||||
*
|
||||
* Famous linux graphics systems like X.Org/X11 or Wayland use fbdev or DRM
|
||||
* internally to access the video hardware. This API allows low-level access to
|
||||
* fbdev and DRM without the need of X.Org/X11 or Wayland. If VT support is
|
||||
* enabled in your kernel, each application can run on a different VT. For
|
||||
* instance, X.Org may run on VT-7, Wayland on VT-8, your application on VT-9
|
||||
* and default consoles on VT-1 to VT-6. You can switch between them with
|
||||
* ctrl-alt-F1-F12.
|
||||
* If VT support is not available (very unlikely) you need other ways to switch
|
||||
* between applications.
|
||||
*
|
||||
* The main object by this API is uterm_video. This object scans the system for
|
||||
* video devices, either fbdev or drm. You cannot use both simulatneously,
|
||||
* though. This wouldn't make sense as often both fbdev and drm devices refer to
|
||||
* the same output. Use "UTERM_VIDEO_DRM" to scan for DRM devices. Otherwise,
|
||||
* fbdev is used. DRM is the recommended way. Use fbdev only on embedded devices
|
||||
* which do not come with an DRM driver.
|
||||
* The uterm_video object scans for graphic-cards and connected displays. Each
|
||||
* display is represented as a uterm_display object. The uterm_video object is
|
||||
* hotplug-capable so it reports if a display is connected or disconnected.
|
||||
* Each uterm_display object can be activated/deactivated independently of the
|
||||
* other displays. To draw to a display you need to create a uterm_screen object
|
||||
* and add your display to the screen. The screen object allows to spread a
|
||||
* single screen onto multiple displays. Currently, the uterm_screen object
|
||||
* allows only one display per screen but we may extend this in the future.
|
||||
*
|
||||
* If you are using fbdev, you *must* correctly destroy your uterm_video object
|
||||
* and also call uterm_video_segfault() if you abnormally abort your
|
||||
* application. Otherwise your video device remains in undefined state and other
|
||||
* applications might not display correctly.
|
||||
* If you use DRM, the same operations are recommended but not required as the
|
||||
* kernel can correctly reset video devices on its own.
|
||||
*/
|
||||
|
||||
struct uterm_screen;
|
||||
struct uterm_mode;
|
||||
struct uterm_display;
|
||||
struct uterm_video;
|
||||
|
||||
enum uterm_display_state {
|
||||
UTERM_DISPLAY_ACTIVE,
|
||||
UTERM_DISPLAY_ASLEEP,
|
||||
UTERM_DISPLAY_INACTIVE,
|
||||
UTERM_DISPLAY_GONE,
|
||||
};
|
||||
|
||||
enum uterm_display_dpms {
|
||||
UTERM_DPMS_ON,
|
||||
UTERM_DPMS_STANDBY,
|
||||
UTERM_DPMS_SUSPEND,
|
||||
UTERM_DPMS_OFF,
|
||||
UTERM_DPMS_UNKNOWN,
|
||||
};
|
||||
|
||||
enum uterm_video_type {
|
||||
UTERM_VIDEO_DRM,
|
||||
UTERM_VIDEO_FBDEV,
|
||||
};
|
||||
|
||||
/* misc */
|
||||
|
||||
const char *uterm_dpms_to_name(int dpms);
|
||||
|
||||
/* screen interface */
|
||||
|
||||
int uterm_screen_new_single(struct uterm_screen **out,
|
||||
struct uterm_display *disp);
|
||||
void uterm_screen_ref(struct uterm_screen *screen);
|
||||
void uterm_screen_unref(struct uterm_screen *screen);
|
||||
|
||||
int uterm_screen_use(struct uterm_screen *screen);
|
||||
int uterm_screen_swap(struct uterm_screen *screen);
|
||||
|
||||
/* display modes interface */
|
||||
|
||||
void uterm_mode_ref(struct uterm_mode *mode);
|
||||
void uterm_mode_unref(struct uterm_mode *mode);
|
||||
struct uterm_mode *uterm_mode_next(struct uterm_mode *mode);
|
||||
|
||||
const char *uterm_mode_get_name(const struct uterm_mode *mode);
|
||||
unsigned int uterm_mode_get_width(const struct uterm_mode *mode);
|
||||
unsigned int uterm_mode_get_height(const struct uterm_mode *mode);
|
||||
|
||||
/* display interface */
|
||||
|
||||
void uterm_display_ref(struct uterm_display *disp);
|
||||
void uterm_display_unref(struct uterm_display *disp);
|
||||
struct uterm_display *uterm_display_next(struct uterm_display *disp);
|
||||
|
||||
struct uterm_mode *uterm_display_get_modes(struct uterm_display *disp);
|
||||
struct uterm_mode *uterm_display_get_current(struct uterm_display *disp);
|
||||
struct uterm_mode *uterm_display_get_default(struct uterm_display *disp);
|
||||
|
||||
int uterm_display_get_state(struct uterm_display *disp);
|
||||
int uterm_display_activate(struct uterm_display *disp, struct uterm_mode *mode);
|
||||
void uterm_display_deactivate(struct uterm_display *disp);
|
||||
int uterm_display_set_dpms(struct uterm_display *disp, int state);
|
||||
int uterm_display_get_dpms(const struct uterm_display *disp);
|
||||
|
||||
/* video interface */
|
||||
|
||||
int uterm_video_new(struct uterm_video **out,
|
||||
int type,
|
||||
struct ev_eloop *eloop);
|
||||
void uterm_video_ref(struct uterm_video *video);
|
||||
void uterm_video_unref(struct uterm_video *video);
|
||||
|
||||
void uterm_video_segfault(struct uterm_video *video);
|
||||
struct uterm_display *uterm_video_get_displays(struct uterm_video *video);
|
||||
|
||||
void uterm_video_sleep(struct uterm_video *video);
|
||||
int uterm_video_wake_up(struct uterm_video *video);
|
||||
bool uterm_video_is_awake(struct uterm_video *video);
|
||||
|
||||
#endif /* UTERM_UTERM_H */
|
278
src/uterm_internal.h
Normal file
278
src/uterm_internal.h
Normal file
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Internal definitions */
|
||||
|
||||
#ifndef UTERM_INTERNAL_H
|
||||
#define UTERM_INTERNAL_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <libudev.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "eloop.h"
|
||||
#include "uterm.h"
|
||||
|
||||
/* backend-operations */
|
||||
|
||||
struct mode_ops {
|
||||
int (*init) (struct uterm_mode *mode);
|
||||
void (*destroy) (struct uterm_mode *mode);
|
||||
const char *(*get_name) (const struct uterm_mode *mode);
|
||||
unsigned int (*get_width) (const struct uterm_mode *mode);
|
||||
unsigned int (*get_height) (const struct uterm_mode *mode);
|
||||
};
|
||||
|
||||
struct display_ops {
|
||||
int (*init) (struct uterm_display *display);
|
||||
void (*destroy) (struct uterm_display *display);
|
||||
int (*activate) (struct uterm_display *disp, struct uterm_mode *mode);
|
||||
void (*deactivate) (struct uterm_display *disp);
|
||||
int (*set_dpms) (struct uterm_display *disp, int state);
|
||||
int (*use) (struct uterm_display *disp);
|
||||
int (*swap) (struct uterm_display *disp);
|
||||
};
|
||||
|
||||
struct video_ops {
|
||||
int (*init) (struct uterm_video *video);
|
||||
void (*destroy) (struct uterm_video *video);
|
||||
void (*segfault) (struct uterm_video *video);
|
||||
int (*poll) (struct uterm_video *video, int mask);
|
||||
void (*sleep) (struct uterm_video *video);
|
||||
int (*wake_up) (struct uterm_video *video);
|
||||
};
|
||||
|
||||
#define VIDEO_CALL(func, els, ...) (func ? func(__VA_ARGS__) : els)
|
||||
|
||||
/* drm */
|
||||
|
||||
#ifdef UTERM_HAVE_DRM
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <gbm.h>
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
struct drm_mode {
|
||||
drmModeModeInfo info;
|
||||
};
|
||||
|
||||
struct drm_rb {
|
||||
struct gbm_bo *bo;
|
||||
uint32_t fb;
|
||||
EGLImageKHR image;
|
||||
GLuint rb;
|
||||
};
|
||||
|
||||
struct drm_display {
|
||||
uint32_t conn_id;
|
||||
int crtc_id;
|
||||
drmModeCrtc *saved_crtc;
|
||||
|
||||
int current_rb;
|
||||
struct drm_rb rb[2];
|
||||
GLuint fb;
|
||||
};
|
||||
|
||||
struct drm_video {
|
||||
int id;
|
||||
int fd;
|
||||
struct ev_fd *efd;
|
||||
struct gbm_device *gbm;
|
||||
EGLDisplay *disp;
|
||||
EGLContext *ctx;
|
||||
};
|
||||
|
||||
static const bool drm_available = true;
|
||||
extern const struct mode_ops drm_mode_ops;
|
||||
extern const struct display_ops drm_display_ops;
|
||||
extern const struct video_ops drm_video_ops;
|
||||
|
||||
#else /* !UTERM_HAVE_DRM */
|
||||
|
||||
struct drm_mode {
|
||||
int unused;
|
||||
};
|
||||
|
||||
struct drm_display {
|
||||
int unused;
|
||||
};
|
||||
|
||||
struct drm_video {
|
||||
int unused;
|
||||
};
|
||||
|
||||
static const bool drm_available = false;
|
||||
static const struct mode_ops drm_mode_ops;
|
||||
static const struct display_ops drm_display_ops;
|
||||
static const struct video_ops drm_video_ops;
|
||||
|
||||
#endif /* UTERM_HAVE_DRM */
|
||||
|
||||
/* fbdev */
|
||||
|
||||
#ifdef UTERM_HAVE_FBDEV
|
||||
|
||||
#include <linux/fb.h>
|
||||
|
||||
struct fbdev_mode {
|
||||
int unused;
|
||||
};
|
||||
|
||||
struct fbdev_display {
|
||||
int id;
|
||||
char *node;
|
||||
int fd;
|
||||
|
||||
size_t len;
|
||||
void *map;
|
||||
unsigned int stride;
|
||||
|
||||
struct fb_fix_screeninfo finfo;
|
||||
struct fb_var_screeninfo vinfo;
|
||||
unsigned int rate;
|
||||
};
|
||||
|
||||
struct fbdev_video {
|
||||
int unused;
|
||||
};
|
||||
|
||||
static const bool fbdev_available = true;
|
||||
extern const struct mode_ops fbdev_mode_ops;
|
||||
extern const struct display_ops fbdev_display_ops;
|
||||
extern const struct video_ops fbdev_video_ops;
|
||||
|
||||
#else /* !UTERM_HAVE_FBDEV */
|
||||
|
||||
struct fbdev_mode {
|
||||
int unused;
|
||||
};
|
||||
|
||||
struct fbdev_display {
|
||||
int unused;
|
||||
};
|
||||
|
||||
struct fbdev_video {
|
||||
int unused;
|
||||
};
|
||||
|
||||
static const bool fbdev_available = false;
|
||||
static const struct mode_ops fbdev_mode_ops;
|
||||
static const struct display_ops fbdev_display_ops;
|
||||
static const struct video_ops fbdev_video_ops;
|
||||
|
||||
#endif /* UTERM_HAVE_FBDEV */
|
||||
|
||||
/* uterm_screen */
|
||||
|
||||
struct uterm_screen {
|
||||
unsigned long ref;
|
||||
struct uterm_display *disp;
|
||||
};
|
||||
|
||||
/* uterm_mode */
|
||||
|
||||
struct uterm_mode {
|
||||
unsigned long ref;
|
||||
struct uterm_mode *next;
|
||||
|
||||
const struct mode_ops *ops;
|
||||
union {
|
||||
struct drm_mode drm;
|
||||
struct fbdev_mode fbdev;
|
||||
};
|
||||
};
|
||||
|
||||
int mode_new(struct uterm_mode **out, const struct mode_ops *ops);
|
||||
|
||||
/* uterm_display */
|
||||
|
||||
#define DISPLAY_ONLINE 0x01
|
||||
#define DISPLAY_VSYNC 0x02
|
||||
#define DISPLAY_AVAILABLE 0x04
|
||||
#define DISPLAY_OPEN 0x08
|
||||
|
||||
struct uterm_display {
|
||||
unsigned long ref;
|
||||
unsigned int flags;
|
||||
struct uterm_display *next;
|
||||
struct uterm_video *video;
|
||||
|
||||
struct uterm_mode *modes;
|
||||
struct uterm_mode *default_mode;
|
||||
struct uterm_mode *current_mode;
|
||||
int dpms;
|
||||
|
||||
const struct display_ops *ops;
|
||||
union {
|
||||
struct drm_display drm;
|
||||
struct fbdev_display fbdev;
|
||||
};
|
||||
};
|
||||
|
||||
int display_new(struct uterm_display **out, const struct display_ops *ops);
|
||||
|
||||
static inline bool display_is_conn(const struct uterm_display *disp)
|
||||
{
|
||||
return disp->video;
|
||||
}
|
||||
|
||||
static inline bool display_is_online(const struct uterm_display *disp)
|
||||
{
|
||||
return display_is_conn(disp) && (disp->flags & DISPLAY_ONLINE);
|
||||
}
|
||||
|
||||
/* uterm_video */
|
||||
|
||||
#define VIDEO_AWAKE 0x01
|
||||
#define VIDEO_HOTPLUG 0x02
|
||||
|
||||
struct uterm_video {
|
||||
unsigned long ref;
|
||||
unsigned int flags;
|
||||
struct ev_eloop *eloop;
|
||||
|
||||
struct udev *udev;
|
||||
struct udev_monitor *umon;
|
||||
struct ev_fd *umon_fd;
|
||||
|
||||
struct uterm_display *displays;
|
||||
|
||||
const struct video_ops *ops;
|
||||
union {
|
||||
struct drm_video drm;
|
||||
struct fbdev_video fbdev;
|
||||
};
|
||||
};
|
||||
|
||||
static inline bool video_is_awake(const struct uterm_video *video)
|
||||
{
|
||||
return video->flags & VIDEO_AWAKE;
|
||||
}
|
||||
|
||||
#endif /* UTERM_INTERNAL_H */
|
524
src/uterm_video.c
Normal file
524
src/uterm_video.c
Normal file
@ -0,0 +1,524 @@
|
||||
/*
|
||||
* 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 "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;
|
||||
|
||||
*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;
|
||||
|
||||
free(screen);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
video->udev = udev_new();
|
||||
if (!video->udev) {
|
||||
log_err("cannot create udev object");
|
||||
ret = -EFAULT;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
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);
|
||||
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_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);
|
||||
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);
|
||||
}
|
||||
|
||||
struct uterm_display *uterm_video_get_displays(struct uterm_video *video)
|
||||
{
|
||||
if (!video)
|
||||
return NULL;
|
||||
|
||||
return video->displays;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user