/* * Copyright © 2008 Kristian Høgsberg * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "i830.h" #undef ALIGN /* HACK, remove this from wayland header */ #include #include #include #include #include /* * TODO: * - lose X kb focus when wayland surface loses it * - active grabs, grab owner crack * - draw decorations in xserver, don't use metacity */ static int wayland_window_private_key_index; static DevPrivateKey wayland_window_private_key = &wayland_window_private_key_index; struct xwl_window { struct xwl_context *context; struct wl_surface *surface; struct wl_visual *visual; WindowPtr window; }; struct xwl_context { ScrnInfoPtr pScrn; int fd; struct wl_display *display; struct wl_compositor *compositor; struct wl_surface *surface; uint32_t mask; Bool rootless; PixmapPtr ref_pixmaps[16]; int ref_index; /* FIXME: Hack. */ int32_t width, height; int32_t root_x, root_y; CreateWindowProcPtr CreateWindow; RealizeWindowProcPtr RealizeWindow; UnrealizeWindowProcPtr UnrealizeWindow; SetWindowPixmapProcPtr SetWindowPixmap; MoveWindowProcPtr MoveWindow; }; struct xwl_output { struct wl_output *output; struct xwl_context *context; struct wl_surface *surface; int32_t x, y, width, height; xf86Monitor monitor; }; #define MODIFIER_META 0x01 struct xwl_input_device { DeviceIntPtr pointer; DeviceIntPtr keyboard; struct xwl_context *context; struct wl_input_device *input_device; int grab; struct xwl_window *focus_window; int32_t grab_x, grab_y; uint32_t modifiers; }; /* OMG the horror of the xf86 input wrapper... Fortunately, we don't * have to use that. */ static int wayland_input_proc(DeviceIntPtr device, int event) { switch (event) { case DEVICE_INIT: break; case DEVICE_ON: break; case DEVICE_OFF: break; case DEVICE_CLOSE: break; } return Success; } static void wayland_input_ptr_ctrl_proc(DeviceIntPtr device, PtrCtrl *ctrl) { /* Nothing to do, dix handles all settings */ } static void wayland_input_kbd_ctrl(DeviceIntPtr device, KeybdCtrl *ctrl) { /* FIXME: Set keyboard leds based on CAPSFLAG etc being set in * ctrl->leds */ } static int wayland_input_init_pointer(struct xwl_input_device *d, struct xwl_context *context) { DeviceIntPtr device; int min_x = 0, min_y = 0; int max_x = context->width, max_y = context->height; static unsigned char map[] = { 0, 1, 2, 3 }; CARD32 atom; char *name = "wayland"; Atom labels[3]; device = AddInputDevice(serverClient, wayland_input_proc, TRUE); d->pointer = device; atom = MakeAtom(name, strlen(name), TRUE); AssignTypeAndName(device, atom, name); device->coreEvents = TRUE; device->type = SLAVE; device->spriteInfo->spriteOwner = FALSE; labels[0] = MakeAtom("x", 1, TRUE); labels[1] = MakeAtom("y", 1, TRUE); if (!InitValuatorClassDeviceStruct(device, 2, labels, GetMotionHistorySize(), Absolute)) return !Success; /* Valuators */ InitValuatorAxisStruct(device, labels[0], 0, min_x, max_x, 10000, 0, 10000); InitValuatorAxisStruct(device, labels[1], 1, min_y, max_y, 10000, 0, 10000); if (!InitPtrFeedbackClassDeviceStruct(device, wayland_input_ptr_ctrl_proc)) return !Success; /* FIXME: count number of actual buttons */ labels[0] = MakeAtom("left", 4, TRUE); labels[1] = MakeAtom("middle", 6, TRUE); labels[2] = MakeAtom("right", 5, TRUE); if (!InitButtonClassDeviceStruct(device, 3, labels, map)) return !Success; return Success; } static int wayland_input_init_keyboard(struct xwl_input_device *d, struct xwl_context *context) { DeviceIntPtr device; CARD32 atom; char *name = "wayland"; KeySymsRec key_syms; XkbRMLVOSet rmlvo; device = AddInputDevice(serverClient, wayland_input_proc, TRUE); d->keyboard = device; atom = MakeAtom(name, strlen(name), TRUE); AssignTypeAndName(device, atom, name); device->coreEvents = TRUE; device->type = SLAVE; device->spriteInfo->spriteOwner = FALSE; key_syms.map = malloc(4 * 248 * sizeof(KeySym)); key_syms.mapWidth = 4; key_syms.minKeyCode = 8; key_syms.maxKeyCode = 254; rmlvo.rules = "evdev"; rmlvo.model = "evdev"; rmlvo.layout = "us"; rmlvo.variant = NULL; rmlvo.options = NULL; if (!InitKeyboardDeviceStruct(device, &rmlvo, NULL, wayland_input_kbd_ctrl)) return !Success; RegisterOtherDevice(device); ActivateDevice(device, FALSE); return Success; } static int source_update(uint32_t mask, void *data) { struct xwl_context *context = data; context->mask = mask; return 0; } static void wakeup_handler(pointer data, int err, pointer read_mask) { struct xwl_context *context = data; if (err >= 0 && FD_ISSET(context->fd, (fd_set *) read_mask)) { wl_display_iterate(context->display, WL_DISPLAY_READABLE); } } static void block_handler(pointer data, struct timeval **tv, pointer read_mask) { struct xwl_context *context = data; /* The X servers "main loop" doesn't let us select for * writable, so let's just do a blocking write here. */ while (context->mask & WL_DISPLAY_WRITABLE) wl_display_iterate(context->display, WL_DISPLAY_WRITABLE); } static void handle_motion(void *data, struct wl_input_device *input_device, int32_t x, int32_t y, int32_t sx, int32_t sy) { ScreenPtr pScreen; struct xwl_window *window; struct xwl_input_device *d = data; struct xwl_context *context = d->context; int32_t dx, dy; if (d->focus_window) { dx = d->focus_window->window->drawable.x; dy = d->focus_window->window->drawable.y; } else { dx = 0; dy = 0; } if (!d->grab) { xf86PostMotionEvent(d->pointer, TRUE, 0, 2, sx + dx, sy + dy); return; } window = d->focus_window; pScreen = window->window->drawable.pScreen; (*pScreen->MoveWindow)(window->window, d->grab_x + x, d->grab_y + y, NullWindow, VTMove); /* We don't get the pScreen->MoveWindow callback for the root * window. Special case it here so we can still move it. */ if (window->window->parent == NULL) { context->root_x = d->grab_x + x; context->root_y = d->grab_y + y; wl_surface_map(window->surface, d->grab_x + x, d->grab_y + y, window->window->drawable.width, window->window->drawable.height); wl_compositor_commit(window->context->compositor, 16); } } static void handle_button(void *data, struct wl_input_device *input_device, uint32_t button, uint32_t state, int32_t x, int32_t y, int32_t sx, int32_t sy) { struct xwl_input_device *d = data; struct xwl_context *context = d->context; int index; if (button == BTN_LEFT && state == 1 && (d->modifiers & MODIFIER_META)) { if (d->focus_window->window->parent == NULL) { d->grab_x = context->root_x - x; d->grab_y = context->root_y - y; } else { d->grab_x = d->focus_window->window->drawable.x - x; d->grab_y = d->focus_window->window->drawable.y - y; } d->grab = TRUE; return; } else if (button == BTN_LEFT && state == 0 && d->grab) { d->grab = FALSE; } switch (button) { case BTN_MIDDLE: index = 2; break; case BTN_RIGHT: index = 3; break; default: index = button - BTN_LEFT + 1; break; } xf86PostButtonEvent(d->pointer, TRUE, index, state, 0, 0); } static void handle_key(void *data, struct wl_input_device *input_device, uint32_t key, uint32_t state) { struct xwl_input_device *d = data; uint32_t modifier; switch (key) { case KEY_LEFTMETA: case KEY_RIGHTMETA: modifier = MODIFIER_META; break; default: modifier = 0; break; } if (state) d->modifiers |= modifier; else d->modifiers &= ~modifier; xf86PostKeyboardEvent(d->keyboard, key + 8, state); } static void handle_pointer_focus(void *data, struct wl_input_device *input_device, struct wl_surface *surface) { struct xwl_input_device *d = data; if (surface) d->focus_window = wl_surface_get_user_data(surface); else d->focus_window = NULL; if (d->focus_window) SetDeviceRedirectWindow(d->pointer, d->focus_window->window); else SetDeviceRedirectWindow(d->pointer, PointerRootWin); } static void handle_keyboard_focus(void *data, struct wl_input_device *input_device, struct wl_surface *surface, struct wl_array *keys) { struct xwl_input_device *d = data; uint32_t *k, *end; end = (uint32_t *) ((char *) keys->data + keys->size); for (k = keys->data; k < end; k++) { switch (*k) { case KEY_LEFTMETA: case KEY_RIGHTMETA: d->modifiers |= MODIFIER_META; break; } } } static const struct wl_input_device_listener input_device_listener = { handle_motion, handle_button, handle_key, handle_pointer_focus, handle_keyboard_focus, }; static void display_handle_geometry(void *data, struct wl_output *output, int32_t width, int32_t height) { struct xwl_output *xwl_output = data; xwl_output->x = 0; xwl_output->y = 0; xwl_output->width = width; xwl_output->height = height; xwl_output->context->width = width; xwl_output->context->height = height; } static const struct wl_output_listener output_listener = { display_handle_geometry, }; static void crtc_dpms(xf86CrtcPtr drmmode_crtc, int mode) { } static Bool crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation, int x, int y) { return TRUE; } static void crtc_set_cursor_colors (xf86CrtcPtr crtc, int bg, int fg) { } static void crtc_set_cursor_position (xf86CrtcPtr crtc, int x, int y) { } static void crtc_show_cursor (xf86CrtcPtr crtc) { } static void crtc_hide_cursor (xf86CrtcPtr crtc) { } static void crtc_load_cursor_argb (xf86CrtcPtr crtc, CARD32 *image) { } static PixmapPtr crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) { return NULL; } static void * crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height) { return NULL; } static void crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data) { } static const xf86CrtcFuncsRec crtc_funcs = { .dpms = crtc_dpms, .set_mode_major = crtc_set_mode_major, .set_cursor_colors = crtc_set_cursor_colors, .set_cursor_position = crtc_set_cursor_position, .show_cursor = crtc_show_cursor, .hide_cursor = crtc_hide_cursor, .load_cursor_argb = crtc_load_cursor_argb, .shadow_create = crtc_shadow_create, .shadow_allocate = crtc_shadow_allocate, .shadow_destroy = crtc_shadow_destroy, .destroy = NULL, /* XXX */ }; static void output_dpms(xf86OutputPtr output, int mode) { return; } static xf86OutputStatus output_detect(xf86OutputPtr output) { return XF86OutputStatusConnected; } static Bool output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes) { return MODE_OK; } static DisplayModePtr output_get_modes(xf86OutputPtr xf86output) { struct xwl_output *output = xf86output->driver_private; struct monitor_ranges *ranges; DisplayModePtr modes; modes = xf86CVTMode(output->width, output->height, 60, TRUE, FALSE); output->monitor.det_mon[0].type = DS_RANGES; ranges = &output->monitor.det_mon[0].section.ranges; ranges->min_h = modes->HSync - 10; ranges->max_h = modes->HSync + 10; ranges->min_v = modes->VRefresh - 10; ranges->max_v = modes->VRefresh + 10; ranges->max_clock = modes->Clock + 100; output->monitor.det_mon[1].type = DT; output->monitor.det_mon[2].type = DT; output->monitor.det_mon[3].type = DT; xf86output->MonInfo = &output->monitor; return modes; } static void output_destroy(xf86OutputPtr xf86output) { struct xwl_output *output = xf86output->driver_private; free(output); } static const xf86OutputFuncsRec output_funcs = { .dpms = output_dpms, .detect = output_detect, .mode_valid = output_mode_valid, .get_modes = output_get_modes, .destroy = output_destroy }; static void create_output(struct xwl_context *context, struct wl_object *object) { struct xwl_output *output; xf86OutputPtr xf86output; xf86CrtcPtr xf86crtc; output = malloc(sizeof *output); output->context = context; output->output = (struct wl_output *) object; output->surface = wl_compositor_create_surface(context->compositor); if (output->surface == NULL) { ErrorF("wl_display_create_surface failed\n"); return; } wl_output_add_listener(output->output, &output_listener, output); context->surface = output->surface; xf86output = xf86OutputCreate(context->pScrn, &output_funcs, "WAYLAND-1"); xf86output->driver_private = output; xf86output->mm_width = 300; xf86output->mm_height = 240; xf86output->subpixel_order = SubPixelHorizontalRGB; xf86output->possible_crtcs = 1; xf86output->possible_clones = 1; xf86crtc = xf86CrtcCreate(context->pScrn, &crtc_funcs); xf86crtc->driver_private = output; return; } static void create_input_device(struct xwl_context *context, struct wl_object *object) { struct xwl_input_device *input_device; struct wl_input_device *device = (struct wl_input_device *) object; input_device = malloc(sizeof *input_device); memset(input_device, 0, sizeof input_device); input_device->input_device = device; input_device->context = context; wayland_input_init_pointer(input_device, context); wayland_input_init_keyboard(input_device, context); wl_input_device_add_listener(device, &input_device_listener, input_device); ErrorF("created input device %p\n", input_device); } static void handle_acknowledge(void *data, struct wl_compositor *compositor, uint32_t key, uint32_t frame) { struct xwl_context *context = data; PixmapPtr pPixmap; ScreenPtr pScreen; if (key < 16) { pPixmap = context->ref_pixmaps[key]; pScreen = pPixmap->drawable.pScreen; (*pScreen->DestroyPixmap)(pPixmap); } } static void handle_frame(void *data, struct wl_compositor *compositor, uint32_t frame, uint32_t timestamp) { } static const struct wl_compositor_listener compositor_listener = { handle_acknowledge, handle_frame, }; static void global_handler(struct wl_display *display, struct wl_object *object, void *data) { struct xwl_context *context = data; if (wl_object_implements(object, "compositor", 1)) { context->compositor = (struct wl_compositor *) object; wl_compositor_add_listener(context->compositor, &compositor_listener, context); } else if (wl_object_implements(object, "output", 1)) { create_output(context, object); } else if (wl_object_implements(object, "input_device", 1)) { create_input_device(context, object); } } static Bool resize (ScrnInfoPtr scrn, int width, int height) { if (scrn->virtualX == width && scrn->virtualY == height) return TRUE; scrn->virtualX = width; scrn->virtualY = height; return TRUE; } static const xf86CrtcConfigFuncsRec config_funcs = { resize }; static const char socket_name[] = "\0wayland"; static void xwl_window_attach(struct xwl_window *window, PixmapPtr pPixmap) { drm_intel_bo *bo; uint32_t name; struct xwl_context *context = window->context; bo = i830_get_pixmap_bo (pPixmap); if (drm_intel_bo_flink(bo, &name) != 0) { ErrorF("failed to name buffer\n"); } wl_surface_attach(window->surface, name, pPixmap->drawable.width, pPixmap->drawable.height, pPixmap->devKind, window->visual); wl_surface_map(window->surface, window->window->drawable.x, window->window->drawable.y, pPixmap->drawable.width, pPixmap->drawable.height); wl_compositor_commit(context->compositor, context->ref_index); context->ref_pixmaps[context->ref_index] = pPixmap; context->ref_index = (context->ref_index + 1) & 15; pPixmap->refcnt++; } static Bool wayland_create_window(WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; I830Ptr pI830 = I830PTR(pScrn); struct xwl_context *context = pI830->wayland_data; Selection *pSel; char buffer[32]; int len, rc; Atom name; Bool ret; pScreen->CreateWindow = context->CreateWindow; ret = (*pScreen->CreateWindow)(pWin); context->CreateWindow = pScreen->CreateWindow; pScreen->CreateWindow = wayland_create_window; if (!context->rootless || pWin->parent != NULL) return ret; len = snprintf(buffer, sizeof buffer, "_NET_WM_CM_S%d", pScreen->myNum); name = MakeAtom(buffer, len, TRUE); rc = AddSelection(&pSel, name, serverClient); if (rc != Success) return ret; pSel->lastTimeChanged = currentTime; pSel->window = pWin->drawable.id; pSel->pWin = pWin; pSel->client = serverClient; CompositeRedirectSubwindows(pWin, CompositeRedirectManual); return ret; } static Bool wayland_realize_window(WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; I830Ptr pI830 = I830PTR(pScrn); struct xwl_context *context = pI830->wayland_data; struct xwl_window *window; VisualID visual; Bool ret; int i; pScreen->RealizeWindow = context->RealizeWindow; ret = (*pScreen->RealizeWindow)(pWin); context->RealizeWindow = pScreen->RealizeWindow; pScreen->RealizeWindow = wayland_realize_window; if (context->rootless) { if (pWin->redirectDraw != RedirectDrawManual) return ret; } else { if (pWin->parent) return ret; } window = malloc(sizeof *window); window->context = context; window->surface = wl_compositor_create_surface(context->compositor); window->window = pWin; if (window->surface == NULL) { ErrorF("wl_display_create_surface failed\n"); return FALSE; } visual = wVisual(pWin); for (i = 0; i < pScreen->numVisuals; i++) if (pScreen->visuals[i].vid == visual) break; if (pScreen->visuals[i].nplanes == 32) window->visual = wl_display_get_premultiplied_argb_visual(context->display); else window->visual = wl_display_get_rgb_visual(context->display); wl_surface_set_user_data(window->surface, window); xwl_window_attach(window, (*pScreen->GetWindowPixmap)(pWin)); dixSetPrivate(&pWin->devPrivates, wayland_window_private_key, window); return ret; } static Bool wayland_unrealize_window(WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; I830Ptr pI830 = I830PTR(pScrn); struct xwl_context *context = pI830->wayland_data; struct xwl_window *window; Bool ret = TRUE; pScreen->UnrealizeWindow = context->UnrealizeWindow; ret = (*pScreen->UnrealizeWindow)(pWin); context->UnrealizeWindow = pScreen->UnrealizeWindow; pScreen->UnrealizeWindow = wayland_unrealize_window; window = dixLookupPrivate(&pWin->devPrivates, wayland_window_private_key); if (window) { wl_surface_destroy(window->surface); free(window); dixSetPrivate(&pWin->devPrivates, wayland_window_private_key, NULL); } return ret; } static void wayland_set_window_pixmap(WindowPtr pWin, PixmapPtr pPixmap) { ScreenPtr pScreen = pWin->drawable.pScreen; ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; I830Ptr pI830 = I830PTR(pScrn); struct xwl_context *context = pI830->wayland_data; struct xwl_window *window; pScreen->SetWindowPixmap = context->SetWindowPixmap; (*pScreen->SetWindowPixmap)(pWin, pPixmap); context->SetWindowPixmap = pScreen->SetWindowPixmap; pScreen->SetWindowPixmap = wayland_set_window_pixmap; window = dixLookupPrivate(&pWin->devPrivates, wayland_window_private_key); if (window) xwl_window_attach(window, pPixmap); } static void wayland_move_window(WindowPtr pWin, int x, int y, WindowPtr pSib, VTKind kind) { ScreenPtr pScreen = pWin->drawable.pScreen; ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; I830Ptr pI830 = I830PTR(pScrn); struct xwl_context *context = pI830->wayland_data; struct xwl_window *window; pScreen->MoveWindow = context->MoveWindow; (*pScreen->MoveWindow)(pWin, x, y, pSib, kind); context->MoveWindow = pScreen->MoveWindow; pScreen->MoveWindow = wayland_move_window; window = dixLookupPrivate(&pWin->devPrivates, wayland_window_private_key); if (window == NULL) return; wl_surface_map(window->surface, window->window->drawable.x, window->window->drawable.y, window->window->drawable.width, window->window->drawable.height); wl_compositor_commit(context->compositor, 16); } void wayland_init(ScrnInfoPtr pScrn) { ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex]; I830Ptr pI830 = I830PTR(pScrn); struct xwl_context *context = pI830->wayland_data; context->CreateWindow = pScreen->CreateWindow; pScreen->CreateWindow = wayland_create_window; context->RealizeWindow = pScreen->RealizeWindow; pScreen->RealizeWindow = wayland_realize_window; context->UnrealizeWindow = pScreen->UnrealizeWindow; pScreen->UnrealizeWindow = wayland_unrealize_window; context->SetWindowPixmap = pScreen->SetWindowPixmap; pScreen->SetWindowPixmap = wayland_set_window_pixmap; context->MoveWindow = pScreen->MoveWindow; pScreen->MoveWindow = wayland_move_window; } Bool wayland_pre_init(ScrnInfoPtr pScrn, Bool rootless) { struct xwl_context *context; I830Ptr pI830 = I830PTR(pScrn); pI830->have_gem = TRUE; i830_init_bufmgr(pScrn); context = malloc(sizeof *context); if (context == NULL) { ErrorF("malloc failed\n"); return FALSE; } memset(context, 0, sizeof *context); context->pScrn = pScrn; context->rootless = rootless; context->display = wl_display_create(socket_name, sizeof socket_name); if (context->display == NULL) { ErrorF("wl_display_create failed\n"); return FALSE; } xf86CrtcConfigInit(pScrn, &config_funcs); xf86CrtcSetSizeRange(pScrn, 320, 200, 8192, 8192); /* Set up listener so we'll catch all events. */ wl_display_add_global_listener(context->display, global_handler, context); /* Process connection events. */ wl_display_iterate(context->display, WL_DISPLAY_READABLE); context->fd = wl_display_get_fd(context->display, source_update, context); pI830->wayland_data = context; AddGeneralSocket(context->fd); RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, context); xf86InitialConfiguration(pScrn, TRUE); return TRUE; } void wayland_post_damage(I830Ptr pI830) { struct xwl_context *context = pI830->wayland_data; /* FIXME: Need a list of surfaces with damage. */ wl_surface_damage(context->surface, 0, 0, 10, 10); wl_compositor_commit(context->compositor, 16); while (context->mask & WL_DISPLAY_WRITABLE) wl_display_iterate(context->display, WL_DISPLAY_WRITABLE); }