/* Wayland compositor running on top of an X server. Copyright (C) 2022 to various contributors. This file is part of 12to11. 12to11 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 12to11 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with 12to11. If not, see . */ #include #include #include #include #include #include #include #include "compositor.h" struct _RootWindowSelection { /* The next and last event selection records in this chain. */ RootWindowSelection *next, *last; /* The event mask one piece of code has selected for. */ unsigned long event_mask; }; /* Events that are being selected for on the root window. */ static RootWindowSelection root_window_events; void XLListFree (XLList *list, void (*item_func) (void *)) { XLList *tem, *last; tem = list; while (tem) { last = tem; tem = tem->next; if (item_func) item_func (last->data); XLFree (last); } } XLList * XLListRemove (XLList *list, void *data) { XLList *tem, **last; last = &list; while (*last) { tem = *last; if (tem->data == data) { *last = tem->next; XLFree (tem); } else last = &tem->next; } return list; } XLList * XLListPrepend (XLList *list, void *data) { XLList *tem; tem = XLMalloc (sizeof *tem); tem->data = data; tem->next = list; return tem; } /* List of XIDs (not pointers). */ void XIDListFree (XIDList *list, void (*item_func) (XID)) { XIDList *tem, *last; tem = list; while (tem) { last = tem; tem = tem->next; if (item_func) item_func (last->data); XLFree (last); } } XIDList * XIDListRemove (XIDList *list, XID resource) { XIDList *tem, **last; last = &list; while (*last) { tem = *last; if (tem->data == resource) { *last = tem->next; XLFree (tem); } else last = &tem->next; } return list; } XIDList * XIDListPrepend (XIDList *list, XID resource) { XIDList *tem; tem = XLMalloc (sizeof *tem); tem->data = resource; tem->next = list; return tem; } /* Hash tables between XIDs and arbitrary data. */ XLAssocTable * XLCreateAssocTable (int size) { XLAssocTable *table; XLAssoc *buckets; table = XLMalloc (sizeof *table); buckets = XLCalloc (size, sizeof *buckets); table->buckets = buckets; table->size = size; while (--size >= 0) { /* Initialize each bucket with the sentinel node. */ buckets->prev = buckets; buckets->next = buckets; buckets++; } return table; } static void Insque (XLAssoc *velem, XLAssoc *vprev) { XLAssoc *elem, *prev, *next; elem = velem; prev = vprev; next = prev->next; prev->next = elem; if (next) next->prev = elem; elem->next = next; elem->prev = prev; } static void Remque (XLAssoc *velem) { XLAssoc *elem, *prev, *next; elem = velem; next = elem->next; prev = elem->prev; if (next) next->prev = prev; if (prev) prev->next = next; } void XLMakeAssoc (XLAssocTable *table, XID x_id, void *data) { int hash; XLAssoc *bucket, *entry, *new_entry; hash = x_id % table->size; bucket = &table->buckets[hash]; entry = bucket->next; if (entry != bucket) { /* Bucket isn't empty, start searching. */ for (; entry != bucket; entry = entry->next) { if (entry->x_id == x_id) { entry->data = data; return; } if (entry->x_id > x_id) break; } } /* Insert new_entry immediately before entry. */ new_entry = XLMalloc (sizeof *new_entry); new_entry->x_id = x_id; new_entry->data = data; Insque (new_entry, entry->prev); } void * XLLookUpAssoc (XLAssocTable *table, XID x_id) { int hash; XLAssoc *bucket, *entry; hash = x_id % table->size; bucket = &table->buckets[hash]; entry = bucket->next; for (; entry != bucket; entry = entry->next) { if (entry->x_id == x_id) return entry->data; if (entry->x_id > x_id) return NULL; } return NULL; } void XLDeleteAssoc (XLAssocTable *table, XID x_id) { int hash; XLAssoc *bucket, *entry; hash = x_id % table->size; bucket = &table->buckets[hash]; entry = bucket->next; for (; entry != bucket; entry = entry->next) { if (entry->x_id == x_id) { Remque (entry); XLFree (entry); return; } if (entry->x_id > x_id) return; } } void XLDestroyAssocTable (XLAssocTable *table) { int i; XLAssoc *bucket, *entry, *entry_next; for (i = 0; i < table->size; i++) { bucket = &table->buckets[i]; for (entry = bucket->next; entry != bucket; entry = entry_next) { entry_next = entry->next; XLFree (entry); } } XLFree (table->buckets); XLFree (table); } void XLAssert (Bool condition) { if (!condition) abort (); } void XLScaleRegion (pixman_region32_t *dst, pixman_region32_t *src, float scale_x, float scale_y) { int nrects, i; pixman_box32_t *src_rects; pixman_box32_t *dst_rects; if (scale_x == 1.0f && scale_y == 1.0f) { pixman_region32_copy (dst, src); return; } src_rects = pixman_region32_rectangles (src, &nrects); if (nrects < 128) dst_rects = alloca (nrects * sizeof *dst_rects); else dst_rects = XLMalloc (nrects * sizeof *dst_rects); for (i = 0; i < nrects; ++i) { dst_rects[i].x1 = floor (src_rects[i].x1 * scale_x); dst_rects[i].x2 = ceil (src_rects[i].x2 * scale_x); dst_rects[i].y1 = floor (src_rects[i].y1 * scale_y); dst_rects[i].y2 = ceil (src_rects[i].y2 * scale_y); } pixman_region32_fini (dst); pixman_region32_init_rects (dst, dst_rects, nrects); if (nrects >= 128) XLFree (dst_rects); } int XLOpenShm (void) { char name[sizeof "SharedBufferXXXXXXXX"]; int fd; unsigned int i; i = 0; while (i <= 0xffffffff) { sprintf (name, "SharedBuffer%x", i); fd = shm_open (name, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd >= 0) { shm_unlink (name); return fd; } if (errno == EEXIST) ++i; else { perror ("shm_open"); exit (1); } } return -1; } static Bool ServerTimePredicate (Display *display, XEvent *event, XPointer arg) { return (event->type == PropertyNotify && event->xproperty.window == selection_transfer_window && event->xproperty.atom == _XL_SERVER_TIME_ATOM); } Time XLGetServerTimeRoundtrip (void) { XEvent event; XChangeProperty (compositor.display, selection_transfer_window, _XL_SERVER_TIME_ATOM, XA_ATOM, 32, PropModeReplace, (unsigned char *) &_XL_SERVER_TIME_ATOM, 1); XIfEvent (compositor.display, &event, ServerTimePredicate, NULL); return event.xproperty.time; } static void ReselectRootWindowInput (void) { unsigned long effective; RootWindowSelection *record; effective = NoEventMask; record = root_window_events.next; if (!record) return; while (record != &root_window_events) { effective |= record->event_mask; record = record->next; } XSelectInput (compositor.display, DefaultRootWindow (compositor.display), effective); } RootWindowSelection * XLSelectInputFromRootWindow (unsigned long event_mask) { RootWindowSelection *selection; /* This lets different pieces of code select for input from the root window without clobbering eachothers event masks. */ selection = XLMalloc (sizeof *selection); /* If the global chain has not yet been initialized, initialize it now. */ if (!root_window_events.next) { root_window_events.next = &root_window_events; root_window_events.last = &root_window_events; } /* Link this onto the chain of events being selected for on the root window. */ selection->next = root_window_events.next; selection->last = &root_window_events; root_window_events.next->last = selection; root_window_events.next = selection; /* Set the event mask. */ selection->event_mask = event_mask; /* Actually select for events. */ ReselectRootWindowInput (); return selection; } void XLDeselectInputFromRootWindow (RootWindowSelection *key) { key->last->next = key->next; key->next->last = key->last; XLFree (key); ReselectRootWindowInput (); }