2 * Copyright © 2008-2010 Kristian Høgsberg
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <linux/input.h>
29 #define GL_GLEXT_PROTOTYPES
30 #define EGL_EGLEXT_PROTOTYPES
31 #include <GLES2/gl2.h>
32 #include <GLES2/gl2ext.h>
34 #include <EGL/eglext.h>
37 #include "wayland-protocol.h"
38 #include "compositor.h"
40 struct evdev_input_device {
41 struct wlsc_input_device *device;
42 struct wl_event_source *source;
43 int tool, new_x, new_y;
48 static void evdev_input_device_data(int fd, uint32_t mask, void *data)
50 struct evdev_input_device *device = data;
51 struct input_event ev[8], *e, *end;
52 int len, value, dx, dy, absolute_event;
58 x = device->device->x;
59 y = device->device->y;
61 len = read(fd, &ev, sizeof ev);
62 if (len < 0 || len % sizeof e[0] != 0) {
63 /* FIXME: handle error... reopen device? */;
68 end = (void *) ev + len;
69 for (e = ev; e < end; e++) {
70 /* Get the signed value, earlier kernels had this as unsigned */
91 device->base_x = x - value;
94 x = device->base_x + value;
98 device->base_y = y - value;
101 y = device->base_y + value;
113 case BTN_TOOL_RUBBER:
115 case BTN_TOOL_PENCIL:
116 case BTN_TOOL_AIRBRUSH:
117 case BTN_TOOL_FINGER:
120 if (device->tool == 0 && value) {
124 device->tool = value ? e->code : 0;
135 notify_button(device->device, e->code, value);
139 notify_key(device->device, e->code, value);
145 if (dx != 0 || dy != 0)
146 notify_motion(device->device, x + dx, y + dy);
147 if (absolute_event && device->tool)
148 notify_motion(device->device, x, y);
151 static struct evdev_input_device *
152 evdev_input_device_create(struct wlsc_input_device *master,
153 struct wl_display *display, const char *path)
155 struct evdev_input_device *device;
156 struct wl_event_loop *loop;
158 device = malloc(sizeof *device);
165 device->device = master;
167 device->fd = open(path, O_RDONLY);
168 if (device->fd < 0) {
170 fprintf(stderr, "couldn't create pointer for %s: %m\n", path);
174 loop = wl_display_get_event_loop(display);
175 device->source = wl_event_loop_add_fd(loop, device->fd,
177 evdev_input_device_data, device);
178 if (device->source == NULL) {
188 wlsc_compositor_present_drm(struct wlsc_compositor *ec)
190 struct wlsc_output *output;
192 wl_list_for_each(output, &ec->output_list, link) {
193 drmModePageFlip(ec->drm_fd, output->crtc_id,
194 output->fb_id[output->current ^ 1],
195 DRM_MODE_PAGE_FLIP_EVENT, output);
200 page_flip_handler(int fd, unsigned int frame,
201 unsigned int sec, unsigned int usec, void *data)
203 struct wlsc_output *output = data;
204 struct wlsc_compositor *compositor = output->compositor;
207 msecs = sec * 1000 + usec / 1000;
208 wlsc_compositor_finish_frame(compositor, msecs);
212 on_drm_input(int fd, uint32_t mask, void *data)
214 drmEventContext evctx;
216 memset(&evctx, 0, sizeof evctx);
217 evctx.version = DRM_EVENT_CONTEXT_VERSION;
218 evctx.page_flip_handler = page_flip_handler;
219 drmHandleEvent(fd, &evctx);
223 init_egl(struct wlsc_compositor *ec, struct udev_device *device)
225 struct wl_event_loop *loop;
226 EGLint major, minor, count;
228 PFNEGLGETTYPEDDISPLAYMESA get_typed_display_mesa;
230 static const EGLint config_attribs[] = {
232 EGL_NO_SURFACE_CAPABLE_MESA, EGL_OPENGL_BIT,
233 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
237 get_typed_display_mesa =
238 (PFNEGLGETTYPEDDISPLAYMESA) eglGetProcAddress("eglGetTypedDisplayMESA");
239 if (get_typed_display_mesa == NULL) {
240 fprintf(stderr, "eglGetTypedDisplayMESA() not found\n");
244 ec->base.device = strdup(udev_device_get_devnode(device));
245 ec->drm_fd = open(ec->base.device, O_RDWR);
246 if (ec->drm_fd < 0) {
247 /* Probably permissions error */
248 fprintf(stderr, "couldn't open %s, skipping\n",
249 udev_device_get_devnode(device));
253 ec->display = get_typed_display_mesa(EGL_DRM_DISPLAY_TYPE_MESA,
254 (void *) ec->drm_fd);
255 if (ec->display == NULL) {
256 fprintf(stderr, "failed to create display\n");
260 if (!eglInitialize(ec->display, &major, &minor)) {
261 fprintf(stderr, "failed to initialize display\n");
265 if (!eglChooseConfig(ec->display, config_attribs, &config, 1, &count) ||
267 fprintf(stderr, "eglChooseConfig() failed\n");
271 eglBindAPI(EGL_OPENGL_API);
272 ec->context = eglCreateContext(ec->display, config, EGL_NO_CONTEXT, NULL);
273 if (ec->context == NULL) {
274 fprintf(stderr, "failed to create context\n");
278 if (!eglMakeCurrent(ec->display, EGL_NO_SURFACE, EGL_NO_SURFACE, ec->context)) {
279 fprintf(stderr, "failed to make context current\n");
283 loop = wl_display_get_event_loop(ec->wl_display);
285 wl_event_loop_add_fd(loop, ec->drm_fd,
286 WL_EVENT_READABLE, on_drm_input, ec);
291 static drmModeModeInfo builtin_1024x768 = {
293 1024, 1072, 1176, 1328, 0,
294 768, 771, 775, 798, 0,
296 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
302 create_output_for_connector(struct wlsc_compositor *ec,
303 drmModeRes *resources,
304 drmModeConnector *connector)
306 struct wlsc_output *output;
307 drmModeEncoder *encoder;
308 drmModeModeInfo *mode;
310 EGLint handle, stride, attribs[] = {
313 EGL_IMAGE_FORMAT_MESA, EGL_IMAGE_FORMAT_ARGB8888_MESA,
314 EGL_IMAGE_USE_MESA, EGL_IMAGE_USE_SCANOUT_MESA,
318 output = malloc(sizeof *output);
322 if (connector->count_modes > 0)
323 mode = &connector->modes[0];
325 mode = &builtin_1024x768;
327 encoder = drmModeGetEncoder(ec->drm_fd, connector->encoders[0]);
328 if (encoder == NULL) {
329 fprintf(stderr, "No encoder for connector.\n");
333 for (i = 0; i < resources->count_crtcs; i++) {
334 if (encoder->possible_crtcs & (1 << i))
337 if (i == resources->count_crtcs) {
338 fprintf(stderr, "No usable crtc for encoder.\n");
342 output->compositor = ec;
343 output->crtc_id = resources->crtcs[i];
344 output->connector_id = connector->connector_id;
345 output->mode = *mode;
348 output->width = mode->hdisplay;
349 output->height = mode->vdisplay;
351 drmModeFreeEncoder(encoder);
353 glGenRenderbuffers(2, output->rbo);
354 for (i = 0; i < 2; i++) {
355 glBindRenderbuffer(GL_RENDERBUFFER, output->rbo[i]);
357 attribs[1] = output->width;
358 attribs[3] = output->height;
359 output->image[i] = eglCreateDRMImageMESA(ec->display, attribs);
360 glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, output->image[i]);
361 eglExportDRMImageMESA(ec->display, output->image[i], NULL, &handle, &stride);
363 ret = drmModeAddFB(ec->drm_fd, output->width, output->height,
364 32, 32, stride, handle, &output->fb_id[i]);
366 fprintf(stderr, "failed to add fb %d: %m\n", i);
372 glFramebufferRenderbuffer(GL_FRAMEBUFFER,
373 GL_COLOR_ATTACHMENT0,
375 output->rbo[output->current]);
376 ret = drmModeSetCrtc(ec->drm_fd, output->crtc_id,
377 output->fb_id[output->current ^ 1], 0, 0,
378 &output->connector_id, 1, &output->mode);
380 fprintf(stderr, "failed to set mode: %m\n");
384 wl_list_insert(ec->output_list.prev, &output->link);
390 create_outputs(struct wlsc_compositor *ec)
392 drmModeConnector *connector;
393 drmModeRes *resources;
396 resources = drmModeGetResources(ec->drm_fd);
398 fprintf(stderr, "drmModeGetResources failed\n");
402 for (i = 0; i < resources->count_connectors; i++) {
403 connector = drmModeGetConnector(ec->drm_fd, resources->connectors[i]);
404 if (connector == NULL)
407 if (connector->connection == DRM_MODE_CONNECTED &&
408 (option_connector == 0 ||
409 connector->connector_id == option_connector))
410 if (create_output_for_connector(ec, resources, connector) < 0)
413 drmModeFreeConnector(connector);
416 if (wl_list_empty(&ec->output_list)) {
417 fprintf(stderr, "No currently active connector found.\n");
421 drmModeFreeResources(resources);
426 static void on_enter_vt(int signal_number, void *data)
428 struct wlsc_compositor *ec = data;
429 struct wlsc_output *output;
432 ret = drmSetMaster(ec->drm_fd);
434 fprintf(stderr, "failed to set drm master\n");
439 fprintf(stderr, "enter vt\n");
441 ioctl(ec->tty_fd, VT_RELDISP, VT_ACKACQ);
444 wl_list_for_each(output, &ec->output_list, link) {
445 ret = drmModeSetCrtc(ec->drm_fd, output->crtc_id,
446 output->fb_id[output->current ^ 1], 0, 0,
447 &output->connector_id, 1, &output->mode);
449 fprintf(stderr, "failed to set mode for connector %d: %m\n",
450 output->connector_id);
454 static void on_leave_vt(int signal_number, void *data)
456 struct wlsc_compositor *ec = data;
459 ret = drmDropMaster(ec->drm_fd);
461 fprintf(stderr, "failed to drop drm master\n");
466 ioctl (ec->tty_fd, VT_RELDISP, 1);
471 on_tty_input(int fd, uint32_t mask, void *data)
473 struct wlsc_compositor *ec = data;
475 /* Ignore input to tty. We get keyboard events from evdev
477 tcflush(ec->tty_fd, TCIFLUSH);
480 static void on_term_signal(int signal_number, void *data)
482 struct wlsc_compositor *ec = data;
484 if (tcsetattr(ec->tty_fd, TCSANOW, &ec->terminal_attributes) < 0)
485 fprintf(stderr, "could not restore terminal to canonical mode\n");
490 static int setup_tty(struct wlsc_compositor *ec, struct wl_event_loop *loop)
492 struct termios raw_attributes;
493 struct vt_mode mode = { 0 };
495 ec->tty_fd = open("/dev/tty0", O_RDWR | O_NOCTTY);
496 if (ec->tty_fd <= 0) {
497 fprintf(stderr, "failed to open active tty: %m\n");
501 if (tcgetattr(ec->tty_fd, &ec->terminal_attributes) < 0) {
502 fprintf(stderr, "could not get terminal attributes: %m\n");
506 /* Ignore control characters and disable echo */
507 raw_attributes = ec->terminal_attributes;
508 cfmakeraw(&raw_attributes);
510 /* Fix up line endings to be normal (cfmakeraw hoses them) */
511 raw_attributes.c_oflag |= OPOST | OCRNL;
513 if (tcsetattr(ec->tty_fd, TCSANOW, &raw_attributes) < 0)
514 fprintf(stderr, "could not put terminal into raw mode: %m\n");
516 ec->term_signal_source =
517 wl_event_loop_add_signal(loop, SIGTERM, on_term_signal, ec);
519 ec->tty_input_source =
520 wl_event_loop_add_fd(loop, ec->tty_fd,
521 WL_EVENT_READABLE, on_tty_input, ec);
524 mode.mode = VT_PROCESS;
525 mode.relsig = SIGUSR1;
526 mode.acqsig = SIGUSR2;
527 if (!ioctl(ec->tty_fd, VT_SETMODE, &mode) < 0) {
528 fprintf(stderr, "failed to take control of vt handling\n");
531 ec->leave_vt_source =
532 wl_event_loop_add_signal(loop, SIGUSR1, on_leave_vt, ec);
533 ec->enter_vt_source =
534 wl_event_loop_add_signal(loop, SIGUSR2, on_enter_vt, ec);
540 wlsc_compositor_init_drm(struct wlsc_compositor *ec)
542 struct udev_enumerate *e;
543 struct udev_list_entry *entry;
544 struct udev_device *device;
546 struct wlsc_input_device *input_device;
547 struct wl_event_loop *loop;
549 ec->udev = udev_new();
550 if (ec->udev == NULL) {
551 fprintf(stderr, "failed to initialize udev context\n");
555 input_device = wlsc_input_device_create(ec);
556 ec->input_device = input_device;
558 e = udev_enumerate_new(ec->udev);
559 udev_enumerate_add_match_subsystem(e, "input");
560 udev_enumerate_add_match_property(e, "WAYLAND_SEAT", "1");
561 udev_enumerate_scan_devices(e);
562 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
563 path = udev_list_entry_get_name(entry);
564 device = udev_device_new_from_syspath(ec->udev, path);
565 evdev_input_device_create(input_device, ec->wl_display,
566 udev_device_get_devnode(device));
568 udev_enumerate_unref(e);
570 e = udev_enumerate_new(ec->udev);
571 udev_enumerate_add_match_subsystem(e, "drm");
572 udev_enumerate_add_match_property(e, "WAYLAND_SEAT", "1");
573 udev_enumerate_scan_devices(e);
574 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
575 path = udev_list_entry_get_name(entry);
576 device = udev_device_new_from_syspath(ec->udev, path);
577 fprintf(stderr, "creating output for %s\n", path);
579 if (init_egl(ec, device) < 0) {
580 fprintf(stderr, "failed to initialize egl\n");
583 if (create_outputs(ec) < 0) {
584 fprintf(stderr, "failed to create output for %s\n", path);
588 udev_enumerate_unref(e);
590 loop = wl_display_get_event_loop(ec->wl_display);