swc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/swc
Log | Files | Refs | README | LICENSE

commit ba51fade7807c030d601cab4256a9e50f1de5abf
Author: Michael Forney <mforney@mforney.org>
Date:   Tue, 15 Jan 2013 02:44:00 -0800

Import

Diffstat:
A.gitignore | 25+++++++++++++++++++++++++
AMakefile.am | 33+++++++++++++++++++++++++++++++++
Aautogen.sh | 13+++++++++++++
Abinding.c | 8++++++++
Abinding.h | 25+++++++++++++++++++++++++
Abuffer.c | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abuffer.h | 32++++++++++++++++++++++++++++++++
Acompositor.c | 405+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acompositor.h | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfigure.ac | 37+++++++++++++++++++++++++++++++++++++
Adrm.c | 355+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrm.h | 42++++++++++++++++++++++++++++++++++++++++++
Aegl.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aegl.h | 25+++++++++++++++++++++++++
Aevdev_device.c | 220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aevdev_device.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
Aevent.h | 11+++++++++++
Ai915/Makefile.am | 14++++++++++++++
Ai915/batch.c | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ai915/batch.h | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ai915/blt.h | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ai915/bo.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Ai915/bo.h | 24++++++++++++++++++++++++
Ai915/mi.h | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amode.c | 20++++++++++++++++++++
Amode.h | 25+++++++++++++++++++++++++
Aoutput.c | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aoutput.h | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arenderer.c | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arenderer.h | 31+++++++++++++++++++++++++++++++
Aseat.c | 294+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aseat.h | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asurface.c | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asurface.h | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asurface_state.h | 29+++++++++++++++++++++++++++++
Atestclient.c | 34++++++++++++++++++++++++++++++++++
Atestwm/Makefile.am | 13+++++++++++++
Atestwm/main.c | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atestwm/shell.c | 29+++++++++++++++++++++++++++++
Atestwm/shell.h | 15+++++++++++++++
Atestwm/shell_surface.c | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atestwm/shell_surface.h | 13+++++++++++++
Atestwm/testwm.c | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atty.c | 221+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atty.h | 44++++++++++++++++++++++++++++++++++++++++++++
Autil.c | 10++++++++++
Autil.h | 20++++++++++++++++++++
Axkb.c | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axkb.h | 28++++++++++++++++++++++++++++
49 files changed, 4191 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,25 @@ +*.o +*.lo +.*.swp + +.deps/ +.libs/ + +Makefile +Makefile.in + +/aclocal.m4 +/autom4te.cache/ +/config.guess +/config.log +/config.status +/configure +/depcomp +/install-sh +/m4/ +/missing + +/libswc.la +/i915/libi915.la +/testwm/testwm + diff --git a/Makefile.am b/Makefile.am @@ -0,0 +1,33 @@ +# swc: Makefile.am + +ACLOCAL_AMFLAGS = -I m4 + +noinst_LTLIBRARIES = libswc.la + +AM_CFLAGS = $(pixman_CFLAGS) + +libswc_la_SOURCES = \ + compositor.c compositor.h \ + util.c util.h \ + output.c output.h \ + buffer.c buffer.h \ + surface.c surface.h \ + renderer.c renderer.h \ + seat.c seat.h \ + mode.c mode.h \ + tty.c tty.h \ + evdev_device.c evdev_device.h \ + xkb.c xkb.h \ + drm.c drm.h \ + egl.c egl.h + +libswc_la_LIBADD = $(wayland_server_LIBS) $(udev_LIBS) $(xkbcommon_LIBS) \ + $(drm_LIBS) $(gbm_LIBS) $(egl_LIBS) $(pixman_LIBS) i915/libi915.la + +# testclient_SOURCES = \ +# testclient.c +# +# testclient_LDADD = $(wayland_client_LIBS) + +SUBDIRS = i915 testwm + diff --git a/autogen.sh b/autogen.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -ex + +rm -rf autom4te.cache + +libtoolize +aclocal -I m4 --force +autoconf -f -W all +automake -f -a -c -W all + +rm -rf autom4te.cache + diff --git a/binding.c b/binding.c @@ -0,0 +1,8 @@ +#include "binding.h" + +bool swc_binding_init(struct wcf_binding * binding, uint32_t value, + uint32_t modifiers, wcf_binding_handler_t handler, + void * data) +{ +} + diff --git a/binding.h b/binding.h @@ -0,0 +1,25 @@ +#ifndef SWC_BINDING_H +#define SWC_BINDING_H 1 + +#include <stdint.h> +#include <linux/input.h> + +#define MOD_CTRL (1 << 0) +#define MOD_ALT (1 << 1) +#define MOD_SUPER (1 << 2) +#define MOD_SHIFT (1 << 3) +#define MOD_ANY (-1) + +typedef void (* swc_binding_handler_t)(uint32_t time, uint32_t value, + void * data); + +struct swc_binding +{ + uint32_t value; + uint32_t modifiers; + swc_binding_handler_t handler; + void * data; +}; + +#endif + diff --git a/buffer.c b/buffer.c @@ -0,0 +1,98 @@ +#include "buffer.h" + +#include <stdio.h> +#include <sys/mman.h> +#include <libdrm/i915_drm.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +/* Returns the next multiple of the eth power of 2 */ +static inline uint32_t next_multiple_power_2(uint32_t n, uint32_t e) +{ + return (n + (1 << e) - 1) & ~((1 << e) - 1); +} + +static void destroy_image(pixman_image_t * image, void * data) +{ + struct swc_buffer * buffer = data; + + munmap(pixman_image_get_data(image), buffer->bo.size); +} + +bool swc_buffer_initialize(struct swc_buffer * buffer, struct swc_drm * drm, + uint32_t width, uint32_t height) +{ + uint32_t size; + + buffer->image = NULL; + + buffer->width = width; + buffer->height = height; + + /* Round width up to next multiple of 512 */ + buffer->pitch = next_multiple_power_2(width * 4, 9); + + /* Round height up to next multiple of 4 */ + size = buffer->pitch * next_multiple_power_2(height, 2); + + printf("width: %u, height: %u, pitch: %u, size: %u\n", width, height, buffer->pitch, size); + + i915_bo_initialize(drm->fd, &buffer->bo, size); + + if (drmModeAddFB(drm->fd, width, height, 24, 32, buffer->pitch, + buffer->bo.handle, &buffer->id) != 0) + { + printf("could not create FB from buffer handle\n"); + goto error_buffer; + } + + return true; + + error_buffer: + { + struct drm_gem_close close_arg = { .handle = buffer->bo.handle }; + drmIoctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &close_arg); + } + error_base: + return false; +} + +void swc_buffer_finish(struct swc_buffer * buffer, struct swc_drm * drm) +{ + drmModeRmFB(drm->fd, buffer->id); + i915_bo_finalize(drm->fd, &buffer->bo); +} + +void swc_buffer_ref_image(struct swc_buffer * buffer, struct swc_drm * drm) +{ + if (!buffer->image) + { + uint32_t * data; + struct drm_i915_gem_mmap mmap_arg = { + .handle = buffer->bo.handle, + .size = buffer->bo.size + }; + + if (drmCommandWriteRead(drm->fd, DRM_I915_GEM_MMAP, &mmap_arg, + sizeof mmap_arg) != 0) + { + printf("could not mmap buffer\n"); + return; + } + + data = (void *) mmap_arg.addr_ptr; + buffer->image = pixman_image_create_bits(PIXMAN_x8r8g8b8, + buffer->width, buffer->height, + data, buffer->pitch); + pixman_image_set_destroy_function(buffer->image, &destroy_image, buffer); + } + else + pixman_image_ref(buffer->image); +} + +void swc_buffer_unref_image(struct swc_buffer * buffer) +{ + if (pixman_image_unref(buffer->image)) + buffer->image = NULL; +} + diff --git a/buffer.h b/buffer.h @@ -0,0 +1,32 @@ +#ifndef SWC_BUFFER_H +#define SWC_BUFFER_H 1 + +#include "drm.h" +#include "i915/bo.h" + +#include <stdbool.h> +#include <pixman.h> + +struct swc_buffer +{ + uint32_t id; + + struct i915_bo bo; + + /* Pixman image using the mapped buffer for use with SHM. */ + pixman_image_t * image; + + uint32_t width, height, pitch; +}; + +bool swc_buffer_initialize(struct swc_buffer * buffer, struct swc_drm * drm, + uint32_t width, uint32_t height); + +void swc_buffer_finish(struct swc_buffer * buffer, struct swc_drm * drm); + +void swc_buffer_ref_image(struct swc_buffer * buffer, struct swc_drm * drm); + +void swc_buffer_unref_image(struct swc_buffer * buffer); + +#endif + diff --git a/compositor.c b/compositor.c @@ -0,0 +1,405 @@ +#include <stdlib.h> +#include <stdio.h> +#include <libudev.h> +#include <gbm.h> + +#include "compositor.h" +#include "tty.h" +#include "output.h" +#include "surface.h" +#include "event.h" + +const char default_seat[] = "seat0"; + +struct repaint_operation +{ + struct swc_renderer * renderer; + struct swc_output * output; + struct wl_list * surfaces; +}; + +static void repaint_output(void * data) +{ + struct repaint_operation * operation = data; + + swc_renderer_repaint_output(operation->renderer, operation->output, + operation->surfaces); + + swc_output_switch_buffer(operation->output); + + /* XXX: should go in page flip handler */ + operation->output->repaint_scheduled = false; + + free(operation); +} + +static void schedule_repaint_for_output(struct swc_compositor * compositor, + struct swc_output * output) +{ + struct wl_event_loop * event_loop; + struct repaint_operation * operation; + + if (output->repaint_scheduled) + return; + + operation = malloc(sizeof *operation); + operation->renderer = &compositor->renderer; + operation->output = output; + operation->surfaces = &compositor->surfaces; + + event_loop = wl_display_get_event_loop(compositor->display); + wl_event_loop_add_idle(event_loop, &repaint_output, operation); + output->repaint_scheduled = true; +} + +static void handle_key(struct wl_keyboard_grab * grab, uint32_t time, + uint32_t key, uint32_t state) +{ + struct wl_keyboard * keyboard = grab->keyboard; + struct swc_seat * seat; + struct wl_resource * resource = keyboard->focus_resource; + struct swc_binding * binding; + struct swc_compositor * compositor; + char keysym_name[64]; + + seat = wl_container_of(keyboard, seat, keyboard); + compositor = wl_container_of(seat, compositor, seat); + + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) + { + xkb_keysym_t keysym; + + keysym = xkb_state_key_get_one_sym(seat->xkb.state, key + 8); + + wl_array_for_each(binding, &compositor->key_bindings) + { + if (binding->value == keysym && (binding->modifiers == MOD_ANY + || binding->modifiers == seat->active_modifiers)) + { + binding->handler(time, keysym, binding->data); + return; + } + } + } + + if (resource) + { + struct wl_display * display; + uint32_t serial; + + display = wl_client_get_display(resource->client); + serial = wl_display_next_serial(display); + wl_keyboard_send_key(resource, serial, time, key, state); + } +} + +static void handle_modifiers(struct wl_keyboard_grab * grab, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + //wl_keyboard_send_modifiers +} + +struct wl_keyboard_grab_interface binding_grab_interface = { + .key = &handle_key, + .modifiers = &handle_modifiers +}; + +/* XXX: maybe this should go in swc_drm */ +static void handle_tty_event(struct wl_listener * listener, void * data) +{ + struct swc_event * event = data; + struct swc_compositor * compositor; + + compositor = wl_container_of(listener, compositor, tty_listener); + + switch (event->type) + { + case SWC_TTY_VT_ENTER: + swc_drm_set_master(&compositor->drm); + break; + case SWC_TTY_VT_LEAVE: + swc_drm_drop_master(&compositor->drm); + break; + } +} + +static void handle_surface_event(struct wl_listener * listener, void * data) +{ + struct swc_event * event = data; + struct swc_surface * surface = event->data; + struct swc_compositor * compositor = surface->compositor_state.compositor; + + switch (event->type) + { + case SWC_SURFACE_ATTACH: + swc_renderer_attach(&compositor->renderer, &compositor->outputs, + surface, surface->state.buffer); + break; + case SWC_SURFACE_REPAINT: + { + struct swc_output * output; + wl_list_for_each(output, &compositor->outputs, link) + { + if (surface->output_mask & (1 << output->id)) + schedule_repaint_for_output(compositor, output); + } + break; + } + } +} + +static void handle_drm_event(struct wl_listener * listener, void * data) +{ + struct swc_event * event = data; + struct swc_compositor * compositor = wl_container_of(listener, compositor, + drm_listener); +} + +static void destroy_surface(struct wl_resource * resource) +{ + struct swc_surface * surface = resource->data; + + free(surface); +} + +static void create_surface(struct wl_client * client, + struct wl_resource * resource, uint32_t id) +{ + printf("compositor_create_surface\n"); + struct swc_compositor * compositor = resource->data; + struct swc_surface * surface; + struct swc_output * output; + + surface = malloc(sizeof *surface); + + if (!surface) + { + wl_resource_post_no_memory(resource); + return; + } + + output = wl_container_of(compositor->outputs.next, output, link); + + /* Initialize compositor state */ + surface->compositor_state = (struct swc_compositor_surface_state) { + .compositor = compositor, + .event_listener = (struct wl_listener) { + .notify = &handle_surface_event + } + }; + + swc_surface_initialize(surface); + wl_signal_add(&surface->event_signal, + &surface->compositor_state.event_listener); + + wl_list_insert(&compositor->surfaces, &surface->link); + surface->output_mask |= 1 << output->id; + + /* For some reason there is no Wayland method to initialize a preallocated + * resource. */ + surface->wayland.resource = (struct wl_resource) { + .object = { + .interface = &wl_surface_interface, + .implementation = (void (**)()) &swc_surface_interface, + .id = id + }, + .destroy = &destroy_surface, + .data = surface + }; + + wl_client_add_resource(client, &surface->wayland.resource); +} + +static void create_region(struct wl_client * client, + struct wl_resource * resource, uint32_t id) +{ +} + +struct wl_compositor_interface compositor_implementation = { + .create_surface = &create_surface, + .create_region = &create_region +}; + +static void bind_compositor(struct wl_client * client, void * data, + uint32_t version, uint32_t id) +{ + struct swc_compositor * compositor = data; + + wl_client_add_object(client, &wl_compositor_interface, + &compositor_implementation, id, compositor); +} + +bool swc_compositor_initialize(struct swc_compositor * compositor, + struct wl_display * display) +{ + struct wl_event_loop * event_loop; + struct udev_device * drm_device; + struct wl_list * outputs; + + if (compositor == NULL) + { + printf("could not allocate compositor\n"); + return NULL; + } + + compositor->display = display; + compositor->tty_listener.notify = &handle_tty_event; + + compositor->udev = udev_new(); + + if (compositor->udev == NULL) + { + printf("could not initialize udev context\n"); + goto error_base; + } + + event_loop = wl_display_get_event_loop(display); + + if (!swc_tty_initialize(&compositor->tty, event_loop, 2)) + { + printf("could not initialize tty\n"); + goto error_udev; + } + + wl_signal_add(&compositor->tty.event_signal, &compositor->tty_listener); + + /* TODO: configurable seat */ + if (!swc_seat_initialize(&compositor->seat, compositor->udev, + default_seat)) + { + printf("could not initialize seat\n"); + goto error_tty; + } + + swc_seat_add_event_sources(&compositor->seat, event_loop); + compositor->seat.keyboard.default_grab.interface = &binding_grab_interface; + + /* TODO: configurable seat */ + if (!swc_drm_initialize(&compositor->drm, compositor->udev, default_seat)) + { + printf("could not initialize drm\n"); + goto error_seat; + } + + swc_drm_add_event_sources(&compositor->drm, event_loop); + + compositor->gbm = gbm_create_device(compositor->drm.fd); + + if (!compositor->gbm) + { + printf("could not create gbm device\n"); + goto error_drm; + } + + if (!swc_egl_initialize(&compositor->egl, compositor->gbm)) + { + printf("could not initialize egl\n"); + goto error_gbm; + } + + if (!swc_egl_bind_display(&compositor->egl, compositor->display)) + { + printf("could not bind egl display\n"); + swc_egl_finish(&compositor->egl); + goto error_gbm; + } + + if (!swc_renderer_initialize(&compositor->renderer, &compositor->drm)) + { + printf("could not initialize renderer\n"); + goto error_egl; + } + + outputs = swc_drm_create_outputs(&compositor->drm); + + if (outputs) + { + wl_list_init(&compositor->outputs); + wl_list_insert_list(&compositor->outputs, outputs); + free(outputs); + } + else + { + printf("could not create outputs\n"); + goto error_renderer; + } + + wl_list_init(&compositor->surfaces); + wl_array_init(&compositor->key_bindings); + wl_signal_init(&compositor->destroy_signal); + + return true; + + error_renderer: + swc_renderer_finalize(&compositor->renderer); + error_egl: + swc_egl_unbind_display(&compositor->egl, compositor->display); + swc_egl_finish(&compositor->egl); + error_gbm: + gbm_device_destroy(compositor->gbm); + error_drm: + swc_drm_finish(&compositor->drm); + error_seat: + swc_seat_finish(&compositor->seat); + error_tty: + swc_tty_finish(&compositor->tty); + error_udev: + udev_unref(compositor->udev); + error_base: + free(compositor); + return false; +} + +void swc_compositor_finish(struct swc_compositor * compositor) +{ + struct swc_output * output, * tmp; + + wl_signal_emit(&compositor->destroy_signal, compositor); + + wl_array_release(&compositor->key_bindings); + + wl_list_for_each_safe(output, tmp, &compositor->outputs, link) + { + swc_output_finish(output); + free(output); + } + + swc_egl_unbind_display(&compositor->egl, compositor->display); + swc_egl_finish(&compositor->egl); + gbm_device_destroy(compositor->gbm); + swc_drm_finish(&compositor->drm); + swc_seat_finish(&compositor->seat); + swc_tty_finish(&compositor->tty); + udev_unref(compositor->udev); +} + +void swc_compositor_add_globals(struct swc_compositor * compositor, + struct wl_display * display) +{ + struct swc_output * output; + + wl_display_add_global(display, &wl_compositor_interface, compositor, + &bind_compositor); + + swc_seat_add_globals(&compositor->seat, display); + + wl_list_for_each(output, &compositor->outputs, link) + { + swc_output_add_globals(output, display); + } +} + +void swc_compositor_add_key_binding(struct swc_compositor * compositor, + uint32_t modifiers, uint32_t value, + swc_binding_handler_t handler, void * data) +{ + struct swc_binding * binding; + + binding = wl_array_add(&compositor->key_bindings, sizeof *binding); + binding->value = value; + binding->modifiers = modifiers; + binding->handler = handler; + binding->data = data; +} + diff --git a/compositor.h b/compositor.h @@ -0,0 +1,50 @@ +#ifndef SWC_COMPOSITOR_H +#define SWC_COMPOSITOR_H 1 + +#include <wayland-server.h> + +#include "drm.h" +#include "tty.h" +#include "seat.h" +#include "egl.h" +#include "binding.h" +#include "event_listener.h" +#include "renderer.h" + +struct swc_compositor +{ + struct wl_display * display; + + struct udev * udev; + struct gbm_device * gbm; + + struct swc_tty tty; + struct swc_seat seat; + struct swc_drm drm; + struct swc_egl egl; + struct swc_renderer renderer; + + struct wl_list outputs; + struct wl_list surfaces; + struct wl_array key_bindings; + + struct wl_listener tty_listener; + struct wl_listener drm_listener; + + struct wl_signal destroy_signal; +}; + +bool swc_compositor_initialize(struct swc_compositor * compositor, + struct wl_display * display); + +void swc_compositor_finish(struct swc_compositor * compositor); + +void swc_compositor_add_globals(struct swc_compositor * compositor, + struct wl_display * display); + +void swc_compositor_add_key_binding(struct swc_compositor * compositor, + uint32_t modifiers, xkb_keysym_t key, + swc_binding_handler_t handler, void * data); + +#endif + diff --git a/configure.ac b/configure.ac @@ -0,0 +1,37 @@ +dnl swc: configure.ac + +AC_PREREQ([2.59]) + +AC_INIT([swc], [0.0.1], [mforney@mforney.org]) +AM_INIT_AUTOMAKE([foreign -Wall -Werror]) +AC_LANG([C]) + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_PROG_CC_C99 +AC_USE_SYSTEM_EXTENSIONS + +AM_PROG_AR +LT_INIT + +AC_CONFIG_SRCDIR([compositor.c]) +AC_CONFIG_MACRO_DIR([m4]) + +PKG_PROG_PKG_CONFIG([0.9.0]) + +dnl Check for libraries {{{ +PKG_CHECK_MODULES([wayland_server], [wayland-server]) +PKG_CHECK_MODULES([udev], [libudev]) +PKG_CHECK_MODULES([xkbcommon], [xkbcommon]) +PKG_CHECK_MODULES([drm], [libdrm]) +PKG_CHECK_MODULES([gbm], [gbm]) +PKG_CHECK_MODULES([egl], [egl]) +PKG_CHECK_MODULES([pixman], [pixman-1]) + +PKG_CHECK_MODULES([wayland_client], [wayland-client]) +dnl }}} + +#AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([Makefile i915/Makefile testwm/Makefile]) +AC_OUTPUT + diff --git a/drm.c b/drm.c @@ -0,0 +1,355 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <libudev.h> +#include <libdrm/drm.h> +#include <xf86drm.h> +#include <libdrm/i915_drm.h> +//#include <xf86drmMode.h> +#include <wayland-util.h> + +#include "drm.h" +#include "output.h" +#include "event.h" + +static struct udev_device * find_primary_drm_device(struct udev * udev, + const char * seat) +{ + struct udev_enumerate * enumerate; + struct udev_list_entry * entry; + const char * path; + const char * device_seat; + const char * boot_vga; + struct udev_device * pci; + struct udev_device * device, * drm_device = NULL; + + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "drm"); + udev_enumerate_add_match_sysname(enumerate, "card[0-9]*"); + + udev_enumerate_scan_devices(enumerate); + + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(enumerate)) + { + path = udev_list_entry_get_name(entry); + device = udev_device_new_from_syspath(udev, path); + + printf("device node path: %s\n", udev_device_get_devnode(device)); + + device_seat = udev_device_get_property_value(device, "ID_SEAT"); + + /* If the ID_SEAT property is not set, the device belongs to seat0. */ + if (!device_seat) + device_seat = "seat0"; + else + printf("device seat: %s\n", device_seat); + + /* Make sure the DRM device belongs to the seat we are in. */ + if (strcmp(device_seat, seat) != 0) + { + udev_device_unref(device); + continue; + } + + pci = udev_device_get_parent_with_subsystem_devtype(device, "pci", + NULL); + + if (pci) + { + /* boot_vga = 1 indicates that this DRM device is the primary GPU. */ + boot_vga = udev_device_get_sysattr_value(pci, "boot_vga"); + if (boot_vga && strcmp(boot_vga, "1") == 0) + { + if (drm_device) + udev_device_unref(drm_device); + drm_device = device; + break; + } + } + + /* Make sure we have a backup device. */ + if (!drm_device) + drm_device = device; + else + udev_device_unref(device); + } + + udev_enumerate_unref(enumerate); + + return drm_device; +} + +static bool find_available_crtc(struct swc_drm * drm, drmModeRes * resources, + drmModeConnector * connector, + uint32_t taken_crtcs, uint32_t * crtc) +{ + uint32_t encoder_index, crtc_index; + uint32_t possible_crtcs; + drmModeEncoder * encoder; + + for (encoder_index = 0; + encoder_index < connector->count_encoders; + ++encoder_index) + { + encoder = drmModeGetEncoder(drm->fd, connector->encoders[encoder_index]); + possible_crtcs = encoder->possible_crtcs; + drmModeFreeEncoder(encoder); + + printf("possible_crtcs: %u\n", possible_crtcs); + printf("taken_crtcs: %u\n", taken_crtcs); + + for (crtc_index = 0; crtc_index < resources->count_crtcs; ++crtc_index) + { + if ((possible_crtcs & (1 << crtc_index)) + && !(taken_crtcs & (1 << crtc_index))) + { + *crtc = crtc_index; + return true; + } + } + } + + return false; +} + +static bool find_available_id(struct swc_drm * drm, uint32_t * id) +{ + uint32_t index = __builtin_ffsl(~drm->taken_output_ids); + + printf("drm->taken_output_ids: %u, index: %u\n", drm->taken_output_ids, index); + + if (index == 0) + return false; + + *id = index - 1; + return true; +} + +static void handle_vblank(int fd, unsigned int sequence, unsigned int sec, + unsigned int usec, void * data) +{ + printf("vblank\n"); +} + +static void handle_page_flip(int fd, unsigned int sequence, unsigned int sec, + unsigned int usec, void * data) +{ + struct swc_output * output = data; + struct swc_event event = { + .type = SWC_DRM_PAGE_FLIP, + .data = output + }; + + printf("page flip\n"); + output->front_buffer ^= 1; + + wl_signal_emit(&output->drm->event_signal, &event); +} + +static drmEventContext event_context = { + .version = DRM_EVENT_CONTEXT_VERSION, + .vblank_handler = &handle_vblank, + .page_flip_handler = &handle_page_flip +}; + +static int handle_data(int fd, uint32_t mask, void * data) +{ + drmHandleEvent(fd, &event_context); + + return 1; +} + +bool swc_drm_initialize(struct swc_drm * drm, struct udev * udev, + const char * seat) +{ + const char * sysnum; + const char * device_path; + char * end; + + wl_signal_init(&drm->event_signal); + + struct udev_device * drm_device = find_primary_drm_device(udev, seat); + + if (!drm_device) + { + printf("couldn't find drm device\n"); + goto error_base; + } + + /* XXX: Why do we need the sysnum? */ + sysnum = udev_device_get_sysnum(drm_device); + drm->id = strtoul(sysnum, &end, 10); + + drm->taken_output_ids = 0; + + if (*end != '\0') + { + printf("couldn't get drm device sysnum\n"); + goto error_device; + } + + printf("sysnum: %s\n", sysnum); + + device_path = udev_device_get_devnode(drm_device); + drm->fd = open(device_path, O_RDWR | O_CLOEXEC); + + if (drm->fd == -1) + { + printf("couldn't open %s\n", device_path); + goto error_device; + } + + { + int value, ret; + struct drm_i915_getparam getparam = { + .param = I915_PARAM_HAS_BLT, + .value = &value + }; + + ret = drmIoctl(drm->fd, DRM_IOCTL_I915_GETPARAM, &getparam); + + printf("has blt: %u\n", ret); + } + + udev_device_unref(drm_device); + + return true; + + error_device: + udev_device_unref(drm_device); + error_base: + return false; +} + +void swc_drm_finish(struct swc_drm * drm) +{ + close(drm->fd); +} + +void swc_drm_add_event_sources(struct swc_drm * drm, + struct wl_event_loop * event_loop) +{ + drm->source = wl_event_loop_add_fd(event_loop, drm->fd, WL_EVENT_READABLE, + &handle_data, NULL); +} + +void swc_drm_set_master(struct swc_drm * drm) +{ + printf("setting drm master\n"); + drmSetMaster(drm->fd); +} + +void swc_drm_drop_master(struct swc_drm * drm) +{ + printf("dropping drm master\n"); + drmDropMaster(drm->fd); +} + +struct wl_list * swc_drm_create_outputs(struct swc_drm * drm) +{ + drmModeRes * resources; + drmModeConnector * connector; + drmModeCrtc * crtc; + uint32_t index; + uint32_t x = 0, y = 0; + struct swc_output * output; + struct wl_list * outputs; + uint32_t taken_crtcs = 0; + + outputs = malloc(sizeof(struct wl_list)); + wl_list_init(outputs); + + resources = drmModeGetResources(drm->fd); + if (!resources) + { + printf("couldn't get DRM resources\n"); + goto error; + } + + printf("crtc count: %u\n", resources->count_crtcs); + + /* XXX: crtcs */ + for (index = 0; index < resources->count_crtcs; ++index) + { + printf("crtc[%u]: %u\n", index, resources->crtcs[index]); + drmModeCrtc * crtc = drmModeGetCrtc(drm->fd, resources->crtcs[index]); + printf("crtc, id: %u, x: %u, y: %u, width: %u, height: %u\n", + crtc->crtc_id, crtc->x, crtc->y, crtc->width, crtc->height); + drmModeFreeCrtc(crtc); + } + + for (index = 0; index < resources->count_encoders; ++index) + { + printf("encoder[%u]: %u\n", index, resources->encoders[index]); + drmModeEncoder * encoder = drmModeGetEncoder(drm->fd, resources->encoders[index]); + printf("encoder, id: %u, type: %u\n", encoder->encoder_id, encoder->encoder_type); + drmModeFreeEncoder(encoder); + } + + for (index = 0; index < resources->count_connectors; ++index) + { + connector = drmModeGetConnector(drm->fd, resources->connectors[index]); + + printf("connector, id: %u, type: %u, type_id: %u, connection: %u\n", + connector->connector_id, connector->connector_type, + connector->connector_type_id, connector->connection); + + /* XXX: connector id? */ + if (connector->connection == DRM_MODE_CONNECTED) + { + uint32_t crtc_index; + uint32_t id; + + if (!find_available_crtc(drm, resources, connector, taken_crtcs, + &crtc_index)) + { + printf("couldn't find crtc for connector %u\n", index); + continue; + } + + if (!find_available_id(drm, &id)) + { + printf("no more available output IDs\n"); + break; + } + + output = malloc(sizeof(struct swc_output)); + + if (!swc_output_initialize(output, drm, id, + resources->crtcs[crtc_index], connector)) + { + drmModeFreeConnector(connector); + free(output); + continue; + } + + output->x = x; + output->y = 0; + + taken_crtcs |= 1 << crtc_index; + drm->taken_output_ids |= 1 << id; + + wl_list_insert(outputs, &output->link); + x += output->width; + } + + drmModeFreeConnector(connector); + } + + drmModeFreeResources(resources); + + if (wl_list_empty(outputs)) + { + printf("couldn't find any outputs\n"); + goto error; + } + + return outputs; + + error: + free(outputs); + return NULL; +} + diff --git a/drm.h b/drm.h @@ -0,0 +1,42 @@ +#ifndef SWC_DRM_H +#define SWC_DRM_H 1 + +#include <stdbool.h> +#include <stdint.h> +#include <libudev.h> +#include <gbm.h> +#include <wayland-server.h> + +enum swc_drm_event +{ + SWC_DRM_PAGE_FLIP +}; + +struct swc_drm +{ + int fd; + uint32_t id; + + uint32_t taken_output_ids; + + struct wl_event_source * source; + + struct wl_signal event_signal; +}; + +bool swc_drm_initialize(struct swc_drm * drm, struct udev * udev, + const char * seat); + +void swc_drm_finish(struct swc_drm * drm); + +void swc_drm_add_event_sources(struct swc_drm * drm, + struct wl_event_loop * event_loop); + +void swc_drm_set_master(struct swc_drm * drm); + +void swc_drm_drop_master(struct swc_drm * drm); + +struct wl_list * swc_drm_create_outputs(struct swc_drm * drm); + +#endif + diff --git a/egl.c b/egl.c @@ -0,0 +1,86 @@ +#include "egl.h" + +#include <stdio.h> +#include <string.h> + +#define EGL_EGLEXT_PROTOTYPES +#include <EGL/eglext.h> +#undef EGL_EGLEXT_PROTOTYPES + +bool swc_egl_initialize(struct swc_egl * egl, struct gbm_device * gbm) +{ + const char * extensions; + + egl->display = eglGetDisplay(gbm); + + if (egl->display == NULL) + { + printf("could not create egl display\n"); + goto error_base; + } + + if (!eglInitialize(egl->display, NULL, NULL)) + { + printf("could not initialize egl display\n"); + goto error_base; + } + +#if EGL_WL_bind_wayland_display + extensions = eglQueryString(egl->display, EGL_EXTENSIONS); + + if (!extensions) + { + printf("could not query EGL extensions\n"); + goto error_display; + } + + if (strstr(extensions, "EGL_WL_bind_wayland_display")) + egl->has_bind_wayland_display = true; + else + { + printf("warning: headers claim EGL_WL_bind_wayland_display exists, " + "but it is not in the queried extension list\n"); + egl->has_bind_wayland_display = false; + } +#else + printf("don't have EGL_WL_bind_wayland_display extension\n"); + egl->has_bind_wayland_display = false; +#endif + + return true; + + error_display: + eglTerminate(egl->display); + eglReleaseThread(); + error_base: + return false; +} + +void swc_egl_finish(struct swc_egl * egl) +{ + eglTerminate(egl->display); + eglReleaseThread(); +} + +bool swc_egl_bind_display(struct swc_egl * egl, struct wl_display * display) +{ +#if EGL_WL_bind_wayland_display + if (egl->has_bind_wayland_display) + return eglBindWaylandDisplayWL(egl->display, display); +#endif + + /* If we don't have this extension, just continue normally. */ + return true; +} + +bool swc_egl_unbind_display(struct swc_egl * egl, struct wl_display * display) +{ +#if EGL_WL_bind_wayland_display + if (egl->has_bind_wayland_display) + return eglUnbindWaylandDisplayWL(egl->display, display); +#endif + + /* If we don't have this extension, just continue normally. */ + return true; +} + diff --git a/egl.h b/egl.h @@ -0,0 +1,25 @@ +#ifndef SWC_EGL_H +#define SWC_EGL_H 1 + +#include <stdlib.h> +#include <stdbool.h> +#include <wayland-server.h> +#include <gbm.h> +#include <EGL/egl.h> + +struct swc_egl +{ + EGLDisplay display; + bool has_bind_wayland_display; +}; + +bool swc_egl_initialize(struct swc_egl * egl, struct gbm_device * gbm); + +void swc_egl_finish(struct swc_egl * egl); + +bool swc_egl_bind_display(struct swc_egl * egl, struct wl_display * display); + +bool swc_egl_unbind_display(struct swc_egl * egl, struct wl_display * display); + +#endif + diff --git a/evdev_device.c b/evdev_device.c @@ -0,0 +1,220 @@ +#include "evdev_device.h" + +#include "seat.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> + +#define BITS(var, n) uint64_t var[((n)-1)/64+1] +#define TEST_BIT(var, n) \ + (((var)[(n)/((sizeof (var)[0])*8)] >> ((n)%((sizeof (var)[0])*8))) & 1) + +static inline uint32_t timeval_to_msec(struct timeval * time) +{ + return time->tv_sec * 1000 + time->tv_usec / 1000; +} + +static void handle_key_event(struct swc_evdev_device * device, + struct input_event * event) +{ + uint32_t time = timeval_to_msec(&event->time); + + if ((event->code >= BTN_MISC && event->code <= BTN_GEAR_UP) + || event->code >= BTN_TRIGGER_HAPPY) + { + swc_seat_handle_button(device->seat, time, event->value, + event->value ? WL_POINTER_BUTTON_STATE_PRESSED + : WL_POINTER_BUTTON_STATE_RELEASED); + } + else + { + swc_seat_handle_key(device->seat, time, event->code, + event->value ? WL_KEYBOARD_KEY_STATE_PRESSED + : WL_KEYBOARD_KEY_STATE_RELEASED); + } +} + +static void handle_rel_event(struct swc_evdev_device * device, + struct input_event * event) +{ + //printf("rel event\n"); + + switch (event->code) + { + case REL_X: + //printf("rel_x: %d\n", event->value); + break; + case REL_Y: + //printf("rel_y: %d\n", event->value); + break; + } +} + +static void handle_abs_event(struct swc_evdev_device * device, + struct input_event * event) +{ + printf("abs event\n"); +} + +static void (* event_handlers[])(struct swc_evdev_device * device, + struct input_event * event) = { + [EV_KEY] = &handle_key_event, + [EV_REL] = &handle_rel_event, + [EV_ABS] = &handle_abs_event +}; + +static bool is_motion_event(struct input_event * event) +{ + return (event->type == EV_REL && (event->code == REL_X || event->code == REL_Y)) + || (event->type == EV_ABS && (event->code == ABS_X || event->code == ABS_Y)); +} + +static void handle_motion_events(struct swc_evdev_device * device, + uint32_t time) +{ +} + +static void process_events(struct swc_evdev_device * device, + struct input_event * events, uint32_t event_count) +{ + struct input_event * event, * end = events + event_count; + + for (event = events; event != end; ++event) + { + if (!is_motion_event(event)) + handle_motion_events(device, timeval_to_msec(&event->time)); + + /* + printf("processing event, type: %u, code: %u, value: %d\n", + event->type, event->code, event->value); + */ + + if (event->type < (sizeof event_handlers / sizeof event_handlers[0]) + && event_handlers[event->type]) + { + event_handlers[event->type](device, event); + } + } +} + +static int handle_data(int fd, uint32_t mask, void * data) +{ + struct swc_evdev_device * device = data; + struct input_event events[32]; + ssize_t bytes_read; + + do + { + bytes_read = read(fd, events, sizeof events); + + /* Stop on error */ + if (bytes_read == -1) + return 1; + + process_events(device, events, bytes_read / sizeof events[0]); + } while (bytes_read > 0); + + return 1; +} + +bool swc_evdev_device_initialize(struct swc_evdev_device * device, + struct swc_seat * seat, + struct udev_device * udev_device) +{ + const char * path, * model, * vendor; + BITS(ev_bits, EV_MAX); + uint32_t index; + + path = udev_device_get_devnode(udev_device); + model = udev_device_get_property_value(udev_device, "ID_MODEL") + ?: "unknown"; + vendor = udev_device_get_property_value(udev_device, "ID_VENDOR") + ?: "unknown"; + + device->seat = seat; + device->model = strdup(model); + device->vendor = strdup(vendor); + device->fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC); + + if (device->fd == -1) + { + printf("couldn't open input device at %s\n", path); + goto error_base; + } + + printf("adding device %s %s\n", device->vendor, device->model); + + ioctl(device->fd, EVIOCGBIT(0, sizeof ev_bits), &ev_bits); + + device->capabilities = 0; + /* Currently, I don't care about touch devices. */ + + if (udev_device_get_property_value(udev_device, "ID_INPUT_KEYBOARD")) + { + device->capabilities |= WL_SEAT_CAPABILITY_KEYBOARD; + printf("\tthis device is a keyboard\n"); + } + + if (udev_device_get_property_value(udev_device, "ID_INPUT_MOUSE") + || udev_device_get_property_value(udev_device, "ID_INPUT_TOUCHPAD")) + { + device->capabilities |= WL_SEAT_CAPABILITY_POINTER; + printf("\tthis device is a pointer\n"); + } + + if (device->capabilities & WL_SEAT_CAPABILITY_POINTER) + { + /* Check if the device has relative motion. */ + if (TEST_BIT(ev_bits, EV_REL)) + { + BITS(rel_bits, REL_MAX); + + ioctl(device->fd, EVIOCGBIT(EV_REL, sizeof rel_bits), &rel_bits); + + if (TEST_BIT(rel_bits, REL_X) || TEST_BIT(rel_bits, REL_Y)) + { + } + } + + /* Check if the device has absolute motion. */ + if (TEST_BIT(ev_bits, EV_ABS)) + { + BITS(abs_bits, ABS_MAX); + + ioctl(device->fd, EVIOCGBIT(EV_ABS, sizeof abs_bits), &abs_bits); + + if (TEST_BIT(abs_bits, ABS_X)) + ioctl(device->fd, EVIOCGABS(ABS_X), &device->abs.info.x); + if (TEST_BIT(abs_bits, ABS_Y)) + ioctl(device->fd, EVIOCGABS(ABS_X), &device->abs.info.y); + } + } + + return true; + + error_fd: + close(device->fd); + error_base: + return false; +} + +void swc_evdev_device_finish(struct swc_evdev_device * device) +{ + wl_event_source_remove(device->source); + free(device->model); + free(device->vendor); + close(device->fd); +} + +void swc_evdev_device_add_event_sources(struct swc_evdev_device * device, + struct wl_event_loop * event_loop) +{ + printf("adding event source for %s %s\n", device->vendor, device->model); + device->source + = wl_event_loop_add_fd(event_loop, device->fd, WL_EVENT_READABLE, + handle_data, device); +} + diff --git a/evdev_device.h b/evdev_device.h @@ -0,0 +1,48 @@ +#ifndef SWC_EVDEV_DEVICE_H +#define SWC_EVDEV_DEVICE_H 1 + +#include <stdbool.h> +#include <libudev.h> +#include <linux/input.h> +#include <wayland-server.h> + +struct swc_evdev_device +{ + struct swc_seat * seat; + + int fd; + char * model, * vendor; + + struct + { + struct + { + struct input_absinfo x, y; + } info; + + int32_t x, y; + bool pending; + } abs; + + struct + { + bool pending; + } rel; + + uint32_t capabilities; + + struct wl_event_source * source; + struct wl_list link; +}; + +bool swc_evdev_device_initialize(struct swc_evdev_device * device, + struct swc_seat * seat, + struct udev_device * udev_device); + +void swc_evdev_device_finish(struct swc_evdev_device * device); + +void swc_evdev_device_add_event_sources(struct swc_evdev_device * device, + struct wl_event_loop * event_loop); + +#endif + diff --git a/event.h b/event.h @@ -0,0 +1,11 @@ +#ifndef SWC_EVENT_H +#define SWC_EVENT_H + +struct swc_event +{ + uint32_t type; + void * data; +}; + +#endif + diff --git a/i915/Makefile.am b/i915/Makefile.am @@ -0,0 +1,14 @@ +# i915/Makefile.am + +AM_CFLAGS = $(drm_CFLAGS) + +noinst_LTLIBRARIES = libi915.la + +libi915_la_SOURCES = \ + bo.c bo.h \ + batch.c batch.h \ + blt.h \ + mi.h + +libi915_la_LIBADD = $(drm_LIBS) + diff --git a/i915/batch.c b/i915/batch.c @@ -0,0 +1,134 @@ +#include "batch.h" +#include "bo.h" +#include "mi.h" + +#include <stdio.h> +#include <xf86drm.h> + +void i915_batch_initialize(struct i915_batch * batch, int drm) +{ + batch->relocation_count = 0; + batch->exec_object_count = 0; + batch->command_count = 0; + batch->drm = drm; +} + +void i915_batch_flush(struct i915_batch * batch) +{ + struct i915_bo bo; + uint32_t index = batch->exec_object_count++; + + mi_batch_buffer_end(batch); + + /* Pad the batch buffer to the next quad-word. */ + if (batch->command_count & 1) + mi_noop(batch, false, 0); + + printf("command count: %u\n", batch->command_count); + + i915_bo_initialize(batch->drm, &bo, batch->command_count << 2); + i915_bo_write(batch->drm, &bo, 0, batch->commands, batch->command_count << 2); + + printf("adding exec object with handle: %u\n", bo.handle); + + /* Add command buffer */ + batch->exec_objects[index] = (struct drm_i915_gem_exec_object2) { + .handle = bo.handle, + .relocation_count = batch->relocation_count, + .relocs_ptr = (uint64_t) batch->relocations + }; + + { + int ret; + struct drm_i915_gem_execbuffer2 execbuffer_arg = { + .buffers_ptr = (uint64_t) batch->exec_objects, + .buffer_count = batch->exec_object_count, + .batch_start_offset = 0, /* XXX: ? */ + .batch_len = batch->command_count << 2, + .flags = I915_EXEC_RENDER + }; + + if ((ret = drmIoctl(batch->drm, DRM_IOCTL_I915_GEM_EXECBUFFER2, + &execbuffer_arg)) != 0) + { + printf("execbuffer failed: %u\n", -ret); + } + } + + i915_bo_finalize(batch->drm, &bo); + + /* Set offsets for all our execution objects (except the last one, our + * command object). */ + for (index = 0; index < batch->exec_object_count - 1; ++index) + *batch->offsets[index] = batch->exec_objects[index].offset; + + batch->command_count = 0; + batch->relocation_count = 0; + batch->exec_object_count = 0; +} + +#if 0 +uint32_t * i915_batch_alloc(struct i915_batch * batch, uint32_t size) +{ + uint32_t * commands; + + if (i915_batch_space(batch) < size) + i915_batch_flush(batch); + + commands = &batch->commands[batch->size]; + batch->command_count += command_count; + + return commands; +} +#endif + +void i915_batch_ensure_space(struct i915_batch * batch, uint32_t size) +{ + if (i915_batch_space(batch) < size) + i915_batch_flush(batch); +} + +uint32_t i915_batch_space(struct i915_batch * batch) +{ + /* XXX: reserved space */ + return I915_MAX_COMMANDS - batch->command_count; +} + +uint64_t i915_batch_add_relocation(struct i915_batch * batch, + uint32_t batch_offset, struct i915_bo * bo, + uint32_t read_domains, uint32_t write_domain) +{ + uint32_t index = batch->relocation_count++; + + i915_batch_add_exec_object(batch, bo); + + printf("offset: %u\n", (batch->command_count + batch_offset) << 2); + printf("current: %u\n", *((uint32_t *)(((void *) batch->commands) + ((batch->command_count + batch_offset) << 2)))); + + batch->relocations[index] = (struct drm_i915_gem_relocation_entry) { + .target_handle = bo->handle, + /* XXX: delta */ + /* XXX: offset */ + .offset = (batch->command_count + batch_offset) << 2, + .presumed_offset = bo->last_offset, + .read_domains = read_domains, + .write_domain = write_domain + }; + + /* Return our offset guess */ + return bo->last_offset; +} + +void i915_batch_add_exec_object(struct i915_batch * batch, struct i915_bo * bo) +{ + uint32_t index = batch->exec_object_count++; + + printf("adding exec object with handle: %u\n", bo->handle); + + batch->exec_objects[index] = (struct drm_i915_gem_exec_object2) { + .handle = bo->handle + }; + + batch->offsets[index] = &bo->last_offset; +} + diff --git a/i915/batch.h b/i915/batch.h @@ -0,0 +1,62 @@ +#ifndef SWC_I915_BATCH_H +#define SWC_I915_BATCH_H 1 + +#include "bo.h" + +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> + +#include <libdrm/i915_drm.h> + +#define I915_MAX_COMMANDS (1 << 15) +#define I915_MAX_RELOCATIONS (1 << 11) +#define I915_MAX_EXEC_OBJECTS (1 << 11) + +struct i915_batch +{ + int drm; + + struct drm_i915_gem_relocation_entry relocations[I915_MAX_RELOCATIONS]; + uint64_t * offsets[I915_MAX_RELOCATIONS]; + uint32_t relocation_count; + + struct drm_i915_gem_exec_object2 exec_objects[I915_MAX_EXEC_OBJECTS]; + uint32_t exec_object_count; + + //uint32_t header[13]; + uint32_t commands[I915_MAX_COMMANDS]; + uint32_t command_count; +}; + +void i915_batch_initialize(struct i915_batch * batch, int drm); + +void i915_batch_flush(struct i915_batch * batch); + +void i915_batch_ensure_space(struct i915_batch * batch, uint32_t size); + +uint32_t i915_batch_space(struct i915_batch * batch); + +static inline void i915_batch_add_dword(struct i915_batch * batch, + uint32_t dword) +{ + batch->commands[batch->command_count++] = dword; +} + +static inline void i915_batch_add_dwords(struct i915_batch * batch, uint32_t count, ...) +{ + va_list dwords; + va_start(dwords, count); + while (count--) + i915_batch_add_dword(batch, va_arg(dwords, int)); + va_end(dwords); +} + +uint64_t i915_batch_add_relocation(struct i915_batch * batch, + uint32_t batch_offset, struct i915_bo * bo, + uint32_t read_domains, uint32_t write_domain); + +void i915_batch_add_exec_object(struct i915_batch * batch, struct i915_bo * bo); + +#endif + diff --git a/i915/blt.h b/i915/blt.h @@ -0,0 +1,180 @@ +#ifndef SWC_I915_BLT_H +#define SWC_I915_BLT_H 1 + +#include "i915/bo.h" +#include "i915/batch.h" + +#define BR00_CLIENT_2D 0x2 + +#define BR00_OPCODE_XY_COLOR_BLT 0x50 +#define BR00_OPCODE_XY_SRC_COPY_BLT 0x53 + +#define BR00_32BPP_BYTE_MASK_ALPHA (1 << 1) +#define BR00_32BPP_BYTE_MASK_COLOR (1 << 2) + +static inline uint32_t br00(uint8_t client, uint8_t opcode, + uint8_t mask_32bpp, + bool src_tiling_enable, bool dst_tiling_enable, + uint8_t dword_length) +{ + return client << 29 /* 31:29 */ + | opcode << 22 /* 28:22 */ + | mask_32bpp << 20 /* 21:20 */ + /* 19:16 */ + | src_tiling_enable << 15 /* 15 */ + /* 14:12 */ + | dst_tiling_enable << 11 /* 11 */ + /* 10:8 */ + | dword_length << 0 /* 7:0 */ + ; +} + +static inline uint32_t br09(uint32_t destination_address) +{ + /* 31:29 */ + return destination_address << 0 /* 28:0 */ + ; +} + +static inline uint32_t br11(uint16_t source_pitch) +{ + /* 31:16 */ + return source_pitch << 0 /* 15:0 */ + ; +} + +static inline uint32_t br12(uint32_t source_address) +{ + /* 31:29 */ + return source_address << 0 /* 28:0 */ + ; +} + +#define BR13_COLOR_DEPTH_8BIT 0x0 +#define BR13_COLOR_DEPTH_16BIT_565 0x1 +#define BR13_COLOR_DEPTH_16BIT_1555 0x2 +#define BR13_COLOR_DEPTH_32BIT 0x3 + +/* Commonly used raster operations */ +#define BR13_RASTER_OPERATION_SOURCE 0xcc +#define BR13_RASTER_OPERATION_PATTERN 0xf0 + +static inline uint32_t br13(bool clipping_enable, uint8_t color_depth, + uint8_t raster_operation, + uint16_t destination_pitch) +{ + /* 31 */ + return clipping_enable << 30 /* 30 */ + /* 29:26 */ + | color_depth << 24 /* 25:24 */ + | raster_operation << 16 /* 23:16 */ + | destination_pitch << 0 /* 15:0 */ + ; +} + +static inline uint32_t br16(uint32_t color) +{ + return color << 0 /* 31:0 */ + ; +} + +static inline uint32_t br22(uint16_t destination_y1, uint16_t destination_x1) +{ + return destination_y1 << 16 /* 31:16 */ + | destination_x1 << 0 /* 15:0 */ + ; +} + +static inline uint32_t br23(uint16_t destination_y2, uint16_t destination_x2) +{ + return destination_y2 << 16 /* 31:16 */ + | destination_x2 << 0 /* 15:0 */ + ; +} + +static inline uint32_t br26(uint16_t source_y1, uint16_t source_x1) +{ + return source_y1 << 16 /* 31:16 */ + | source_x1 << 16 /* 15:0 */ + ; +}; + +static inline void xy_src_copy_blt(struct i915_batch * batch, + struct i915_bo * src, uint16_t src_pitch, + uint16_t src_x, uint16_t src_y, + struct i915_bo * dst, uint16_t dst_pitch, + uint16_t dst_x, uint16_t dst_y, + uint16_t width, uint16_t height) +{ +#if 0 + i915_batch_add_dword(batch, + uint32_t * commands = i915_batch_alloc(batch, 8); + commands = (uint32_t *) + *commands++ = br00(BR00_CLIENT_2D, BR00_OPCODE_XY_SRC_COPY_BLT, + BR00_32BPP_BYTE_MASK_ALPHA | BR00_32BPP_BYTE_MASK_COLOR, + false, false, 6); + *commands++ = br13(false, BR13_COLOR_DEPTH_32BIT, + BR13_RASTER_OPERATION_SRC, + dst_pitch); + *commands++ = br22(dst_y, dst_x); + *commands++ = br23(dst_y + height, dst_x + width); + *commands++ = br09(0); /* XXX: dst address */ + *commands++ = br26(src_y, src_x); + *commands++ = br11(src_pitch); + *commands++ = br12(0); /* XXX: src address */ +#endif + + uint32_t dst_address, src_address; + + i915_batch_ensure_space(batch, 8); + + dst_address = i915_batch_add_relocation(batch, 4, dst, + I915_GEM_DOMAIN_RENDER, + I915_GEM_DOMAIN_RENDER); + src_address = i915_batch_add_relocation(batch, 7, src, + I915_GEM_DOMAIN_RENDER, 0); + + i915_batch_add_dwords(batch, 8, + br00(BR00_CLIENT_2D, BR00_OPCODE_XY_SRC_COPY_BLT, + BR00_32BPP_BYTE_MASK_ALPHA | BR00_32BPP_BYTE_MASK_COLOR, + false, false, 6), + br13(false, BR13_COLOR_DEPTH_32BIT, BR13_RASTER_OPERATION_SOURCE, + dst_pitch), + br22(dst_y, dst_x), + br23(dst_y + height, dst_x + width), + br09(dst_address), + br26(src_y, src_x), + br11(src_pitch), + br12(src_address) + ); +} + +static inline void xy_color_blt(struct i915_batch * batch, + struct i915_bo * dst, uint16_t dst_pitch, + uint16_t dst_x, uint16_t dst_y, + uint16_t width, uint16_t height, + uint32_t color) +{ + uint32_t dst_address; + + i915_batch_ensure_space(batch, 6); + + dst_address = i915_batch_add_relocation(batch, 4, dst, + I915_GEM_DOMAIN_RENDER, + I915_GEM_DOMAIN_RENDER); + + i915_batch_add_dwords(batch, 6, + br00(BR00_CLIENT_2D, BR00_OPCODE_XY_COLOR_BLT, + BR00_32BPP_BYTE_MASK_ALPHA | BR00_32BPP_BYTE_MASK_COLOR, + false, false, 4), + br13(false, BR13_COLOR_DEPTH_32BIT, BR13_RASTER_OPERATION_PATTERN, + dst_pitch), + br22(dst_y, dst_x), + br23(dst_y + height, dst_x + width), + br09(dst_address), + br16(color) + ); +} + +#endif + diff --git a/i915/bo.c b/i915/bo.c @@ -0,0 +1,52 @@ +#include "bo.h" + +#include <stdio.h> +#include <xf86drm.h> +#include <libdrm/i915_drm.h> + +bool i915_bo_initialize(int drm, struct i915_bo * bo, uint32_t size) +{ + struct drm_i915_gem_create create_arg = { .size = size }; + + if (drmCommandWriteRead(drm, DRM_I915_GEM_CREATE, &create_arg, + sizeof create_arg) != 0) + { + printf("could not create bo\n"); + goto error_base; + } + + bo->handle = create_arg.handle; + bo->size = size; + + bo->last_offset = 0; + + return true; + + error_base: + return false; +} + +void i915_bo_finalize(int drm, struct i915_bo * bo) +{ + struct drm_gem_close close_arg = { .handle = bo->handle }; + + drmIoctl(drm, DRM_IOCTL_GEM_CLOSE, &close_arg); +} + +void i915_bo_write(int drm, struct i915_bo * bo, uint32_t offset, + void * data, size_t size) +{ + struct drm_i915_gem_pwrite pwrite_arg = { + .handle = bo->handle, + .offset = offset, + .size = size, + .data_ptr = (uint64_t) data + }; + + if (drmCommandWrite(drm, DRM_I915_GEM_PWRITE, &pwrite_arg, + sizeof pwrite_arg) != 0) + { + printf("write failed\n"); + } +} + diff --git a/i915/bo.h b/i915/bo.h @@ -0,0 +1,24 @@ +#ifndef SWC_I915_BO_H +#define SWC_I915_BO_H 1 + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> + +struct i915_bo +{ + uint32_t handle; + uint32_t size; + + uint64_t last_offset; +}; + +bool i915_bo_initialize(int drm, struct i915_bo * bo, uint32_t size); + +void i915_bo_finalize(int drm, struct i915_bo * bo); + +void i915_bo_write(int drm, struct i915_bo * bo, uint32_t offset, + void * data, size_t size); + +#endif + diff --git a/i915/mi.h b/i915/mi.h @@ -0,0 +1,97 @@ +#ifndef SWC_I915_MI_H +#define SWC_I915_MI_H 1 + +#include "batch.h" + +#include <stdint.h> +#include <stdbool.h> + +#define COMMAND_TYPE_MI 0x0 + +#define MI_OPCODE_NOOP 0x00 +#define MI_OPCODE_FLUSH 0x04 +#define MI_OPCODE_BATCH_BUFFER_END 0x0A +#define MI_OPCODE_FLUSH_DW 0x04 +#define MI_OPCODE_BATCH_BUFFER_START 0x31 + +static inline void mi_noop(struct i915_batch * batch, + bool identification_number_write_enable, + uint32_t identification_number) +{ + i915_batch_add_dword(batch, + COMMAND_TYPE_MI << 29 /* 31:29 */ + | MI_OPCODE_NOOP << 23 /* 28:23 */ + | identification_number_write_enable << 22 /* 22 */ + | identification_number << 0 /* 21:0 */ + ); +} + +static inline void mi_flush(struct i915_batch * batch, + bool protected_memory_enable, + bool indirect_state_pointers_disable, + bool generic_media_state_clear, + bool global_snapshot_count_reset, + bool render_cache_flush_inhibit, + bool state_cache_invalidate) +{ + i915_batch_add_dword(batch, + COMMAND_TYPE_MI << 29 /* 31:29 */ + | MI_OPCODE_FLUSH << 23 /* 28:23 */ + /* 22:7 */ + | protected_memory_enable << 6 /* 6 */ + | indirect_state_pointers_disable << 5 /* 5 */ + | generic_media_state_clear << 4 /* 4 */ + | global_snapshot_count_reset << 3 /* 3 */ + | render_cache_flush_inhibit << 2 /* 2 */ + | state_cache_invalidate /* 1 */ + /* 0 */ + ); +} + +static inline void mi_flush_dw(struct i915_batch * batch) +{ + i915_batch_add_dwords(batch, 4, + COMMAND_TYPE_MI << 29 + | MI_OPCODE_FLUSH_DW << 23 + | 2 + , + 0, + 0, + 0 + ); +} + +static inline void mi_batch_buffer_end(struct i915_batch * batch) +{ + /* XXX: semaphore data dword / semaphore address */ + i915_batch_add_dword(batch, + COMMAND_TYPE_MI << 29 /* 31:29 */ + | MI_OPCODE_BATCH_BUFFER_END << 23 /* 28:23 */ + /* 22:0 */ + ); +} + +static inline void mi_batch_buffer_start(struct i915_batch * batch, + bool encrypted_memory_enable, + bool clear_command_buffer_enable, + bool buffer_non_secure, + uint32_t buffer_address) +{ + i915_batch_ensure_space(batch, 2); + + i915_batch_add_dwords(batch, 2, + COMMAND_TYPE_MI << 29 /* 31:29 */ + | MI_OPCODE_BATCH_BUFFER_START << 23 /* 28:23 */ + /* 22:13 */ + | encrypted_memory_enable << 12 /* 12 */ + | clear_command_buffer_enable << 11 /* 11 */ + /* 10:9 */ + | buffer_non_secure << 8 /* 8 */ + | 0 /* 7:0 */ + , + buffer_address + ); +} + +#endif + diff --git a/mode.c b/mode.c @@ -0,0 +1,20 @@ +#include <stdio.h> + +#include "mode.h" + +bool swc_mode_initialize(struct swc_mode * mode, drmModeModeInfo * mode_info) +{ + mode->width = mode_info->hdisplay; + mode->height = mode_info->vdisplay; + mode->refresh = mode_info->vrefresh * 1000; + mode->preferred = mode_info->type & DRM_MODE_TYPE_PREFERRED; + + mode->info = *mode_info; + + return true; +} + +void swc_mode_finish(struct swc_mode * mode) +{ +} + diff --git a/mode.h b/mode.h @@ -0,0 +1,25 @@ +#ifndef SWC_MODE_H +#define SWC_MODE_H + +#include <stdint.h> +#include <stdbool.h> +#include <xf86drmMode.h> + +#include <wayland-util.h> + +struct swc_mode +{ + uint16_t width, height; + uint32_t refresh; + + bool preferred; + + drmModeModeInfo info; +}; + +bool swc_mode_initialize(struct swc_mode * mode, drmModeModeInfo * mode_info); + +void swc_mode_finish(struct swc_mode * mode); + +#endif + diff --git a/output.c b/output.c @@ -0,0 +1,196 @@ +#include "output.h" + +#include "mode.h" +#include "util.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <libdrm/i915_drm.h> +#include <xf86drm.h> + +static void bind_output(struct wl_client * client, void * data, + uint32_t version, uint32_t id) +{ + struct swc_output * output = data; + struct swc_mode * mode; + struct wl_resource * resource; + uint32_t flags; + + resource = wl_client_add_object(client, &wl_output_interface, NULL, id, + output); + wl_list_insert(&output->resource_list, &resource->link); + resource->destroy = &swc_unbind_resource; + + wl_output_send_geometry(resource, output->x, output->y, + output->physical_width, output->physical_height, 0, "unknown", + "unknown", WL_OUTPUT_TRANSFORM_NORMAL); + + wl_array_for_each(mode, &output->modes) + { + flags = 0; + if (mode->preferred) + flags |= WL_OUTPUT_MODE_PREFERRED; + if (output->current_mode == mode) + flags |= WL_OUTPUT_MODE_CURRENT; + + wl_output_send_mode(resource, flags, mode->width, mode->height, + mode->refresh); + } +} + +bool swc_output_initialize(struct swc_output * output, struct swc_drm * drm, + uint32_t id, uint32_t crtc_id, + drmModeConnector * connector) +{ + drmModeEncoder * encoder; + drmModeCrtc * current_crtc; + struct swc_mode * modes; + uint32_t index; + + output->drm = drm; + + printf("initializing output with id: %u\n", id); + + output->id = id; + output->repaint_scheduled = false; + output->front_buffer = 0; + + output->physical_width = connector->mmWidth; + output->physical_height = connector->mmHeight; + + wl_list_init(&output->resource_list); + wl_array_init(&output->modes); + + output->crtc_id = crtc_id; + output->connector_id = connector->connector_id; + + /* Determine the current CRTC of this output. */ + encoder = drmModeGetEncoder(drm->fd, connector->encoder_id); + current_crtc = drmModeGetCrtc(drm->fd, encoder->crtc_id); + drmModeFreeEncoder(encoder); + + modes = wl_array_add(&output->modes, connector->count_modes * sizeof *modes); + + for (index = 0; index < connector->count_modes; ++index) + { + swc_mode_initialize(&modes[index], &connector->modes[index]); + + if (memcmp(&modes[index].info, &current_crtc->mode, + sizeof(drmModeModeInfo)) == 0) + output->current_mode = &modes[index]; + if (modes[index].preferred) + output->preferred_mode = &modes[index]; + } + + if (output->preferred_mode) + output->current_mode = output->preferred_mode; + + output->width = output->current_mode->width; + output->height = output->current_mode->height; + + /* Create output buffers */ + if (!swc_buffer_initialize(&output->buffers[0], drm, output->width, + output->height)) + { + printf("could not initialize buffer 0 for output\n"); + goto error_base; + } + + if (!swc_buffer_initialize(&output->buffers[1], drm, output->width, + output->height)) + { + printf("could not initialize buffer 1 for output\n"); + goto error_buffer0; + } + + { + uint32_t color = 0x00339933; + uint32_t line[output->width]; + uint32_t x, y; + struct drm_i915_gem_pwrite arg = { + .handle = output->buffers[0].bo.handle, + .size = sizeof line, + .data_ptr = (uint64_t) line + }; + + for (x = 0; x < output->width; ++x) + line[x] = color; + + for (y = 0; y < output->height; ++y) + { + arg.offset += output->buffers[0].pitch; + drmCommandWrite(drm->fd, DRM_I915_GEM_PWRITE, &arg, sizeof arg); + } + + color = 0x00333399; + + arg.offset = 0; + arg.handle = output->buffers[1].bo.handle; + + for (x = 0; x < output->width; ++x) + line[x] = color; + + for (y = 0; y < output->height; ++y) + { + arg.offset += output->buffers[1].pitch; + drmCommandWrite(drm->fd, DRM_I915_GEM_PWRITE, &arg, sizeof arg); + } + } + + output->original_state.crtc = current_crtc; + + if (drmModeSetCrtc(drm->fd, output->crtc_id, output->buffers[0].id, 0, 0, + &output->connector_id, 1, &output->current_mode->info) != 0) + { + printf("could not set crtc for output\n"); + goto error_buffer1; + } + + return true; + + error_buffer1: + swc_buffer_finish(&output->buffers[1], drm); + error_buffer0: + swc_buffer_finish(&output->buffers[0], drm); + error_base: + return false; +} + +void swc_output_finish(struct swc_output * output) +{ + struct swc_mode * mode; + drmModeCrtc * crtc = output->original_state.crtc; + + wl_array_for_each(mode, &output->modes) + swc_mode_finish(mode); + wl_array_release(&output->modes); + + drmModeSetCrtc(output->drm->fd, crtc->crtc_id, crtc->buffer_id, crtc->x, + crtc->y, &output->connector_id, 1, &crtc->mode); + drmModeFreeCrtc(crtc); + + swc_buffer_finish(&output->buffers[0], output->drm); + swc_buffer_finish(&output->buffers[1], output->drm); +} + +void swc_output_add_globals(struct swc_output * output, + struct wl_display * display) +{ + wl_display_add_global(display, &wl_output_interface, output, &bind_output); +} + +void swc_output_switch_buffer(struct swc_output * output) +{ + printf("queueing pageflip\n"); + + /* Queue a page flip */ + if (drmModePageFlip(output->drm->fd, output->crtc_id, + swc_output_get_back_buffer(output)->id, + DRM_MODE_PAGE_FLIP_EVENT, output) != 0) + { + printf("could not queue pageflip\n"); + } +} + diff --git a/output.h b/output.h @@ -0,0 +1,69 @@ +#ifndef SWC_OUTPUT_H +#define SWC_OUTPUT_H 1 + +#include "buffer.h" + +#include <stdint.h> +#include <wayland-util.h> +#include <wayland-server.h> +#include <xf86drmMode.h> + +struct swc_output +{ + /* Outputs need IDs so surfaces can keep track of which output they are + * visible on. */ + uint32_t id; + + struct swc_drm * drm; + + /* The geometry of this output */ + uint32_t x, y, width, height; + uint32_t physical_width, physical_height; + + struct wl_array modes; + struct swc_mode * current_mode, * preferred_mode; + + /* Use double buffering */ + struct swc_buffer buffers[2]; + uint8_t front_buffer; + + /* The CRTC and connector we are using to drive this output */ + uint32_t crtc_id; + uint32_t connector_id; + + struct + { + drmModeCrtc * crtc; + } original_state; + + bool repaint_scheduled; + + struct wl_list resource_list; + struct wl_list link; +}; + +bool swc_output_initialize(struct swc_output * output, struct swc_drm * drm, + uint32_t id, uint32_t crtc_id, + drmModeConnector * connector); + +void swc_output_finish(struct swc_output * output); + +void swc_output_add_globals(struct swc_output * output, + struct wl_display * display); + +void swc_output_switch_buffer(struct swc_output * output); + +static inline struct swc_buffer * swc_output_get_front_buffer + (struct swc_output * output) +{ + return &output->buffers[output->front_buffer]; +} + +static inline struct swc_buffer * swc_output_get_back_buffer + (struct swc_output * output) +{ + return &output->buffers[!output->front_buffer]; +} + +#endif + diff --git a/renderer.c b/renderer.c @@ -0,0 +1,175 @@ +#include "renderer.h" +#include "i915/blt.h" +#include "i915/mi.h" + +#include <stdio.h> +#include <GLES2/gl2.h> +#include <libdrm/intel_bufmgr.h> + +struct wl_drm_buffer +{ + struct wl_buffer buffer; + struct wl_drm * drm; + uint32_t format; + const void * driver_format; + int32_t offset[3]; + int32_t stride[3]; + void * driver_buffer; +}; + +struct __DRIimageRec +{ + struct intel_region * region; + GLenum internal_format; + uint32_t dri_format; + GLuint format; + uint32_t offset; + uint32_t strides[3]; + uint32_t offsets[3]; + struct intel_image_format * planar_format; + void * data; +}; + +struct intel_region +{ + drm_intel_bo * bo; + GLuint refcount; + GLuint cpp; + GLuint width; + GLuint height; + GLuint pitch; + GLubyte * map; + GLuint map_refcount; + uint32_t tiling; + uint32_t name; + struct intel_screen * screen; +}; + +static inline uint32_t format_wayland_to_pixman(uint32_t wayland_format) +{ + switch (wayland_format) + { + case WL_SHM_FORMAT_XRGB8888: + return PIXMAN_x8r8g8b8; + case WL_SHM_FORMAT_ARGB8888: + return PIXMAN_a8r8g8b8; + } + + return 0; +} + +static void repaint_surface_for_output(struct swc_renderer * renderer, + struct swc_surface * surface, + struct swc_output * output) +{ + struct swc_buffer * back_buffer = swc_output_get_back_buffer(output); + + if (wl_buffer_is_shm(surface->state.buffer)) + { + printf("repainting shm surface\n"); + pixman_image_composite32(PIXMAN_OP_SRC, + surface->renderer_state.shm.image, NULL, + back_buffer->image, + 0, 0, 0, 0, 0, 0, + surface->geometry.width, + surface->geometry.height); + } + else + { + /* + struct i915_bo * src = &surface->renderer_state.drm.bo; + uint32_t src_pitch = surface->renderer_state.drm.pitch; + + xy_src_copy_blt(&renderer->batch, src, src_pitch, 0, 0, + &back_buffer->bo, back_buffer->pitch, 0, 0, + surface->geometry.width, surface->geometry.height); + */ + } +} + +bool swc_renderer_initialize(struct swc_renderer * renderer, + struct swc_drm * drm) +{ + renderer->drm = drm; + + i915_batch_initialize(&renderer->batch, drm->fd); + + return true; +} + +void swc_renderer_finalize(struct swc_renderer * renderer) +{ +} + +void swc_renderer_repaint_output(struct swc_renderer * renderer, + struct swc_output * output, + struct wl_list * surfaces) +{ + struct swc_surface * surface; + + printf("repainting output %u\n", output->id); + + wl_list_for_each(surface, surfaces, link) + { + if (surface->output_mask & (1 << output->id)) + { + repaint_surface_for_output(renderer, surface, output); + } + } + + xy_color_blt(&renderer->batch, &swc_output_get_back_buffer(output)->bo, + swc_output_get_back_buffer(output)->pitch, 0, 0, 500, 500, + 0xffffffff); + + //mi_flush(&renderer->batch, false, false, false, false, false, false); + + i915_batch_flush(&renderer->batch); +} + +void swc_renderer_attach(struct swc_renderer * renderer, + struct wl_list * outputs, + struct swc_surface * surface, + struct wl_buffer * buffer) +{ + if (wl_buffer_is_shm(buffer)) + { + struct swc_output * output; + uint32_t wayland_format = wl_shm_buffer_get_format(buffer); + + surface->renderer_state.shm.image + = pixman_image_create_bits(format_wayland_to_pixman(wayland_format), + wl_shm_buffer_get_width(buffer), + wl_shm_buffer_get_height(buffer), + wl_shm_buffer_get_data(buffer), + wl_shm_buffer_get_stride(buffer)); + + wl_list_for_each(output, outputs, link) + { + if (surface->output_mask & (1 << output->id)) + { + swc_buffer_ref_image(&output->buffers[0], renderer->drm); + swc_buffer_ref_image(&output->buffers[1], renderer->drm); + } + } + } + else + { + struct wl_drm_buffer * drm_buffer = (void *) surface->state.buffer; + struct __DRIimageRec * image = drm_buffer->driver_buffer; + struct intel_region * region = image->region; + drm_intel_bo * bo = region->bo; + + surface->renderer_state.drm.bo = (struct i915_bo) { + .handle = bo->handle + }; + + surface->renderer_state.drm.pitch = region->pitch; + + printf("buffer width: %u, height: %u\n", buffer->width, buffer->height); + + printf("bo width: %u, height: %u, stride: %u, handle: %u\n", + region->width, region->height, + region->pitch, bo->handle); + } +} + diff --git a/renderer.h b/renderer.h @@ -0,0 +1,31 @@ +#ifndef SWC_RENDERER_H +#define SWC_RENDERER_H 1 + +#include "output.h" +#include "surface.h" +#include "drm.h" +#include "i915/batch.h" + +struct swc_renderer +{ + struct swc_drm * drm; + + struct i915_batch batch; +}; + +bool swc_renderer_initialize(struct swc_renderer * renderer, + struct swc_drm * drm); + +void swc_renderer_finalize(struct swc_renderer * renderer); + +void swc_renderer_repaint_output(struct swc_renderer * renderer, + struct swc_output * output, + struct wl_list * surfaces); + +void swc_renderer_attach(struct swc_renderer * renderer, + struct wl_list * outputs, + struct swc_surface * surface, + struct wl_buffer * buffer); + +#endif + diff --git a/seat.c b/seat.c @@ -0,0 +1,294 @@ +#include "seat.h" + +#include "evdev_device.h" +#include "util.h" +#include "binding.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +struct wl_seat_interface swc_seat_interface = { + .get_pointer = &swc_seat_get_pointer, + .get_keyboard = &swc_seat_get_keyboard, + .get_touch = &swc_seat_get_touch +}; + +static void bind_seat(struct wl_client * client, void * data, uint32_t version, + uint32_t id) +{ + struct swc_seat * seat = data; + struct wl_resource * resource; + + resource = wl_client_add_object(client, &wl_seat_interface, + &swc_seat_interface, id, seat); + wl_list_insert(&seat->wayland.base_resource_list, &resource->link); + resource->destroy = &swc_unbind_resource; + + wl_seat_send_capabilities(resource, seat->capabilities); +} + +static void add_device(struct swc_seat * seat, struct udev_device * udev_device) +{ + const char * device_seat; + const char * device_path; + struct swc_evdev_device * evdev_device; + + device_seat = udev_device_get_property_value(udev_device, "ID_SEAT"); + + /* If the ID_SEAT property is not set, the device belongs to seat0. */ + if (!device_seat) + device_seat = "seat0"; + + if (strcmp(device_seat, seat->name) != 0) + return; + + evdev_device = malloc(sizeof *evdev_device); + + if (!swc_evdev_device_initialize(evdev_device, seat, udev_device)) + { + free(evdev_device); + return; + } + + if (!(seat->capabilities & WL_SEAT_CAPABILITY_POINTER) + && evdev_device->capabilities & WL_SEAT_CAPABILITY_POINTER) + { + printf("initializing pointer\n"); + wl_pointer_init(&seat->pointer); + wl_seat_set_pointer(&seat->wayland, &seat->pointer); + } + + if (!(seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD) + && evdev_device->capabilities & WL_SEAT_CAPABILITY_KEYBOARD) + { + printf("initializing keyboard\n"); + wl_keyboard_init(&seat->keyboard); + wl_seat_set_keyboard(&seat->wayland, &seat->keyboard); + } + + seat->capabilities |= evdev_device->capabilities; + + wl_list_insert(&seat->devices, &evdev_device->link); +} + +bool swc_seat_initialize(struct swc_seat * seat, struct udev * udev, + const char * seat_name) +{ + wl_seat_init(&seat->wayland); + + seat->name = strdup(seat_name); + seat->capabilities = 0; + seat->active_modifiers = 0; + + if (!swc_xkb_initialize(&seat->xkb)) + { + printf("could not initialize XKB\n"); + goto error_name; + } + + wl_list_init(&seat->devices); + swc_seat_add_devices(seat, udev); + + return true; + + error_name: + free(seat->name); + error_base: + return false; +} + +void swc_seat_finish(struct swc_seat * seat) +{ + struct swc_evdev_device * device, * tmp; + + wl_seat_release(&seat->wayland); + free(seat->name); + swc_xkb_finish(&seat->xkb); + + wl_list_for_each_safe(device, tmp, &seat->devices, link) + { + swc_evdev_device_finish(device); + free(device); + } +} + +void swc_seat_add_globals(struct swc_seat * seat, struct wl_display * display) +{ + wl_display_add_global(display, &wl_seat_interface, seat, &bind_seat); +} + +void swc_seat_add_event_sources(struct swc_seat * seat, + struct wl_event_loop * event_loop) +{ + struct swc_evdev_device * device; + + wl_list_for_each(device, &seat->devices, link) + { + swc_evdev_device_add_event_sources(device, event_loop); + } +} + +void swc_seat_add_devices(struct swc_seat * seat, struct udev * udev) +{ + struct udev_enumerate * enumerate; + struct udev_list_entry * entry; + const char * path; + struct udev_device * device; + + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "input"); + udev_enumerate_add_match_sysname(enumerate, "event[0-9]*"); + + udev_enumerate_scan_devices(enumerate); + + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(enumerate)) + { + path = udev_list_entry_get_name(entry); + device = udev_device_new_from_syspath(udev, path); + add_device(seat, device); + udev_device_unref(device); + } + + udev_enumerate_unref(enumerate); +} + +void swc_seat_handle_key(struct swc_seat * seat, uint32_t time, uint32_t key, + uint32_t state) +{ + uint32_t * pressed_key; + struct wl_keyboard * keyboard = &seat->keyboard; + struct swc_xkb * xkb = &seat->xkb; + enum xkb_key_direction direction; + + /* Update XKB state */ + direction = state == WL_KEYBOARD_KEY_STATE_PRESSED ? XKB_KEY_DOWN + : XKB_KEY_UP; + + /* Apparently these are offset by 8 in X. */ + xkb_state_update_key(xkb->state, key + 8, direction); + + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) + { + keyboard->grab_key = key; + keyboard->grab_time = time; + pressed_key = wl_array_add(&keyboard->keys, sizeof key); + *pressed_key = key; + } + else + { + wl_array_for_each(pressed_key, &keyboard->keys) + { + if (*pressed_key == key) + { + /* Remove the key from the array */ + uint32_t bytes_to_copy = keyboard->keys.size + 1 + - (((void *) pressed_key) - keyboard->keys.data); + memmove(pressed_key, pressed_key + 1, bytes_to_copy); + --keyboard->keys.size; + break; + } + } + } + + keyboard->grab->interface->key(keyboard->grab, time, key, state); + + { + struct wl_keyboard * keyboard = &seat->keyboard; + uint32_t mods_depressed, mods_latched, mods_locked, mods_active; + uint32_t layout; + + mods_depressed = xkb_state_serialize_mods(xkb->state, XKB_STATE_DEPRESSED); + mods_latched = xkb_state_serialize_mods(xkb->state, XKB_STATE_LATCHED); + mods_locked = xkb_state_serialize_mods(xkb->state, XKB_STATE_LOCKED); + mods_active = mods_depressed | mods_latched; + + layout = xkb_state_serialize_layout(xkb->state, XKB_STATE_LAYOUT_EFFECTIVE); + + if (mods_depressed != keyboard->modifiers.mods_depressed + || mods_latched != keyboard->modifiers.mods_latched + || mods_locked != keyboard->modifiers.mods_locked + || layout != keyboard->modifiers.group) + { + } + + keyboard->modifiers.mods_depressed = mods_depressed; + keyboard->modifiers.mods_latched = mods_latched; + keyboard->modifiers.mods_locked = mods_locked; + keyboard->modifiers.group = layout; + + seat->active_modifiers = 0; + + if (mods_active & (1 << xkb->indices.ctrl)) + seat->active_modifiers |= MOD_CTRL; + if (mods_active & (1 << xkb->indices.alt)) + seat->active_modifiers |= MOD_ALT; + if (mods_active & (1 << xkb->indices.super)) + seat->active_modifiers |= MOD_SUPER; + if (mods_active & (1 << xkb->indices.shift)) + seat->active_modifiers |= MOD_SHIFT; + } +} + +void swc_seat_handle_button(struct swc_seat * seat, uint32_t time, + uint32_t button, uint32_t state) +{ +} + +/* Wayland Seat Interface */ +void swc_seat_get_pointer(struct wl_client * client, + struct wl_resource * resource, uint32_t id) +{ + struct wl_resource * client_resource; + struct swc_seat * seat = resource->data; + struct wl_pointer * pointer = &seat->pointer; + + /* pointer interface? */ + client_resource = wl_client_add_object(client, &wl_pointer_interface, + NULL, id, seat); + client_resource->destroy = &swc_unbind_resource; + + wl_list_insert(&pointer->resource_list, &client_resource->link); + + if (pointer->focus && pointer->focus->resource.client == client) + { + //wl_pointer_set_focus(pointer, pointer->focus); + } +} + +void swc_seat_get_keyboard(struct wl_client * client, + struct wl_resource * resource, uint32_t id) +{ + struct wl_resource * client_resource; + struct swc_seat * seat = resource->data; + struct wl_keyboard * keyboard = &seat->keyboard; + + client_resource = wl_client_add_object(client, &wl_keyboard_interface, + NULL, id, seat); + client_resource->destroy = &swc_unbind_resource; + + wl_list_insert(&keyboard->resource_list, &client_resource->link); + + wl_keyboard_send_keymap(client_resource, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, + seat->xkb.keymap.fd, seat->xkb.keymap.size); + + if (keyboard->focus && keyboard->focus->resource.client == client) + wl_keyboard_set_focus(keyboard, keyboard->focus); +} + +void swc_seat_get_touch(struct wl_client * client, + struct wl_resource * resource, uint32_t id) +{ + /* + struct wl_resource * client_resource; + struct swc_seat * seat = resource->data; + struct wl_touch * touch = &seat->touch; + + client_resource = wl_client_add_object(client, &wl_touch_interface, + NULL, id, seat); + client_resource->destroy = &swc_unbind_resource; + + wl_list_insert(&touch->resource_list, &client_resource->link); + */ +} + diff --git a/seat.h b/seat.h @@ -0,0 +1,59 @@ +#ifndef SWC_SEAT_H +#define SWC_SEAT_H 1 + +#include "xkb.h" + +#include <stdint.h> +#include <stdbool.h> +#include <libudev.h> +#include <wayland-server.h> + +struct swc_seat +{ + struct wl_seat wayland; + + char * name; + + struct swc_xkb xkb; + + struct wl_keyboard keyboard; + struct wl_pointer pointer; + + uint32_t active_modifiers; + uint32_t capabilities; + + struct wl_list devices; +}; + +bool swc_seat_initialize(struct swc_seat * seat, struct udev * udev, + const char * seat_name); + +void swc_seat_finish(struct swc_seat * seat); + +void swc_seat_add_globals(struct swc_seat * seat, struct wl_display * display); + +void swc_seat_add_event_sources(struct swc_seat * seat, + struct wl_event_loop * event_loop); + +void swc_seat_add_devices(struct swc_seat * seat, struct udev * udev); + +void swc_seat_handle_key(struct swc_seat * seat, uint32_t time, uint32_t key, + uint32_t state); + +void swc_seat_handle_button(struct swc_seat * seat, uint32_t time, + uint32_t button, uint32_t state); + +/* Wayland Seat Interface */ +extern struct wl_seat_interface swc_seat_interface; + +void swc_seat_get_pointer(struct wl_client * client, + struct wl_resource * resource, uint32_t id); + +void swc_seat_get_keyboard(struct wl_client * client, + struct wl_resource * resource, uint32_t id); + +void swc_seat_get_touch(struct wl_client * client, + struct wl_resource * resource, uint32_t id); + +#endif + diff --git a/surface.c b/surface.c @@ -0,0 +1,154 @@ +#include "surface.h" +#include "event.h" + +#include <stdio.h> + +struct wl_surface_interface swc_surface_interface = { + .destroy = &swc_surface_destroy, + .attach = &swc_surface_attach, + .damage = &swc_surface_damage, + .frame = &swc_surface_frame, + .set_opaque_region = &swc_surface_set_opaque_region, + .set_input_region = &swc_surface_set_input_region, + .commit = &swc_surface_commit, +}; + +static void state_initialize(struct swc_surface_state * state) +{ + state->buffer = NULL; + + pixman_region32_init(&state->damage); + pixman_region32_init(&state->opaque); + pixman_region32_init(&state->input); + + wl_list_init(&state->frame_callbacks); +} + +bool swc_surface_initialize(struct swc_surface * surface) +{ + state_initialize(&surface->state); + state_initialize(&surface->pending.state); + + wl_signal_init(&surface->event_signal); + + surface->output_mask = 0; + + return true; +} + +void swc_surface_finish(struct swc_surface * surface) +{ +} + +void swc_surface_destroy(struct wl_client * client, + struct wl_resource * resource) +{ + wl_resource_destroy(resource); +} + +void swc_surface_attach(struct wl_client * client, + struct wl_resource * resource, + struct wl_resource * buffer_resource, + int32_t x, + int32_t y) +{ + struct swc_surface * surface = resource->data; + struct wl_buffer * buffer = buffer_resource->data; + + printf("surface_attach\n"); + + surface->pending.state.buffer = buffer; + surface->pending.x = x; + surface->pending.y = y; + + surface->geometry.width = buffer->width; + surface->geometry.height = buffer->height; +} + +void swc_surface_damage(struct wl_client * client, + struct wl_resource * resource, + int32_t x, int32_t y, + int32_t width, int32_t height) +{ + printf("surface_damage\n"); + struct swc_surface * surface = resource->data; + + pixman_region32_union_rect(&surface->pending.state.damage, + &surface->pending.state.damage, + x, y, width, height); +} + +void swc_surface_frame(struct wl_client * client, + struct wl_resource * resource, + uint32_t id) +{ + struct swc_surface * surface = resource->data; + struct wl_resource * callback_resource; + + printf("surface_frame\n"); + + callback_resource = wl_client_add_object(client, &wl_callback_interface, + NULL, id, NULL); + wl_list_insert(surface->pending.state.frame_callbacks.prev, &resource->link); +} + +void swc_surface_set_opaque_region(struct wl_client * client, + struct wl_resource * resource, + struct wl_resource * region_resource) +{ + struct swc_surface * surface = resource->data; + + printf("surface_set_opaque_region\n"); + + if (region_resource) + { + } + else + { + } +} + +void swc_surface_set_input_region(struct wl_client * client, + struct wl_resource * resource, + struct wl_resource * region) +{ + printf("surface_set_input_region\n"); +} + +void swc_surface_commit(struct wl_client * client, + struct wl_resource * resource) +{ + struct swc_surface * surface = resource->data; + struct swc_event event; + + printf("surface_commit\n"); + + event.data = surface; + + if (surface->pending.state.buffer != surface->state.buffer) + { + surface->state.buffer = surface->pending.state.buffer; + event.type = SWC_SURFACE_ATTACH; + wl_signal_emit(&surface->event_signal, &event); + } + + pixman_region32_union(&surface->state.damage, &surface->state.damage, + &surface->pending.state.damage); + pixman_region32_intersect_rect(&surface->state.damage, + &surface->state.damage, 0, 0, + surface->geometry.width, + surface->geometry.height); + + + wl_list_insert_list(&surface->state.frame_callbacks, + &surface->pending.state.frame_callbacks); + + /* Reset pending state */ + pixman_region32_clear(&surface->pending.state.damage); + surface->pending.state.buffer = surface->state.buffer; + wl_list_init(&surface->pending.state.frame_callbacks); + + event.type = SWC_SURFACE_REPAINT; + wl_signal_emit(&surface->event_signal, &event); +} + diff --git a/surface.h b/surface.h @@ -0,0 +1,93 @@ +#ifndef SWC_SURFACE_H +#define SWC_SURFACE_H 1 + +#include "surface_state.h" + +#include <stdbool.h> +#include <wayland-server.h> +#include <pixman.h> + +enum swc_surface_event +{ + SWC_SURFACE_ATTACH, + SWC_SURFACE_REPAINT +}; + +struct swc_surface_state +{ + struct wl_buffer * buffer; + + /* The region that needs to be repainted */ + pixman_region32_t damage; + + /* ? */ + pixman_region32_t opaque; + + /* ? */ + pixman_region32_t input; + + struct wl_list frame_callbacks; +}; + +struct swc_surface +{ + struct wl_surface wayland; + + struct swc_surface_state state; + + union swc_renderer_surface_state renderer_state; + struct swc_compositor_surface_state compositor_state; + + struct + { + struct swc_surface_state state; + int32_t x, y; + } pending; + + struct + { + uint32_t width, height; + } geometry; + + uint32_t output_mask; + + struct wl_signal event_signal; + struct wl_list link; +}; + +bool swc_surface_initialize(struct swc_surface * surface); + +void swc_surface_finish(struct swc_surface * surface); + +extern struct wl_surface_interface swc_surface_interface; + +void swc_surface_destroy(struct wl_client * client, + struct wl_resource * resource); + +void swc_surface_attach(struct wl_client * client, + struct wl_resource * resource, + struct wl_resource * buffer_resource, + int32_t x, int32_t y); + +void swc_surface_damage(struct wl_client * client, + struct wl_resource * resource, + int32_t x, int32_t y, + int32_t width, int32_t height); + +void swc_surface_frame(struct wl_client * client, + struct wl_resource * resource, + uint32_t callback); + +void swc_surface_set_opaque_region(struct wl_client * client, + struct wl_resource * resource, + struct wl_resource * region_resource); + +void swc_surface_set_input_region(struct wl_client * client, + struct wl_resource * resource, + struct wl_resource * region_resource); + +void swc_surface_commit(struct wl_client * client, + struct wl_resource * resource); + +#endif + diff --git a/surface_state.h b/surface_state.h @@ -0,0 +1,29 @@ +#ifndef SWC_SURFACE_STATE_H +#define SWC_SURFACE_STATE_H 1 + +#include "i915/bo.h" + +#include <wayland-server.h> +#include <pixman.h> + +union swc_renderer_surface_state +{ + struct + { + pixman_image_t * image; + } shm; + struct + { + struct i915_bo bo; + uint32_t pitch; + } drm; +}; + +struct swc_compositor_surface_state +{ + struct swc_compositor * compositor; + struct wl_listener event_listener; +}; + +#endif + diff --git a/testclient.c b/testclient.c @@ -0,0 +1,34 @@ +#include <stdbool.h> +#include <stdio.h> +#include <wayland-client.h> + +void registry_handle_global(void * data, struct wl_registry * wl_registry, + uint32_t name, const char * interface, + uint32_t version) +{ + printf("handle global\n"); + printf("\tinterface: %s\n", interface); +} + +static const struct wl_registry_listener registry_listener = { + .global = &registry_handle_global +}; + +int main(int argc, char * argv[]) +{ + struct wl_display * display; + struct wl_registry * registry; + bool running = true; + + display = wl_display_connect(NULL); + registry = wl_display_get_registry(display); + + wl_registry_add_listener(registry, &registry_listener, NULL); + wl_display_dispatch(display); + + while (running) + { + wl_display_dispatch_pending(display); + wl_display_flush(display); + } +} diff --git a/testwm/Makefile.am b/testwm/Makefile.am @@ -0,0 +1,13 @@ +# testwm/Makefile.am + +AM_CFLAGS = -I.. $(pixman_CFLAGS) + +bin_PROGRAMS = testwm + +testwm_SOURCES = \ + main.c \ + shell.c shell.h \ + shell_surface.c shell_surface.h + +testwm_LDADD = ../libswc.la $(wayland_server_LIBS) + diff --git a/testwm/main.c b/testwm/main.c @@ -0,0 +1,173 @@ +#include "shell.h" + +#include <compositor.h> + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <sys/wait.h> + +#include <wayland-server.h> +#include <xkbcommon/xkbcommon-keysyms.h> + +const char socket_name[] = "wayland-0"; + +void handle_terminate(uint32_t time, uint32_t value, void * data) +{ + struct wl_display * display = data; + printf("handling terminate\n"); + wl_display_terminate(display); +} + +void handle_switch_vt(uint32_t time, uint32_t value, void * data) +{ + struct swc_tty * tty = data; + uint32_t vt = value - XKB_KEY_XF86Switch_VT_1 + 1; + printf("handle switch vt%u\n", vt); + swc_tty_switch_vt(tty, value - XKB_KEY_XF86Switch_VT_1 + 1); +} + +void handle_test_shm(uint32_t time, uint32_t value, void * data) +{ + printf("handle test shm\n"); + if (fork() == 0) + { + printf("launching shm\n"); + execlp("/home/michael/scm/freedesktop/weston/clients/simple-shm", "simple-shm", NULL); + exit(EXIT_SUCCESS); + } +} + +void handle_test_term(uint32_t time, uint32_t value, void * data) +{ + printf("handle test term\n"); + if (fork() == 0) + { + printf("launching term\n"); + execlp("weston-terminal", "weston-terminal", NULL); + exit(EXIT_SUCCESS); + } +} + +void handle_test_info(uint32_t time, uint32_t value, void * data) +{ + printf("handle test info\n"); + if (fork() == 0) + { + printf("launching info\n"); + execlp("weston-info", "weston-info", NULL); + exit(EXIT_SUCCESS); + } +} + +void handle_test_egl(uint32_t time, uint32_t value, void * data) +{ + printf("handle test egl\n"); + if (fork() == 0) + { + printf("launching info\n"); + execlp("/home/michael/scm/freedesktop/weston/clients/simple-egl", "simple-egl", NULL); + exit(EXIT_SUCCESS); + } +} + +void handle_flip(uint32_t time, uint32_t value, void * data) +{ + struct swc_compositor * compositor = data; + struct swc_output * output; + + output = wl_container_of(compositor->outputs.next, output, link); + + printf("output: 0x%x\n", output); + printf("output id: %u\n", output->id); + printf("compositor: 0x%x\n", compositor); + printf("handle flip\n"); + swc_renderer_repaint_output(&compositor->renderer, output, &compositor->surfaces); + swc_output_switch_buffer(output); +} + +int handle_sigint(int signal_number, void * data) +{ + struct wl_display * display = data; + printf("handle sigint\n"); + wl_display_terminate(display); + + return 1; +} + +int handle_sigchld(int signal_number, void * data) +{ + printf("handle SIGCHLD\n"); + while (waitpid(-1, NULL, WNOHANG) != -1); + + return 1; +} + +int main(int argc, char * argv[]) +{ + struct wl_display * display; + struct swc_compositor compositor; + struct wl_event_loop * event_loop; + struct wl_event_source * sigint_source, * sigchld_source; + xkb_keysym_t keysym; + + //struct shell shell; + + display = wl_display_create(); + + wl_display_init_shm(display); + wl_data_device_manager_init(display); + + event_loop = wl_display_get_event_loop(display); + sigint_source = wl_event_loop_add_signal(event_loop, SIGINT, &handle_sigint, + display); + + sigchld_source = wl_event_loop_add_signal(event_loop, SIGCHLD, &handle_sigchld, + NULL); + + swc_compositor_initialize(&compositor, display); + swc_compositor_add_globals(&compositor, display); + + //shell_initialize(&shell, display); + + swc_compositor_add_key_binding(&compositor, + MOD_CTRL | MOD_ALT, XKB_KEY_BackSpace, &handle_terminate, display); + + for (keysym = XKB_KEY_XF86Switch_VT_1; + keysym <= XKB_KEY_XF86Switch_VT_12; + ++keysym) + { + swc_compositor_add_key_binding(&compositor, MOD_ANY, keysym, + &handle_switch_vt, &compositor.tty); + } + + swc_compositor_add_key_binding(&compositor, MOD_SUPER, XKB_KEY_Return, + &handle_test_term, NULL); + + swc_compositor_add_key_binding(&compositor, MOD_SUPER, XKB_KEY_1, + &handle_test_info, NULL); + + swc_compositor_add_key_binding(&compositor, MOD_SUPER, XKB_KEY_2, + &handle_test_shm, NULL); + + swc_compositor_add_key_binding(&compositor, MOD_SUPER, XKB_KEY_3, + &handle_test_egl, NULL); + + swc_compositor_add_key_binding(&compositor, MOD_SUPER, XKB_KEY_0, + &handle_flip, &compositor); + + wl_display_add_socket(display, socket_name); + setenv("WAYLAND_DISPLAY", socket_name, 1); + + wl_display_run(display); + + /* Cleanup */ + wl_event_source_remove(sigint_source); + wl_event_source_remove(sigchld_source); + swc_compositor_finish(&compositor); + wl_display_destroy(display); + + return EXIT_SUCCESS; +} + diff --git a/testwm/shell.c b/testwm/shell.c @@ -0,0 +1,29 @@ +#include "shell.h" + +#include "shell_surface.h" + +static void get_shell_surface(struct wl_client * client, + struct wl_resource * resource, uint32_t id, + struct wl_resource * surface) +{ + wl_client_add_object(client, &wl_shell_surface_interface, + &shell_surface_implementation, id, surface->data); +} + +struct wl_shell_interface shell_implementation = { + .get_shell_surface = &get_shell_surface +}; + +static void bind_shell(struct wl_client * client, void * data, uint32_t version, + uint32_t id) +{ + wl_client_add_object(client, &wl_shell_interface, &shell_implementation, + id, NULL); +} + +void shell_initialize(struct shell * shell, struct wl_display * display) +{ + wl_display_add_global(display, &wl_shell_interface, &shell_implementation, + &bind_shell); +} + diff --git a/testwm/shell.h b/testwm/shell.h @@ -0,0 +1,15 @@ +#ifndef TESTWM_SHELL_H +#define TESTWM_SHELL_H 1 + +#include <wayland-server.h> + +struct shell +{ +}; + +extern struct wl_shell_interface shell_implementation; + +void shell_initialize(struct shell * shell, struct wl_display * display); + +#endif + diff --git a/testwm/shell_surface.c b/testwm/shell_surface.c @@ -0,0 +1,79 @@ +#include "shell_surface.h" + +#include <stdlib.h> +#include <wayland-server.h> + +static void pong(struct wl_client * client, struct wl_resource * resource, + uint32_t serial) +{ +} + +static void move(struct wl_client * client, struct wl_resource * resource, + struct wl_resource * seat_resource, uint32_t serial) +{ +} + +static void resize(struct wl_client * client, struct wl_resource * resource, + struct wl_resource * seat_resource, uint32_t serial, + uint32_t edges) +{ +} + +static void set_toplevel(struct wl_client * client, + struct wl_resource * resource) +{ +} + +static void set_transient(struct wl_client * client, + struct wl_resource * resource, + struct wl_resource * parent_resource, + int32_t x, int32_t y, uint32_t flags) +{ +} + +static void set_fullscreen(struct wl_client * client, + struct wl_resource * resource, uint32_t method, + uint32_t framerate, struct wl_resource * output) +{ +} + +static void set_popup(struct wl_client * client, struct wl_resource * resource, + struct wl_resource * seat_resource, uint32_t serial, + struct wl_resource * parent_resource, + int32_t x, int32_t y, uint32_t flags) +{ +} + +static void set_maximized(struct wl_client * client, + struct wl_resource * resource, + struct wl_resource * output) +{ +} + +static void set_class(struct wl_client * client, struct wl_resource * resource, + const char * class_) +{ +} + +struct wl_shell_surface_interface shell_surface_implementation = { + .pong = &pong, + .move = &move, + .resize = &resize, + .set_toplevel = &set_toplevel, + .set_transient = &set_transient, + .set_fullscreen = &set_fullscreen, + .set_popup = &set_popup, + .set_maximized = &set_maximized, + .set_class = &set_class +}; + +struct shell_surface * shell_surface_create(struct swc_surface * surface) +{ + struct shell_surface * shell_surface; + + shell_surface = malloc(sizeof *shell_surface); + shell_surface->surface = surface; + + return shell_surface; +} + diff --git a/testwm/shell_surface.h b/testwm/shell_surface.h @@ -0,0 +1,13 @@ +#ifndef TESTWM_SHELL_SURFACE_H +#define TESTWM_SHELL_SURFACE_H 1 + +struct shell_surface +{ + struct swc_surface * surface; + struct wl_resource * resource; +}; + +extern struct wl_shell_surface_interface shell_surface_implementation; + +#endif + diff --git a/testwm/testwm.c b/testwm/testwm.c @@ -0,0 +1,151 @@ +#include "compositor.h" + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <sys/wait.h> + +#include <wayland-server.h> +#include <xkbcommon/xkbcommon-keysyms.h> + +const char socket_name[] = "wayland-0"; + +static void get_shell_surface(struct wl_client * client, + struct wl_resource * resource, uint32_t id, + struct wl_resource * surface) +{ + wl_client_add_object(client, &wl_shell_surface_interface, NULL, id, surface->data); +} + +struct wl_shell_interface shell_interface = { + .get_shell_surface = &get_shell_surface +}; + +void handle_terminate(uint32_t time, uint32_t value, void * data) +{ + struct wl_display * display = data; + printf("handling terminate\n"); + wl_display_terminate(display); +} + +void handle_switch_vt(uint32_t time, uint32_t value, void * data) +{ + struct swc_tty * tty = data; + uint32_t vt = value - XKB_KEY_XF86Switch_VT_1 + 1; + printf("handle switch vt%u\n", vt); + swc_tty_switch_vt(tty, value - XKB_KEY_XF86Switch_VT_1 + 1); +} + +void handle_test_shm(uint32_t time, uint32_t value, void * data) +{ + printf("handle test shm\n"); + if (fork() == 0) + { + printf("launching shm\n"); + execlp("/home/michael/scm/freedesktop/weston/clients/simple-shm", "simple-shm", NULL); + exit(EXIT_SUCCESS); + } +} + +void handle_test_term(uint32_t time, uint32_t value, void * data) +{ + printf("handle test term\n"); + if (fork() == 0) + { + printf("launching term\n"); + execlp("weston-terminal", "weston-terminal", NULL); + exit(EXIT_SUCCESS); + } +} + +void handle_test_info(uint32_t time, uint32_t value, void * data) +{ + printf("handle test term\n"); + if (fork() == 0) + { + printf("launching info\n"); + execlp("weston-info", "weston-info", NULL); + exit(EXIT_SUCCESS); + } +} + +int handle_sigint(int signal_number, void * data) +{ + struct wl_display * display = data; + printf("handle sigint\n"); + wl_display_terminate(display); + + return 1; +} + +int handle_sigchld(int signal_number, void * data) +{ + printf("handle SIGCHLD\n"); + while (waitpid(-1, NULL, WNOHANG) != -1); +} + +static void bind_shell(struct wl_client * client, void * data, uint32_t version, uint32_t id) +{ + wl_client_add_object(client, &wl_shell_interface, &shell_interface, id, NULL); +} + +int main(int argc, char * argv[]) +{ + struct wl_display * display; + struct swc_compositor compositor; + struct wl_event_loop * event_loop; + struct wl_event_source * sigint_source, * sigchld_source; + xkb_keysym_t keysym; + + display = wl_display_create(); + + wl_display_init_shm(display); + wl_data_device_manager_init(display); + + event_loop = wl_display_get_event_loop(display); + sigint_source = wl_event_loop_add_signal(event_loop, SIGINT, &handle_sigint, + display); + + sigchld_source = wl_event_loop_add_signal(event_loop, SIGCHLD, &handle_sigchld, + NULL); + + swc_compositor_initialize(&compositor, display); + swc_compositor_add_globals(&compositor, display); + + wl_display_add_global(display, &wl_shell_interface, NULL, &bind_shell); + + swc_compositor_add_key_binding(&compositor, + MOD_CTRL | MOD_ALT, XKB_KEY_BackSpace, &handle_terminate, display); + + for (keysym = XKB_KEY_XF86Switch_VT_1; + keysym <= XKB_KEY_XF86Switch_VT_12; + ++keysym) + { + swc_compositor_add_key_binding(&compositor, MOD_ANY, keysym, + &handle_switch_vt, &compositor.tty); + } + + swc_compositor_add_key_binding(&compositor, MOD_SUPER, XKB_KEY_Return, + &handle_test_term, NULL); + + swc_compositor_add_key_binding(&compositor, MOD_SUPER, XKB_KEY_1, + &handle_test_info, NULL); + + swc_compositor_add_key_binding(&compositor, MOD_SUPER, XKB_KEY_2, + &handle_test_shm, NULL); + + wl_display_add_socket(display, socket_name); + setenv("WAYLAND_DISPLAY", socket_name, 1); + + wl_display_run(display); + + /* Cleanup */ + wl_event_source_remove(sigint_source); + wl_event_source_remove(sigchld_source); + swc_compositor_finish(&compositor); + wl_display_destroy(display); + + return EXIT_SUCCESS; +} + diff --git a/tty.c b/tty.c @@ -0,0 +1,221 @@ +/* swc: tty.c + * + * Copyright © 2012 Michael Forney + * + * Based in part upon tty.c from weston, which is: + * + * Copyright © 2010 Intel Corporation + * + * 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/vt.h> +#include <linux/kd.h> + +#include <wayland-server.h> + +#include "tty.h" +#include "event.h" + +static void restore_tty(struct swc_tty * tty); +static int handle_vt_signal(int signal_number, void * data); + +bool swc_tty_initialize(struct swc_tty * tty, + struct wl_event_loop * event_loop, + uint8_t tty_number) +{ + struct vt_stat state; + char tty_device[16]; + struct vt_mode mode; + + wl_signal_init(&tty->event_signal); + + if (tty_number == 0) + { + char * vt_string = getenv("XDG_VTNR"); + if (vt_string) + { + char * end; + tty_number = strtoul(vt_string, &end, 10); + if (*end != '\0') + tty_number = 0; + } + } + + /* If we still don't have a VT number. */ + if (tty_number == 0) + { + printf("don't know which VT to run on\n"); + goto error_base; + } + + snprintf(tty_device, sizeof(tty_device), "/dev/tty%u", tty_number); + + /* Open the TTY. */ + tty->fd = open(tty_device, O_RDWR | O_NOCTTY | O_CLOEXEC); + + if (tty->fd == -1) + { + printf("couldn't open tty\n"); + goto error_base; + } + + tty->vt = tty_number; + + /* Determine the current VT state. */ + if (ioctl(tty->fd, VT_GETSTATE, &state) != 0) + { + printf("could not determine starting vt\n"); + goto error_tty; + } + + tty->original_state.vt = state.v_active; + printf("starting vt: %u\n", tty->original_state.vt); + + /* Switch to the new VT if necessary. */ + if (tty->original_state.vt != tty->vt) + { + if (ioctl(tty->fd, VT_ACTIVATE, tty->vt) != 0 + || ioctl(tty->fd, VT_WAITACTIVE, tty->vt) != 0) + { + printf("couldn't switch to vt%u\n", tty->vt); + goto error_tty; + } + } + + tty->active = true; + + /* Save current kb_mode. */ + if (ioctl(tty->fd, KDGKBMODE, &tty->original_state.kb_mode) != 0) + { + printf("couldn't determine kb_mode of vt%u\n", tty->vt); + goto error_tty; + } + + /* Turn off keyboard, we will use evdev for input. */ + if (ioctl(tty->fd, KDSKBMODE, K_OFF) != 0) + { + printf("couldn't set kb_mode of vt%u to K_OFF\n", tty->vt); + goto error_tty; + } + + /* Set VT to graphics mode. */ + if (ioctl(tty->fd, KDSETMODE, KD_GRAPHICS) != 0) + { + printf("couldn't set mode of vt%u to KD_GRAPHICS\n", tty->vt); + goto error_kdkbmode; + } + + mode = (struct vt_mode) { + .mode = VT_PROCESS, + .relsig = SIGUSR1, + .acqsig = SIGUSR1 + }; + + /* Set up VT switching handler. */ + if (ioctl(tty->fd, VT_SETMODE, &mode) != 0) + { + printf("could not set VT mode on vt%u\n", tty->vt); + goto error_kdmode; + } + + tty->vt_source = wl_event_loop_add_signal(event_loop, SIGUSR1, + &handle_vt_signal, tty); + + if (!tty->vt_source) + { + printf("could not create VT event source\n"); + goto error_vtmode; + } + + return true; + + error_vtmode: + mode = (struct vt_mode) { .mode = VT_AUTO }; + ioctl(tty->fd, VT_SETMODE, &mode); + error_kdmode: + ioctl(tty->fd, KDSETMODE, KD_TEXT); + error_kdkbmode: + ioctl(tty->fd, KDSKBMODE, tty->original_state.kb_mode); + error_tty: + close(tty->fd); + error_base: + return false; +} + +void swc_tty_finish(struct swc_tty * tty) +{ + wl_event_source_remove(tty->vt_source); + restore_tty(tty); + close(tty->fd); +} + +void swc_tty_switch_vt(struct swc_tty * tty, uint32_t vt) +{ + ioctl(tty->fd, VT_ACTIVATE, vt); +} + +void restore_tty(struct swc_tty * tty) +{ + struct vt_mode mode = { .mode = VT_AUTO }; + + if (ioctl(tty->fd, KDSKBMODE, tty->original_state.kb_mode) != 0) + printf("failed to restore keyboard mode\n"); + if (ioctl(tty->fd, KDSETMODE, KD_TEXT) != 0) + printf("failed to set mode to KD_TEXT\n"); + if (ioctl(tty->fd, VT_SETMODE, &mode) != 0) + printf("failed to restore VT handling\n"); + if (tty->vt != tty->original_state.vt + && (ioctl(tty->fd, VT_ACTIVATE, tty->original_state.vt) != 0 + || ioctl(tty->fd, VT_WAITACTIVE, tty->original_state.vt) != 0)) + { + printf("failed to restore VT\n"); + } +} + +static int handle_vt_signal(int signal_number, void * data) +{ + struct swc_tty * tty = data; + struct swc_event event; + + if (tty->active) + { + event.type = SWC_TTY_VT_LEAVE; + wl_signal_emit(&tty->event_signal, &event); + + ioctl(tty->fd, VT_RELDISP, 1); + tty->active = false; + } + else + { + ioctl(tty->fd, VT_RELDISP, VT_ACKACQ); + tty->active = true; + + event.type = SWC_TTY_VT_ENTER; + wl_signal_emit(&tty->event_signal, &event); + } + + return 1; +} + diff --git a/tty.h b/tty.h @@ -0,0 +1,44 @@ +#ifndef SWC_TTY_H +#define SWC_TTY_H 1 + +#include <stdint.h> +#include <stdbool.h> +#include <signal.h> + +enum swc_tty_event +{ + SWC_TTY_VT_ENTER = 0, + SWC_TTY_VT_LEAVE +}; + +struct swc_tty +{ + int fd; + uint8_t vt; + + bool active; + + /* The state of the VT the compositor was started on so we have a state to + * restore to when the compositor closes. */ + struct + { + uint8_t vt; + long kb_mode; + } original_state; + + /* Receives events when switching from/to the VT the compositor is running on. */ + struct wl_event_source * vt_source; + + struct wl_signal event_signal; +}; + +bool swc_tty_initialize(struct swc_tty * tty, + struct wl_event_loop * event_loop, + uint8_t tty_number); + +void swc_tty_finish(struct swc_tty * tty); + +void swc_tty_switch_vt(struct swc_tty * tty, uint32_t vt); + +#endif + diff --git a/util.c b/util.c @@ -0,0 +1,10 @@ +#include "util.h" + +#include <stdlib.h> + +void swc_unbind_resource(struct wl_resource * resource) +{ + wl_list_remove(&resource->link); + free(resource); +} + diff --git a/util.h b/util.h @@ -0,0 +1,20 @@ +#ifndef SWC_UTIL_H +#define SWC_UTIL_H 1 + +#include <wayland-server.h> + +/* +void swc_object_init(struct wl_object * object, + const struct wl_interface * interface, + const void * implementation, uint32_t id); + +void swc_resource_init(struct wl_resource * resource, + struct wl_client * client, + +void swc_client_add_object(struct wl_clientstruct wl_resource * resource, ); +*/ + +void swc_unbind_resource(struct wl_resource * resource); + +#endif + diff --git a/xkb.c b/xkb.c @@ -0,0 +1,140 @@ +#include "xkb.h" + +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <sys/mman.h> + +const struct xkb_rule_names rule_names = { + .layout = "us,us", + .variant = "dvorak,", + .options = "grp:alt_shift_toggle" +}; + +const char keymap_file_template[] = "swc-xkb-keymap-XXXXXX"; + +bool swc_xkb_initialize(struct swc_xkb * xkb) +{ + xkb->context = xkb_context_new(0); + + if (!xkb->context) + { + printf("could not create XKB context\n"); + goto error_base; + } + + xkb->keymap.map = xkb_keymap_new_from_names(xkb->context, &rule_names, 0); + + if (!xkb->keymap.map) + { + printf("could not create XKB keymap\n"); + goto error_context; + } + + xkb->state = xkb_state_new(xkb->keymap.map); + + if (!swc_xkb_update_keymap(xkb)) + { + printf("could not update XKB keymap\n"); + goto error_state; + } + + return true; + + error_state: + xkb_state_unref(xkb->state); + error_keymap: + xkb_keymap_unref(xkb->keymap.map); + error_context: + xkb_context_unref(xkb->context); + error_base: + return false; +} + +void swc_xkb_finish(struct swc_xkb * xkb) +{ + munmap(xkb->keymap.area, xkb->keymap.size); + close(xkb->keymap.fd); + xkb_state_unref(xkb->state); + xkb_keymap_unref(xkb->keymap.map); + xkb_context_unref(xkb->context); +} + +bool swc_xkb_update_keymap(struct swc_xkb * xkb) +{ + char * keymap_string; + + xkb->indices.ctrl + = xkb_keymap_mod_get_index(xkb->keymap.map, XKB_MOD_NAME_CTRL); + xkb->indices.alt + = xkb_keymap_mod_get_index(xkb->keymap.map, XKB_MOD_NAME_ALT); + xkb->indices.super + = xkb_keymap_mod_get_index(xkb->keymap.map, XKB_MOD_NAME_LOGO); + xkb->indices.shift + = xkb_keymap_mod_get_index(xkb->keymap.map, XKB_MOD_NAME_SHIFT); + + /* Keymap string */ + { + const char * keymap_directory = getenv("XDG_RUNTIME_DIR") ?: "/tmp"; + char keymap_path[strlen(keymap_directory) + 1 + + sizeof keymap_file_template]; + + /* In order to send the keymap to clients, we must first convert it to a + * string and then mmap it to a file. */ + keymap_string = xkb_keymap_get_as_string(xkb->keymap.map, + XKB_KEYMAP_FORMAT_TEXT_V1); + + if (!keymap_string) + { + printf("could not get XKB keymap as a string\n"); + goto error_base; + } + + sprintf(keymap_path, "%s/%s", keymap_directory, keymap_file_template); + + xkb->keymap.size = strlen(keymap_string) + 1; + xkb->keymap.fd = mkostemp(keymap_path, O_CLOEXEC); + + if (xkb->keymap.fd == -1) + { + printf("could not create XKB keymap file\n"); + goto error_string; + } + + unlink(keymap_path); + + if (ftruncate(xkb->keymap.fd, xkb->keymap.size) == -1) + { + printf("could not resize XKB keymap file\n"); + goto error_fd; + } + + xkb->keymap.area = mmap(NULL, xkb->keymap.size, PROT_READ | PROT_WRITE, + MAP_SHARED, xkb->keymap.fd, 0); + + if (xkb->keymap.area == MAP_FAILED) + { + printf("could not mmap XKB keymap string\n"); + goto error_fd; + } + + strcpy(xkb->keymap.area, keymap_string); + + free(keymap_string); + } + + return true; + + error_fd: + close(xkb->keymap.fd); + error_string: + free(keymap_string); + error_base: + return false; +} + +void swc_xkb_update_key_indices(struct swc_xkb * xkb) +{ +} + diff --git a/xkb.h b/xkb.h @@ -0,0 +1,28 @@ +#include <stdbool.h> +#include <xkbcommon/xkbcommon.h> + +struct swc_xkb +{ + struct xkb_context * context; + struct xkb_state * state; + + struct + { + struct xkb_keymap * map; + int fd; + uint32_t size; + char * area; + } keymap; + + struct + { + uint32_t ctrl, alt, super, shift; + } indices; +}; + +bool swc_xkb_initialize(struct swc_xkb * xkb); + +void swc_xkb_finish(struct swc_xkb * xkb); + +bool swc_xkb_update_keymap(struct swc_xkb * xkb); +