Users might be interested in vertical-blank events so we now allow them to register event-callbacks on displays. The only event that we currently pass is a page-flip event that is always sent as reaction to a swap. If there is no hardware support for such events, we simply throw it from within the swap() function. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
433 lines
14 KiB
C
433 lines
14 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.
|
|
*/
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef UTERM_UTERM_H
|
|
#define UTERM_UTERM_H
|
|
|
|
#include <inttypes.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.
|
|
*
|
|
* 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 attaches to a single
|
|
* graphics card via DRM or on a single frambuffer via fbdev. Many DRM drivers
|
|
* also provide an fbdev driver so you must go sure not to write to both
|
|
* simulatneously. 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_DUMB,
|
|
UTERM_VIDEO_FBDEV,
|
|
};
|
|
|
|
enum uterm_video_action {
|
|
UTERM_WAKE_UP,
|
|
UTERM_SLEEP,
|
|
UTERM_NEW,
|
|
UTERM_GONE,
|
|
};
|
|
|
|
struct uterm_video_hotplug {
|
|
struct uterm_display *display;
|
|
int action;
|
|
};
|
|
|
|
enum uterm_display_action {
|
|
UTERM_PAGE_FLIP,
|
|
};
|
|
|
|
struct uterm_display_event {
|
|
int action;
|
|
};
|
|
|
|
enum uterm_video_format {
|
|
UTERM_FORMAT_GREY,
|
|
UTERM_FORMAT_XRGB32,
|
|
};
|
|
|
|
struct uterm_video_buffer {
|
|
unsigned int width;
|
|
unsigned int height;
|
|
unsigned int stride;
|
|
unsigned int format;
|
|
uint8_t *data;
|
|
};
|
|
|
|
struct uterm_video_blend_req {
|
|
const struct uterm_video_buffer *buf;
|
|
unsigned int x;
|
|
unsigned int y;
|
|
uint8_t fr;
|
|
uint8_t fg;
|
|
uint8_t fb;
|
|
uint8_t br;
|
|
uint8_t bg;
|
|
uint8_t bb;
|
|
};
|
|
|
|
typedef void (*uterm_video_cb) (struct uterm_video *video,
|
|
struct uterm_video_hotplug *arg,
|
|
void *data);
|
|
typedef void (*uterm_display_cb) (struct uterm_display *disp,
|
|
struct uterm_display_event *arg,
|
|
void *data);
|
|
|
|
/* 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);
|
|
|
|
unsigned int uterm_screen_width(struct uterm_screen *screen);
|
|
unsigned int uterm_screen_height(struct uterm_screen *screen);
|
|
|
|
int uterm_screen_use(struct uterm_screen *screen);
|
|
int uterm_screen_swap(struct uterm_screen *screen);
|
|
int uterm_screen_blit(struct uterm_screen *screen,
|
|
const struct uterm_video_buffer *buf,
|
|
unsigned int x, unsigned int y);
|
|
int uterm_screen_blend(struct uterm_screen *screen,
|
|
const struct uterm_video_buffer *buf,
|
|
unsigned int x, unsigned int y,
|
|
uint8_t fr, uint8_t fg, uint8_t fb,
|
|
uint8_t br, uint8_t bg, uint8_t bb);
|
|
int uterm_screen_blendv(struct uterm_screen *screen,
|
|
const struct uterm_video_blend_req *req, size_t num);
|
|
int uterm_screen_fill(struct uterm_screen *screen,
|
|
uint8_t r, uint8_t g, uint8_t b,
|
|
unsigned int x, unsigned int y,
|
|
unsigned int width, unsigned int height);
|
|
|
|
/* 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);
|
|
|
|
int uterm_display_register_cb(struct uterm_display *disp, uterm_display_cb cb,
|
|
void *data);
|
|
void uterm_display_unregister_cb(struct uterm_display *disp,
|
|
uterm_display_cb cb, void *data);
|
|
|
|
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);
|
|
|
|
int uterm_display_use(struct uterm_display *disp);
|
|
int uterm_display_swap(struct uterm_display *disp);
|
|
|
|
int uterm_display_fill(struct uterm_display *disp,
|
|
uint8_t r, uint8_t g, uint8_t b,
|
|
unsigned int x, unsigned int y,
|
|
unsigned int width, unsigned int height);
|
|
int uterm_display_blit(struct uterm_display *disp,
|
|
const struct uterm_video_buffer *buf,
|
|
unsigned int x, unsigned int y);
|
|
int uterm_display_fake_blend(struct uterm_display *disp,
|
|
const struct uterm_video_buffer *buf,
|
|
unsigned int x, unsigned int y,
|
|
uint8_t fr, uint8_t fg, uint8_t fb,
|
|
uint8_t br, uint8_t bg, uint8_t bb);
|
|
int uterm_display_fake_blendv(struct uterm_display *disp,
|
|
const struct uterm_video_blend_req *req,
|
|
size_t num);
|
|
|
|
/* video interface */
|
|
|
|
int uterm_video_new(struct uterm_video **out,
|
|
struct ev_eloop *eloop,
|
|
unsigned int type,
|
|
const char *node);
|
|
void uterm_video_ref(struct uterm_video *video);
|
|
void uterm_video_unref(struct uterm_video *video);
|
|
|
|
void uterm_video_segfault(struct uterm_video *video);
|
|
int uterm_video_use(struct uterm_video *video);
|
|
struct uterm_display *uterm_video_get_displays(struct uterm_video *video);
|
|
int uterm_video_register_cb(struct uterm_video *video, uterm_video_cb cb,
|
|
void *data);
|
|
void uterm_video_unregister_cb(struct uterm_video *video, uterm_video_cb cb,
|
|
void *data);
|
|
|
|
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);
|
|
void uterm_video_poll(struct uterm_video *video);
|
|
|
|
/*
|
|
* Input Devices
|
|
* This input object can combine multiple linux input devices into a single
|
|
* device and notifies the application about events. It has several different
|
|
* keyboard backends so the full XKB feature set is available.
|
|
*/
|
|
|
|
struct uterm_input;
|
|
|
|
/* keep in sync with shl_xkb_mods */
|
|
enum uterm_input_modifier {
|
|
UTERM_SHIFT_MASK = (1 << 0),
|
|
UTERM_LOCK_MASK = (1 << 1),
|
|
UTERM_CONTROL_MASK = (1 << 2),
|
|
UTERM_ALT_MASK = (1 << 3),
|
|
UTERM_LOGO_MASK = (1 << 4),
|
|
};
|
|
|
|
/* keep in sync with TSM_VTE_INVALID */
|
|
#define UTERM_INPUT_INVALID 0xffffffff
|
|
|
|
struct uterm_input_event {
|
|
bool handled; /* user-controlled, default is false */
|
|
uint16_t keycode; /* linux keycode - KEY_* - linux/input.h */
|
|
uint32_t ascii; /* ascii keysym for @keycode */
|
|
unsigned int mods; /* active modifiers - uterm_modifier mask */
|
|
|
|
unsigned int num_syms; /* number of keysyms */
|
|
uint32_t *keysyms; /* XKB-common keysym-array - XKB_KEY_* */
|
|
uint32_t *codepoints; /* ucs4 unicode value or UTERM_INPUT_INVALID */
|
|
};
|
|
|
|
#define UTERM_INPUT_HAS_MODS(_ev, _mods) (((_ev)->mods & (_mods)) == (_mods))
|
|
|
|
typedef void (*uterm_input_cb) (struct uterm_input *input,
|
|
struct uterm_input_event *ev,
|
|
void *data);
|
|
|
|
int uterm_input_new(struct uterm_input **out, struct ev_eloop *eloop,
|
|
const char *layout,
|
|
const char *variant,
|
|
const char *options,
|
|
unsigned int repeat_delay,
|
|
unsigned int repeat_rate);
|
|
void uterm_input_ref(struct uterm_input *input);
|
|
void uterm_input_unref(struct uterm_input *input);
|
|
|
|
void uterm_input_add_dev(struct uterm_input *input, const char *node);
|
|
void uterm_input_remove_dev(struct uterm_input *input, const char *node);
|
|
|
|
int uterm_input_register_cb(struct uterm_input *input,
|
|
uterm_input_cb cb,
|
|
void *data);
|
|
void uterm_input_unregister_cb(struct uterm_input *input,
|
|
uterm_input_cb cb,
|
|
void *data);
|
|
|
|
void uterm_input_sleep(struct uterm_input *input);
|
|
void uterm_input_wake_up(struct uterm_input *input);
|
|
bool uterm_input_is_awake(struct uterm_input *input);
|
|
|
|
void uterm_input_keysym_to_string(struct uterm_input *input,
|
|
uint32_t keysym, char *str, size_t size);
|
|
int uterm_input_string_to_keysym(struct uterm_input *input, const char *n,
|
|
uint32_t *out);
|
|
|
|
/*
|
|
* Virtual Terminals
|
|
* Virtual terminals allow controlling multiple virtual terminals on one real
|
|
* terminal. It is multi-seat capable and fully asynchronous.
|
|
*/
|
|
|
|
struct uterm_vt;
|
|
struct uterm_vt_master;
|
|
|
|
enum uterm_vt_action {
|
|
UTERM_VT_ACTIVATE,
|
|
UTERM_VT_DEACTIVATE,
|
|
};
|
|
|
|
enum uterm_vt_mode {
|
|
UTERM_VT_REAL,
|
|
UTERM_VT_FAKE,
|
|
};
|
|
|
|
typedef int (*uterm_vt_cb) (struct uterm_vt *vt, unsigned int action,
|
|
void *data);
|
|
|
|
int uterm_vt_master_new(struct uterm_vt_master **out,
|
|
struct ev_eloop *eloop);
|
|
void uterm_vt_master_ref(struct uterm_vt_master *vtm);
|
|
void uterm_vt_master_unref(struct uterm_vt_master *vtm);
|
|
|
|
int uterm_vt_master_activate_all(struct uterm_vt_master *vtm);
|
|
int uterm_vt_master_deactivate_all(struct uterm_vt_master *vtm);
|
|
|
|
int uterm_vt_allocate(struct uterm_vt_master *vt, struct uterm_vt **out,
|
|
const char *seat, struct uterm_input *input,
|
|
const char *vt_for_seat0, uterm_vt_cb cb, void *data);
|
|
void uterm_vt_deallocate(struct uterm_vt *vt);
|
|
void uterm_vt_ref(struct uterm_vt *vt);
|
|
void uterm_vt_unref(struct uterm_vt *vt);
|
|
|
|
int uterm_vt_activate(struct uterm_vt *vt);
|
|
int uterm_vt_deactivate(struct uterm_vt *vt);
|
|
|
|
/*
|
|
* System Monitor
|
|
* This watches the system for new seats, graphics devices or other devices that
|
|
* are used by terminals.
|
|
*/
|
|
|
|
struct uterm_monitor;
|
|
struct uterm_monitor_seat;
|
|
struct uterm_monitor_dev;
|
|
|
|
enum uterm_monitor_event_type {
|
|
UTERM_MONITOR_NEW_SEAT,
|
|
UTERM_MONITOR_FREE_SEAT,
|
|
UTERM_MONITOR_NEW_DEV,
|
|
UTERM_MONITOR_FREE_DEV,
|
|
UTERM_MONITOR_HOTPLUG_DEV,
|
|
};
|
|
|
|
enum uterm_monitor_dev_type {
|
|
UTERM_MONITOR_DRM,
|
|
UTERM_MONITOR_FBDEV,
|
|
UTERM_MONITOR_FBDEV_DRM,
|
|
UTERM_MONITOR_INPUT,
|
|
};
|
|
|
|
struct uterm_monitor_event {
|
|
unsigned int type;
|
|
|
|
struct uterm_monitor_seat *seat;
|
|
const char *seat_name;
|
|
void *seat_data;
|
|
|
|
struct uterm_monitor_dev *dev;
|
|
unsigned int dev_type;
|
|
const char *dev_node;
|
|
void *dev_data;
|
|
};
|
|
|
|
typedef void (*uterm_monitor_cb) (struct uterm_monitor *mon,
|
|
struct uterm_monitor_event *event,
|
|
void *data);
|
|
|
|
int uterm_monitor_new(struct uterm_monitor **out,
|
|
struct ev_eloop *eloop,
|
|
uterm_monitor_cb cb,
|
|
void *data);
|
|
void uterm_monitor_ref(struct uterm_monitor *mon);
|
|
void uterm_monitor_unref(struct uterm_monitor *mon);
|
|
void uterm_monitor_scan(struct uterm_monitor *mon);
|
|
|
|
void uterm_monitor_set_seat_data(struct uterm_monitor_seat *seat, void *data);
|
|
void uterm_monitor_set_dev_data(struct uterm_monitor_dev *dev, void *data);
|
|
|
|
#endif /* UTERM_UTERM_H */
|