summaryrefslogtreecommitdiff
path: root/src/uterm.h
blob: 98232ad2daa2f0ef6818bf73e94d733ffaa9c33d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
/*
 * 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_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);

/* 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);

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_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 tsm_vte_modified */
enum uterm_input_modifier {
	UTERM_SHIFT_MASK	= (1 << 0),
	UTERM_LOCK_MASK		= (1 << 1),
	UTERM_CONTROL_MASK	= (1 << 2),
	UTERM_MOD1_MASK		= (1 << 3),
	UTERM_MOD2_MASK		= (1 << 4),
	UTERM_MOD3_MASK		= (1 << 5),
	UTERM_MOD4_MASK		= (1 << 6),
	UTERM_MOD5_MASK		= (1 << 7),
};

#define UTERM_INPUT_INVALID 0xffffffff

struct uterm_input_event {
	uint16_t keycode;	/* linux keycode - KEY_* - linux/input.h */
	uint32_t keysym;	/* X keysym - XKB_KEY_* - X11/keysym.h */
	unsigned int mods;	/* active modifiers - uterm_modifier mask */
	uint32_t unicode;	/* ucs4 unicode value or UTERM_INPUT_INVALID */
};

#define UTERM_INPUT_HAS_MODS(_ev, _mods) (((_ev)->mods & (_mods)) == (_mods))

struct uterm_input_grab {
	unsigned int mods;
	uint32_t keysym;
};

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);
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,
	UTERM_VT_DEAD,
};

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_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 */