parent
525e4c0bea
commit
b2d71852fd
@ -1,3 +1 @@ |
|||||||
SUBDIRS = wayland compositor clients data
|
SUBDIRS = compositor clients data
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
|
|
||||||
|
@ -1,130 +1,6 @@ |
|||||||
What is Wayland |
Wayland Demos |
||||||
|
|
||||||
Wayland is a project to define a protocol for a compositor to talk to |
This repository contains a few demos application for the Wayland |
||||||
its clients as well as a library implementation of the protocol. The |
project. There's a sample compositor that can run on KMS, under X11 |
||||||
compositor can be a standalone display server running on Linux kernel |
or under another Wayland compositor and there's a handful of simple |
||||||
modesetting and evdev input devices, an X applications, or a wayland |
clients that demonstrate various aspects of Wayland: |
||||||
client itself. The clients can be traditional applications, X servers |
|
||||||
(rootless or fullscreen) or other display servers. |
|
||||||
|
|
||||||
The wayland protocol is essentially only about input handling and |
|
||||||
buffer management. The compositor receives input events and forwards |
|
||||||
them to the relevant client. The clients creates buffers and renders |
|
||||||
into them and notifies the compositor when it needs to redraw. The |
|
||||||
protocol also handles drag and drop, selections, window management and |
|
||||||
other interactions that must go throught the compositor. However, the |
|
||||||
protocol does not handle rendering, which is one of the features that |
|
||||||
makes wayland so simple. All clients are expected to handle rendering |
|
||||||
themselves, typically through cairo or OpenGL. |
|
||||||
|
|
||||||
The wayland repository includes a compositor and a few clients, but |
|
||||||
both the compositor and clients are essentially test cases. |
|
||||||
|
|
||||||
|
|
||||||
Building Instructions |
|
||||||
|
|
||||||
The instructions below assume some familiarity with git and building |
|
||||||
and running experimental software. And be prepared that this project |
|
||||||
isn't at all useful right now, it's still very much a prototype. When |
|
||||||
the instructions suggest to clone a git repo, you can of course just |
|
||||||
add a remote and fetch instead, if you have a clone of that repo |
|
||||||
around already. I usually install all software I'm working on into |
|
||||||
$HOME/install, so that's what I'll use in the instructions below, but |
|
||||||
you can use your favorite directory of course or install over your |
|
||||||
system copy (pass --prefix=/usr --sysconfdir=/etc, generally). |
|
||||||
|
|
||||||
|
|
||||||
Modesetting |
|
||||||
|
|
||||||
At this point, kernel modesetting is upstream for Intel, AMD and |
|
||||||
nVidia chipsets. Most distributions ship with kernel modesetting |
|
||||||
enabled by default and will work with Wayland out of the box. The |
|
||||||
modesetting driver must also support the page flip ioctl, which only |
|
||||||
the intel driver does at this point. |
|
||||||
|
|
||||||
|
|
||||||
Building mesa |
|
||||||
|
|
||||||
Wayland uses the mesa EGL stack, and all extensions required to run |
|
||||||
EGL on KMS are now upstream on the master branch. The 7.9 release of |
|
||||||
mesa will have all these extensions, but for now you'll need to build |
|
||||||
mesa master: |
|
||||||
|
|
||||||
$ git clone git://anongit.freedesktop.org/mesa/mesa |
|
||||||
$ cd mesa |
|
||||||
$ ./configure --prefix=$HOME/install --enable-egl --enable-gles2 |
|
||||||
$ make && make install |
|
||||||
|
|
||||||
If you're using an intel chipset, it's best to also pass |
|
||||||
--disable-gallium to ./configure, since otherwise libEGL will try to |
|
||||||
load the gallium sw rasterizer before loading the Intel DRI driver. |
|
||||||
|
|
||||||
|
|
||||||
libxkbcommon |
|
||||||
|
|
||||||
Wayland needs libxkbcommon for translating evdev keycodes to keysyms. |
|
||||||
There's a couple of repos around, and we're trying to consolidate the |
|
||||||
development, but for wayland you'll need the repo from my git |
|
||||||
repository. For this you'll need development packages for xproto, |
|
||||||
kbproto and libX11. |
|
||||||
|
|
||||||
$ git clone git://people.freedesktop.org/~krh/libxkbcommon.git |
|
||||||
$ cd libxkbcommon/ |
|
||||||
$ ./autogen.sh --prefix=$HOME/install |
|
||||||
$ make && make install |
|
||||||
|
|
||||||
|
|
||||||
cairo-gl |
|
||||||
|
|
||||||
The Wayland clients render using cairo-gl, which is an experimental |
|
||||||
cairo backend. It has been available since cairo 1.10. Unless your |
|
||||||
distribution ships cairo with the gl backend enabled, you'll need to |
|
||||||
compile your own version of cairo: |
|
||||||
|
|
||||||
$ git clone git://anongit.freedesktop.org/cairo |
|
||||||
$ cd cairo |
|
||||||
$ ./autogen.sh --prefix=$HOME/install --enable-gl |
|
||||||
$ make && make install |
|
||||||
|
|
||||||
|
|
||||||
Wayland |
|
||||||
|
|
||||||
With mesa and libxkbcommon in place, we can checkout and build |
|
||||||
Wayland. Aside from mesa, Wayland needs development packages for |
|
||||||
gdk-pixbuf-2.0, libudev, libdrm, xcb-dri2, xcb-fixes (for X |
|
||||||
compositor) cairo-gl, glib-2.0, gdk-2.0 (for poppler) and |
|
||||||
poppler-glib: |
|
||||||
|
|
||||||
$ git clone git://people.freedesktop.org/~krh/wayland |
|
||||||
$ ./autogen.sh --prefix=$HOME/install |
|
||||||
$ make && make install |
|
||||||
|
|
||||||
Installing into a non-/usr prefix is fine, but the 70-wayland.rules |
|
||||||
udev rule file has to be installed in /etc/udev/rules.d. Once |
|
||||||
installed, either reboot or run |
|
||||||
|
|
||||||
$ sudo udevadm trigger --subsystem-match=drm --subsystem-match=input |
|
||||||
|
|
||||||
to make udev label the devices wayland will use. |
|
||||||
|
|
||||||
If DISPLAY is set, the wayland compositor will run under X in a window |
|
||||||
and take input from X. Otherwise it will run on the KMS framebuffer |
|
||||||
and take input from evdev devices. Pick a background image that you |
|
||||||
like and copy it to the Wayland source directory as background.jpg or |
|
||||||
use the -b command line option: |
|
||||||
|
|
||||||
$ ./wayland-system-compositor -b my-image.jpg |
|
||||||
|
|
||||||
To run clients, switch to a different VT and run the client from |
|
||||||
there. Or run it under X and start up the clients from a terminal |
|
||||||
window. There are a few demo clients available, but they are all |
|
||||||
pretty simple and mostly for testing specific features in the wayland |
|
||||||
protocol: 'terminal' is a simple terminal emulator, not very compliant |
|
||||||
at all, but works well enough for bash |
|
||||||
|
|
||||||
'flower' moves a flower around the screen, testing the frame protocol |
|
||||||
'gears' glxgears, but for wayland, currently broken |
|
||||||
'image' loads the image files passed on the command line and shows them |
|
||||||
|
|
||||||
'view' does the same for pdf files, but needs file URIs |
|
||||||
(file:///path/to/pdf) |
|
||||||
|
@ -1,246 +0,0 @@ |
|||||||
Core wayland protocol |
|
||||||
|
|
||||||
- surface.set_grab_mode(GRAB_OWNER_EVENTS vs GRAB_SURFACE_EVENTS), to |
|
||||||
make menus work right: click and drag in a menubar grabs the |
|
||||||
pointer to the menubar (which we need for detecting motion into |
|
||||||
another menu item), but we need events for the popup menu surface |
|
||||||
as well. |
|
||||||
|
|
||||||
- The message format has to include information about number of fds |
|
||||||
in the message so we can skip a message correctly. Or we should |
|
||||||
just give up on trying to recover from unknown messages. We need |
|
||||||
to make sure you never get a message from an interface you don't |
|
||||||
know about (using per-client id space and subscribe) or include |
|
||||||
information on number of fds, so marshalling logic can skip. |
|
||||||
|
|
||||||
- generate pointer_focus (and drag focus) on raise/lower, move |
|
||||||
windows, all kinds of changes in surface stacking. |
|
||||||
|
|
||||||
- glyph cache |
|
||||||
|
|
||||||
buffer = drm.create_buffer(); /* buffer with stuff in it */ |
|
||||||
|
|
||||||
cache.upload(buffer, x, y, width, height, int hash) |
|
||||||
|
|
||||||
drm.buffer: id, name, stride etc /* event to announce cache buffer */ |
|
||||||
|
|
||||||
cache.image: hash, buffer, x, y, stride /* event to announce |
|
||||||
* location in cache */ |
|
||||||
|
|
||||||
cache.reject: hash /* no upload for you! */ |
|
||||||
|
|
||||||
cache.retire: buffer /* cache has stopped using buffer, please |
|
||||||
* reupload whatever you had in that buffer */ |
|
||||||
|
|
||||||
- DnD issues: |
|
||||||
|
|
||||||
Root window must send NULL type (to decline drop) or |
|
||||||
x-wayland/root-something type if the source offers that. But the |
|
||||||
target deletes the drag_offer object when drag.pointer_focus leaves |
|
||||||
the surface... |
|
||||||
|
|
||||||
How do we animate the drag icon back to the drag origin in case of |
|
||||||
a failed drag? |
|
||||||
|
|
||||||
How to handle surfaces from clients that don't know about dnd or |
|
||||||
don't care? Maybe the dnd object should have a |
|
||||||
dnd.register_surface() method so clients can opt-in the surfaces |
|
||||||
that will participate in dnd. Or just assume client is not |
|
||||||
participating until we receive an accept request. |
|
||||||
|
|
||||||
- Selection/copy+paste issues: is it sufficient to only introduce the |
|
||||||
selection offer when a client receives kb focus? Or maybe it is |
|
||||||
actually a security feature? Clipboard manager in server for |
|
||||||
retained selections? |
|
||||||
|
|
||||||
- Pointer image issue: |
|
||||||
|
|
||||||
- A direct touch input device (eg touch screen) doesn't have a |
|
||||||
pointer; indicate that somehow. |
|
||||||
|
|
||||||
- Cursor themes, tie in with glyph/image cache. |
|
||||||
|
|
||||||
- Discard buffer, as in "wayland discarded your buffer, it's no |
|
||||||
longer visible, you can stop updating it now.", reattach, as in "oh |
|
||||||
hey, I'm about to show your buffer that I threw away, what was it |
|
||||||
again?". for wayland system compositor vt switcing, for example, |
|
||||||
to be able to throw away the surfaces in the session we're |
|
||||||
switching away from. for minimized windows that we don't want live |
|
||||||
thumb nails for. etc. |
|
||||||
|
|
||||||
- Import relevants bits from EWMH spec, stuff like window title, |
|
||||||
window class, app groups, icons, lower window, need attention, |
|
||||||
fullscreen (maybe different types of fullscreen). |
|
||||||
|
|
||||||
- Per client id space. Each client has an entire 32 bit id namespace |
|
||||||
to itself. On the server side, each struct wl_client has an object |
|
||||||
hash table. Object announcements use a server id space and clients |
|
||||||
must respond with subscribe request with a client id for the |
|
||||||
object. Part of wl_proxy_create_for_id(): |
|
||||||
|
|
||||||
wl_display_subscribe(display, id, new_id, my_version); |
|
||||||
|
|
||||||
or maybe |
|
||||||
|
|
||||||
wl_display_bind(display, id, new_id, my_version); |
|
||||||
|
|
||||||
Fixes a few things: |
|
||||||
|
|
||||||
- Maps the global object into the client id space, lets client |
|
||||||
allocate the id. All ids are allocated by the client this way, |
|
||||||
which fixes the range protocol problem. |
|
||||||
|
|
||||||
- Tells the server that the client is interested in events from |
|
||||||
the object. Lets the server know that a client participates in a |
|
||||||
certain protocol (like drag and drop), so the server can account |
|
||||||
for whether or not the client is expected to reply |
|
||||||
|
|
||||||
- Server emits initial object state event(s) in reponse to |
|
||||||
receiving the subscribe request. Introduces an extra round trip |
|
||||||
at initialization time, but the server will still announces all |
|
||||||
objects in one burst and the client can subscribe in a burst as |
|
||||||
well. |
|
||||||
|
|
||||||
- Separates client resources, since each client will have it's own |
|
||||||
hash table. It's not longer possible to guess the id of another |
|
||||||
surface and access it. |
|
||||||
|
|
||||||
- Server must track the client id for each client an object is |
|
||||||
exposed to. In some cases we know this (a surface is always |
|
||||||
only owned by one client), in other cases it provides a way to |
|
||||||
track who's interested in the object events. For input device |
|
||||||
events, we can look up the client name when it receives pointer |
|
||||||
focus or keyboard focus and cache it in the device. |
|
||||||
|
|
||||||
- Server must know which id to send when passing object references |
|
||||||
in events. We could say that any object we're passing to a |
|
||||||
client must have a server id, and each client has a server id -> |
|
||||||
client id hash. |
|
||||||
|
|
||||||
- When a surface is the size of the screen and on top, we can set the |
|
||||||
scanout buffer to that surface directly. Like compiz unredirect |
|
||||||
top-level window feature. Except it won't have any protocol state |
|
||||||
side-effects and the client that owns the surface won't know. We |
|
||||||
lose control of updates. Should work well for X server root window |
|
||||||
under wayland. Should be possible for yuv overlays as well. |
|
||||||
|
|
||||||
- what about cursors then? maybe use hw cursors if the cursor |
|
||||||
satisfies hw limitations (64x64, only one cursor), switch to |
|
||||||
composited cursors if not. |
|
||||||
|
|
||||||
- clients needs to allocate the surface to be suitable for |
|
||||||
scanout, which they can do whenever they go fullscreen. |
|
||||||
|
|
||||||
- multihead, screen geometry and crtc layout protocol, hotplug, lcd |
|
||||||
subpixel info |
|
||||||
|
|
||||||
- a wayland settings protocol to tell clients about themes (icons, |
|
||||||
cursors, widget themes), fonts details (family, hinting |
|
||||||
preferences) etc. Just send all settings at connect time, send |
|
||||||
updates when a setting change. Getting a little close to gconf |
|
||||||
here, but could be pretty simple: |
|
||||||
|
|
||||||
interface "settings": |
|
||||||
event int_value(string name, int value) |
|
||||||
event string_value(string name, string value) |
|
||||||
|
|
||||||
but maybe it's better to just require that clients get that from |
|
||||||
somewhere else (gconf/dbus). |
|
||||||
|
|
||||||
- input device discovery, hotplug |
|
||||||
|
|
||||||
- Advertise axes as part of the discovery, use something like |
|
||||||
"org.wayland.input.x" to identify the axes. |
|
||||||
|
|
||||||
- keyboard state, layout events at connect time and when it |
|
||||||
changes, keyboard leds |
|
||||||
|
|
||||||
- relative events |
|
||||||
|
|
||||||
- multi touch? |
|
||||||
|
|
||||||
- synaptics, 3-button emulation, scim |
|
||||||
|
|
||||||
- drm bo access control, authentication, flink_to |
|
||||||
|
|
||||||
- Range protocol may not be sufficient... if a server cycles through |
|
||||||
2^32 object IDs we don't have a way to handle wrapping. And since |
|
||||||
we hand out a range of 256 IDs to each new clients, we're just |
|
||||||
talking about 2^24 clients. That's 31 years with a new client |
|
||||||
every minute... Maybe just use bigger ranges, then it's feasible |
|
||||||
to track and garbage collect them when a client dies. |
|
||||||
|
|
||||||
- Add protocol to let applications specify the effective/logical |
|
||||||
surface rectangle, that is, the edge of the window, ignoring drop |
|
||||||
shadows and other padding. The compositor needs this for snapping |
|
||||||
and constraining window motion. Also, maybe communicate the opaque |
|
||||||
region of the window (or just a conservative, simple estimate), to |
|
||||||
let the compositor reduce overdraw. |
|
||||||
|
|
||||||
- multi gpu, needs queue and seqno to wait on in requests |
|
||||||
|
|
||||||
Clients and ports |
|
||||||
|
|
||||||
- port gtk+ |
|
||||||
|
|
||||||
- draw window decorations in gtkwindow.c |
|
||||||
|
|
||||||
- Details about pointer grabs. wayland doesn't have active grabs, |
|
||||||
menus will behave subtly different. Under X, clicking a menu |
|
||||||
open grabs the pointer and clicking outside the window pops down |
|
||||||
the menu and swallows the click. without active grabs we can't |
|
||||||
swallow the click. I'm sure there much more... |
|
||||||
|
|
||||||
- Port Qt? There's already talk about this on the list. |
|
||||||
|
|
||||||
- X on Wayland |
|
||||||
|
|
||||||
- move most of the code from xf86-video-intel into a Xorg wayland |
|
||||||
module. |
|
||||||
|
|
||||||
- don't ask KMS for available output and modes, use the info from |
|
||||||
the wayland server. then stop mooching off of drmmode.c. |
|
||||||
|
|
||||||
- map multiple wayland input devices to MPX in Xorg. |
|
||||||
|
|
||||||
- rootless; avoid allocating and setting the front buffer, draw |
|
||||||
window decorations in the X server (!), how to map input? |
|
||||||
|
|
||||||
- gnome-shell as a wayland session compositor |
|
||||||
|
|
||||||
- runs as a client of the wayland session compositor, uses |
|
||||||
clutter+egl on wayland |
|
||||||
|
|
||||||
- talks to an Xorg server as the compositing and window manager |
|
||||||
for that server and renders the output to a wayland surface. |
|
||||||
the Xorg server should be modified to take input from the system |
|
||||||
compositor through gnome-shell, but not allocate a front buffer. |
|
||||||
|
|
||||||
- make gnome-shell itself a nested wayland server and allow native |
|
||||||
wayland clients to connect and can native wayland windows with |
|
||||||
the windows from the X server. |
|
||||||
|
|
||||||
- qemu as a wayland client; session surface as X case |
|
||||||
|
|
||||||
- qemu has too simple acceleration, so a Wayland backend like the |
|
||||||
SDL/VNC ones it has now is trivial. |
|
||||||
|
|
||||||
- paravirt: forward wayland screen info as mmio, expose gem ioctls as mmio |
|
||||||
|
|
||||||
- mapping vmem is tricky, should try to only use ioctl (pwrite+pread) |
|
||||||
|
|
||||||
- not useful for Windows without a windows paravirt driver. |
|
||||||
|
|
||||||
- two approaches: 1) do a toplevel qemu window, or 2) expose a |
|
||||||
wayland server in the guest that forwards to the host wayland |
|
||||||
server, ie a "remote" compositor, but with the gem buffers |
|
||||||
shared. could do a wl_connection directly on mmio memory, with |
|
||||||
head and tail pointers. use an alloc_head register to indicate |
|
||||||
desired data to write, if it overwrites tail, block guest. just |
|
||||||
a socket would be easier. |
|
||||||
|
|
||||||
- moblin as a wayland compositor |
|
||||||
|
|
||||||
- clutter as a wayland compositors |
|
||||||
|
|
||||||
- argh, mutter |
|
@ -1,5 +0,0 @@ |
|||||||
libtool.m4 |
|
||||||
ltoptions.m4 |
|
||||||
ltsugar.m4 |
|
||||||
ltversion.m4 |
|
||||||
lt~obsolete.m4 |
|
@ -1,483 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<protocol name="wayland"> |
|
||||||
|
|
||||||
<!-- The core global object. This is a special singleton object. |
|
||||||
It is used for internal wayland protocol features. --> |
|
||||||
<interface name="display" version="1"> |
|
||||||
<!-- sync is an just an echo, which will reply with a sync event. |
|
||||||
Since requests are handled in-order, this can be used as a |
|
||||||
barrier to ensure all previous requests have ben handled. |
|
||||||
The key argument can be used to correlate between multiple |
|
||||||
sync invocations. --> |
|
||||||
<request name="sync"> |
|
||||||
<arg name="key" type="uint"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Request notification when the next frame is displayed. |
|
||||||
Useful for throttling redrawing operations, and driving |
|
||||||
animations. The notification will only be posted for one |
|
||||||
frame unless requested again. --> |
|
||||||
<request name="frame"> |
|
||||||
<arg name="key" type="uint"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- A request addressed a non-existent object id. This is |
|
||||||
tyipcally a fatal error. --> |
|
||||||
<event name="invalid_object"> |
|
||||||
<arg name="object_id" type="uint"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- A request tried to invoke an opcode out of range. This is |
|
||||||
typically a fatal error. --> |
|
||||||
<event name="invalid_method"> |
|
||||||
<arg name="object_id" type="uint"/> |
|
||||||
<arg name="opcode" type="uint"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- A request has failed due to an out of memory error. --> |
|
||||||
<event name="no_memory"/> |
|
||||||
|
|
||||||
<!-- Notify the client of global objects. These are objects that |
|
||||||
are created by the server. Globals are published on the |
|
||||||
initial client connection sequence, upon device hotplugs, |
|
||||||
device disconnects, reconfiguration or other events. The |
|
||||||
server will always announce an object before the object sends |
|
||||||
out events. --> |
|
||||||
<event name="global"> |
|
||||||
<arg name="id" type="new_id" interface="object"/> |
|
||||||
<arg name="name" type="string"/> |
|
||||||
<arg name="version" type="uint"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- Internal, deprecated, and will be changed. This is an object |
|
||||||
IDs range that is used by the client to allocate object IDs |
|
||||||
in "new_id" type arguments. The server sends range |
|
||||||
allocations to the client before the next range is about to |
|
||||||
be depleted. --> |
|
||||||
<event name="range"> |
|
||||||
<arg name="base" type="uint"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- A reply to the frame or sync request. The key is the one |
|
||||||
used in the request. time is in millisecond units, and |
|
||||||
denotes the time when the frame was posted on the |
|
||||||
display. time can be used to estimaate frame rate, determine |
|
||||||
how much to advance animations and compensate for jitter. --> |
|
||||||
<event name="key"> |
|
||||||
<arg name="key" type="uint"/> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
</event> |
|
||||||
</interface> |
|
||||||
|
|
||||||
|
|
||||||
<!-- A compositor. This object is a global. The compositor is in |
|
||||||
charge of combining the contents of multiple surfaces into one |
|
||||||
displayable output. --> |
|
||||||
<interface name="compositor" version="1"> |
|
||||||
<!-- Factory request for a surface objects. A surface is akin to a |
|
||||||
window. --> |
|
||||||
<request name="create_surface"> |
|
||||||
<arg name="id" type="new_id" interface="surface"/> |
|
||||||
</request> |
|
||||||
</interface> |
|
||||||
|
|
||||||
|
|
||||||
<!-- drm support. This object is created by the server and published |
|
||||||
using the display's global event. --> |
|
||||||
<interface name="drm" version="1"> |
|
||||||
<!-- Call this request with the magic received from drmGetMagic(). |
|
||||||
It will be passed on to the drmAuthMagic() or |
|
||||||
DRIAuthConnection() call. This authentication must be |
|
||||||
completed before create_buffer could be used. --> |
|
||||||
<request name="authenticate"> |
|
||||||
<arg name="id" type="uint"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Create a wayland buffer for the named DRM buffer. The DRM |
|
||||||
surface must have a name using the flink ioctl --> |
|
||||||
<request name="create_buffer"> |
|
||||||
<arg name="id" type="new_id" interface="buffer"/> |
|
||||||
<arg name="name" type="uint"/> |
|
||||||
<arg name="width" type="int"/> |
|
||||||
<arg name="height" type="int"/> |
|
||||||
<arg name="stride" type="uint"/> |
|
||||||
<arg name="visual" type="object" interface="visual"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Notification of the path of the drm device which is used by |
|
||||||
the server. The client should use this device for creating |
|
||||||
local buffers. Only buffers created from this device should |
|
||||||
be be passed to the server using this drm object's |
|
||||||
create_buffer request. --> |
|
||||||
<event name="device"> |
|
||||||
<arg name="name" type="string"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- Raised if the authenticate request succeeded --> |
|
||||||
<event name="authenticated"/> |
|
||||||
</interface> |
|
||||||
|
|
||||||
|
|
||||||
<!-- Shared memory support --> |
|
||||||
<interface name="shm" version="1"> |
|
||||||
<!-- Transfer a shm buffer to the server. The allocated buffer |
|
||||||
would include at least stride * height bytes starting at the |
|
||||||
beginning of fd. The file descriptor is transferred over the |
|
||||||
socket using AF_UNIX magical features. width, height, stride |
|
||||||
and visual describe the respective properties of the pixel |
|
||||||
data contained in the buffer. --> |
|
||||||
<request name="create_buffer"> |
|
||||||
<arg name="id" type="new_id" interface="buffer"/> |
|
||||||
<arg name="fd" type="fd"/> |
|
||||||
<arg name="width" type="int"/> |
|
||||||
<arg name="height" type="int"/> |
|
||||||
<arg name="stride" type="uint"/> |
|
||||||
<arg name="visual" type="object" interface="visual"/> |
|
||||||
</request> |
|
||||||
</interface> |
|
||||||
|
|
||||||
|
|
||||||
<!-- A pixel buffer. Created using the drm, shm or similar objects. |
|
||||||
It has a size, visual and contents, but not a location on the |
|
||||||
screen --> |
|
||||||
<interface name="buffer" version="1"> |
|
||||||
<!-- Abandon a buffer. This will invalidate the object id. --> |
|
||||||
<request name="destroy" type="destructor"/> |
|
||||||
</interface> |
|
||||||
|
|
||||||
<interface name="shell" version="1"> |
|
||||||
<request name="move"> |
|
||||||
<arg name="surface" type="object" interface="surface"/> |
|
||||||
<arg name="input_device" type="object" interface="input_device"/> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<enum name="resize"> |
|
||||||
<entry name="none" value="0"/> |
|
||||||
<entry name="top" value="1"/> |
|
||||||
<entry name="bottom" value="2"/> |
|
||||||
<entry name="left" value="4"/> |
|
||||||
<entry name="top_left" value="5"/> |
|
||||||
<entry name="bottom_left" value="6"/> |
|
||||||
<entry name="right" value="8"/> |
|
||||||
<entry name="top_right" value="9"/> |
|
||||||
<entry name="bottom_right" value="10"/> |
|
||||||
</enum> |
|
||||||
|
|
||||||
<request name="resize"> |
|
||||||
<arg name="surface" type="object" interface="surface"/> |
|
||||||
<arg name="input_device" type="object" interface="input_device"/> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<!-- edges is an enum, need to get the values in here --> |
|
||||||
<arg name="edges" type="uint"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<request name="create_drag"> |
|
||||||
<arg name="id" type="new_id" interface="drag"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<request name="create_selection"> |
|
||||||
<arg name="id" type="new_id" interface="selection"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- The configure event asks the client to resize its surface. |
|
||||||
The size is a hint, in the sense that the client is free to |
|
||||||
ignore it if it doesn't resize, pick a smaller size (to |
|
||||||
satisfy aspect ration or resize in steps of NxM pixels). The |
|
||||||
client is free to dismiss all but the last configure event it |
|
||||||
received. --> |
|
||||||
<event name="configure"> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<arg name="edges" type="uint"/> |
|
||||||
<arg name="surface" type="object" interface="surface"/> |
|
||||||
<arg name="width" type="int"/> |
|
||||||
<arg name="height" type="int"/> |
|
||||||
</event> |
|
||||||
</interface> |
|
||||||
|
|
||||||
<interface name="selection" version="1"> |
|
||||||
<!-- Add an offered mime type. Can be called several times to |
|
||||||
offer multiple types, but must be called before 'activate'. --> |
|
||||||
<request name="offer"> |
|
||||||
<arg name="type" type="string"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Can the selection be activated for multiple devices? --> |
|
||||||
<request name="activate"> |
|
||||||
<arg name="input_device" type="object" interface="input_device"/> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Destroy the selection. --> |
|
||||||
<request name="destroy" type="destructor"/> |
|
||||||
|
|
||||||
<!-- Another client pasted the selection, send the mime-type over |
|
||||||
the passed fd. --> |
|
||||||
<event name="send"> |
|
||||||
<arg name="mime_type" type="string"/> |
|
||||||
<arg name="fd" type="fd"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- Another selection became active. --> |
|
||||||
<event name="cancelled"/> |
|
||||||
</interface> |
|
||||||
|
|
||||||
<interface name="selection_offer" version="1"> |
|
||||||
<!-- Called to receive the selection data as the specified type. |
|
||||||
Sends the pipe fd to the compositor, which forwards it to the |
|
||||||
source in the 'send' event --> |
|
||||||
<request name="receive"> |
|
||||||
<arg name="mime_type" type="string"/> |
|
||||||
<arg name="fd" type="fd"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Sent before the keyboard_focus event to announce the types |
|
||||||
offered. One event per offered mime type. A mime type of |
|
||||||
NULL means the selection offer is going away. --> |
|
||||||
<event name="offer"> |
|
||||||
<arg name="type" type="string"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<event name="keyboard_focus"> |
|
||||||
<arg name="input_device" type="object" interface="input_device"/> |
|
||||||
</event> |
|
||||||
</interface> |
|
||||||
|
|
||||||
<interface name="drag" version="1"> |
|
||||||
<!-- Add an offered mime type. Can be called several times to |
|
||||||
offer multiple types, but must be called before 'activate'. --> |
|
||||||
<request name="offer"> |
|
||||||
<arg name="type" type="string"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<request name="activate"> |
|
||||||
<arg name="surface" type="object" interface="surface"/> |
|
||||||
<arg name="input_device" type="object" interface="input_device"/> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Destroy the drag and cancel the session. --> |
|
||||||
<request name="destroy" type="destructor"/> |
|
||||||
|
|
||||||
<!-- Sent when a target accepts pointer_focus or motion events. |
|
||||||
If a target does not accept any of the offered types, type is |
|
||||||
NULL --> |
|
||||||
<event name="target"> |
|
||||||
<arg name="mime_type" type="string"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- Sent when the drag is finished. The final mime type is that |
|
||||||
of the last target event. If that was NULL, no drag target |
|
||||||
was valid when the drag finished, fd is undefined and the |
|
||||||
source should not send data. The event is also sent in case |
|
||||||
a drag source tries to activate a drag after the grab was |
|
||||||
released, in which case mime_type will also be NULL. --> |
|
||||||
<event name="finish"> |
|
||||||
<arg name="fd" type="fd"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<event name="reject"/> |
|
||||||
</interface> |
|
||||||
|
|
||||||
|
|
||||||
<interface name="drag_offer" version="1"> |
|
||||||
<!-- Call to accept the offer of the given type --> |
|
||||||
<request name="accept"> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<arg name="type" type="string"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Called to initiate the drag finish sequence. Sends the pipe |
|
||||||
fd to the compositor, which forwards it to the source in the |
|
||||||
'finish' event --> |
|
||||||
<request name="receive"> |
|
||||||
<arg name="fd" type="fd"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Called to reject a drop --> |
|
||||||
<request name="reject"/> |
|
||||||
|
|
||||||
<!-- Sent before the pointer_focus event to announce the types |
|
||||||
offered. One event per offered mime type. --> |
|
||||||
<event name="offer"> |
|
||||||
<arg name="type" type="string"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- Similar to device::pointer_focus. Sent to potential target |
|
||||||
surfaces to offer drag data. If the device leaves the |
|
||||||
window, the drag stops or the originator cancels the drag, |
|
||||||
this event is sent with the NULL surface, at which point the |
|
||||||
drag object may no longer be valid. --> |
|
||||||
<event name="pointer_focus"> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<arg name="surface" type="object" interface="surface"/> |
|
||||||
<arg name="x" type="int"/> |
|
||||||
<arg name="y" type="int"/> |
|
||||||
<arg name="surface_x" type="int"/> |
|
||||||
<arg name="surface_y" type="int"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- Similar to device::motion. Sent to potential target surfaces |
|
||||||
as the drag pointer moves around in the surface. --> |
|
||||||
<event name="motion"> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<arg name="x" type="int"/> |
|
||||||
<arg name="y" type="int"/> |
|
||||||
<arg name="surface_x" type="int"/> |
|
||||||
<arg name="surface_y" type="int"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- Sent to indicate that the drag is finishing. The last |
|
||||||
motion/pointer_focus event gives the location of the drop. |
|
||||||
Target must respond with the 'receive' request, which sends |
|
||||||
an fd to the source for writing the drag data. --> |
|
||||||
<event name="drop"/> |
|
||||||
</interface> |
|
||||||
|
|
||||||
|
|
||||||
<!-- A surface. This is an image that is displayed on the screen. |
|
||||||
It has a location, size and pixel contents. Similar to a window. --> |
|
||||||
<interface name="surface" version="1"> |
|
||||||
<!-- Deletes the surface and invalidates its object id. --> |
|
||||||
<request name="destroy" type="destructor"/> |
|
||||||
|
|
||||||
<!-- Copy the contents of a buffer into this surface. The x and y |
|
||||||
arguments specify the location of the new buffers upper left |
|
||||||
corner, relative to the old buffers upper left corner. --> |
|
||||||
<request name="attach"> |
|
||||||
<arg name="buffer" type="object" interface="buffer"/> |
|
||||||
<arg name="x" type="int"/> |
|
||||||
<arg name="y" type="int"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Make the surface visible as a toplevel window. --> |
|
||||||
<request name="map_toplevel"/> |
|
||||||
|
|
||||||
<!-- Map the surface relative to an existing surface. The x and y |
|
||||||
arguments specify the locations of the upper left corner of |
|
||||||
the surface relative to the upper left corner of the parent |
|
||||||
surface. The flags argument controls overflow/clipping |
|
||||||
behaviour when the surface would intersect a screen edge, |
|
||||||
panel or such. And possibly whether the offset only |
|
||||||
determines the initial position or if the surface is locked |
|
||||||
to that relative position during moves. --> |
|
||||||
<request name="map_transient"> |
|
||||||
<arg name="parent" type="object" interface="surface"/> |
|
||||||
<arg name="x" type="int"/> |
|
||||||
<arg name="y" type="int"/> |
|
||||||
<arg name="flags" type="uint"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Map the surface as a fullscreen surface. There are a number |
|
||||||
of options here: on which output? if the surface size doesn't |
|
||||||
match the output size, do we scale, change resolution, or add |
|
||||||
black borders? is that something the client controls? what |
|
||||||
about transient surfaces, do they float on top of the |
|
||||||
fullscreen? what if there's already a fullscreen surface on |
|
||||||
the output, maybe you can only go fullscreen if you're |
|
||||||
active? --> |
|
||||||
<request name="map_fullscreen"/> |
|
||||||
|
|
||||||
<!-- Notify the server that the attached buffer's contents have |
|
||||||
changed, and request a redraw. The arguments allow you to |
|
||||||
damage only a part of the surface, but the server may ignore |
|
||||||
it and redraw the entire contents of the surface. To |
|
||||||
describe a more complicated area of damage, use this request |
|
||||||
several times. --> |
|
||||||
<request name="damage"> |
|
||||||
<arg name="x" type="int"/> |
|
||||||
<arg name="y" type="int"/> |
|
||||||
<arg name="width" type="int"/> |
|
||||||
<arg name="height" type="int"/> |
|
||||||
</request> |
|
||||||
</interface> |
|
||||||
|
|
||||||
|
|
||||||
<!-- A group of keyboards and pointer devices (mice, for |
|
||||||
example). This object is published as a global during start up, |
|
||||||
or when such a device is hot plugged. A input_device group |
|
||||||
typically has a pointer and maintains a keyboard_focus and a |
|
||||||
pointer_focus. --> |
|
||||||
<interface name="input_device" version="1"> |
|
||||||
<!-- Set the pointer's image. This request only takes effect if |
|
||||||
the pointer focus for this device is one of the requesting |
|
||||||
clients surfaces. --> |
|
||||||
<request name="attach"> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<arg name="buffer" type="object" interface="buffer"/> |
|
||||||
<arg name="hotspot_x" type="int"/> |
|
||||||
<arg name="hotspot_y" type="int"/> |
|
||||||
</request> |
|
||||||
|
|
||||||
<!-- Notification of pointer location change. |
|
||||||
x,y are the absolute location on the screen. |
|
||||||
surface_[xy] are the location relative to the focused surface. --> |
|
||||||
<event name="motion"> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<arg name="x" type="int"/> |
|
||||||
<arg name="y" type="int"/> |
|
||||||
<arg name="surface_x" type="int"/> |
|
||||||
<arg name="surface_y" type="int"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- Mouse button click and release notifications. The location |
|
||||||
of the click is given by the last motion or pointer_focus |
|
||||||
event. --> |
|
||||||
<event name="button"> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<arg name="button" type="uint"/> |
|
||||||
<arg name="state" type="uint"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- Keyboard press. --> |
|
||||||
<event name="key"> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<arg name="key" type="uint"/> |
|
||||||
<arg name="state" type="uint"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<!-- Notification that this input device's pointer is focused on |
|
||||||
certain surface. When an input_device enters a surface, the |
|
||||||
pointer image is undefined and a client should respond to |
|
||||||
this event by setting an apropriate pointer image. --> |
|
||||||
<event name="pointer_focus"> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<arg name="surface" type="object" interface="surface"/> |
|
||||||
<arg name="x" type="int"/> |
|
||||||
<arg name="y" type="int"/> |
|
||||||
<arg name="surface_x" type="int"/> |
|
||||||
<arg name="surface_y" type="int"/> |
|
||||||
</event> |
|
||||||
|
|
||||||
<event name="keyboard_focus"> |
|
||||||
<arg name="time" type="uint"/> |
|
||||||
<arg name="surface" type="object" interface="surface"/> |
|
||||||
<arg name="keys" type="array"/> |
|
||||||
</event> |
|
||||||
</interface> |
|
||||||
|
|
||||||
|
|
||||||
<!-- An output describes part of the compositor geometry. The |
|
||||||
compositor work in the 'compositor coordinate system' and an |
|
||||||
output corresponds to rectangular area in that space that is |
|
||||||
actually visible. This typically corresponds to a monitor that |
|
||||||
displays part of the compositor space. This object is |
|
||||||
published as global during start up, or when a screen is hot |
|
||||||
plugged. --> |
|
||||||
<interface name="output" version="1"> |
|
||||||
<!-- Notification about the screen size. --> |
|
||||||
<event name="geometry"> |
|
||||||
<arg name="x" type="int"/> |
|
||||||
<arg name="y" type="int"/> |
|
||||||
<arg name="width" type="int"/> |
|
||||||
<arg name="height" type="int"/> |
|
||||||
</event> |
|
||||||
</interface> |
|
||||||
|
|
||||||
|
|
||||||
<!-- A visual is the pixel format. The different visuals are |
|
||||||
currently only identified by the order they are advertised by |
|
||||||
the 'global' events. We need something better. --> |
|
||||||
<interface name="visual" version="1"/> |
|
||||||
|
|
||||||
</protocol> |
|
@ -1,3 +0,0 @@ |
|||||||
main.aux |
|
||||||
main.log |
|
||||||
main.pdf |
|
@ -1,575 +0,0 @@ |
|||||||
\documentclass{article} |
|
||||||
\usepackage{palatino} |
|
||||||
|
|
||||||
\author{Kristian Høgsberg\\ |
|
||||||
\texttt{krh@bitplanet.net} |
|
||||||
} |
|
||||||
|
|
||||||
\title{The Wayland Display Server} |
|
||||||
|
|
||||||
\begin{document} |
|
||||||
|
|
||||||
\maketitle |
|
||||||
|
|
||||||
\section{Wayland Overview} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item wayland is a protocol for a new display server. |
|
||||||
\item wayland is an implementation |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Replacing X11} |
|
||||||
|
|
||||||
Over time, a lot of functionality have slowly moved out of the X |
|
||||||
server and into client-side libraries or kernel drivers. One of the |
|
||||||
first components to move out was font rendering, with freetype and |
|
||||||
fontconfig providing an alternative to the core X fonts. Direct |
|
||||||
rendering OpenGL as a graphics driver in a client side library. Then |
|
||||||
cairo came along and provided a modern 2D rendering library |
|
||||||
independent of X and compositing managers took over control of the |
|
||||||
rendering of the desktop. Recently with GEM and KMS in the Linux |
|
||||||
kernel, we can do modesetting outside X and schedule several direct |
|
||||||
rendering clients. The end result is a highly modular graphics stack. |
|
||||||
|
|
||||||
\subsection{Make the compositing manager the display server} |
|
||||||
|
|
||||||
Wayland is a new display server building on top of all those |
|
||||||
components. We are trying to distill out the functionality in the X |
|
||||||
server that is still used by the modern Linux desktop. This turns out |
|
||||||
to be not a whole lot. Applications can allocate their own off-screen |
|
||||||
buffers and render their window contents by themselves. In the end, |
|
||||||
what’s needed is a way to present the resulting window surface to a |
|
||||||
compositor and a way to receive input. This is what Wayland provides, |
|
||||||
by piecing together the components already in the eco-system in a |
|
||||||
slightly different way. |
|
||||||
|
|
||||||
X will always be relevant, in the same way Fortran compilers and VRML |
|
||||||
browsers are, but it’s time that we think about moving it out of the |
|
||||||
critical path and provide it as an optional component for legacy |
|
||||||
applications. |
|
||||||
|
|
||||||
|
|
||||||
\section{Wayland protocol} |
|
||||||
|
|
||||||
\subsection{Basic Principles} |
|
||||||
|
|
||||||
The wayland protocol is an asynchronous object oriented protocol. All |
|
||||||
requests are method invocations on some object. The request include |
|
||||||
an object id that uniquely identifies an object on the server. Each |
|
||||||
object implements an interface and the requests include an opcode that |
|
||||||
identifies which method in the interface to invoke. |
|
||||||
|
|
||||||
The server sends back events to the client, each event is emitted from |
|
||||||
an object. Events can be error conditions. The event includes the |
|
||||||
object id and the event opcode, from which the client can determine |
|
||||||
the type of event. Events are generated both in response to a request |
|
||||||
(in which case the request and the event constitutes a round trip) or |
|
||||||
spontanously when the server state changes. |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item state is broadcast on connect, events sent out when state |
|
||||||
change. client must listen for these changes and cache the state. |
|
||||||
no need (or mechanism) to query server state. |
|
||||||
|
|
||||||
\item server will broadcast presence of a number of global objects, |
|
||||||
which in turn will broadcast their current state. |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Code generation} |
|
||||||
|
|
||||||
The interfaces, requests and events are defined in protocol/wayland.xml. |
|
||||||
This xml is used to generate the function prototypes that can be used by |
|
||||||
clients and compositors. |
|
||||||
|
|
||||||
The protocol entry points are generated as inline functions which just |
|
||||||
wraps the \verb:wl_proxy_*: functions. The inline functions aren't |
|
||||||
part of the library ABI and languange bindings should generate their |
|
||||||
own stubs for the protocl entry points from the xml. |
|
||||||
|
|
||||||
\subsection{Wire format} |
|
||||||
|
|
||||||
The protocol is sent over a UNIX domain stream socket. Currently, the |
|
||||||
endpoint is named \texttt{\textbackslash0wayland}, but it is subject |
|
||||||
to change. The protocol is message-based. A message sent by a client |
|
||||||
to the server is called \texttt{request}. A message from the server |
|
||||||
to a client is called \texttt{event}. Every message is structured as |
|
||||||
32-bit words, values are represented in the host's byte-order. |
|
||||||
|
|
||||||
The message header has 2 words in it: |
|
||||||
\begin{itemize} |
|
||||||
\item The first word is the sender's object id (32-bit). |
|
||||||
\item The second has 2 parts of 16-bit. The upper 16-bits are the message |
|
||||||
size in bytes, starting at the header (i.e. it has a minimum value of 8). |
|
||||||
The lower is the request/event opcode. |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
The payload describes the request/event arguments. Every argument is always |
|
||||||
aligned to 32-bit. There is no prefix that describes the type, but it is |
|
||||||
inferred implicitly from the xml specification. |
|
||||||
|
|
||||||
The representation of argument types are as follows: |
|
||||||
\begin{itemize} |
|
||||||
\item "int" or "uint": The value is the 32-bit value of the signed/unsigned |
|
||||||
int. |
|
||||||
\item "string": Starts with an unsigned 32-bit length, followed by the |
|
||||||
string contents, including terminating NUL byte, then padding to a |
|
||||||
32-bit boundary. |
|
||||||
\item "object": A 32-bit object ID. |
|
||||||
\item "new\_id": the 32-bit object ID. On requests, the client |
|
||||||
decides the ID. The only event with "new\_id" is advertisements of |
|
||||||
globals, and the server will use IDs below 0x10000. |
|
||||||
\item "array": Starts with 32-bit array size in bytes, followed by the array |
|
||||||
contents verbatim, and finally padding to a 32-bit boundary. |
|
||||||
\item "fd": the file descriptor is not stored in the message buffer, but in |
|
||||||
the ancillary data of the UNIX domain socket message (msg\_control). |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Connect Time} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item no fixed format connect block, the server emits a bunch of |
|
||||||
events at connect time |
|
||||||
\item presence events for global objects: output, compositor, input |
|
||||||
devices |
|
||||||
\end{itemize} |
|
||||||
\subsection{Security and Authentication} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item mostly about access to underlying buffers, need new drm auth |
|
||||||
mechanism (the grant-to ioctl idea), need to check the cmd stream? |
|
||||||
|
|
||||||
\item getting the server socket depends on the compositor type, could |
|
||||||
be a system wide name, through fd passing on the session dbus. or |
|
||||||
the client is forked by the compositor and the fd is already opened. |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Creating Objects} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item client allocates object ID, uses range protocol |
|
||||||
\item server tracks how many IDs are left in current range, sends new |
|
||||||
range when client is about to run out. |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Compositor} |
|
||||||
|
|
||||||
The compositor is a global object, advertised at connect time. |
|
||||||
|
|
||||||
\begin{tabular}{l} |
|
||||||
\hline |
|
||||||
Interface \texttt{compositor} \\ \hline |
|
||||||
Requests \\ \hline |
|
||||||
\texttt{create\_surface(id)} \\ |
|
||||||
\texttt{commit()} \\ \hline |
|
||||||
Events \\ \hline |
|
||||||
\texttt{device(device)} \\ |
|
||||||
\texttt{acknowledge(key, frame)} \\ |
|
||||||
\texttt{frame(frame, time)} \\ \hline |
|
||||||
\end{tabular} |
|
||||||
|
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item a global object |
|
||||||
\item broadcasts drm file name, or at least a string like drm:/dev/card0 |
|
||||||
\item commit/ack/frame protocol |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Surface} |
|
||||||
|
|
||||||
Created by the client. |
|
||||||
|
|
||||||
\begin{tabular}{l} |
|
||||||
\hline |
|
||||||
Interface \texttt{surface} \\ \hline |
|
||||||
Requests \\ \hline |
|
||||||
\texttt{destroy()} \\ |
|
||||||
\texttt{attach()} \\ |
|
||||||
\texttt{map()} \\ |
|
||||||
\texttt{damage()} \\ \hline |
|
||||||
Events \\ \hline |
|
||||||
no events \\ \hline |
|
||||||
\end{tabular} |
|
||||||
|
|
||||||
Needs a way to set input region, opaque region. |
|
||||||
|
|
||||||
\subsection{Input} |
|
||||||
|
|
||||||
Represents a group of input devices, including mice, keyboards. Has a |
|
||||||
keyboard and pointer focus. Global object. Pointer events are |
|
||||||
delivered in both screen coordinates and surface local coordinates. |
|
||||||
|
|
||||||
\begin{tabular}{l} |
|
||||||
\hline |
|
||||||
Interface \texttt{cache} \\ \hline |
|
||||||
Requests \\ \hline |
|
||||||
\texttt{attach(buffer, x, y)} \\ |
|
||||||
Events \\ \hline |
|
||||||
\texttt{motion(x, y, sx, sy)} \\ |
|
||||||
\texttt{button(button, state, x, y, sx, sy)} \\ |
|
||||||
\texttt{key(key, state)} \\ |
|
||||||
\texttt{pointer\_focus(surface)} \\ |
|
||||||
\texttt{keyboard\_focus(surface, keys)} \\ \hline |
|
||||||
\end{tabular} |
|
||||||
|
|
||||||
Talk about: |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item keyboard map, change events |
|
||||||
\item xkb on wayland |
|
||||||
\item multi pointer wayland |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
A surface can change the pointer image when the surface is the pointer |
|
||||||
focus of the input device. Wayland doesn't automatically change the |
|
||||||
pointer image when a pointer enters a surface, but expects the |
|
||||||
application to set the cursor it wants in response the the pointer |
|
||||||
focus and motion events. The rationale is that a client has to manage |
|
||||||
changing pointer images for UI elements within the surface in response |
|
||||||
to motion events anyway, so we'll make that the only mechanism for |
|
||||||
setting changing the pointer image. If the server receives a request |
|
||||||
to set the pointer image after the surface loses pointer focus, the |
|
||||||
request is ignored. To the client this will look like it successfully |
|
||||||
set the pointer image. |
|
||||||
|
|
||||||
The compositor will revert the pointer image back to a default image |
|
||||||
when no surface has the pointer focus for that device. Clients can |
|
||||||
revert the pointer image back to the default image by setting a NULL |
|
||||||
image. |
|
||||||
|
|
||||||
What if the pointer moves from one window which has set a special |
|
||||||
pointer image to a surface that doesn't set an image in response to |
|
||||||
the motion event? The new surface will be stuck with the special |
|
||||||
pointer image. We can't just revert the pointer image on leaving a |
|
||||||
surface, since if we immediately enter a surface that sets a different |
|
||||||
image, the image will flicker. Broken app, I suppose. |
|
||||||
|
|
||||||
\subsection{Output} |
|
||||||
|
|
||||||
A output is a global object, advertised at connect time or as they |
|
||||||
come and go. |
|
||||||
|
|
||||||
\begin{tabular}{l} |
|
||||||
\hline |
|
||||||
Interface \texttt{output} \\ \hline |
|
||||||
Requests \\ \hline |
|
||||||
no requests \\ \hline |
|
||||||
Events \\ \hline |
|
||||||
\texttt{geometry(width, height)} \\ \hline |
|
||||||
\end{tabular} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item laid out in a big (compositor) coordinate system |
|
||||||
\item basically xrandr over wayland |
|
||||||
\item geometry needs position in compositor coordinate system\ |
|
||||||
\item events to advertise available modes, requests to move and change |
|
||||||
modes |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Shared object cache} |
|
||||||
|
|
||||||
Cache for sharing glyphs, icons, cursors across clients. Lets clients |
|
||||||
share identical objects. The cache is a global object, advertised at |
|
||||||
connect time. |
|
||||||
|
|
||||||
\begin{tabular}{l} |
|
||||||
\hline |
|
||||||
Interface \texttt{cache} \\ \hline |
|
||||||
Requests \\ \hline |
|
||||||
\texttt{upload(key, visual, bo, stride, width, height)} \\ \hline |
|
||||||
Events \\ \hline |
|
||||||
\texttt{item(key, bo, x, y, stride)} \\ |
|
||||||
\texttt{retire(bo)} \\ \hline |
|
||||||
\end{tabular} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
|
|
||||||
\item Upload by passing a visual, bo, stride, width, height to the |
|
||||||
cache. |
|
||||||
|
|
||||||
\item Upload returns a bo name, stride, and x, y location of object in |
|
||||||
the buffer. Clients take a reference on the atlas bo. |
|
||||||
|
|
||||||
\item Shared objects are refcounted, freed by client (when purging |
|
||||||
glyphs from the local cache) or when a client exits. |
|
||||||
|
|
||||||
\item Server can't delete individual items from an atlas, but it can |
|
||||||
throw out an entire atlas bo if it becomes too sparse. The server |
|
||||||
sends out an \texttt{retire} event when this happens, and clients |
|
||||||
must throw away any objects from that bo and reupload. Between the |
|
||||||
server dropping the atlas and the client receiving the retire event, |
|
||||||
clients can still legally use the old atlas since they have a ref on |
|
||||||
the bo. |
|
||||||
|
|
||||||
\item cairo needs to hook into the glyph cache, and maybe also a way |
|
||||||
to create a read-only surface based on an object form the cache |
|
||||||
(icons). |
|
||||||
|
|
||||||
\texttt{cairo\_wayland\_create\_cached\_surface(surface-data)}. |
|
||||||
|
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
|
|
||||||
\subsection{Drag and Drop} |
|
||||||
|
|
||||||
Multi-device aware. Orthogonal to rest of wayland, as it is its own |
|
||||||
toplevel object. Since the compositor determines the drag target, it |
|
||||||
works with transformed surfaces (dragging to a scaled down window in |
|
||||||
expose mode, for example). |
|
||||||
|
|
||||||
Issues: |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item we can set the cursor image to the current cursor + dragged |
|
||||||
object, which will last as long as the drag, but maybe an request to |
|
||||||
attach an image to the cursor will be more convenient? |
|
||||||
|
|
||||||
\item Should drag.send() destroy the object? There's nothing to do |
|
||||||
after the data has been transferred. |
|
||||||
|
|
||||||
\item How do we marshall several mime-types? We could make the drag |
|
||||||
setup a multi-step operation: dnd.create, drag.offer(mime-type1), |
|
||||||
drag.offer(mime-type2), drag.activate(). The drag object could send |
|
||||||
multiple offer events on each motion event. Or we could just |
|
||||||
implement an array type, but that's a pain to work with. |
|
||||||
|
|
||||||
\item Middle-click drag to pop up menu? Ctrl/Shift/Alt drag? |
|
||||||
|
|
||||||
\item Send a file descriptor over the protocol to let initiator and |
|
||||||
source exchange data out of band? |
|
||||||
|
|
||||||
\item Action? Specify action when creating the drag object? Ask |
|
||||||
action? |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
New objects, requests and events: |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item New toplevel dnd global. One method, creates a drag object: |
|
||||||
\texttt{dnd.start(new object id, surface, input device, mime |
|
||||||
types)}. Starts drag for the device, if it's grabbed by the |
|
||||||
surface. drag ends when button is released. Caller is responsible |
|
||||||
for destroying the drag object. |
|
||||||
|
|
||||||
\item Drag object methods: |
|
||||||
|
|
||||||
\texttt{drag.destroy(id)}, destroy drag object. |
|
||||||
|
|
||||||
\texttt{drag.send(id, data)}, send drag data. |
|
||||||
|
|
||||||
\texttt{drag.accept(id, mime type)}, accept drag offer, called by |
|
||||||
target surface. |
|
||||||
|
|
||||||
\item Drag object events: |
|
||||||
|
|
||||||
\texttt{drag.offer(id, mime-types)}, sent to potential destination |
|
||||||
surfaces to offer drag data. If the device leaves the window or the |
|
||||||
originator cancels the drag, this event is sent with mime-types = |
|
||||||
NULL. |
|
||||||
|
|
||||||
\texttt{drag.target(id, mime-type)}, sent to drag originator when a |
|
||||||
target surface has accepted the offer. if a previous target goes |
|
||||||
away, this event is sent with mime-type = NULL. |
|
||||||
|
|
||||||
\texttt{drag.data(id, data)}, sent to target, contains dragged data. |
|
||||||
ends transaction on the target side. |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
Sequence of events: |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item The initiator surface receives a click (which grabs the input |
|
||||||
device to that surface) and then enough motion to decide that a drag |
|
||||||
is starting. Wayland has no subwindows, so it's entirely up to the |
|
||||||
application to decide whether or not a draggable object within the |
|
||||||
surface was clicked. |
|
||||||
|
|
||||||
\item The initiator creates a drag object by calling the |
|
||||||
\texttt{create\_drag} method on the dnd global object. As for any |
|
||||||
client created object, the client allocates the id. The |
|
||||||
\texttt{create\_drag} method also takes the originating surface, the |
|
||||||
device that's dragging and the mime-types supported. If the surface |
|
||||||
has indeed grabbed the device passed in, the server will create an |
|
||||||
active drag object for the device. If the grab was released in the |
|
||||||
meantime, the drag object will be in-active, that is, the same state |
|
||||||
as when the grab is released. In that case, the client will receive |
|
||||||
a button up event, which will let it know that the drag finished. |
|
||||||
To the client it will look like the drag was immediately cancelled |
|
||||||
by the grab ending. |
|
||||||
|
|
||||||
The special mime-type application/x-root-target indicates that the |
|
||||||
initiator is looking for drag events to the root window as well. |
|
||||||
|
|
||||||
\item To indicate the object being dragged, the initiator can replace |
|
||||||
the pointer image with an larger image representing the data being |
|
||||||
dragged with the cursor image overlaid. The pointer image will |
|
||||||
remain in place as long as the grab is in effect, since the |
|
||||||
initiating surface keeps pointer focus, and no other surface |
|
||||||
receives enter events. |
|
||||||
|
|
||||||
\item As long as the grab is active (or until the initiator cancels |
|
||||||
the drag by destroying the drag object), the drag object will send |
|
||||||
\texttt{offer} events to surfaces it moves across. As for motion |
|
||||||
events, these events contain the surface local coordinates of the |
|
||||||
device as well as the list of mime-types offered. When a device |
|
||||||
leaves a surface, it will send an \texttt{offer} event with an empty |
|
||||||
list of mime-types to indicate that the device left the surface. |
|
||||||
|
|
||||||
\item If a surface receives an offer event and decides that it's in an |
|
||||||
area that can accept a drag event, it should call the |
|
||||||
\texttt{accept} method on the drag object in the event. The surface |
|
||||||
passes a mime-type in the request, picked from the list in the offer |
|
||||||
event, to indicate which of the types it wants. At this point, the |
|
||||||
surface can update the appearance of the drop target to give |
|
||||||
feedback to the user that the drag has a valid target. If the |
|
||||||
\texttt{offer} event moves to a different drop target (the surface |
|
||||||
decides the offer coordinates is outside the drop target) or leaves |
|
||||||
the surface (the offer event has an empty list of mime-types) it |
|
||||||
should revert the appearance of the drop target to the inactive |
|
||||||
state. A surface can also decide to retract its drop target (if the |
|
||||||
drop target disappears or moves, for example), by calling the accept |
|
||||||
method with a NULL mime-type. |
|
||||||
|
|
||||||
\item When a target surface sends an \texttt{accept} request, the drag |
|
||||||
object will send a \texttt{target} event to the initiator surface. |
|
||||||
This tells the initiator that the drag currently has a potential |
|
||||||
target and which of the offered mime-types the target wants. The |
|
||||||
initiator can change the pointer image or drag source appearance to |
|
||||||
reflect this new state. If the target surface retracts its drop |
|
||||||
target of if the surface disappears, a \texttt{target} event with a |
|
||||||
NULL mime-type will be sent. |
|
||||||
|
|
||||||
If the initiator listed application/x-root-target as a valid |
|
||||||
mime-type, dragging into the root window will make the drag object |
|
||||||
send a \texttt{target} event with the application/x-root-target |
|
||||||
mime-type. |
|
||||||
|
|
||||||
\item When the grab is released (indicated by the button release |
|
||||||
event), if the drag has an active target, the initiator calls the |
|
||||||
\texttt{send} method on the drag object to send the data to be |
|
||||||
transferred by the drag operation, in the format requested by the |
|
||||||
target. The initiator can then destroy the drag object by calling |
|
||||||
the \texttt{destroy} method. |
|
||||||
|
|
||||||
\item The drop target receives a \texttt{data} event from the drag |
|
||||||
object with the requested data. |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
MIME is defined in RFC's 2045-2049. A registry of MIME types is |
|
||||||
maintained by the Internet Assigned Numbers Authority (IANA). |
|
||||||
|
|
||||||
ftp://ftp.isi.edu/in-notes/iana/assignments/media-types/ |
|
||||||
|
|
||||||
|
|
||||||
\section{Types of compositors} |
|
||||||
|
|
||||||
\subsection{System Compositor} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item ties in with graphical boot |
|
||||||
\item hosts different types of session compositors |
|
||||||
\item lets us switch between multiple sessions (fast user switching, |
|
||||||
secure/personal desktop switching) |
|
||||||
\item multiseat |
|
||||||
\item linux implementation using libudev, egl, kms, evdev, cairo |
|
||||||
\item for fullscreen clients, the system compositor can reprogram the |
|
||||||
video scanout address to source from the client provided buffer. |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Session Compositor} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item nested under the system compositor. nesting is feasible because |
|
||||||
protocol is async, roundtrip would break nesting |
|
||||||
\item gnome-shell |
|
||||||
\item moblin |
|
||||||
\item compiz? |
|
||||||
\item kde compositor? |
|
||||||
\item text mode using vte |
|
||||||
\item rdp session |
|
||||||
\item fullscreen X session under wayland |
|
||||||
\item can run without system compositor, on the hw where it makes |
|
||||||
sense |
|
||||||
\item root window less X server, bridging X windows into a wayland |
|
||||||
session compositor |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Embbedding Compositor} |
|
||||||
|
|
||||||
X11 lets clients embed windows from other clients, or lets client copy |
|
||||||
pixmap contents rendered by another client into their window. This is |
|
||||||
often used for applets in a panel, browser plugins and similar. |
|
||||||
Wayland doesn't directly allow this, but clients can communicate GEM |
|
||||||
buffer names out-of-band, for example, using d-bus or as command line |
|
||||||
arguments when the panel launches the applet. Another option is to |
|
||||||
use a nested wayland instance. For this, the wayland server will have |
|
||||||
to be a library that the host application links to. The host |
|
||||||
application will then pass the wayland server socket name to the |
|
||||||
embedded application, and will need to implement the wayland |
|
||||||
compositor interface. The host application composites the client |
|
||||||
surfaces as part of it's window, that is, in the web page or in the |
|
||||||
panel. The benefit of nesting the wayland server is that it provides |
|
||||||
the requests the embedded client needs to inform the host about buffer |
|
||||||
updates and a mechanism for forwarding input events from the host |
|
||||||
application. |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item firefox embedding flash by being a special purpose compositor to |
|
||||||
the plugin |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\section{Implementation} |
|
||||||
|
|
||||||
what's currently implemented |
|
||||||
|
|
||||||
\subsection{Wayland Server Library} |
|
||||||
|
|
||||||
\texttt{libwayland-server.so} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item implements protocol side of a compositor |
|
||||||
\item minimal, doesn't include any rendering or input device handling |
|
||||||
\item helpers for running on egl and evdev, and for nested wayland |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Wayland Client Library} |
|
||||||
|
|
||||||
\texttt{libwayland.so} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item minimal, designed to support integration with real toolkits such as |
|
||||||
Qt, GTK+ or Clutter. |
|
||||||
|
|
||||||
\item doesn't cache state, but lets the toolkits cache server state in |
|
||||||
native objects (GObject or QObject or whatever). |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{Wayland System Compositor} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item implementation of the system compositor |
|
||||||
|
|
||||||
\item uses libudev, eagle (egl), evdev and drm |
|
||||||
|
|
||||||
\item integrates with ConsoleKit, can create new sessions |
|
||||||
|
|
||||||
\item allows multi seat setups |
|
||||||
|
|
||||||
\item configurable through udev rules and maybe /etc/wayland.d type thing |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\subsection{X Server Session} |
|
||||||
|
|
||||||
\begin{itemize} |
|
||||||
\item xserver module and driver support |
|
||||||
|
|
||||||
\item uses wayland client library |
|
||||||
|
|
||||||
\item same X.org server as we normally run, the front buffer is a wayland |
|
||||||
surface but all accel code, 3d and extensions are there |
|
||||||
|
|
||||||
\item when full screen the session compositor will scan out from the X |
|
||||||
server wayland surface, at which point X is running pretty much as it |
|
||||||
does natively. |
|
||||||
\end{itemize} |
|
||||||
|
|
||||||
\end{document} |
|
@ -1,4 +0,0 @@ |
|||||||
scanner |
|
||||||
wayland-client-protocol.h |
|
||||||
wayland-protocol.c |
|
||||||
wayland-server-protocol.h |
|
@ -1,52 +0,0 @@ |
|||||||
lib_LTLIBRARIES = libwayland-server.la libwayland-client.la
|
|
||||||
noinst_LTLIBRARIES = libwayland-util.la
|
|
||||||
|
|
||||||
include_HEADERS = \
|
|
||||||
wayland-util.h \
|
|
||||||
wayland-server-protocol.h \
|
|
||||||
wayland-server.h \
|
|
||||||
wayland-client-protocol.h \
|
|
||||||
wayland-client.h \
|
|
||||||
wayland-egl.h
|
|
||||||
|
|
||||||
libwayland_util_la_SOURCES = \
|
|
||||||
connection.c \
|
|
||||||
connection.h \
|
|
||||||
wayland-util.c \
|
|
||||||
wayland-util.h \
|
|
||||||
wayland-hash.c
|
|
||||||
|
|
||||||
libwayland_server_la_LIBADD = $(FFI_LIBS) libwayland-util.la
|
|
||||||
libwayland_server_la_SOURCES = \
|
|
||||||
wayland-protocol.c \
|
|
||||||
wayland-server.c \
|
|
||||||
event-loop.c
|
|
||||||
|
|
||||||
libwayland_client_la_LIBADD = $(FFI_LIBS) libwayland-util.la
|
|
||||||
libwayland_client_la_SOURCES = \
|
|
||||||
wayland-protocol.c \
|
|
||||||
wayland-client.c
|
|
||||||
|
|
||||||
pkgconfigdir = $(libdir)/pkgconfig
|
|
||||||
pkgconfig_DATA = wayland-client.pc wayland-server.pc
|
|
||||||
|
|
||||||
AM_CPPFLAGS = $(FFI_CFLAGS)
|
|
||||||
AM_CFLAGS = $(GCC_CFLAGS)
|
|
||||||
|
|
||||||
include $(top_srcdir)/wayland/scanner.mk |
|
||||||
|
|
||||||
noinst_PROGRAMS = scanner
|
|
||||||
|
|
||||||
scanner_SOURCES = \
|
|
||||||
scanner.c
|
|
||||||
|
|
||||||
scanner_LDADD = $(EXPAT_LIBS) libwayland-util.la
|
|
||||||
|
|
||||||
$(BUILT_SOURCES) : scanner |
|
||||||
|
|
||||||
BUILT_SOURCES = \
|
|
||||||
wayland-server-protocol.h \
|
|
||||||
wayland-client-protocol.h \
|
|
||||||
wayland-protocol.c
|
|
||||||
|
|
||||||
CLEANFILES = $(BUILT_SOURCES)
|
|
@ -1,729 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <string.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <errno.h> |
|
||||||
#include <sys/uio.h> |
|
||||||
#include <ffi.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <fcntl.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#include <sys/socket.h> |
|
||||||
|
|
||||||
#include "wayland-util.h" |
|
||||||
#include "connection.h" |
|
||||||
|
|
||||||
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) |
|
||||||
|
|
||||||
struct wl_buffer { |
|
||||||
char data[4096]; |
|
||||||
int head, tail; |
|
||||||
}; |
|
||||||
|
|
||||||
#define MASK(i) ((i) & 4095) |
|
||||||
|
|
||||||
struct wl_closure { |
|
||||||
int count; |
|
||||||
const struct wl_message *message; |
|
||||||
ffi_type *types[20]; |
|
||||||
ffi_cif cif; |
|
||||||
void *args[20]; |
|
||||||
uint32_t buffer[64]; |
|
||||||
uint32_t *start; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_connection { |
|
||||||
struct wl_buffer in, out; |
|
||||||
struct wl_buffer fds_in, fds_out; |
|
||||||
int fd; |
|
||||||
void *data; |
|
||||||
wl_connection_update_func_t update; |
|
||||||
struct wl_closure receive_closure, send_closure; |
|
||||||
}; |
|
||||||
|
|
||||||
union wl_value { |
|
||||||
uint32_t uint32; |
|
||||||
char *string; |
|
||||||
struct wl_object *object; |
|
||||||
uint32_t new_id; |
|
||||||
struct wl_array *array; |
|
||||||
}; |
|
||||||
|
|
||||||
static void |
|
||||||
wl_buffer_put(struct wl_buffer *b, const void *data, size_t count) |
|
||||||
{ |
|
||||||
int head, size; |
|
||||||
|
|
||||||
head = MASK(b->head); |
|
||||||
if (head + count <= sizeof b->data) { |
|
||||||
memcpy(b->data + head, data, count); |
|
||||||
} else { |
|
||||||
size = sizeof b->data - head; |
|
||||||
memcpy(b->data + head, data, size); |
|
||||||
memcpy(b->data, (const char *) data + size, count - size); |
|
||||||
} |
|
||||||
|
|
||||||
b->head += count; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
wl_buffer_put_iov(struct wl_buffer *b, struct iovec *iov, int *count) |
|
||||||
{ |
|
||||||
int head, tail; |
|
||||||
|
|
||||||
head = MASK(b->head); |
|
||||||
tail = MASK(b->tail); |
|
||||||
if (head < tail) { |
|
||||||
iov[0].iov_base = b->data + head; |
|
||||||
iov[0].iov_len = tail - head; |
|
||||||
*count = 1; |
|
||||||
} else if (tail == 0) { |
|
||||||
iov[0].iov_base = b->data + head; |
|
||||||
iov[0].iov_len = sizeof b->data - head; |
|
||||||
*count = 1; |
|
||||||
} else { |
|
||||||
iov[0].iov_base = b->data + head; |
|
||||||
iov[0].iov_len = sizeof b->data - head; |
|
||||||
iov[1].iov_base = b->data; |
|
||||||
iov[1].iov_len = tail; |
|
||||||
*count = 2; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
wl_buffer_get_iov(struct wl_buffer *b, struct iovec *iov, int *count) |
|
||||||
{ |
|
||||||
int head, tail; |
|
||||||
|
|
||||||
head = MASK(b->head); |
|
||||||
tail = MASK(b->tail); |
|
||||||
if (tail < head) { |
|
||||||
iov[0].iov_base = b->data + tail; |
|
||||||
iov[0].iov_len = head - tail; |
|
||||||
*count = 1; |
|
||||||
} else if (head == 0) { |
|
||||||
iov[0].iov_base = b->data + tail; |
|
||||||
iov[0].iov_len = sizeof b->data - tail; |
|
||||||
*count = 1; |
|
||||||
} else { |
|
||||||
iov[0].iov_base = b->data + tail; |
|
||||||
iov[0].iov_len = sizeof b->data - tail; |
|
||||||
iov[1].iov_base = b->data; |
|
||||||
iov[1].iov_len = head; |
|
||||||
*count = 2; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
wl_buffer_copy(struct wl_buffer *b, void *data, size_t count) |
|
||||||
{ |
|
||||||
int tail, size; |
|
||||||
|
|
||||||
tail = MASK(b->tail); |
|
||||||
if (tail + count <= sizeof b->data) { |
|
||||||
memcpy(data, b->data + tail, count); |
|
||||||
} else { |
|
||||||
size = sizeof b->data - tail; |
|
||||||
memcpy(data, b->data + tail, size); |
|
||||||
memcpy((char *) data + size, b->data, count - size); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_connection * |
|
||||||
wl_connection_create(int fd, |
|
||||||
wl_connection_update_func_t update, |
|
||||||
void *data) |
|
||||||
{ |
|
||||||
struct wl_connection *connection; |
|
||||||
|
|
||||||
connection = malloc(sizeof *connection); |
|
||||||
memset(connection, 0, sizeof *connection); |
|
||||||
connection->fd = fd; |
|
||||||
connection->update = update; |
|
||||||
connection->data = data; |
|
||||||
|
|
||||||
connection->update(connection, |
|
||||||
WL_CONNECTION_READABLE, |
|
||||||
connection->data); |
|
||||||
|
|
||||||
return connection; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
wl_connection_destroy(struct wl_connection *connection) |
|
||||||
{ |
|
||||||
close(connection->fd); |
|
||||||
free(connection); |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
wl_connection_copy(struct wl_connection *connection, void *data, size_t size) |
|
||||||
{ |
|
||||||
wl_buffer_copy(&connection->in, data, size); |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
wl_connection_consume(struct wl_connection *connection, size_t size) |
|
||||||
{ |
|
||||||
connection->in.tail += size; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
build_cmsg(struct wl_buffer *buffer, char *data, int *clen) |
|
||||||
{ |
|
||||||
struct cmsghdr *cmsg; |
|
||||||
size_t size; |
|
||||||
|
|
||||||
size = buffer->head - buffer->tail; |
|
||||||
if (size > 0) { |
|
||||||
cmsg = (struct cmsghdr *) data; |
|
||||||
cmsg->cmsg_level = SOL_SOCKET; |
|
||||||
cmsg->cmsg_type = SCM_RIGHTS; |
|
||||||
cmsg->cmsg_len = CMSG_LEN(size); |
|
||||||
wl_buffer_copy(buffer, CMSG_DATA(cmsg), size); |
|
||||||
*clen = cmsg->cmsg_len; |
|
||||||
} else { |
|
||||||
*clen = 0; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
close_fds(struct wl_buffer *buffer) |
|
||||||
{ |
|
||||||
int fds[32], i, count; |
|
||||||
size_t size; |
|
||||||
|
|
||||||
size = buffer->head - buffer->tail; |
|
||||||
if (size == 0) |
|
||||||
return; |
|
||||||
|
|
||||||
wl_buffer_copy(buffer, fds, size); |
|
||||||
count = size / sizeof fds[0]; |
|
||||||
for (i = 0; i < count; i++) |
|
||||||
close(fds[i]); |
|
||||||
buffer->tail += size; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
decode_cmsg(struct wl_buffer *buffer, struct msghdr *msg) |
|
||||||
{ |
|
||||||
struct cmsghdr *cmsg; |
|
||||||
size_t size; |
|
||||||
|
|
||||||
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; |
|
||||||
cmsg = CMSG_NXTHDR(msg, cmsg)) { |
|
||||||
if (cmsg->cmsg_level == SOL_SOCKET && |
|
||||||
cmsg->cmsg_type == SCM_RIGHTS) { |
|
||||||
size = cmsg->cmsg_len - CMSG_LEN(0); |
|
||||||
wl_buffer_put(buffer, CMSG_DATA(cmsg), size); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int |
|
||||||
wl_connection_data(struct wl_connection *connection, uint32_t mask) |
|
||||||
{ |
|
||||||
struct iovec iov[2]; |
|
||||||
struct msghdr msg; |
|
||||||
char cmsg[128]; |
|
||||||
int len, count, clen; |
|
||||||
|
|
||||||
if (mask & WL_CONNECTION_WRITABLE) { |
|
||||||
wl_buffer_get_iov(&connection->out, iov, &count); |
|
||||||
|
|
||||||
build_cmsg(&connection->fds_out, cmsg, &clen); |
|
||||||
|
|
||||||
msg.msg_name = NULL; |
|
||||||
msg.msg_namelen = 0; |
|
||||||
msg.msg_iov = iov; |
|
||||||
msg.msg_iovlen = count; |
|
||||||
msg.msg_control = cmsg; |
|
||||||
msg.msg_controllen = clen; |
|
||||||
msg.msg_flags = 0; |
|
||||||
|
|
||||||
do { |
|
||||||
len = sendmsg(connection->fd, &msg, MSG_NOSIGNAL); |
|
||||||
} while (len < 0 && errno == EINTR); |
|
||||||
|
|
||||||
if (len == -1 && errno == EPIPE) { |
|
||||||
return -1; |
|
||||||
} else if (len < 0) { |
|
||||||
fprintf(stderr, |
|
||||||
"write error for connection %p, fd %d: %m\n", |
|
||||||
connection, connection->fd); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
close_fds(&connection->fds_out); |
|
||||||
|
|
||||||
connection->out.tail += len; |
|
||||||
if (connection->out.tail == connection->out.head) |
|
||||||
connection->update(connection, |
|
||||||
WL_CONNECTION_READABLE, |
|
||||||
connection->data); |
|
||||||
} |
|
||||||
|
|
||||||
if (mask & WL_CONNECTION_READABLE) { |
|
||||||
wl_buffer_put_iov(&connection->in, iov, &count); |
|
||||||
|
|
||||||
msg.msg_name = NULL; |
|
||||||
msg.msg_namelen = 0; |
|
||||||
msg.msg_iov = iov; |
|
||||||
msg.msg_iovlen = count; |
|
||||||
msg.msg_control = cmsg; |
|
||||||
msg.msg_controllen = sizeof cmsg; |
|
||||||
msg.msg_flags = 0; |
|
||||||
|
|
||||||
do { |
|
||||||
len = recvmsg(connection->fd, &msg, 0); |
|
||||||
} while (len < 0 && errno == EINTR); |
|
||||||
|
|
||||||
if (len < 0) { |
|
||||||
fprintf(stderr, |
|
||||||
"read error from connection %p: %m (%d)\n", |
|
||||||
connection, errno); |
|
||||||
return -1; |
|
||||||
} else if (len == 0) { |
|
||||||
/* FIXME: Handle this better? */ |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
decode_cmsg(&connection->fds_in, &msg); |
|
||||||
|
|
||||||
connection->in.head += len; |
|
||||||
}
|
|
||||||
|
|
||||||
return connection->in.head - connection->in.tail; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
wl_connection_write(struct wl_connection *connection, |
|
||||||
const void *data, size_t count) |
|
||||||
{ |
|
||||||
wl_buffer_put(&connection->out, data, count); |
|
||||||
|
|
||||||
if (connection->out.head - connection->out.tail == count) |
|
||||||
connection->update(connection, |
|
||||||
WL_CONNECTION_READABLE | |
|
||||||
WL_CONNECTION_WRITABLE, |
|
||||||
connection->data); |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
wl_message_size_extra(const struct wl_message *message) |
|
||||||
{ |
|
||||||
int i, extra; |
|
||||||
|
|
||||||
for (i = 0, extra = 0; message->signature[i]; i++) { |
|
||||||
|
|
||||||
switch (message->signature[i]) { |
|
||||||
case 's': |
|
||||||
case 'o': |
|
||||||
extra += sizeof (void *); |
|
||||||
break; |
|
||||||
case 'a': |
|
||||||
extra += sizeof (void *) + sizeof (struct wl_array); |
|
||||||
break; |
|
||||||
case 'h': |
|
||||||
extra += sizeof (uint32_t); |
|
||||||
break; |
|
||||||
default: |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return extra; |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_closure * |
|
||||||
wl_connection_vmarshal(struct wl_connection *connection, |
|
||||||
struct wl_object *sender, |
|
||||||
uint32_t opcode, va_list ap, |
|
||||||
const struct wl_message *message) |
|
||||||
{ |
|
||||||
struct wl_closure *closure = &connection->send_closure; |
|
||||||
struct wl_object **objectp, *object; |
|
||||||
uint32_t length, *p, *start, size; |
|
||||||
int dup_fd; |
|
||||||
struct wl_array **arrayp, *array; |
|
||||||
const char **sp, *s; |
|
||||||
char *extra; |
|
||||||
int i, count, fd, extra_size, *fd_ptr; |
|
||||||
|
|
||||||
extra_size = wl_message_size_extra(message); |
|
||||||
count = strlen(message->signature) + 2; |
|
||||||
extra = (char *) closure->buffer; |
|
||||||
start = &closure->buffer[DIV_ROUNDUP(extra_size, sizeof *p)]; |
|
||||||
p = &start[2]; |
|
||||||
for (i = 2; i < count; i++) { |
|
||||||
switch (message->signature[i - 2]) { |
|
||||||
case 'u': |
|
||||||
closure->types[i] = &ffi_type_uint32; |
|
||||||
closure->args[i] = p; |
|
||||||
*p++ = va_arg(ap, uint32_t); |
|
||||||
break; |
|
||||||
case 'i': |
|
||||||
closure->types[i] = &ffi_type_sint32; |
|
||||||
closure->args[i] = p; |
|
||||||
*p++ = va_arg(ap, int32_t); |
|
||||||
break; |
|
||||||
case 's': |
|
||||||
closure->types[i] = &ffi_type_pointer; |
|
||||||
closure->args[i] = extra; |
|
||||||
sp = (const char **) extra; |
|
||||||
extra += sizeof *sp; |
|
||||||
|
|
||||||
s = va_arg(ap, const char *); |
|
||||||
length = s ? strlen(s) + 1: 0; |
|
||||||
*p++ = length; |
|
||||||
|
|
||||||
if (length > 0) |
|
||||||
*sp = (const char *) p; |
|
||||||
else |
|
||||||
*sp = NULL; |
|
||||||
|
|
||||||
memcpy(p, s, length); |
|
||||||
p += DIV_ROUNDUP(length, sizeof *p); |
|
||||||
break; |
|
||||||
case 'o': |
|
||||||
closure->types[i] = &ffi_type_pointer; |
|
||||||
closure->args[i] = extra; |
|
||||||
objectp = (struct wl_object **) extra; |
|
||||||
extra += sizeof *objectp; |
|
||||||
|
|
||||||
object = va_arg(ap, struct wl_object *); |
|
||||||
*objectp = object; |
|
||||||
*p++ = object ? object->id : 0; |
|
||||||
break; |
|
||||||
|
|
||||||
case 'n': |
|
||||||
closure->types[i] = &ffi_type_uint32; |
|
||||||
closure->args[i] = p; |
|
||||||
object = va_arg(ap, struct wl_object *); |
|
||||||
*p++ = object->id; |
|
||||||
break; |
|
||||||
|
|
||||||
case 'a': |
|
||||||
closure->types[i] = &ffi_type_pointer; |
|
||||||
closure->args[i] = extra; |
|
||||||
arrayp = (struct wl_array **) extra; |
|
||||||
extra += sizeof *arrayp; |
|
||||||
|
|
||||||
*arrayp = (struct wl_array *) extra; |
|
||||||
extra += sizeof **arrayp; |
|
||||||
|
|
||||||
array = va_arg(ap, struct wl_array *); |
|
||||||
if (array == NULL || array->size == 0) { |
|
||||||
*p++ = 0; |
|
||||||
break; |
|
||||||
} |
|
||||||
*p++ = array->size; |
|
||||||
memcpy(p, array->data, array->size); |
|
||||||
|
|
||||||
(*arrayp)->size = array->size; |
|
||||||
(*arrayp)->alloc = array->alloc; |
|
||||||
(*arrayp)->data = p; |
|
||||||
|
|
||||||
p += DIV_ROUNDUP(array->size, sizeof *p); |
|
||||||
break; |
|
||||||
|
|
||||||
case 'h': |
|
||||||
closure->types[i] = &ffi_type_sint; |
|
||||||
closure->args[i] = extra; |
|
||||||
fd_ptr = (int *) extra; |
|
||||||
extra += sizeof *fd_ptr; |
|
||||||
|
|
||||||
fd = va_arg(ap, int); |
|
||||||
dup_fd = dup(fd); |
|
||||||
if (dup_fd < 0) { |
|
||||||
fprintf(stderr, "dup failed: %m"); |
|
||||||
abort(); |
|
||||||
} |
|
||||||
*fd_ptr = dup_fd; |
|
||||||
wl_buffer_put(&connection->fds_out, |
|
||||||
&dup_fd, sizeof dup_fd); |
|
||||||
break; |
|
||||||
default: |
|
||||||
assert(0); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
size = (p - start) * sizeof *p; |
|
||||||
start[0] = sender->id; |
|
||||||
start[1] = opcode | (size << 16); |
|
||||||
|
|
||||||
closure->start = start; |
|
||||||
closure->message = message; |
|
||||||
closure->count = count; |
|
||||||
|
|
||||||
return closure; |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_closure * |
|
||||||
wl_connection_demarshal(struct wl_connection *connection, |
|
||||||
uint32_t size, |
|
||||||
struct wl_hash_table *objects, |
|
||||||
const struct wl_message *message) |
|
||||||
{ |
|
||||||
uint32_t *p, *next, *end, length; |
|
||||||
int *fd; |
|
||||||
char *extra, **s; |
|
||||||
int i, count, extra_space; |
|
||||||
struct wl_object **object; |
|
||||||
struct wl_array **array; |
|
||||||
struct wl_closure *closure = &connection->receive_closure; |
|
||||||
|
|
||||||
count = strlen(message->signature) + 2; |
|
||||||
if (count > ARRAY_LENGTH(closure->types)) { |
|
||||||
printf("too many args (%d)\n", count); |
|
||||||
assert(0); |
|
||||||
} |
|
||||||
|
|
||||||
extra_space = wl_message_size_extra(message); |
|
||||||
if (sizeof closure->buffer < size + extra_space) { |
|
||||||
printf("request too big, should malloc tmp buffer here\n"); |
|
||||||
assert(0); |
|
||||||
} |
|
||||||
|
|
||||||
closure->message = message; |
|
||||||
closure->types[0] = &ffi_type_pointer; |
|
||||||
closure->types[1] = &ffi_type_pointer; |
|
||||||
|
|
||||||
wl_connection_copy(connection, closure->buffer, size); |
|
||||||
p = &closure->buffer[2]; |
|
||||||
end = (uint32_t *) ((char *) (p + size)); |
|
||||||
extra = (char *) end; |
|
||||||
for (i = 2; i < count; i++) { |
|
||||||
if (p + 1 > end) { |
|
||||||
printf("message too short, " |
|
||||||
"object (%d), message %s(%s)\n", |
|
||||||
*p, message->name, message->signature); |
|
||||||
errno = EINVAL; |
|
||||||
goto err; |
|
||||||
} |
|
||||||
|
|
||||||
switch (message->signature[i - 2]) { |
|
||||||
case 'u': |
|
||||||
closure->types[i] = &ffi_type_uint32; |
|
||||||
closure->args[i] = p++; |
|
||||||
break; |
|
||||||
case 'i': |
|
||||||
closure->types[i] = &ffi_type_sint32; |
|
||||||
closure->args[i] = p++; |
|
||||||
break; |
|
||||||
case 's': |
|
||||||
closure->types[i] = &ffi_type_pointer; |
|
||||||
length = *p++; |
|
||||||
|
|
||||||
next = p + DIV_ROUNDUP(length, sizeof *p); |
|
||||||
if (next > end) { |
|
||||||
printf("message too short, " |
|
||||||
"object (%d), message %s(%s)\n", |
|
||||||
*p, message->name, message->signature); |
|
||||||
errno = EINVAL; |
|
||||||
goto err; |
|
||||||
} |
|
||||||
|
|
||||||
s = (char **) extra; |
|
||||||
extra += sizeof *s; |
|
||||||
closure->args[i] = s; |
|
||||||
|
|
||||||
if (length == 0) { |
|
||||||
*s = NULL; |
|
||||||
} else { |
|
||||||
*s = (char *) p; |
|
||||||
} |
|
||||||
|
|
||||||
if (length > 0 && (*s)[length - 1] != '\0') { |
|
||||||
printf("string not nul-terminated, " |
|
||||||
"message %s(%s)\n", |
|
||||||
message->name, message->signature); |
|
||||||
errno = EINVAL; |
|
||||||
goto err; |
|
||||||
} |
|
||||||
p = next; |
|
||||||
break; |
|
||||||
case 'o': |
|
||||||
closure->types[i] = &ffi_type_pointer; |
|
||||||
object = (struct wl_object **) extra; |
|
||||||
extra += sizeof *object; |
|
||||||
closure->args[i] = object; |
|
||||||
|
|
||||||
*object = wl_hash_table_lookup(objects, *p); |
|
||||||
if (*object == NULL && *p != 0) { |
|
||||||
printf("unknown object (%d), message %s(%s)\n", |
|
||||||
*p, message->name, message->signature); |
|
||||||
errno = EINVAL; |
|
||||||
goto err; |
|
||||||
} |
|
||||||
|
|
||||||
p++; |
|
||||||
break; |
|
||||||
case 'n': |
|
||||||
closure->types[i] = &ffi_type_uint32; |
|
||||||
closure->args[i] = p; |
|
||||||
object = wl_hash_table_lookup(objects, *p); |
|
||||||
if (object != NULL) { |
|
||||||
printf("not a new object (%d), " |
|
||||||
"message %s(%s)\n", |
|
||||||
*p, message->name, message->signature); |
|
||||||
errno = EINVAL; |
|
||||||
goto err; |
|
||||||
} |
|
||||||
p++; |
|
||||||
break; |
|
||||||
case 'a': |
|
||||||
closure->types[i] = &ffi_type_pointer; |
|
||||||
length = *p++; |
|
||||||
|
|
||||||
next = p + DIV_ROUNDUP(length, sizeof *p); |
|
||||||
if (next > end) { |
|
||||||
printf("message too short, " |
|
||||||
"object (%d), message %s(%s)\n", |
|
||||||
*p, message->name, message->signature); |
|
||||||
errno = EINVAL; |
|
||||||
goto err; |
|
||||||
} |
|
||||||
|
|
||||||
array = (struct wl_array **) extra; |
|
||||||
extra += sizeof *array; |
|
||||||
closure->args[i] = array; |
|
||||||
|
|
||||||
*array = (struct wl_array *) extra; |
|
||||||
extra += sizeof **array; |
|
||||||
|
|
||||||
(*array)->size = length; |
|
||||||
(*array)->alloc = 0; |
|
||||||
(*array)->data = p; |
|
||||||
p = next; |
|
||||||
break; |
|
||||||
case 'h': |
|
||||||
closure->types[i] = &ffi_type_sint; |
|
||||||
|
|
||||||
fd = (int *) extra; |
|
||||||
extra += sizeof *fd; |
|
||||||
closure->args[i] = fd; |
|
||||||
|
|
||||||
wl_buffer_copy(&connection->fds_in, fd, sizeof *fd); |
|
||||||
connection->fds_in.tail += sizeof *fd; |
|
||||||
break; |
|
||||||
default: |
|
||||||
printf("unknown type\n"); |
|
||||||
assert(0); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
closure->count = i; |
|
||||||
ffi_prep_cif(&closure->cif, FFI_DEFAULT_ABI, |
|
||||||
closure->count, &ffi_type_uint32, closure->types); |
|
||||||
|
|
||||||
wl_connection_consume(connection, size); |
|
||||||
|
|
||||||
return closure; |
|
||||||
|
|
||||||
err: |
|
||||||
closure->count = i; |
|
||||||
wl_closure_destroy(closure); |
|
||||||
wl_connection_consume(connection, size); |
|
||||||
|
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
wl_closure_invoke(struct wl_closure *closure, |
|
||||||
struct wl_object *target, void (*func)(void), void *data) |
|
||||||
{ |
|
||||||
int result; |
|
||||||
|
|
||||||
closure->args[0] = &data; |
|
||||||
closure->args[1] = ⌖ |
|
||||||
|
|
||||||
ffi_call(&closure->cif, func, &result, closure->args); |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
wl_closure_send(struct wl_closure *closure, struct wl_connection *connection) |
|
||||||
{ |
|
||||||
uint32_t size; |
|
||||||
|
|
||||||
size = closure->start[1] >> 16; |
|
||||||
wl_connection_write(connection, closure->start, size); |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
wl_closure_print(struct wl_closure *closure, struct wl_object *target) |
|
||||||
{ |
|
||||||
union wl_value *value; |
|
||||||
int i; |
|
||||||
|
|
||||||
fprintf(stderr, "%s@%d.%s(", |
|
||||||
target->interface->name, target->id, |
|
||||||
closure->message->name); |
|
||||||
|
|
||||||
for (i = 2; i < closure->count; i++) { |
|
||||||
if (i > 2) |
|
||||||
fprintf(stderr, ", "); |
|
||||||
|
|
||||||
value = closure->args[i]; |
|
||||||
switch (closure->message->signature[i - 2]) { |
|
||||||
case 'u': |
|
||||||
fprintf(stderr, "%u", value->uint32); |
|
||||||
break; |
|
||||||
case 'i': |
|
||||||
fprintf(stderr, "%d", value->uint32); |
|
||||||
break; |
|
||||||
case 's': |
|
||||||
fprintf(stderr, "\"%s\"", value->string); |
|
||||||
break; |
|
||||||
case 'o': |
|
||||||
if (value->object) |
|
||||||
fprintf(stderr, "%s@%u", |
|
||||||
value->object->interface->name, |
|
||||||
value->object->id); |
|
||||||
else |
|
||||||
fprintf(stderr, "nil"); |
|
||||||
break; |
|
||||||
case 'n': |
|
||||||
fprintf(stderr, "new id %u", value->uint32); |
|
||||||
break; |
|
||||||
case 'a': |
|
||||||
fprintf(stderr, "array"); |
|
||||||
break; |
|
||||||
case 'h': |
|
||||||
fprintf(stderr, "fd %d", value->uint32); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fprintf(stderr, ")\n"); |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
wl_closure_destroy(struct wl_closure *closure) |
|
||||||
{ |
|
||||||
} |
|
@ -1,68 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef _CONNECTION_H_ |
|
||||||
#define _CONNECTION_H_ |
|
||||||
|
|
||||||
#include <stdarg.h> |
|
||||||
#include "wayland-util.h" |
|
||||||
|
|
||||||
struct wl_connection; |
|
||||||
struct wl_closure; |
|
||||||
|
|
||||||
#define WL_CONNECTION_READABLE 0x01 |
|
||||||
#define WL_CONNECTION_WRITABLE 0x02 |
|
||||||
|
|
||||||
typedef int (*wl_connection_update_func_t)(struct wl_connection *connection, |
|
||||||
uint32_t mask, void *data); |
|
||||||
|
|
||||||
struct wl_connection *wl_connection_create(int fd, |
|
||||||
wl_connection_update_func_t update, |
|
||||||
void *data); |
|
||||||
void wl_connection_destroy(struct wl_connection *connection); |
|
||||||
void wl_connection_copy(struct wl_connection *connection, void *data, size_t size); |
|
||||||
void wl_connection_consume(struct wl_connection *connection, size_t size); |
|
||||||
int wl_connection_data(struct wl_connection *connection, uint32_t mask); |
|
||||||
void wl_connection_write(struct wl_connection *connection, const void *data, size_t count); |
|
||||||
|
|
||||||
struct wl_closure * |
|
||||||
wl_connection_vmarshal(struct wl_connection *connection, |
|
||||||
struct wl_object *sender, |
|
||||||
uint32_t opcode, va_list ap, |
|
||||||
const struct wl_message *message); |
|
||||||
|
|
||||||
struct wl_closure * |
|
||||||
wl_connection_demarshal(struct wl_connection *connection, |
|
||||||
uint32_t size, |
|
||||||
struct wl_hash_table *objects, |
|
||||||
const struct wl_message *message); |
|
||||||
void |
|
||||||
wl_closure_invoke(struct wl_closure *closure, |
|
||||||
struct wl_object *target, void (*func)(void), void *data); |
|
||||||
void |
|
||||||
wl_closure_send(struct wl_closure *closure, struct wl_connection *connection); |
|
||||||
void |
|
||||||
wl_closure_print(struct wl_closure *closure, struct wl_object *target); |
|
||||||
void |
|
||||||
wl_closure_destroy(struct wl_closure *closure); |
|
||||||
|
|
||||||
#endif |
|
@ -1,444 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <stddef.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <errno.h> |
|
||||||
#include <signal.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <string.h> |
|
||||||
#include <sys/socket.h> |
|
||||||
#include <sys/un.h> |
|
||||||
#include <sys/epoll.h> |
|
||||||
#include <sys/signalfd.h> |
|
||||||
#include <sys/timerfd.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <assert.h> |
|
||||||
#include "wayland-server.h" |
|
||||||
|
|
||||||
struct wl_event_loop { |
|
||||||
int epoll_fd; |
|
||||||
struct wl_list idle_list; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_event_source_interface { |
|
||||||
void (*dispatch)(struct wl_event_source *source, |
|
||||||
struct epoll_event *ep); |
|
||||||
int (*remove)(struct wl_event_source *source); |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_event_source { |
|
||||||
struct wl_event_source_interface *interface; |
|
||||||
struct wl_event_loop *loop; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_event_source_fd { |
|
||||||
struct wl_event_source base; |
|
||||||
int fd; |
|
||||||
wl_event_loop_fd_func_t func; |
|
||||||
void *data; |
|
||||||
}; |
|
||||||
|
|
||||||
static void |
|
||||||
wl_event_source_fd_dispatch(struct wl_event_source *source, |
|
||||||
struct epoll_event *ep) |
|
||||||
{ |
|
||||||
struct wl_event_source_fd *fd_source = (struct wl_event_source_fd *) source; |
|
||||||
uint32_t mask; |
|
||||||
|
|
||||||
mask = 0; |
|
||||||
if (ep->events & EPOLLIN) |
|
||||||
mask |= WL_EVENT_READABLE; |
|
||||||
if (ep->events & EPOLLOUT) |
|
||||||
mask |= WL_EVENT_WRITEABLE; |
|
||||||
|
|
||||||
fd_source->func(fd_source->fd, mask, fd_source->data); |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
wl_event_source_fd_remove(struct wl_event_source *source) |
|
||||||
{ |
|
||||||
struct wl_event_source_fd *fd_source = |
|
||||||
(struct wl_event_source_fd *) source; |
|
||||||
struct wl_event_loop *loop = source->loop; |
|
||||||
int fd; |
|
||||||
|
|
||||||
fd = fd_source->fd; |
|
||||||
free(source); |
|
||||||
|
|
||||||
return epoll_ctl(loop->epoll_fd, EPOLL_CTL_DEL, fd, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_event_source_interface fd_source_interface = { |
|
||||||
wl_event_source_fd_dispatch, |
|
||||||
wl_event_source_fd_remove |
|
||||||
}; |
|
||||||
|
|
||||||
WL_EXPORT struct wl_event_source * |
|
||||||
wl_event_loop_add_fd(struct wl_event_loop *loop, |
|
||||||
int fd, uint32_t mask, |
|
||||||
wl_event_loop_fd_func_t func, |
|
||||||
void *data) |
|
||||||
{ |
|
||||||
struct wl_event_source_fd *source; |
|
||||||
struct epoll_event ep; |
|
||||||
|
|
||||||
source = malloc(sizeof *source); |
|
||||||
if (source == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
source->base.interface = &fd_source_interface; |
|
||||||
source->base.loop = loop; |
|
||||||
source->fd = fd; |
|
||||||
source->func = func; |
|
||||||
source->data = data; |
|
||||||
|
|
||||||
memset(&ep, 0, sizeof ep); |
|
||||||
if (mask & WL_EVENT_READABLE) |
|
||||||
ep.events |= EPOLLIN; |
|
||||||
if (mask & WL_EVENT_WRITEABLE) |
|
||||||
ep.events |= EPOLLOUT; |
|
||||||
ep.data.ptr = source; |
|
||||||
|
|
||||||
if (epoll_ctl(loop->epoll_fd, EPOLL_CTL_ADD, fd, &ep) < 0) { |
|
||||||
free(source); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
return &source->base; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_event_source_fd_update(struct wl_event_source *source, uint32_t mask) |
|
||||||
{ |
|
||||||
struct wl_event_source_fd *fd_source = |
|
||||||
(struct wl_event_source_fd *) source; |
|
||||||
struct wl_event_loop *loop = source->loop; |
|
||||||
struct epoll_event ep; |
|
||||||
|
|
||||||
memset(&ep, 0, sizeof ep); |
|
||||||
if (mask & WL_EVENT_READABLE) |
|
||||||
ep.events |= EPOLLIN; |
|
||||||
if (mask & WL_EVENT_WRITEABLE) |
|
||||||
ep.events |= EPOLLOUT; |
|
||||||
ep.data.ptr = source; |
|
||||||
|
|
||||||
return epoll_ctl(loop->epoll_fd, |
|
||||||
EPOLL_CTL_MOD, fd_source->fd, &ep); |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_event_source_timer { |
|
||||||
struct wl_event_source base; |
|
||||||
int fd; |
|
||||||
wl_event_loop_timer_func_t func; |
|
||||||
void *data; |
|
||||||
}; |
|
||||||
|
|
||||||
static void |
|
||||||
wl_event_source_timer_dispatch(struct wl_event_source *source, |
|
||||||
struct epoll_event *ep) |
|
||||||
{ |
|
||||||
struct wl_event_source_timer *timer_source = |
|
||||||
(struct wl_event_source_timer *) source; |
|
||||||
uint64_t expires; |
|
||||||
|
|
||||||
read(timer_source->fd, &expires, sizeof expires); |
|
||||||
|
|
||||||
timer_source->func(timer_source->data); |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
wl_event_source_timer_remove(struct wl_event_source *source) |
|
||||||
{ |
|
||||||
struct wl_event_source_timer *timer_source = |
|
||||||
(struct wl_event_source_timer *) source; |
|
||||||
struct wl_event_loop *loop = source->loop; |
|
||||||
int fd; |
|
||||||
|
|
||||||
fd = timer_source->fd; |
|
||||||
free(source); |
|
||||||
|
|
||||||
return epoll_ctl(loop->epoll_fd, EPOLL_CTL_DEL, fd, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_event_source_interface timer_source_interface = { |
|
||||||
wl_event_source_timer_dispatch, |
|
||||||
wl_event_source_timer_remove |
|
||||||
}; |
|
||||||
|
|
||||||
WL_EXPORT struct wl_event_source * |
|
||||||
wl_event_loop_add_timer(struct wl_event_loop *loop, |
|
||||||
wl_event_loop_timer_func_t func, |
|
||||||
void *data) |
|
||||||
{ |
|
||||||
struct wl_event_source_timer *source; |
|
||||||
struct epoll_event ep; |
|
||||||
|
|
||||||
source = malloc(sizeof *source); |
|
||||||
if (source == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
source->base.interface = &timer_source_interface; |
|
||||||
source->base.loop = loop; |
|
||||||
|
|
||||||
source->fd = timerfd_create(CLOCK_MONOTONIC, 0); |
|
||||||
if (source->fd < 0) { |
|
||||||
fprintf(stderr, "could not create timerfd\n: %m"); |
|
||||||
free(source); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
source->func = func; |
|
||||||
source->data = data; |
|
||||||
|
|
||||||
memset(&ep, 0, sizeof ep); |
|
||||||
ep.events = EPOLLIN; |
|
||||||
ep.data.ptr = source; |
|
||||||
|
|
||||||
if (epoll_ctl(loop->epoll_fd, EPOLL_CTL_ADD, source->fd, &ep) < 0) { |
|
||||||
free(source); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
return &source->base; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_event_source_timer_update(struct wl_event_source *source, int ms_delay) |
|
||||||
{ |
|
||||||
struct wl_event_source_timer *timer_source = |
|
||||||
(struct wl_event_source_timer *) source; |
|
||||||
struct itimerspec its; |
|
||||||
|
|
||||||
its.it_interval.tv_sec = 0; |
|
||||||
its.it_interval.tv_nsec = 0; |
|
||||||
its.it_value.tv_sec = 0; |
|
||||||
its.it_value.tv_nsec = ms_delay * 1000 * 1000; |
|
||||||
if (timerfd_settime(timer_source->fd, 0, &its, NULL) < 0) { |
|
||||||
fprintf(stderr, "could not set timerfd\n: %m"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_event_source_signal { |
|
||||||
struct wl_event_source base; |
|
||||||
int fd; |
|
||||||
int signal_number; |
|
||||||
wl_event_loop_signal_func_t func; |
|
||||||
void *data; |
|
||||||
}; |
|
||||||
|
|
||||||
static void |
|
||||||
wl_event_source_signal_dispatch(struct wl_event_source *source, |
|
||||||
struct epoll_event *ep) |
|
||||||
{ |
|
||||||
struct wl_event_source_signal *signal_source = |
|
||||||
(struct wl_event_source_signal *) source; |
|
||||||
struct signalfd_siginfo signal_info; |
|
||||||
|
|
||||||
read(signal_source->fd, &signal_info, sizeof signal_info); |
|
||||||
|
|
||||||
signal_source->func(signal_source->signal_number, signal_source->data); |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
wl_event_source_signal_remove(struct wl_event_source *source) |
|
||||||
{ |
|
||||||
struct wl_event_source_signal *signal_source = |
|
||||||
(struct wl_event_source_signal *) source; |
|
||||||
struct wl_event_loop *loop = source->loop; |
|
||||||
int fd; |
|
||||||
|
|
||||||
fd = signal_source->fd; |
|
||||||
free(source); |
|
||||||
|
|
||||||
return epoll_ctl(loop->epoll_fd, EPOLL_CTL_DEL, fd, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_event_source_interface signal_source_interface = { |
|
||||||
wl_event_source_signal_dispatch, |
|
||||||
wl_event_source_signal_remove |
|
||||||
}; |
|
||||||
|
|
||||||
WL_EXPORT struct wl_event_source * |
|
||||||
wl_event_loop_add_signal(struct wl_event_loop *loop, |
|
||||||
int signal_number, |
|
||||||
wl_event_loop_signal_func_t func, |
|
||||||
void *data) |
|
||||||
{ |
|
||||||
struct wl_event_source_signal *source; |
|
||||||
struct epoll_event ep; |
|
||||||
sigset_t mask; |
|
||||||
|
|
||||||
source = malloc(sizeof *source); |
|
||||||
if (source == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
source->base.interface = &signal_source_interface; |
|
||||||
source->base.loop = loop; |
|
||||||
|
|
||||||
sigemptyset(&mask); |
|
||||||
sigaddset(&mask, signal_number); |
|
||||||
source->fd = signalfd(-1, &mask, 0); |
|
||||||
if (source->fd < 0) { |
|
||||||
fprintf(stderr, "could not create fd to watch signal\n: %m"); |
|
||||||
free(source); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
sigprocmask(SIG_BLOCK, &mask, NULL); |
|
||||||
|
|
||||||
source->func = func; |
|
||||||
source->data = data; |
|
||||||
|
|
||||||
memset(&ep, 0, sizeof ep); |
|
||||||
ep.events = EPOLLIN; |
|
||||||
ep.data.ptr = source; |
|
||||||
|
|
||||||
if (epoll_ctl(loop->epoll_fd, EPOLL_CTL_ADD, source->fd, &ep) < 0) { |
|
||||||
free(source); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
return &source->base; |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_event_source_idle { |
|
||||||
struct wl_event_source base; |
|
||||||
struct wl_list link; |
|
||||||
wl_event_loop_idle_func_t func; |
|
||||||
void *data; |
|
||||||
}; |
|
||||||
|
|
||||||
static void |
|
||||||
wl_event_source_idle_dispatch(struct wl_event_source *source, |
|
||||||
struct epoll_event *ep) |
|
||||||
{ |
|
||||||
assert(0); |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
wl_event_source_idle_remove(struct wl_event_source *source) |
|
||||||
{ |
|
||||||
struct wl_event_source_idle *idle_source = |
|
||||||
(struct wl_event_source_idle *) source; |
|
||||||
|
|
||||||
wl_list_remove(&idle_source->link); |
|
||||||
free(source); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_event_source_interface idle_source_interface = { |
|
||||||
wl_event_source_idle_dispatch, |
|
||||||
wl_event_source_idle_remove |
|
||||||
}; |
|
||||||
|
|
||||||
WL_EXPORT struct wl_event_source * |
|
||||||
wl_event_loop_add_idle(struct wl_event_loop *loop, |
|
||||||
wl_event_loop_idle_func_t func, |
|
||||||
void *data) |
|
||||||
{ |
|
||||||
struct wl_event_source_idle *source; |
|
||||||
|
|
||||||
source = malloc(sizeof *source); |
|
||||||
if (source == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
source->base.interface = &idle_source_interface; |
|
||||||
source->base.loop = loop; |
|
||||||
|
|
||||||
source->func = func; |
|
||||||
source->data = data; |
|
||||||
wl_list_insert(loop->idle_list.prev, &source->link); |
|
||||||
|
|
||||||
return &source->base; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_event_source_remove(struct wl_event_source *source) |
|
||||||
{ |
|
||||||
source->interface->remove(source); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT struct wl_event_loop * |
|
||||||
wl_event_loop_create(void) |
|
||||||
{ |
|
||||||
struct wl_event_loop *loop; |
|
||||||
|
|
||||||
loop = malloc(sizeof *loop); |
|
||||||
if (loop == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
loop->epoll_fd = epoll_create(16); |
|
||||||
if (loop->epoll_fd < 0) { |
|
||||||
free(loop); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
wl_list_init(&loop->idle_list); |
|
||||||
|
|
||||||
return loop; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_event_loop_destroy(struct wl_event_loop *loop) |
|
||||||
{ |
|
||||||
close(loop->epoll_fd); |
|
||||||
free(loop); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout) |
|
||||||
{ |
|
||||||
struct epoll_event ep[32]; |
|
||||||
struct wl_event_source *source; |
|
||||||
struct wl_event_source_idle *idle; |
|
||||||
int i, count; |
|
||||||
|
|
||||||
count = epoll_wait(loop->epoll_fd, ep, ARRAY_LENGTH(ep), timeout); |
|
||||||
if (count < 0) |
|
||||||
return -1; |
|
||||||
|
|
||||||
for (i = 0; i < count; i++) { |
|
||||||
source = ep[i].data.ptr; |
|
||||||
source->interface->dispatch(source, &ep[i]); |
|
||||||
} |
|
||||||
|
|
||||||
while (!wl_list_empty(&loop->idle_list)) { |
|
||||||
idle = container_of(loop->idle_list.next, |
|
||||||
struct wl_event_source_idle, link); |
|
||||||
wl_list_remove(&idle->link); |
|
||||||
idle->func(idle->data); |
|
||||||
free(idle); |
|
||||||
} |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_event_loop_get_fd(struct wl_event_loop *loop) |
|
||||||
{ |
|
||||||
return loop->epoll_fd; |
|
||||||
} |
|
@ -1,760 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2010 Intel Corporation |
|
||||||
* |
|
||||||
* This program 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 2 of the License, or |
|
||||||
* (at your option) any later version. |
|
||||||
* |
|
||||||
* This program 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 this program; if not, write to the Free Software Foundation, |
|
||||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
#include <string.h> |
|
||||||
#include <errno.h> |
|
||||||
#include <ctype.h> |
|
||||||
#include <expat.h> |
|
||||||
|
|
||||||
#include "wayland-util.h" |
|
||||||
|
|
||||||
static const char copyright[] = |
|
||||||
"/*\n" |
|
||||||
" * Copyright © 2010 Kristian Høgsberg\n" |
|
||||||
" *\n" |
|
||||||
" * Permission to use, copy, modify, distribute, and sell this software and its\n" |
|
||||||
" * documentation for any purpose is hereby granted without fee, provided that\n" |
|
||||||
" * the above copyright notice appear in all copies and that both that copyright\n" |
|
||||||
" * notice and this permission notice appear in supporting documentation, and\n" |
|
||||||
" * that the name of the copyright holders not be used in advertising or\n" |
|
||||||
" * publicity pertaining to distribution of the software without specific,\n" |
|
||||||
" * written prior permission. The copyright holders make no representations\n" |
|
||||||
" * about the suitability of this software for any purpose. It is provided \"as\n" |
|
||||||
" * is\" without express or implied warranty.\n" |
|
||||||
" *\n" |
|
||||||
" * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n" |
|
||||||
" * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO\n" |
|
||||||
" * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n" |
|
||||||
" * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,\n" |
|
||||||
" * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n" |
|
||||||
" * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE\n" |
|
||||||
" * OF THIS SOFTWARE.\n" |
|
||||||
" */\n"; |
|
||||||
|
|
||||||
static int |
|
||||||
usage(int ret) |
|
||||||
{ |
|
||||||
fprintf(stderr, "usage: ./scanner [header|code]\n"); |
|
||||||
exit(ret); |
|
||||||
} |
|
||||||
|
|
||||||
#define XML_BUFFER_SIZE 4096 |
|
||||||
|
|
||||||
struct protocol { |
|
||||||
char *name; |
|
||||||
char *uppercase_name; |
|
||||||
struct wl_list interface_list; |
|
||||||
}; |
|
||||||
|
|
||||||
struct interface { |
|
||||||
char *name; |
|
||||||
char *uppercase_name; |
|
||||||
int version; |
|
||||||
struct wl_list request_list; |
|
||||||
struct wl_list event_list; |
|
||||||
struct wl_list enumeration_list; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct message { |
|
||||||
char *name; |
|
||||||
char *uppercase_name; |
|
||||||
struct wl_list arg_list; |
|
||||||
struct wl_list link; |
|
||||||
int destructor; |
|
||||||
}; |
|
||||||
|
|
||||||
enum arg_type { |
|
||||||
NEW_ID, |
|
||||||
INT, |
|
||||||
UNSIGNED, |
|
||||||
STRING, |
|
||||||
OBJECT, |
|
||||||
ARRAY, |
|
||||||
FD |
|
||||||
}; |
|
||||||
|
|
||||||
struct arg { |
|
||||||
char *name; |
|
||||||
enum arg_type type; |
|
||||||
char *interface_name; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct enumeration { |
|
||||||
char *name; |
|
||||||
char *uppercase_name; |
|
||||||
struct wl_list entry_list; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct entry { |
|
||||||
char *name; |
|
||||||
char *uppercase_name; |
|
||||||
char *value; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct parse_context { |
|
||||||
struct protocol *protocol; |
|
||||||
struct interface *interface; |
|
||||||
struct message *message; |
|
||||||
struct enumeration *enumeration; |
|
||||||
}; |
|
||||||
|
|
||||||
static char * |
|
||||||
uppercase_dup(const char *src) |
|
||||||
{ |
|
||||||
char *u; |
|
||||||
int i; |
|
||||||
|
|
||||||
u = strdup(src); |
|
||||||
for (i = 0; u[i]; i++) |
|
||||||
u[i] = toupper(u[i]); |
|
||||||
u[i] = '\0'; |
|
||||||
|
|
||||||
return u; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
start_element(void *data, const char *element_name, const char **atts) |
|
||||||
{ |
|
||||||
struct parse_context *ctx = data; |
|
||||||
struct interface *interface; |
|
||||||
struct message *message; |
|
||||||
struct arg *arg; |
|
||||||
struct enumeration *enumeration; |
|
||||||
struct entry *entry; |
|
||||||
const char *name, *type, *interface_name, *value; |
|
||||||
int i, version; |
|
||||||
|
|
||||||
name = NULL; |
|
||||||
type = NULL; |
|
||||||
version = 0; |
|
||||||
interface_name = NULL; |
|
||||||
value = NULL; |
|
||||||
for (i = 0; atts[i]; i += 2) { |
|
||||||
if (strcmp(atts[i], "name") == 0) |
|
||||||
name = atts[i + 1]; |
|
||||||
if (strcmp(atts[i], "version") == 0) |
|
||||||
version = atoi(atts[i + 1]); |
|
||||||
if (strcmp(atts[i], "type") == 0) |
|
||||||
type = atts[i + 1]; |
|
||||||
if (strcmp(atts[i], "value") == 0) |
|
||||||
value = atts[i + 1]; |
|
||||||
if (strcmp(atts[i], "interface") == 0) |
|
||||||
interface_name = atts[i + 1]; |
|
||||||
} |
|
||||||
|
|
||||||
if (strcmp(element_name, "protocol") == 0) { |
|
||||||
if (name == NULL) { |
|
||||||
fprintf(stderr, "no protocol name given\n"); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
|
|
||||||
ctx->protocol->name = strdup(name); |
|
||||||
ctx->protocol->uppercase_name = uppercase_dup(name); |
|
||||||
} else if (strcmp(element_name, "interface") == 0) { |
|
||||||
if (name == NULL) { |
|
||||||
fprintf(stderr, "no interface name given\n"); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
|
|
||||||
if (version == 0) { |
|
||||||
fprintf(stderr, "no interface version given\n"); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
|
|
||||||
interface = malloc(sizeof *interface); |
|
||||||
interface->name = strdup(name); |
|
||||||
interface->uppercase_name = uppercase_dup(name); |
|
||||||
interface->version = version; |
|
||||||
wl_list_init(&interface->request_list); |
|
||||||
wl_list_init(&interface->event_list); |
|
||||||
wl_list_init(&interface->enumeration_list); |
|
||||||
wl_list_insert(ctx->protocol->interface_list.prev, |
|
||||||
&interface->link); |
|
||||||
ctx->interface = interface; |
|
||||||
} else if (strcmp(element_name, "request") == 0 || |
|
||||||
strcmp(element_name, "event") == 0) { |
|
||||||
if (name == NULL) { |
|
||||||
fprintf(stderr, "no request name given\n"); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
|
|
||||||
message = malloc(sizeof *message); |
|
||||||
message->name = strdup(name); |
|
||||||
message->uppercase_name = uppercase_dup(name); |
|
||||||
wl_list_init(&message->arg_list); |
|
||||||
|
|
||||||
if (strcmp(element_name, "request") == 0) |
|
||||||
wl_list_insert(ctx->interface->request_list.prev, |
|
||||||
&message->link); |
|
||||||
else |
|
||||||
wl_list_insert(ctx->interface->event_list.prev, |
|
||||||
&message->link); |
|
||||||
|
|
||||||
if (type != NULL && strcmp(type, "destructor") == 0) |
|
||||||
message->destructor = 1; |
|
||||||
else |
|
||||||
message->destructor = 0; |
|
||||||
|
|
||||||
ctx->message = message; |
|
||||||
} else if (strcmp(element_name, "arg") == 0) { |
|
||||||
arg = malloc(sizeof *arg); |
|
||||||
arg->name = strdup(name); |
|
||||||
|
|
||||||
if (strcmp(type, "int") == 0) |
|
||||||
arg->type = INT; |
|
||||||
else if (strcmp(type, "uint") == 0) |
|
||||||
arg->type = UNSIGNED; |
|
||||||
else if (strcmp(type, "string") == 0) |
|
||||||
arg->type = STRING; |
|
||||||
else if (strcmp(type, "array") == 0) |
|
||||||
arg->type = ARRAY; |
|
||||||
else if (strcmp(type, "fd") == 0) |
|
||||||
arg->type = FD; |
|
||||||
else if (strcmp(type, "new_id") == 0) { |
|
||||||
if (interface_name == NULL) { |
|
||||||
fprintf(stderr, "no interface name given\n"); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
arg->type = NEW_ID; |
|
||||||
arg->interface_name = strdup(interface_name); |
|
||||||
} else if (strcmp(type, "object") == 0) { |
|
||||||
if (interface_name == NULL) { |
|
||||||
fprintf(stderr, "no interface name given\n"); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
arg->type = OBJECT; |
|
||||||
arg->interface_name = strdup(interface_name); |
|
||||||
} else { |
|
||||||
fprintf(stderr, "unknown type: %s\n", type); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
|
|
||||||
wl_list_insert(ctx->message->arg_list.prev, &arg->link); |
|
||||||
} else if (strcmp(element_name, "enum") == 0) { |
|
||||||
if (name == NULL) { |
|
||||||
fprintf(stderr, "no enum name given\n"); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
|
|
||||||
enumeration = malloc(sizeof *enumeration); |
|
||||||
enumeration->name = strdup(name); |
|
||||||
enumeration->uppercase_name = uppercase_dup(name); |
|
||||||
wl_list_init(&enumeration->entry_list); |
|
||||||
|
|
||||||
wl_list_insert(ctx->interface->enumeration_list.prev, |
|
||||||
&enumeration->link); |
|
||||||
|
|
||||||
ctx->enumeration = enumeration; |
|
||||||
} else if (strcmp(element_name, "entry") == 0) { |
|
||||||
entry = malloc(sizeof *entry); |
|
||||||
entry->name = strdup(name); |
|
||||||
entry->uppercase_name = uppercase_dup(name); |
|
||||||
entry->value = strdup(value); |
|
||||||
wl_list_insert(ctx->enumeration->entry_list.prev, |
|
||||||
&entry->link); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
emit_opcodes(struct wl_list *message_list, struct interface *interface) |
|
||||||
{ |
|
||||||
struct message *m; |
|
||||||
int opcode; |
|
||||||
|
|
||||||
if (wl_list_empty(message_list)) |
|
||||||
return; |
|
||||||
|
|
||||||
opcode = 0; |
|
||||||
wl_list_for_each(m, message_list, link) |
|
||||||
printf("#define WL_%s_%s\t%d\n", |
|
||||||
interface->uppercase_name, m->uppercase_name, opcode++); |
|
||||||
|
|
||||||
printf("\n"); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
emit_type(struct arg *a) |
|
||||||
{ |
|
||||||
switch (a->type) { |
|
||||||
default: |
|
||||||
case INT: |
|
||||||
case FD: |
|
||||||
printf("int "); |
|
||||||
break; |
|
||||||
case NEW_ID: |
|
||||||
case UNSIGNED: |
|
||||||
printf("uint32_t "); |
|
||||||
break; |
|
||||||
case STRING: |
|
||||||
printf("const char *"); |
|
||||||
break; |
|
||||||
case OBJECT: |
|
||||||
printf("struct wl_%s *", a->interface_name); |
|
||||||
break; |
|
||||||
case ARRAY: |
|
||||||
printf("struct wl_array *"); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
emit_stubs(struct wl_list *message_list, struct interface *interface) |
|
||||||
{ |
|
||||||
struct message *m; |
|
||||||
struct arg *a, *ret; |
|
||||||
int has_destructor, has_destroy; |
|
||||||
|
|
||||||
/* We provide a hand written constructor for the display object */ |
|
||||||
if (strcmp(interface->name, "display") != 0) |
|
||||||
printf("static inline struct wl_%s *\n" |
|
||||||
"wl_%s_create(struct wl_display *display, uint32_t id)\n" |
|
||||||
"{\n" |
|
||||||
"\treturn (struct wl_%s *)\n" |
|
||||||
"\t\twl_proxy_create_for_id(display, &wl_%s_interface, id);\n" |
|
||||||
"}\n\n", |
|
||||||
interface->name, |
|
||||||
interface->name, |
|
||||||
interface->name, |
|
||||||
interface->name); |
|
||||||
|
|
||||||
printf("static inline void\n" |
|
||||||
"wl_%s_set_user_data(struct wl_%s *%s, void *user_data)\n" |
|
||||||
"{\n" |
|
||||||
"\twl_proxy_set_user_data((struct wl_proxy *) %s, user_data);\n" |
|
||||||
"}\n\n", |
|
||||||
interface->name, interface->name, interface->name, |
|
||||||
interface->name); |
|
||||||
|
|
||||||
printf("static inline void *\n" |
|
||||||
"wl_%s_get_user_data(struct wl_%s *%s)\n" |
|
||||||
"{\n" |
|
||||||
"\treturn wl_proxy_get_user_data((struct wl_proxy *) %s);\n" |
|
||||||
"}\n\n", |
|
||||||
interface->name, interface->name, interface->name, |
|
||||||
interface->name); |
|
||||||
|
|
||||||
has_destructor = 0; |
|
||||||
has_destroy = 0; |
|
||||||
wl_list_for_each(m, message_list, link) { |
|
||||||
if (m->destructor) |
|
||||||
has_destructor = 1; |
|
||||||
if (strcmp(m->name, "destroy)") == 0) |
|
||||||
has_destroy = 1; |
|
||||||
} |
|
||||||
|
|
||||||
if (!has_destructor && has_destroy) { |
|
||||||
fprintf(stderr, |
|
||||||
"interface %s has method named destroy but" |
|
||||||
"no destructor", interface->name); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
|
|
||||||
/* And we have a hand-written display destructor */ |
|
||||||
if (!has_destructor && strcmp(interface->name, "display") != 0) |
|
||||||
printf("static inline void\n" |
|
||||||
"wl_%s_destroy(struct wl_%s *%s)\n" |
|
||||||
"{\n" |
|
||||||
"\twl_proxy_destroy(" |
|
||||||
"(struct wl_proxy *) %s);\n" |
|
||||||
"}\n\n", |
|
||||||
interface->name, interface->name, interface->name, |
|
||||||
interface->name); |
|
||||||
|
|
||||||
if (wl_list_empty(message_list)) |
|
||||||
return; |
|
||||||
|
|
||||||
wl_list_for_each(m, message_list, link) { |
|
||||||
ret = NULL; |
|
||||||
wl_list_for_each(a, &m->arg_list, link) { |
|
||||||
if (a->type == NEW_ID) |
|
||||||
ret = a; |
|
||||||
} |
|
||||||
|
|
||||||
if (ret) |
|
||||||
printf("static inline struct wl_%s *\n", |
|
||||||
ret->interface_name); |
|
||||||
else |
|
||||||
printf("static inline void\n"); |
|
||||||
|
|
||||||
printf("wl_%s_%s(struct wl_%s *%s", |
|
||||||
interface->name, m->name, |
|
||||||
interface->name, interface->name); |
|
||||||
|
|
||||||
wl_list_for_each(a, &m->arg_list, link) { |
|
||||||
if (a->type == NEW_ID) |
|
||||||
continue; |
|
||||||
printf(", "); |
|
||||||
emit_type(a); |
|
||||||
printf("%s", a->name); |
|
||||||
} |
|
||||||
|
|
||||||
printf(")\n" |
|
||||||
"{\n"); |
|
||||||
if (ret) |
|
||||||
printf("\tstruct wl_proxy *%s;\n\n" |
|
||||||
"\t%s = wl_proxy_create(" |
|
||||||
"(struct wl_proxy *) %s,\n" |
|
||||||
"\t\t\t &wl_%s_interface);\n" |
|
||||||
"\tif (!%s)\n" |
|
||||||
"\t\treturn NULL;\n\n", |
|
||||||
ret->name, |
|
||||||
ret->name, |
|
||||||
interface->name, ret->interface_name, |
|
||||||
ret->name); |
|
||||||
|
|
||||||
printf("\twl_proxy_marshal((struct wl_proxy *) %s,\n" |
|
||||||
"\t\t\t WL_%s_%s", |
|
||||||
interface->name, |
|
||||||
interface->uppercase_name, |
|
||||||
m->uppercase_name); |
|
||||||
|
|
||||||
wl_list_for_each(a, &m->arg_list, link) { |
|
||||||
printf(", "); |
|
||||||
printf("%s", a->name); |
|
||||||
} |
|
||||||
printf(");\n"); |
|
||||||
|
|
||||||
if (m->destructor) |
|
||||||
printf("\n\twl_proxy_destroy(" |
|
||||||
"(struct wl_proxy *) %s);\n", |
|
||||||
interface->name); |
|
||||||
|
|
||||||
if (ret) |
|
||||||
printf("\n\treturn (struct wl_%s *) %s;\n", |
|
||||||
ret->interface_name, ret->name); |
|
||||||
|
|
||||||
printf("}\n\n"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static const char *indent(int n) |
|
||||||
{ |
|
||||||
const char *whitespace[] = { |
|
||||||
"\t\t\t\t\t\t\t\t\t\t\t\t", |
|
||||||
"\t\t\t\t\t\t\t\t\t\t\t\t ", |
|
||||||
"\t\t\t\t\t\t\t\t\t\t\t\t ", |
|
||||||
"\t\t\t\t\t\t\t\t\t\t\t\t ", |
|
||||||
"\t\t\t\t\t\t\t\t\t\t\t\t ", |
|
||||||
"\t\t\t\t\t\t\t\t\t\t\t\t ", |
|
||||||
"\t\t\t\t\t\t\t\t\t\t\t\t ", |
|
||||||
"\t\t\t\t\t\t\t\t\t\t\t\t " |
|
||||||
}; |
|
||||||
|
|
||||||
return whitespace[n % 8] + 12 - n / 8; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
emit_enumerations(struct interface *interface) |
|
||||||
{ |
|
||||||
struct enumeration *e; |
|
||||||
struct entry *entry; |
|
||||||
|
|
||||||
wl_list_for_each(e, &interface->enumeration_list, link) { |
|
||||||
printf("#ifndef WL_%s_%s_ENUM\n", |
|
||||||
interface->uppercase_name, e->uppercase_name); |
|
||||||
printf("#define WL_%s_%s_ENUM\n", |
|
||||||
interface->uppercase_name, e->uppercase_name); |
|
||||||
printf("enum wl_%s_%s {\n", interface->name, e->name); |
|
||||||
wl_list_for_each(entry, &e->entry_list, link) |
|
||||||
printf("\tWL_%s_%s_%s = %s,\n", |
|
||||||
interface->uppercase_name, |
|
||||||
e->uppercase_name, |
|
||||||
entry->uppercase_name, entry->value); |
|
||||||
printf("};\n"); |
|
||||||
printf("#endif /* WL_%s_%s_ENUM */\n\n", |
|
||||||
interface->uppercase_name, e->uppercase_name); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
emit_structs(struct wl_list *message_list, struct interface *interface) |
|
||||||
{ |
|
||||||
struct message *m; |
|
||||||
struct arg *a; |
|
||||||
int is_interface, n; |
|
||||||
|
|
||||||
if (wl_list_empty(message_list)) |
|
||||||
return; |
|
||||||
|
|
||||||
is_interface = message_list == &interface->request_list; |
|
||||||
printf("struct wl_%s_%s {\n", interface->name, |
|
||||||
is_interface ? "interface" : "listener"); |
|
||||||
|
|
||||||
wl_list_for_each(m, message_list, link) { |
|
||||||
printf("\tvoid (*%s)(", m->name); |
|
||||||
|
|
||||||
n = strlen(m->name) + 17; |
|
||||||
if (is_interface) { |
|
||||||
printf("struct wl_client *client,\n" |
|
||||||
"%sstruct wl_%s *%s", |
|
||||||
indent(n), |
|
||||||
interface->name, interface->name); |
|
||||||
} else { |
|
||||||
printf("void *data,\n"), |
|
||||||
printf("%sstruct wl_%s *%s", |
|
||||||
indent(n), interface->name, interface->name); |
|
||||||
} |
|
||||||
|
|
||||||
wl_list_for_each(a, &m->arg_list, link) { |
|
||||||
printf(",\n%s", indent(n)); |
|
||||||
|
|
||||||
emit_type(a); |
|
||||||
printf("%s", a->name); |
|
||||||
} |
|
||||||
|
|
||||||
printf(");\n"); |
|
||||||
} |
|
||||||
|
|
||||||
printf("};\n\n"); |
|
||||||
|
|
||||||
if (!is_interface) { |
|
||||||
printf("static inline int\n" |
|
||||||
"wl_%s_add_listener(struct wl_%s *%s,\n" |
|
||||||
"%sconst struct wl_%s_listener *listener, void *data)\n" |
|
||||||
"{\n" |
|
||||||
"\treturn wl_proxy_add_listener((struct wl_proxy *) %s,\n" |
|
||||||
"%s(void (**)(void)) listener, data);\n" |
|
||||||
"}\n\n", |
|
||||||
interface->name, interface->name, interface->name, |
|
||||||
indent(17 + strlen(interface->name)), |
|
||||||
interface->name, |
|
||||||
interface->name, |
|
||||||
indent(37)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static const char client_prototypes[] = |
|
||||||
"struct wl_proxy;\n\n" |
|
||||||
|
|
||||||
"extern void\n" |
|
||||||
"wl_proxy_marshal(struct wl_proxy *p, uint32_t opcode, ...);\n" |
|
||||||
|
|
||||||
"extern struct wl_proxy *\n" |
|
||||||
"wl_proxy_create(struct wl_proxy *factory,\n" |
|
||||||
"\t\tconst struct wl_interface *interface);\n" |
|
||||||
|
|
||||||
"extern struct wl_proxy *\n" |
|
||||||
"wl_proxy_create_for_id(struct wl_display *display,\n" |
|
||||||
"\t\t const struct wl_interface *interface, uint32_t id);\n" |
|
||||||
|
|
||||||
"extern void\n" |
|
||||||
"wl_proxy_destroy(struct wl_proxy *proxy);\n\n" |
|
||||||
|
|
||||||
"extern int\n" |
|
||||||
"wl_proxy_add_listener(struct wl_proxy *proxy,\n" |
|
||||||
"\t\t void (**implementation)(void), void *data);\n\n" |
|
||||||
|
|
||||||
"extern void\n" |
|
||||||
"wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data);\n\n" |
|
||||||
|
|
||||||
"extern void *\n" |
|
||||||
"wl_proxy_get_user_data(struct wl_proxy *proxy);\n\n"; |
|
||||||
|
|
||||||
|
|
||||||
static void |
|
||||||
emit_header(struct protocol *protocol, int server) |
|
||||||
{ |
|
||||||
struct interface *i; |
|
||||||
const char *s = server ? "SERVER" : "CLIENT"; |
|
||||||
|
|
||||||
printf("%s\n\n" |
|
||||||
"#ifndef %s_%s_PROTOCOL_H\n" |
|
||||||
"#define %s_%s_PROTOCOL_H\n" |
|
||||||
"\n" |
|
||||||
"#ifdef __cplusplus\n" |
|
||||||
"extern \"C\" {\n" |
|
||||||
"#endif\n" |
|
||||||
"\n" |
|
||||||
"#include <stdint.h>\n" |
|
||||||
"#include <stddef.h>\n" |
|
||||||
"#include \"wayland-util.h\"\n\n" |
|
||||||
"struct wl_client;\n\n", |
|
||||||
copyright, |
|
||||||
protocol->uppercase_name, s, |
|
||||||
protocol->uppercase_name, s); |
|
||||||
|
|
||||||
wl_list_for_each(i, &protocol->interface_list, link) |
|
||||||
printf("struct wl_%s;\n", i->name); |
|
||||||
printf("\n"); |
|
||||||
|
|
||||||
if (!server) |
|
||||||
printf(client_prototypes); |
|
||||||
|
|
||||||
wl_list_for_each(i, &protocol->interface_list, link) { |
|
||||||
printf("extern const struct wl_interface " |
|
||||||
"wl_%s_interface;\n", |
|
||||||
i->name); |
|
||||||
} |
|
||||||
printf("\n"); |
|
||||||
|
|
||||||
wl_list_for_each(i, &protocol->interface_list, link) { |
|
||||||
|
|
||||||
emit_enumerations(i); |
|
||||||
|
|
||||||
if (server) { |
|
||||||
emit_structs(&i->request_list, i); |
|
||||||
emit_opcodes(&i->event_list, i); |
|
||||||
} else { |
|
||||||
emit_structs(&i->event_list, i); |
|
||||||
emit_opcodes(&i->request_list, i); |
|
||||||
emit_stubs(&i->request_list, i); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
printf("#ifdef __cplusplus\n" |
|
||||||
"}\n" |
|
||||||
"#endif\n" |
|
||||||
"\n" |
|
||||||
"#endif\n"); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
emit_messages(struct wl_list *message_list, |
|
||||||
struct interface *interface, const char *suffix) |
|
||||||
{ |
|
||||||
struct message *m; |
|
||||||
struct arg *a; |
|
||||||
|
|
||||||
if (wl_list_empty(message_list)) |
|
||||||
return; |
|
||||||
|
|
||||||
printf("static const struct wl_message " |
|
||||||
"%s_%s[] = {\n", |
|
||||||
interface->name, suffix); |
|
||||||
|
|
||||||
wl_list_for_each(m, message_list, link) { |
|
||||||
printf("\t{ \"%s\", \"", m->name); |
|
||||||
wl_list_for_each(a, &m->arg_list, link) { |
|
||||||
switch (a->type) { |
|
||||||
default: |
|
||||||
case INT: |
|
||||||
printf("i"); |
|
||||||
break; |
|
||||||
case NEW_ID: |
|
||||||
printf("n"); |
|
||||||
break; |
|
||||||
case UNSIGNED: |
|
||||||
printf("u"); |
|
||||||
break; |
|
||||||
case STRING: |
|
||||||
printf("s"); |
|
||||||
break; |
|
||||||
case OBJECT: |
|
||||||
printf("o"); |
|
||||||
break; |
|
||||||
case ARRAY: |
|
||||||
printf("a"); |
|
||||||
break; |
|
||||||
case FD: |
|
||||||
printf("h"); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
printf("\" },\n"); |
|
||||||
} |
|
||||||
|
|
||||||
printf("};\n\n"); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
emit_code(struct protocol *protocol) |
|
||||||
{ |
|
||||||
struct interface *i; |
|
||||||
|
|
||||||
printf("%s\n\n" |
|
||||||
"#include <stdlib.h>\n" |
|
||||||
"#include <stdint.h>\n" |
|
||||||
"#include \"wayland-util.h\"\n\n", |
|
||||||
copyright); |
|
||||||
|
|
||||||
wl_list_for_each(i, &protocol->interface_list, link) { |
|
||||||
|
|
||||||
emit_messages(&i->request_list, i, "requests"); |
|
||||||
emit_messages(&i->event_list, i, "events"); |
|
||||||
|
|
||||||
printf("WL_EXPORT const struct wl_interface " |
|
||||||
"wl_%s_interface = {\n" |
|
||||||
"\t\"%s\", %d,\n", |
|
||||||
i->name, i->name, i->version); |
|
||||||
|
|
||||||
if (!wl_list_empty(&i->request_list)) |
|
||||||
printf("\tARRAY_LENGTH(%s_requests), %s_requests,\n", |
|
||||||
i->name, i->name); |
|
||||||
else |
|
||||||
printf("\t0, NULL,\n"); |
|
||||||
|
|
||||||
if (!wl_list_empty(&i->event_list)) |
|
||||||
printf("\tARRAY_LENGTH(%s_events), %s_events,\n", |
|
||||||
i->name, i->name); |
|
||||||
else |
|
||||||
printf("\t0, NULL,\n"); |
|
||||||
|
|
||||||
printf("};\n\n"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int main(int argc, char *argv[]) |
|
||||||
{ |
|
||||||
struct parse_context ctx; |
|
||||||
struct protocol protocol; |
|
||||||
XML_Parser parser; |
|
||||||
int len; |
|
||||||
void *buf; |
|
||||||
|
|
||||||
if (argc != 2) |
|
||||||
usage(EXIT_FAILURE); |
|
||||||
|
|
||||||
wl_list_init(&protocol.interface_list); |
|
||||||
ctx.protocol = &protocol; |
|
||||||
|
|
||||||
parser = XML_ParserCreate(NULL); |
|
||||||
XML_SetUserData(parser, &ctx); |
|
||||||
if (parser == NULL) { |
|
||||||
fprintf(stderr, "failed to create parser\n"); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
|
|
||||||
XML_SetElementHandler(parser, start_element, NULL); |
|
||||||
do { |
|
||||||
buf = XML_GetBuffer(parser, XML_BUFFER_SIZE); |
|
||||||
len = fread(buf, 1, XML_BUFFER_SIZE, stdin); |
|
||||||
if (len < 0) { |
|
||||||
fprintf(stderr, "fread: %s\n", strerror(errno)); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
XML_ParseBuffer(parser, len, len == 0); |
|
||||||
|
|
||||||
} while (len > 0); |
|
||||||
|
|
||||||
XML_ParserFree(parser); |
|
||||||
|
|
||||||
if (strcmp(argv[1], "client-header") == 0) { |
|
||||||
emit_header(&protocol, 0); |
|
||||||
} else if (strcmp(argv[1], "server-header") == 0) { |
|
||||||
emit_header(&protocol, 1); |
|
||||||
} else if (strcmp(argv[1], "code") == 0) { |
|
||||||
emit_code(&protocol); |
|
||||||
} |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
@ -1,8 +0,0 @@ |
|||||||
%-protocol.c : $(top_srcdir)/protocol/%.xml |
|
||||||
$(AM_V_GEN)$(top_builddir)/wayland/scanner code < $< > $@
|
|
||||||
|
|
||||||
%-server-protocol.h : $(top_srcdir)/protocol/%.xml |
|
||||||
$(AM_V_GEN)$(top_builddir)/wayland/scanner server-header < $< > $@
|
|
||||||
|
|
||||||
%-client-protocol.h : $(top_srcdir)/protocol/%.xml |
|
||||||
$(AM_V_GEN)$(top_builddir)/wayland/scanner client-header < $< > $@
|
|
@ -1,565 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <stddef.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <errno.h> |
|
||||||
#include <string.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <sys/socket.h> |
|
||||||
#include <sys/un.h> |
|
||||||
#include <ctype.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <sys/poll.h> |
|
||||||
|
|
||||||
#include "wayland-client-protocol.h" |
|
||||||
#include "connection.h" |
|
||||||
#include "wayland-util.h" |
|
||||||
#include "wayland-client.h" |
|
||||||
|
|
||||||
struct wl_global_listener { |
|
||||||
wl_display_global_func_t handler; |
|
||||||
void *data; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_listener { |
|
||||||
void (**implementation)(void); |
|
||||||
void *data; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_proxy { |
|
||||||
struct wl_object object; |
|
||||||
struct wl_display *display; |
|
||||||
struct wl_list listener_list; |
|
||||||
void *user_data; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_sync_handler { |
|
||||||
wl_display_sync_func_t func; |
|
||||||
uint32_t key; |
|
||||||
void *data; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_frame_handler { |
|
||||||
wl_display_frame_func_t func; |
|
||||||
uint32_t key; |
|
||||||
void *data; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_display { |
|
||||||
struct wl_proxy proxy; |
|
||||||
struct wl_connection *connection; |
|
||||||
int fd; |
|
||||||
uint32_t id, id_count, next_range; |
|
||||||
uint32_t mask; |
|
||||||
struct wl_hash_table *objects; |
|
||||||
struct wl_listener listener; |
|
||||||
struct wl_list global_listener_list; |
|
||||||
|
|
||||||
struct wl_visual *argb_visual; |
|
||||||
struct wl_visual *premultiplied_argb_visual; |
|
||||||
struct wl_visual *rgb_visual; |
|
||||||
|
|
||||||
wl_display_update_func_t update; |
|
||||||
void *update_data; |
|
||||||
|
|
||||||
wl_display_global_func_t global_handler; |
|
||||||
void *global_handler_data; |
|
||||||
|
|
||||||
struct wl_list sync_list, frame_list; |
|
||||||
uint32_t key; |
|
||||||
}; |
|
||||||
|
|
||||||
static int wl_debug = 0; |
|
||||||
|
|
||||||
static int |
|
||||||
connection_update(struct wl_connection *connection, |
|
||||||
uint32_t mask, void *data) |
|
||||||
{ |
|
||||||
struct wl_display *display = data; |
|
||||||
|
|
||||||
display->mask = mask; |
|
||||||
if (display->update) |
|
||||||
return display->update(display->mask, |
|
||||||
display->update_data); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT struct wl_global_listener * |
|
||||||
wl_display_add_global_listener(struct wl_display *display, |
|
||||||
wl_display_global_func_t handler, void *data) |
|
||||||
{ |
|
||||||
struct wl_global_listener *listener; |
|
||||||
|
|
||||||
listener = malloc(sizeof *listener); |
|
||||||
if (listener == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
listener->handler = handler; |
|
||||||
listener->data = data; |
|
||||||
wl_list_insert(display->global_listener_list.prev, &listener->link); |
|
||||||
|
|
||||||
return listener; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_display_remove_global_listener(struct wl_display *display, |
|
||||||
struct wl_global_listener *listener) |
|
||||||
{ |
|
||||||
wl_list_remove(&listener->link); |
|
||||||
free(listener); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT struct wl_proxy * |
|
||||||
wl_proxy_create_for_id(struct wl_display *display, |
|
||||||
const struct wl_interface *interface, uint32_t id) |
|
||||||
{ |
|
||||||
struct wl_proxy *proxy; |
|
||||||
|
|
||||||
proxy = malloc(sizeof *proxy); |
|
||||||
if (proxy == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
proxy->object.interface = interface; |
|
||||||
proxy->object.id = id; |
|
||||||
proxy->display = display; |
|
||||||
wl_list_init(&proxy->listener_list); |
|
||||||
wl_hash_table_insert(display->objects, proxy->object.id, proxy); |
|
||||||
|
|
||||||
return proxy; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT struct wl_proxy * |
|
||||||
wl_proxy_create(struct wl_proxy *factory, |
|
||||||
const struct wl_interface *interface) |
|
||||||
{ |
|
||||||
return wl_proxy_create_for_id(factory->display, interface, |
|
||||||
wl_display_allocate_id(factory->display)); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_proxy_destroy(struct wl_proxy *proxy) |
|
||||||
{ |
|
||||||
struct wl_listener *listener, *next; |
|
||||||
|
|
||||||
wl_list_for_each_safe(listener, next, &proxy->listener_list, link) |
|
||||||
free(listener); |
|
||||||
|
|
||||||
wl_hash_table_remove(proxy->display->objects, proxy->object.id); |
|
||||||
free(proxy); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_proxy_add_listener(struct wl_proxy *proxy, |
|
||||||
void (**implementation)(void), void *data) |
|
||||||
{ |
|
||||||
struct wl_listener *listener; |
|
||||||
|
|
||||||
listener = malloc(sizeof *listener); |
|
||||||
if (listener == NULL) |
|
||||||
return -1; |
|
||||||
|
|
||||||
listener->implementation = (void (**)(void)) implementation; |
|
||||||
listener->data = data; |
|
||||||
wl_list_insert(proxy->listener_list.prev, &listener->link); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_proxy_marshal(struct wl_proxy *proxy, uint32_t opcode, ...) |
|
||||||
{ |
|
||||||
struct wl_closure *closure; |
|
||||||
va_list ap; |
|
||||||
|
|
||||||
va_start(ap, opcode); |
|
||||||
closure = wl_connection_vmarshal(proxy->display->connection, |
|
||||||
&proxy->object, opcode, ap, |
|
||||||
&proxy->object.interface->methods[opcode]); |
|
||||||
va_end(ap); |
|
||||||
|
|
||||||
wl_closure_send(closure, proxy->display->connection); |
|
||||||
|
|
||||||
if (wl_debug) { |
|
||||||
fprintf(stderr, " -> "); |
|
||||||
wl_closure_print(closure, &proxy->object); |
|
||||||
} |
|
||||||
|
|
||||||
wl_closure_destroy(closure); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
add_visual(struct wl_display *display, uint32_t id) |
|
||||||
{ |
|
||||||
struct wl_visual *visual; |
|
||||||
|
|
||||||
visual = (struct wl_visual *) |
|
||||||
wl_proxy_create_for_id(display, &wl_visual_interface, id); |
|
||||||
if (display->argb_visual == NULL) |
|
||||||
display->argb_visual = visual; |
|
||||||
else if (display->premultiplied_argb_visual == NULL) |
|
||||||
display->premultiplied_argb_visual = visual; |
|
||||||
else |
|
||||||
display->rgb_visual = visual; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT struct wl_visual * |
|
||||||
wl_display_get_argb_visual(struct wl_display *display) |
|
||||||
{ |
|
||||||
return display->argb_visual; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT struct wl_visual * |
|
||||||
wl_display_get_premultiplied_argb_visual(struct wl_display *display) |
|
||||||
{ |
|
||||||
return display->premultiplied_argb_visual; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT struct wl_visual * |
|
||||||
wl_display_get_rgb_visual(struct wl_display *display) |
|
||||||
{ |
|
||||||
return display->rgb_visual; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
display_handle_invalid_object(void *data, |
|
||||||
struct wl_display *display, uint32_t id) |
|
||||||
{ |
|
||||||
fprintf(stderr, "sent request to invalid object\n"); |
|
||||||
abort(); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
display_handle_invalid_method(void *data,
|
|
||||||
struct wl_display *display, |
|
||||||
uint32_t id, uint32_t opcode) |
|
||||||
{ |
|
||||||
fprintf(stderr, "sent invalid request opcode\n"); |
|
||||||
abort(); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
display_handle_no_memory(void *data, |
|
||||||
struct wl_display *display) |
|
||||||
{ |
|
||||||
fprintf(stderr, "server out of memory\n"); |
|
||||||
abort(); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
display_handle_global(void *data, |
|
||||||
struct wl_display *display, |
|
||||||
uint32_t id, const char *interface, uint32_t version) |
|
||||||
{ |
|
||||||
struct wl_global_listener *listener; |
|
||||||
|
|
||||||
if (strcmp(interface, "display") == 0) |
|
||||||
wl_hash_table_insert(display->objects, |
|
||||||
id, &display->proxy.object); |
|
||||||
else if (strcmp(interface, "visual") == 0) |
|
||||||
add_visual(display, id); |
|
||||||
|
|
||||||
wl_list_for_each(listener, &display->global_listener_list, link) |
|
||||||
(*listener->handler)(display, |
|
||||||
id, interface, version, listener->data); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
display_handle_range(void *data, |
|
||||||
struct wl_display *display, uint32_t range) |
|
||||||
{ |
|
||||||
display->next_range = range; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
display_handle_key(void *data, |
|
||||||
struct wl_display *display, uint32_t key, uint32_t time) |
|
||||||
{ |
|
||||||
struct wl_sync_handler *sync_handler; |
|
||||||
struct wl_frame_handler *frame_handler; |
|
||||||
|
|
||||||
sync_handler = container_of(display->sync_list.next, |
|
||||||
struct wl_sync_handler, link); |
|
||||||
if (!wl_list_empty(&display->sync_list) && sync_handler->key == key) { |
|
||||||
wl_list_remove(&sync_handler->link); |
|
||||||
sync_handler->func(sync_handler->data); |
|
||||||
free(sync_handler); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
frame_handler = container_of(display->frame_list. next, |
|
||||||
struct wl_frame_handler, link); |
|
||||||
if (!wl_list_empty(&display->frame_list) && |
|
||||||
frame_handler->key == key) { |
|
||||||
wl_list_remove(&frame_handler->link); |
|
||||||
frame_handler->func(frame_handler->data, time); |
|
||||||
free(frame_handler); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
fprintf(stderr, "unsolicited sync event, client gone?\n"); |
|
||||||
} |
|
||||||
|
|
||||||
static const struct wl_display_listener display_listener = { |
|
||||||
display_handle_invalid_object, |
|
||||||
display_handle_invalid_method, |
|
||||||
display_handle_no_memory, |
|
||||||
display_handle_global, |
|
||||||
display_handle_range, |
|
||||||
display_handle_key |
|
||||||
}; |
|
||||||
|
|
||||||
WL_EXPORT struct wl_display * |
|
||||||
wl_display_connect(const char *name) |
|
||||||
{ |
|
||||||
struct wl_display *display; |
|
||||||
struct sockaddr_un addr; |
|
||||||
socklen_t size; |
|
||||||
const char *runtime_dir; |
|
||||||
const char *debug; |
|
||||||
size_t name_size; |
|
||||||
|
|
||||||
debug = getenv("WAYLAND_DEBUG"); |
|
||||||
if (debug) |
|
||||||
wl_debug = 1; |
|
||||||
|
|
||||||
display = malloc(sizeof *display); |
|
||||||
if (display == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
memset(display, 0, sizeof *display); |
|
||||||
display->fd = socket(PF_LOCAL, SOCK_STREAM, 0); |
|
||||||
if (display->fd < 0) { |
|
||||||
free(display); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
runtime_dir = getenv("XDG_RUNTIME_DIR"); |
|
||||||
if (runtime_dir == NULL) { |
|
||||||
runtime_dir = "."; |
|
||||||
fprintf(stderr, |
|
||||||
"XDG_RUNTIME_DIR not set, falling back to %s\n", |
|
||||||
runtime_dir); |
|
||||||
} |
|
||||||
|
|
||||||
if (name == NULL) |
|
||||||
name = getenv("WAYLAND_DISPLAY"); |
|
||||||
if (name == NULL) |
|
||||||
name = "wayland-0"; |
|
||||||
|
|
||||||
memset(&addr, 0, sizeof addr); |
|
||||||
addr.sun_family = AF_LOCAL; |
|
||||||
name_size = |
|
||||||
snprintf(addr.sun_path, sizeof addr.sun_path, |
|
||||||
"%s/%s", runtime_dir, name) + 1; |
|
||||||
|
|
||||||
size = offsetof (struct sockaddr_un, sun_path) + name_size; |
|
||||||
|
|
||||||
if (connect(display->fd, (struct sockaddr *) &addr, size) < 0) { |
|
||||||
close(display->fd); |
|
||||||
free(display); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
display->objects = wl_hash_table_create(); |
|
||||||
wl_list_init(&display->global_listener_list); |
|
||||||
|
|
||||||
display->proxy.object.interface = &wl_display_interface; |
|
||||||
display->proxy.object.id = 1; |
|
||||||
display->proxy.display = display; |
|
||||||
wl_list_init(&display->proxy.listener_list); |
|
||||||
|
|
||||||
wl_list_init(&display->sync_list); |
|
||||||
wl_list_init(&display->frame_list); |
|
||||||
|
|
||||||
display->listener.implementation = (void(**)(void)) &display_listener; |
|
||||||
wl_list_insert(display->proxy.listener_list.prev, &display->listener.link); |
|
||||||
|
|
||||||
display->connection = wl_connection_create(display->fd, |
|
||||||
connection_update, |
|
||||||
display); |
|
||||||
|
|
||||||
return display; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_display_destroy(struct wl_display *display) |
|
||||||
{ |
|
||||||
wl_connection_destroy(display->connection); |
|
||||||
close(display->fd); |
|
||||||
free(display); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_display_get_fd(struct wl_display *display, |
|
||||||
wl_display_update_func_t update, void *data) |
|
||||||
{ |
|
||||||
display->update = update; |
|
||||||
display->update_data = data; |
|
||||||
|
|
||||||
display->update(display->mask, display->update_data); |
|
||||||
|
|
||||||
return display->fd; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_display_sync_callback(struct wl_display *display, |
|
||||||
wl_display_sync_func_t func, void *data) |
|
||||||
{ |
|
||||||
struct wl_sync_handler *handler; |
|
||||||
|
|
||||||
handler = malloc(sizeof *handler); |
|
||||||
if (handler == NULL) |
|
||||||
return -1; |
|
||||||
|
|
||||||
handler->func = func; |
|
||||||
handler->key = display->key++; |
|
||||||
handler->data = data; |
|
||||||
|
|
||||||
wl_list_insert(display->sync_list.prev, &handler->link); |
|
||||||
wl_display_sync(display, handler->key); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_display_frame_callback(struct wl_display *display, |
|
||||||
wl_display_frame_func_t func, void *data) |
|
||||||
{ |
|
||||||
struct wl_frame_handler *handler; |
|
||||||
|
|
||||||
handler = malloc(sizeof *handler); |
|
||||||
if (handler == NULL) |
|
||||||
return -1; |
|
||||||
|
|
||||||
handler->func = func; |
|
||||||
handler->key = display->key++; |
|
||||||
handler->data = data; |
|
||||||
|
|
||||||
wl_list_insert(display->frame_list.prev, &handler->link); |
|
||||||
wl_display_frame(display, handler->key); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
handle_event(struct wl_display *display, |
|
||||||
uint32_t id, uint32_t opcode, uint32_t size) |
|
||||||
{ |
|
||||||
uint32_t p[32]; |
|
||||||
struct wl_listener *listener; |
|
||||||
struct wl_proxy *proxy; |
|
||||||
struct wl_closure *closure; |
|
||||||
const struct wl_message *message; |
|
||||||
|
|
||||||
wl_connection_copy(display->connection, p, size); |
|
||||||
if (id == 1) |
|
||||||
proxy = &display->proxy; |
|
||||||
else |
|
||||||
proxy = wl_hash_table_lookup(display->objects, id); |
|
||||||
|
|
||||||
if (proxy == NULL) { |
|
||||||
wl_connection_consume(display->connection, size); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
message = &proxy->object.interface->events[opcode]; |
|
||||||
closure = wl_connection_demarshal(display->connection, |
|
||||||
size, display->objects, message); |
|
||||||
|
|
||||||
if (wl_debug) |
|
||||||
wl_closure_print(closure, &proxy->object); |
|
||||||
|
|
||||||
wl_list_for_each(listener, &proxy->listener_list, link) |
|
||||||
wl_closure_invoke(closure, &proxy->object, |
|
||||||
listener->implementation[opcode], |
|
||||||
listener->data); |
|
||||||
|
|
||||||
wl_closure_destroy(closure); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_display_iterate(struct wl_display *display, uint32_t mask) |
|
||||||
{ |
|
||||||
uint32_t p[2], object, opcode, size; |
|
||||||
int len; |
|
||||||
|
|
||||||
mask &= display->mask; |
|
||||||
if (mask == 0) { |
|
||||||
fprintf(stderr, |
|
||||||
"wl_display_iterate called with unsolicited flags"); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
len = wl_connection_data(display->connection, mask); |
|
||||||
while (len > 0) { |
|
||||||
if (len < sizeof p) |
|
||||||
break; |
|
||||||
|
|
||||||
wl_connection_copy(display->connection, p, sizeof p); |
|
||||||
object = p[0]; |
|
||||||
opcode = p[1] & 0xffff; |
|
||||||
size = p[1] >> 16; |
|
||||||
if (len < size) |
|
||||||
break; |
|
||||||
|
|
||||||
handle_event(display, object, opcode, size); |
|
||||||
len -= size; |
|
||||||
} |
|
||||||
|
|
||||||
if (len < 0) { |
|
||||||
fprintf(stderr, "read error: %m\n"); |
|
||||||
exit(EXIT_FAILURE); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT uint32_t |
|
||||||
wl_display_allocate_id(struct wl_display *display) |
|
||||||
{ |
|
||||||
if (display->id_count == 0) { |
|
||||||
display->id_count = 256; |
|
||||||
display->id = display->next_range; |
|
||||||
} |
|
||||||
|
|
||||||
display->id_count--; |
|
||||||
|
|
||||||
return display->id++; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data) |
|
||||||
{ |
|
||||||
proxy->user_data = user_data; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void * |
|
||||||
wl_proxy_get_user_data(struct wl_proxy *proxy) |
|
||||||
{ |
|
||||||
return proxy->user_data; |
|
||||||
} |
|
@ -1,75 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef _WAYLAND_CLIENT_H |
|
||||||
#define _WAYLAND_CLIENT_H |
|
||||||
|
|
||||||
#include "wayland-util.h" |
|
||||||
#include "wayland-client-protocol.h" |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
#define WL_DISPLAY_READABLE 0x01 |
|
||||||
#define WL_DISPLAY_WRITABLE 0x02 |
|
||||||
|
|
||||||
typedef int (*wl_display_update_func_t)(uint32_t mask, void *data); |
|
||||||
typedef void (*wl_display_sync_func_t)(void *data); |
|
||||||
typedef void (*wl_display_frame_func_t)(void *data, uint32_t time); |
|
||||||
|
|
||||||
struct wl_display *wl_display_connect(const char *name); |
|
||||||
void wl_display_destroy(struct wl_display *display); |
|
||||||
int wl_display_get_fd(struct wl_display *display, |
|
||||||
wl_display_update_func_t update, void *data); |
|
||||||
uint32_t wl_display_allocate_id(struct wl_display *display); |
|
||||||
void wl_display_iterate(struct wl_display *display, uint32_t mask); |
|
||||||
int wl_display_sync_callback(struct wl_display *display, |
|
||||||
wl_display_sync_func_t func, void *data); |
|
||||||
int wl_display_frame_callback(struct wl_display *display, |
|
||||||
wl_display_frame_func_t func, void *data); |
|
||||||
|
|
||||||
struct wl_global_listener; |
|
||||||
typedef void (*wl_display_global_func_t)(struct wl_display *display, |
|
||||||
uint32_t id, |
|
||||||
const char *interface, |
|
||||||
uint32_t version, |
|
||||||
void *data); |
|
||||||
void |
|
||||||
wl_display_remove_global_listener(struct wl_display *display, |
|
||||||
struct wl_global_listener *listener); |
|
||||||
|
|
||||||
struct wl_global_listener * |
|
||||||
wl_display_add_global_listener(struct wl_display *display, |
|
||||||
wl_display_global_func_t handler, void *data); |
|
||||||
struct wl_visual * |
|
||||||
wl_display_get_argb_visual(struct wl_display *display); |
|
||||||
struct wl_visual * |
|
||||||
wl_display_get_premultiplied_argb_visual(struct wl_display *display); |
|
||||||
struct wl_visual * |
|
||||||
wl_display_get_rgb_visual(struct wl_display *display); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif |
|
@ -1,10 +0,0 @@ |
|||||||
prefix=@prefix@ |
|
||||||
exec_prefix=${prefix} |
|
||||||
libdir=${exec_prefix}/lib |
|
||||||
includedir=${prefix}/include |
|
||||||
|
|
||||||
Name: Wayland Client |
|
||||||
Description: Wayland client side library |
|
||||||
Version: 0.1 |
|
||||||
Cflags: -I${includedir} |
|
||||||
Libs: -L${libdir} -lwayland-client |
|
@ -1,82 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2011 Kristian Høgsberg |
|
||||||
* Copyright © 2011 Benjamin Franzke |
|
||||||
* |
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef _WAYLAND_EGL_H |
|
||||||
#define _WAYLAND_EGL_H |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
#include <wayland-client.h> |
|
||||||
|
|
||||||
#define WL_EGL_PLATFORM 1 |
|
||||||
|
|
||||||
struct wl_egl_display; |
|
||||||
struct wl_egl_window; |
|
||||||
struct wl_egl_pixmap; |
|
||||||
|
|
||||||
struct wl_egl_display * |
|
||||||
wl_egl_display_create(struct wl_display *egl_display); |
|
||||||
|
|
||||||
void |
|
||||||
wl_egl_display_destroy(struct wl_egl_display *egl_display); |
|
||||||
|
|
||||||
struct wl_egl_window * |
|
||||||
wl_egl_window_create(struct wl_egl_display *egl_display, |
|
||||||
struct wl_surface *surface, |
|
||||||
int width, int height, |
|
||||||
struct wl_visual *visual); |
|
||||||
|
|
||||||
void |
|
||||||
wl_egl_window_destroy(struct wl_egl_window *egl_window); |
|
||||||
|
|
||||||
void |
|
||||||
wl_egl_window_resize(struct wl_egl_window *egl_window, |
|
||||||
int width, int height, |
|
||||||
int dx, int dy); |
|
||||||
|
|
||||||
void |
|
||||||
wl_egl_window_get_attached_size(struct wl_egl_window *egl_window, |
|
||||||
int *width, int *height); |
|
||||||
|
|
||||||
struct wl_egl_pixmap * |
|
||||||
wl_egl_pixmap_create(struct wl_egl_display *egl_display, |
|
||||||
int width, int height, |
|
||||||
struct wl_visual *visual, uint32_t flags); |
|
||||||
void |
|
||||||
wl_egl_pixmap_destroy(struct wl_egl_pixmap *egl_pixmap); |
|
||||||
|
|
||||||
struct wl_buffer * |
|
||||||
wl_egl_pixmap_create_buffer(struct wl_egl_display *egl_display, |
|
||||||
struct wl_egl_pixmap *egl_pixmap); |
|
||||||
|
|
||||||
void |
|
||||||
wl_egl_pixmap_flush(struct wl_egl_display *egl_display, |
|
||||||
struct wl_egl_pixmap *egl_pixmap); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif |
|
@ -1,296 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2009 Intel Corporation |
|
||||||
* Copyright © 1988-2004 Keith Packard and Bart Massey. |
|
||||||
* |
|
||||||
* 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 (including the next |
|
||||||
* paragraph) 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. |
|
||||||
* |
|
||||||
* Except as contained in this notice, the names of the authors |
|
||||||
* or their institutions shall not be used in advertising or |
|
||||||
* otherwise to promote the sale, use or other dealings in this |
|
||||||
* Software without prior written authorization from the |
|
||||||
* authors. |
|
||||||
* |
|
||||||
* Authors: |
|
||||||
* Eric Anholt <eric@anholt.net> |
|
||||||
* Keith Packard <keithp@keithp.com> |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
|
|
||||||
#include "wayland-util.h" |
|
||||||
|
|
||||||
struct hash_entry { |
|
||||||
uint32_t hash; |
|
||||||
void *data; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_hash_table { |
|
||||||
struct hash_entry *table; |
|
||||||
uint32_t size; |
|
||||||
uint32_t rehash; |
|
||||||
uint32_t max_entries; |
|
||||||
uint32_t size_index; |
|
||||||
uint32_t entries; |
|
||||||
uint32_t deleted_entries; |
|
||||||
}; |
|
||||||
|
|
||||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) |
|
||||||
|
|
||||||
/*
|
|
||||||
* From Knuth -- a good choice for hash/rehash values is p, p-2 where |
|
||||||
* p and p-2 are both prime. These tables are sized to have an extra 10% |
|
||||||
* free to avoid exponential performance degradation as the hash table fills |
|
||||||
*/ |
|
||||||
|
|
||||||
static const uint32_t deleted_data; |
|
||||||
|
|
||||||
static const struct { |
|
||||||
uint32_t max_entries, size, rehash; |
|
||||||
} hash_sizes[] = { |
|
||||||
{ 2, 5, 3 }, |
|
||||||
{ 4, 7, 5 }, |
|
||||||
{ 8, 13, 11 }, |
|
||||||
{ 16, 19, 17 }, |
|
||||||
{ 32, 43, 41 }, |
|
||||||
{ 64, 73, 71 }, |
|
||||||
{ 128, 151, 149 }, |
|
||||||
{ 256, 283, 281 }, |
|
||||||
{ 512, 571, 569 }, |
|
||||||
{ 1024, 1153, 1151 }, |
|
||||||
{ 2048, 2269, 2267 }, |
|
||||||
{ 4096, 4519, 4517 }, |
|
||||||
{ 8192, 9013, 9011 }, |
|
||||||
{ 16384, 18043, 18041 }, |
|
||||||
{ 32768, 36109, 36107 }, |
|
||||||
{ 65536, 72091, 72089 }, |
|
||||||
{ 131072, 144409, 144407 }, |
|
||||||
{ 262144, 288361, 288359 }, |
|
||||||
{ 524288, 576883, 576881 }, |
|
||||||
{ 1048576, 1153459, 1153457 }, |
|
||||||
{ 2097152, 2307163, 2307161 }, |
|
||||||
{ 4194304, 4613893, 4613891 }, |
|
||||||
{ 8388608, 9227641, 9227639 }, |
|
||||||
{ 16777216, 18455029, 18455027 }, |
|
||||||
{ 33554432, 36911011, 36911009 }, |
|
||||||
{ 67108864, 73819861, 73819859 }, |
|
||||||
{ 134217728, 147639589, 147639587 }, |
|
||||||
{ 268435456, 295279081, 295279079 }, |
|
||||||
{ 536870912, 590559793, 590559791 }, |
|
||||||
{ 1073741824, 1181116273, 1181116271}, |
|
||||||
{ 2147483648ul, 2362232233ul, 2362232231ul} |
|
||||||
}; |
|
||||||
|
|
||||||
static int |
|
||||||
entry_is_free(struct hash_entry *entry) |
|
||||||
{ |
|
||||||
return entry->data == NULL; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
entry_is_deleted(struct hash_entry *entry) |
|
||||||
{ |
|
||||||
return entry->data == &deleted_data; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
entry_is_present(struct hash_entry *entry) |
|
||||||
{ |
|
||||||
return entry->data != NULL && entry->data != &deleted_data; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT struct wl_hash_table * |
|
||||||
wl_hash_table_create(void) |
|
||||||
{ |
|
||||||
struct wl_hash_table *ht; |
|
||||||
|
|
||||||
ht = malloc(sizeof(*ht)); |
|
||||||
if (ht == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
ht->size_index = 0; |
|
||||||
ht->size = hash_sizes[ht->size_index].size; |
|
||||||
ht->rehash = hash_sizes[ht->size_index].rehash; |
|
||||||
ht->max_entries = hash_sizes[ht->size_index].max_entries; |
|
||||||
ht->table = calloc(ht->size, sizeof(*ht->table)); |
|
||||||
ht->entries = 0; |
|
||||||
ht->deleted_entries = 0; |
|
||||||
|
|
||||||
if (ht->table == NULL) { |
|
||||||
free(ht); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
return ht; |
|
||||||
} |
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees the given hash table. |
|
||||||
*/ |
|
||||||
WL_EXPORT void |
|
||||||
wl_hash_table_destroy(struct wl_hash_table *ht) |
|
||||||
{ |
|
||||||
if (!ht) |
|
||||||
return; |
|
||||||
|
|
||||||
free(ht->table); |
|
||||||
free(ht); |
|
||||||
} |
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a hash table entry with the given key and hash of that key. |
|
||||||
* |
|
||||||
* Returns NULL if no entry is found. Note that the data pointer may be |
|
||||||
* modified by the user. |
|
||||||
*/ |
|
||||||
static void * |
|
||||||
hash_table_search(struct wl_hash_table *ht, uint32_t hash) |
|
||||||
{ |
|
||||||
uint32_t hash_address; |
|
||||||
|
|
||||||
hash_address = hash % ht->size; |
|
||||||
do { |
|
||||||
uint32_t double_hash; |
|
||||||
|
|
||||||
struct hash_entry *entry = ht->table + hash_address; |
|
||||||
|
|
||||||
if (entry_is_free(entry)) { |
|
||||||
return NULL; |
|
||||||
} else if (entry_is_present(entry) && entry->hash == hash) { |
|
||||||
return entry; |
|
||||||
} |
|
||||||
|
|
||||||
double_hash = hash % ht->rehash; |
|
||||||
if (double_hash == 0) |
|
||||||
double_hash = 1; |
|
||||||
|
|
||||||
hash_address = (hash_address + double_hash) % ht->size; |
|
||||||
} while (hash_address != hash % ht->size); |
|
||||||
|
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void * |
|
||||||
wl_hash_table_lookup(struct wl_hash_table *ht, uint32_t hash) |
|
||||||
{ |
|
||||||
struct hash_entry *entry; |
|
||||||
|
|
||||||
entry = hash_table_search(ht, hash); |
|
||||||
if (entry != NULL) |
|
||||||
return entry->data; |
|
||||||
|
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
hash_table_rehash(struct wl_hash_table *ht, int new_size_index) |
|
||||||
{ |
|
||||||
struct wl_hash_table old_ht; |
|
||||||
struct hash_entry *table, *entry; |
|
||||||
|
|
||||||
if (new_size_index >= ARRAY_SIZE(hash_sizes)) |
|
||||||
return; |
|
||||||
|
|
||||||
table = calloc(hash_sizes[new_size_index].size, sizeof(*ht->table)); |
|
||||||
if (table == NULL) |
|
||||||
return; |
|
||||||
|
|
||||||
old_ht = *ht; |
|
||||||
|
|
||||||
ht->table = table; |
|
||||||
ht->size_index = new_size_index; |
|
||||||
ht->size = hash_sizes[ht->size_index].size; |
|
||||||
ht->rehash = hash_sizes[ht->size_index].rehash; |
|
||||||
ht->max_entries = hash_sizes[ht->size_index].max_entries; |
|
||||||
ht->entries = 0; |
|
||||||
ht->deleted_entries = 0; |
|
||||||
|
|
||||||
for (entry = old_ht.table; |
|
||||||
entry != old_ht.table + old_ht.size; |
|
||||||
entry++) { |
|
||||||
if (entry_is_present(entry)) { |
|
||||||
wl_hash_table_insert(ht, entry->hash, entry->data); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
free(old_ht.table); |
|
||||||
} |
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts the data with the given hash into the table. |
|
||||||
* |
|
||||||
* Note that insertion may rearrange the table on a resize or rehash, |
|
||||||
* so previously found hash_entries are no longer valid after this function. |
|
||||||
*/ |
|
||||||
WL_EXPORT int |
|
||||||
wl_hash_table_insert(struct wl_hash_table *ht, uint32_t hash, void *data) |
|
||||||
{ |
|
||||||
uint32_t hash_address; |
|
||||||
|
|
||||||
if (ht->entries >= ht->max_entries) { |
|
||||||
hash_table_rehash(ht, ht->size_index + 1); |
|
||||||
} else if (ht->deleted_entries + ht->entries >= ht->max_entries) { |
|
||||||
hash_table_rehash(ht, ht->size_index); |
|
||||||
} |
|
||||||
|
|
||||||
hash_address = hash % ht->size; |
|
||||||
do { |
|
||||||
struct hash_entry *entry = ht->table + hash_address; |
|
||||||
uint32_t double_hash; |
|
||||||
|
|
||||||
if (!entry_is_present(entry)) { |
|
||||||
if (entry_is_deleted(entry)) |
|
||||||
ht->deleted_entries--; |
|
||||||
entry->hash = hash; |
|
||||||
entry->data = data; |
|
||||||
ht->entries++; |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
double_hash = hash % ht->rehash; |
|
||||||
if (double_hash == 0) |
|
||||||
double_hash = 1; |
|
||||||
|
|
||||||
hash_address = (hash_address + double_hash) % ht->size; |
|
||||||
} while (hash_address != hash % ht->size); |
|
||||||
|
|
||||||
/* We could hit here if a required resize failed. An unchecked-malloc
|
|
||||||
* application could ignore this result. |
|
||||||
*/ |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/**
|
|
||||||
* This function deletes the given hash table entry. |
|
||||||
* |
|
||||||
* Note that deletion doesn't otherwise modify the table, so an iteration over |
|
||||||
* the table deleting entries is safe. |
|
||||||
*/ |
|
||||||
WL_EXPORT void |
|
||||||
wl_hash_table_remove(struct wl_hash_table *ht, uint32_t hash) |
|
||||||
{ |
|
||||||
struct hash_entry *entry; |
|
||||||
|
|
||||||
entry = hash_table_search(ht, hash); |
|
||||||
if (entry != NULL) { |
|
||||||
entry->data = (void *) &deleted_data; |
|
||||||
ht->entries--; |
|
||||||
ht->deleted_entries++; |
|
||||||
} |
|
||||||
} |
|
@ -1,726 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <stddef.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <stdarg.h> |
|
||||||
#include <errno.h> |
|
||||||
#include <string.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <sys/socket.h> |
|
||||||
#include <sys/un.h> |
|
||||||
#include <dlfcn.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <sys/time.h> |
|
||||||
#include <ffi.h> |
|
||||||
|
|
||||||
#include "wayland-server.h" |
|
||||||
#include "wayland-server-protocol.h" |
|
||||||
#include "connection.h" |
|
||||||
|
|
||||||
struct wl_socket { |
|
||||||
int fd; |
|
||||||
struct sockaddr_un addr; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_client { |
|
||||||
struct wl_connection *connection; |
|
||||||
struct wl_event_source *source; |
|
||||||
struct wl_display *display; |
|
||||||
struct wl_list resource_list; |
|
||||||
uint32_t id_count; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_display { |
|
||||||
struct wl_object object; |
|
||||||
struct wl_event_loop *loop; |
|
||||||
struct wl_hash_table *objects; |
|
||||||
int run; |
|
||||||
|
|
||||||
struct wl_list frame_list; |
|
||||||
uint32_t client_id_range; |
|
||||||
uint32_t id; |
|
||||||
|
|
||||||
struct wl_list global_list; |
|
||||||
struct wl_list socket_list; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_frame_listener { |
|
||||||
struct wl_resource resource; |
|
||||||
struct wl_client *client; |
|
||||||
uint32_t key; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_global { |
|
||||||
struct wl_object *object; |
|
||||||
wl_client_connect_func_t func; |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
static int wl_debug = 0; |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_client_post_event(struct wl_client *client, struct wl_object *sender, |
|
||||||
uint32_t opcode, ...) |
|
||||||
{ |
|
||||||
struct wl_closure *closure; |
|
||||||
va_list ap; |
|
||||||
|
|
||||||
va_start(ap, opcode); |
|
||||||
closure = wl_connection_vmarshal(client->connection, |
|
||||||
sender, opcode, ap, |
|
||||||
&sender->interface->events[opcode]); |
|
||||||
va_end(ap); |
|
||||||
|
|
||||||
wl_closure_send(closure, client->connection); |
|
||||||
|
|
||||||
if (wl_debug) { |
|
||||||
fprintf(stderr, " -> "); |
|
||||||
wl_closure_print(closure, sender); |
|
||||||
} |
|
||||||
|
|
||||||
wl_closure_destroy(closure); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
wl_client_connection_data(int fd, uint32_t mask, void *data) |
|
||||||
{ |
|
||||||
struct wl_client *client = data; |
|
||||||
struct wl_connection *connection = client->connection; |
|
||||||
struct wl_object *object; |
|
||||||
struct wl_closure *closure; |
|
||||||
const struct wl_message *message; |
|
||||||
uint32_t p[2], opcode, size; |
|
||||||
uint32_t cmask = 0; |
|
||||||
int len; |
|
||||||
|
|
||||||
if (mask & WL_EVENT_READABLE) |
|
||||||
cmask |= WL_CONNECTION_READABLE; |
|
||||||
if (mask & WL_EVENT_WRITEABLE) |
|
||||||
cmask |= WL_CONNECTION_WRITABLE; |
|
||||||
|
|
||||||
len = wl_connection_data(connection, cmask); |
|
||||||
if (len < 0) { |
|
||||||
wl_client_destroy(client); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
while (len >= sizeof p) { |
|
||||||
wl_connection_copy(connection, p, sizeof p); |
|
||||||
opcode = p[1] & 0xffff; |
|
||||||
size = p[1] >> 16; |
|
||||||
if (len < size) |
|
||||||
break; |
|
||||||
|
|
||||||
object = wl_hash_table_lookup(client->display->objects, p[0]); |
|
||||||
if (object == NULL) { |
|
||||||
wl_client_post_event(client, &client->display->object, |
|
||||||
WL_DISPLAY_INVALID_OBJECT, p[0]); |
|
||||||
wl_connection_consume(connection, size); |
|
||||||
len -= size; |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (opcode >= object->interface->method_count) { |
|
||||||
wl_client_post_event(client, &client->display->object, |
|
||||||
WL_DISPLAY_INVALID_METHOD, p[0], opcode); |
|
||||||
wl_connection_consume(connection, size); |
|
||||||
len -= size; |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
message = &object->interface->methods[opcode]; |
|
||||||
closure = wl_connection_demarshal(client->connection, size, |
|
||||||
client->display->objects, |
|
||||||
message); |
|
||||||
len -= size; |
|
||||||
|
|
||||||
if (closure == NULL && errno == EINVAL) { |
|
||||||
wl_client_post_event(client, &client->display->object, |
|
||||||
WL_DISPLAY_INVALID_METHOD, |
|
||||||
p[0], opcode); |
|
||||||
continue; |
|
||||||
} else if (closure == NULL && errno == ENOMEM) { |
|
||||||
wl_client_post_no_memory(client); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
if (wl_debug) |
|
||||||
wl_closure_print(closure, object); |
|
||||||
|
|
||||||
wl_closure_invoke(closure, object, |
|
||||||
object->implementation[opcode], client); |
|
||||||
|
|
||||||
wl_closure_destroy(closure); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
wl_client_connection_update(struct wl_connection *connection, |
|
||||||
uint32_t mask, void *data) |
|
||||||
{ |
|
||||||
struct wl_client *client = data; |
|
||||||
uint32_t emask = 0; |
|
||||||
|
|
||||||
if (mask & WL_CONNECTION_READABLE) |
|
||||||
emask |= WL_EVENT_READABLE; |
|
||||||
if (mask & WL_CONNECTION_WRITABLE) |
|
||||||
emask |= WL_EVENT_WRITEABLE; |
|
||||||
|
|
||||||
return wl_event_source_fd_update(client->source, emask); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT struct wl_display * |
|
||||||
wl_client_get_display(struct wl_client *client) |
|
||||||
{ |
|
||||||
return client->display; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
wl_display_post_range(struct wl_display *display, struct wl_client *client) |
|
||||||
{ |
|
||||||
wl_client_post_event(client, &client->display->object, |
|
||||||
WL_DISPLAY_RANGE, display->client_id_range); |
|
||||||
display->client_id_range += 256; |
|
||||||
client->id_count += 256; |
|
||||||
} |
|
||||||
|
|
||||||
static struct wl_client * |
|
||||||
wl_client_create(struct wl_display *display, int fd) |
|
||||||
{ |
|
||||||
struct wl_client *client; |
|
||||||
struct wl_global *global; |
|
||||||
|
|
||||||
client = malloc(sizeof *client); |
|
||||||
if (client == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
memset(client, 0, sizeof *client); |
|
||||||
client->display = display; |
|
||||||
client->source = wl_event_loop_add_fd(display->loop, fd, |
|
||||||
WL_EVENT_READABLE, |
|
||||||
wl_client_connection_data, client); |
|
||||||
client->connection = |
|
||||||
wl_connection_create(fd, wl_client_connection_update, client); |
|
||||||
|
|
||||||
wl_list_init(&client->resource_list); |
|
||||||
|
|
||||||
wl_display_post_range(display, client); |
|
||||||
|
|
||||||
wl_list_for_each(global, &display->global_list, link) |
|
||||||
wl_client_post_event(client, &client->display->object, |
|
||||||
WL_DISPLAY_GLOBAL, |
|
||||||
global->object, |
|
||||||
global->object->interface->name, |
|
||||||
global->object->interface->version); |
|
||||||
|
|
||||||
wl_list_for_each(global, &display->global_list, link) |
|
||||||
if (global->func) |
|
||||||
global->func(client, global->object); |
|
||||||
|
|
||||||
return client; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_client_add_resource(struct wl_client *client, |
|
||||||
struct wl_resource *resource) |
|
||||||
{ |
|
||||||
struct wl_display *display = client->display; |
|
||||||
|
|
||||||
if (client->id_count-- < 64) |
|
||||||
wl_display_post_range(display, client); |
|
||||||
|
|
||||||
wl_hash_table_insert(client->display->objects, |
|
||||||
resource->object.id, resource); |
|
||||||
wl_list_insert(client->resource_list.prev, &resource->link); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_client_post_no_memory(struct wl_client *client) |
|
||||||
{ |
|
||||||
wl_client_post_event(client, |
|
||||||
&client->display->object, |
|
||||||
WL_DISPLAY_NO_MEMORY); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_client_post_global(struct wl_client *client, struct wl_object *object) |
|
||||||
{ |
|
||||||
wl_client_post_event(client, |
|
||||||
&client->display->object, |
|
||||||
WL_DISPLAY_GLOBAL, |
|
||||||
object, |
|
||||||
object->interface->name, |
|
||||||
object->interface->version); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_resource_destroy(struct wl_resource *resource, struct wl_client *client) |
|
||||||
{ |
|
||||||
struct wl_display *display = client->display; |
|
||||||
|
|
||||||
wl_list_remove(&resource->link); |
|
||||||
if (resource->object.id > 0) |
|
||||||
wl_hash_table_remove(display->objects, resource->object.id); |
|
||||||
resource->destroy(resource, client); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_client_destroy(struct wl_client *client) |
|
||||||
{ |
|
||||||
struct wl_resource *resource, *tmp; |
|
||||||
|
|
||||||
printf("disconnect from client %p\n", client); |
|
||||||
|
|
||||||
wl_list_for_each_safe(resource, tmp, &client->resource_list, link) |
|
||||||
wl_resource_destroy(resource, client); |
|
||||||
|
|
||||||
wl_event_source_remove(client->source); |
|
||||||
wl_connection_destroy(client->connection); |
|
||||||
free(client); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
lose_pointer_focus(struct wl_listener *listener, |
|
||||||
struct wl_surface *surface, uint32_t time) |
|
||||||
{ |
|
||||||
struct wl_input_device *device = |
|
||||||
container_of(listener, struct wl_input_device, |
|
||||||
pointer_focus_listener); |
|
||||||
|
|
||||||
wl_input_device_set_pointer_focus(device, NULL, time, 0, 0, 0, 0); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
lose_keyboard_focus(struct wl_listener *listener, |
|
||||||
struct wl_surface *surface, uint32_t time) |
|
||||||
{ |
|
||||||
struct wl_input_device *device = |
|
||||||
container_of(listener, struct wl_input_device, |
|
||||||
keyboard_focus_listener); |
|
||||||
|
|
||||||
wl_input_device_set_keyboard_focus(device, NULL, time); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_input_device_init(struct wl_input_device *device, |
|
||||||
struct wl_compositor *compositor) |
|
||||||
{ |
|
||||||
wl_list_init(&device->pointer_focus_listener.link); |
|
||||||
device->pointer_focus_listener.func = lose_pointer_focus; |
|
||||||
wl_list_init(&device->keyboard_focus_listener.link); |
|
||||||
device->keyboard_focus_listener.func = lose_keyboard_focus; |
|
||||||
|
|
||||||
device->x = 100; |
|
||||||
device->y = 100; |
|
||||||
device->compositor = compositor; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_input_device_set_pointer_focus(struct wl_input_device *device, |
|
||||||
struct wl_surface *surface, |
|
||||||
uint32_t time, |
|
||||||
int32_t x, int32_t y, |
|
||||||
int32_t sx, int32_t sy) |
|
||||||
{ |
|
||||||
if (device->pointer_focus == surface) |
|
||||||
return; |
|
||||||
|
|
||||||
if (device->pointer_focus && |
|
||||||
(!surface || device->pointer_focus->client != surface->client)) |
|
||||||
wl_client_post_event(device->pointer_focus->client, |
|
||||||
&device->object, |
|
||||||
WL_INPUT_DEVICE_POINTER_FOCUS, |
|
||||||
time, NULL, 0, 0, 0, 0); |
|
||||||
if (surface) |
|
||||||
wl_client_post_event(surface->client, |
|
||||||
&device->object, |
|
||||||
WL_INPUT_DEVICE_POINTER_FOCUS, |
|
||||||
time, surface, x, y, sx, sy); |
|
||||||
|
|
||||||
device->pointer_focus = surface; |
|
||||||
device->pointer_focus_time = time; |
|
||||||
|
|
||||||
wl_list_remove(&device->pointer_focus_listener.link); |
|
||||||
if (surface) |
|
||||||
wl_list_insert(surface->destroy_listener_list.prev, |
|
||||||
&device->pointer_focus_listener.link); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_input_device_set_keyboard_focus(struct wl_input_device *device, |
|
||||||
struct wl_surface *surface, |
|
||||||
uint32_t time) |
|
||||||
{ |
|
||||||
if (device->keyboard_focus == surface) |
|
||||||
return; |
|
||||||
|
|
||||||
if (device->keyboard_focus && |
|
||||||
(!surface || device->keyboard_focus->client != surface->client)) |
|
||||||
wl_client_post_event(device->keyboard_focus->client, |
|
||||||
&device->object, |
|
||||||
WL_INPUT_DEVICE_KEYBOARD_FOCUS, |
|
||||||
time, NULL, &device->keys); |
|
||||||
|
|
||||||
if (surface) |
|
||||||
wl_client_post_event(surface->client, |
|
||||||
&device->object, |
|
||||||
WL_INPUT_DEVICE_KEYBOARD_FOCUS, |
|
||||||
time, surface, &device->keys); |
|
||||||
|
|
||||||
device->keyboard_focus = surface; |
|
||||||
device->keyboard_focus_time = time; |
|
||||||
|
|
||||||
wl_list_remove(&device->keyboard_focus_listener.link); |
|
||||||
if (surface) |
|
||||||
wl_list_insert(surface->destroy_listener_list.prev, |
|
||||||
&device->keyboard_focus_listener.link); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_input_device_end_grab(struct wl_input_device *device, uint32_t time) |
|
||||||
{ |
|
||||||
const struct wl_grab_interface *interface; |
|
||||||
|
|
||||||
interface = device->grab->interface; |
|
||||||
interface->end(device->grab, time); |
|
||||||
device->grab->input_device = NULL; |
|
||||||
device->grab = NULL; |
|
||||||
|
|
||||||
wl_list_remove(&device->grab_listener.link); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
lose_grab_surface(struct wl_listener *listener, |
|
||||||
struct wl_surface *surface, uint32_t time) |
|
||||||
{ |
|
||||||
struct wl_input_device *device = |
|
||||||
container_of(listener, |
|
||||||
struct wl_input_device, grab_listener); |
|
||||||
|
|
||||||
wl_input_device_end_grab(device, time); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_input_device_start_grab(struct wl_input_device *device, |
|
||||||
struct wl_grab *grab, |
|
||||||
uint32_t button, uint32_t time) |
|
||||||
{ |
|
||||||
struct wl_surface *focus = device->pointer_focus; |
|
||||||
|
|
||||||
device->grab = grab; |
|
||||||
device->grab_button = button; |
|
||||||
device->grab_time = time; |
|
||||||
device->grab_x = device->x; |
|
||||||
device->grab_y = device->y; |
|
||||||
|
|
||||||
device->grab_listener.func = lose_grab_surface; |
|
||||||
wl_list_insert(focus->destroy_listener_list.prev, |
|
||||||
&device->grab_listener.link); |
|
||||||
|
|
||||||
grab->input_device = device; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_input_device_update_grab(struct wl_input_device *device, |
|
||||||
struct wl_grab *grab, |
|
||||||
struct wl_surface *surface, uint32_t time) |
|
||||||
{ |
|
||||||
if (device->grab != &device->motion_grab || |
|
||||||
device->grab_time != time || |
|
||||||
device->pointer_focus != surface) |
|
||||||
return -1; |
|
||||||
|
|
||||||
device->grab = grab; |
|
||||||
grab->input_device = device; |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
display_sync(struct wl_client *client, |
|
||||||
struct wl_display *display, uint32_t key) |
|
||||||
{ |
|
||||||
wl_client_post_event(client, &display->object, WL_DISPLAY_KEY, key, 0); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
destroy_frame_listener(struct wl_resource *resource, struct wl_client *client) |
|
||||||
{ |
|
||||||
struct wl_frame_listener *listener = |
|
||||||
container_of(resource, struct wl_frame_listener, resource); |
|
||||||
|
|
||||||
wl_list_remove(&listener->link); |
|
||||||
free(listener); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
display_frame(struct wl_client *client, |
|
||||||
struct wl_display *display, uint32_t key) |
|
||||||
{ |
|
||||||
struct wl_frame_listener *listener; |
|
||||||
|
|
||||||
listener = malloc(sizeof *listener); |
|
||||||
if (listener == NULL) { |
|
||||||
wl_client_post_no_memory(client); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
/* The listener is a resource so we destroy it when the client
|
|
||||||
* goes away. */ |
|
||||||
listener->resource.destroy = destroy_frame_listener; |
|
||||||
listener->resource.object.id = 0; |
|
||||||
listener->client = client; |
|
||||||
listener->key = key; |
|
||||||
wl_list_insert(client->resource_list.prev, &listener->resource.link); |
|
||||||
wl_list_insert(display->frame_list.prev, &listener->link); |
|
||||||
} |
|
||||||
|
|
||||||
struct wl_display_interface display_interface = { |
|
||||||
display_sync, |
|
||||||
display_frame |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
WL_EXPORT struct wl_display * |
|
||||||
wl_display_create(void) |
|
||||||
{ |
|
||||||
struct wl_display *display; |
|
||||||
const char *debug; |
|
||||||
|
|
||||||
debug = getenv("WAYLAND_DEBUG"); |
|
||||||
if (debug) |
|
||||||
wl_debug = 1; |
|
||||||
|
|
||||||
display = malloc(sizeof *display); |
|
||||||
if (display == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
display->loop = wl_event_loop_create(); |
|
||||||
if (display->loop == NULL) { |
|
||||||
free(display); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
display->objects = wl_hash_table_create(); |
|
||||||
if (display->objects == NULL) { |
|
||||||
free(display); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
wl_list_init(&display->frame_list); |
|
||||||
wl_list_init(&display->global_list); |
|
||||||
wl_list_init(&display->socket_list); |
|
||||||
|
|
||||||
display->client_id_range = 256; /* Gah, arbitrary... */ |
|
||||||
|
|
||||||
display->id = 1; |
|
||||||
display->object.interface = &wl_display_interface; |
|
||||||
display->object.implementation = (void (**)(void)) &display_interface; |
|
||||||
wl_display_add_object(display, &display->object); |
|
||||||
if (wl_display_add_global(display, &display->object, NULL)) { |
|
||||||
wl_event_loop_destroy(display->loop); |
|
||||||
free(display); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
return display; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_display_destroy(struct wl_display *display) |
|
||||||
{ |
|
||||||
struct wl_socket *s, *next; |
|
||||||
|
|
||||||
wl_event_loop_destroy(display->loop); |
|
||||||
wl_hash_table_destroy(display->objects); |
|
||||||
|
|
||||||
wl_list_for_each_safe(s, next, &display->socket_list, link) { |
|
||||||
close(s->fd); |
|
||||||
unlink(s->addr.sun_path); |
|
||||||
free(s); |
|
||||||
} |
|
||||||
|
|
||||||
free(display); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_display_add_object(struct wl_display *display, struct wl_object *object) |
|
||||||
{ |
|
||||||
object->id = display->id++; |
|
||||||
wl_hash_table_insert(display->objects, object->id, object); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_display_add_global(struct wl_display *display, |
|
||||||
struct wl_object *object, wl_client_connect_func_t func) |
|
||||||
{ |
|
||||||
struct wl_global *global; |
|
||||||
|
|
||||||
global = malloc(sizeof *global); |
|
||||||
if (global == NULL) |
|
||||||
return -1; |
|
||||||
|
|
||||||
global->object = object; |
|
||||||
global->func = func; |
|
||||||
wl_list_insert(display->global_list.prev, &global->link); |
|
||||||
|
|
||||||
return 0;
|
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_display_post_frame(struct wl_display *display, uint32_t time) |
|
||||||
{ |
|
||||||
struct wl_frame_listener *listener, *next; |
|
||||||
|
|
||||||
wl_list_for_each_safe(listener, next, &display->frame_list, link) { |
|
||||||
wl_client_post_event(listener->client, &display->object, |
|
||||||
WL_DISPLAY_KEY, listener->key, time); |
|
||||||
wl_resource_destroy(&listener->resource, listener->client); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT struct wl_event_loop * |
|
||||||
wl_display_get_event_loop(struct wl_display *display) |
|
||||||
{ |
|
||||||
return display->loop; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_display_terminate(struct wl_display *display) |
|
||||||
{ |
|
||||||
display->run = 0; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_display_run(struct wl_display *display) |
|
||||||
{ |
|
||||||
display->run = 1; |
|
||||||
|
|
||||||
while (display->run) |
|
||||||
wl_event_loop_dispatch(display->loop, -1); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
socket_data(int fd, uint32_t mask, void *data) |
|
||||||
{ |
|
||||||
struct wl_display *display = data; |
|
||||||
struct sockaddr_un name; |
|
||||||
socklen_t length; |
|
||||||
int client_fd; |
|
||||||
|
|
||||||
length = sizeof name; |
|
||||||
client_fd = accept (fd, (struct sockaddr *) &name, &length); |
|
||||||
if (client_fd < 0) |
|
||||||
fprintf(stderr, "failed to accept\n"); |
|
||||||
|
|
||||||
wl_client_create(display, client_fd); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_display_add_socket(struct wl_display *display, const char *name) |
|
||||||
{ |
|
||||||
struct wl_socket *s; |
|
||||||
socklen_t size, name_size; |
|
||||||
const char *runtime_dir; |
|
||||||
|
|
||||||
s = malloc(sizeof *s); |
|
||||||
if (s == NULL) |
|
||||||
return -1; |
|
||||||
|
|
||||||
s->fd = socket(PF_LOCAL, SOCK_STREAM, 0); |
|
||||||
if (s->fd < 0) |
|
||||||
return -1; |
|
||||||
|
|
||||||
runtime_dir = getenv("XDG_RUNTIME_DIR"); |
|
||||||
if (runtime_dir == NULL) { |
|
||||||
runtime_dir = "."; |
|
||||||
fprintf(stderr, |
|
||||||
"XDG_RUNTIME_DIR not set, falling back to %s\n", |
|
||||||
runtime_dir); |
|
||||||
} |
|
||||||
|
|
||||||
if (name == NULL) |
|
||||||
name = getenv("WAYLAND_DISPLAY"); |
|
||||||
if (name == NULL) |
|
||||||
name = "wayland-0"; |
|
||||||
|
|
||||||
memset(&s->addr, 0, sizeof s->addr); |
|
||||||
s->addr.sun_family = AF_LOCAL; |
|
||||||
name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, |
|
||||||
"%s/%s", runtime_dir, name) + 1; |
|
||||||
fprintf(stderr, "using socket %s\n", s->addr.sun_path); |
|
||||||
|
|
||||||
size = offsetof (struct sockaddr_un, sun_path) + name_size; |
|
||||||
if (bind(s->fd, (struct sockaddr *) &s->addr, size) < 0) |
|
||||||
return -1; |
|
||||||
|
|
||||||
if (listen(s->fd, 1) < 0) |
|
||||||
return -1; |
|
||||||
|
|
||||||
wl_event_loop_add_fd(display->loop, s->fd, |
|
||||||
WL_EVENT_READABLE, |
|
||||||
socket_data, display); |
|
||||||
wl_list_insert(display->socket_list.prev, &s->link); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_compositor_init(struct wl_compositor *compositor, |
|
||||||
const struct wl_compositor_interface *interface, |
|
||||||
struct wl_display *display) |
|
||||||
{ |
|
||||||
compositor->object.interface = &wl_compositor_interface; |
|
||||||
compositor->object.implementation = (void (**)(void)) interface; |
|
||||||
wl_display_add_object(display, &compositor->object); |
|
||||||
if (wl_display_add_global(display, &compositor->object, NULL)) |
|
||||||
return -1; |
|
||||||
|
|
||||||
compositor->argb_visual.object.interface = &wl_visual_interface; |
|
||||||
compositor->argb_visual.object.implementation = NULL; |
|
||||||
wl_display_add_object(display, &compositor->argb_visual.object); |
|
||||||
wl_display_add_global(display, &compositor->argb_visual.object, NULL); |
|
||||||
|
|
||||||
compositor->premultiplied_argb_visual.object.interface = |
|
||||||
&wl_visual_interface; |
|
||||||
compositor->premultiplied_argb_visual.object.implementation = NULL; |
|
||||||
wl_display_add_object(display, |
|
||||||
&compositor->premultiplied_argb_visual.object); |
|
||||||
wl_display_add_global(display, |
|
||||||
&compositor->premultiplied_argb_visual.object, |
|
||||||
NULL); |
|
||||||
|
|
||||||
compositor->rgb_visual.object.interface = &wl_visual_interface; |
|
||||||
compositor->rgb_visual.object.implementation = NULL; |
|
||||||
wl_display_add_object(display, |
|
||||||
&compositor->rgb_visual.object); |
|
||||||
wl_display_add_global(display, |
|
||||||
&compositor->rgb_visual.object, NULL); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
@ -1,265 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef WAYLAND_H |
|
||||||
#define WAYLAND_H |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
#include "wayland-util.h" |
|
||||||
#include "wayland-server-protocol.h" |
|
||||||
|
|
||||||
enum { |
|
||||||
WL_EVENT_READABLE = 0x01, |
|
||||||
WL_EVENT_WRITEABLE = 0x02 |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_event_loop; |
|
||||||
struct wl_event_source; |
|
||||||
typedef void (*wl_event_loop_fd_func_t)(int fd, uint32_t mask, void *data); |
|
||||||
typedef void (*wl_event_loop_timer_func_t)(void *data); |
|
||||||
typedef void (*wl_event_loop_signal_func_t)(int signal_number, void *data); |
|
||||||
typedef void (*wl_event_loop_idle_func_t)(void *data); |
|
||||||
|
|
||||||
struct wl_event_loop *wl_event_loop_create(void); |
|
||||||
void wl_event_loop_destroy(struct wl_event_loop *loop); |
|
||||||
struct wl_event_source *wl_event_loop_add_fd(struct wl_event_loop *loop, |
|
||||||
int fd, uint32_t mask, |
|
||||||
wl_event_loop_fd_func_t func, |
|
||||||
void *data); |
|
||||||
int wl_event_source_fd_update(struct wl_event_source *source, uint32_t mask); |
|
||||||
struct wl_event_source *wl_event_loop_add_timer(struct wl_event_loop *loop, |
|
||||||
wl_event_loop_timer_func_t func, |
|
||||||
void *data); |
|
||||||
struct wl_event_source * |
|
||||||
wl_event_loop_add_signal(struct wl_event_loop *loop, |
|
||||||
int signal_number, |
|
||||||
wl_event_loop_signal_func_t func, |
|
||||||
void *data); |
|
||||||
|
|
||||||
int wl_event_source_timer_update(struct wl_event_source *source, |
|
||||||
int ms_delay); |
|
||||||
int wl_event_source_remove(struct wl_event_source *source); |
|
||||||
|
|
||||||
|
|
||||||
int wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout); |
|
||||||
struct wl_event_source *wl_event_loop_add_idle(struct wl_event_loop *loop, |
|
||||||
wl_event_loop_idle_func_t func, |
|
||||||
void *data); |
|
||||||
int wl_event_loop_get_fd(struct wl_event_loop *loop); |
|
||||||
|
|
||||||
struct wl_client; |
|
||||||
struct wl_display; |
|
||||||
struct wl_input_device; |
|
||||||
|
|
||||||
struct wl_display *wl_display_create(void); |
|
||||||
void wl_display_destroy(struct wl_display *display); |
|
||||||
struct wl_event_loop *wl_display_get_event_loop(struct wl_display *display); |
|
||||||
int wl_display_add_socket(struct wl_display *display, const char *name); |
|
||||||
void wl_display_terminate(struct wl_display *display); |
|
||||||
void wl_display_run(struct wl_display *display); |
|
||||||
|
|
||||||
void wl_display_add_object(struct wl_display *display, struct wl_object *object); |
|
||||||
|
|
||||||
typedef void (*wl_client_connect_func_t)(struct wl_client *client, struct wl_object *global); |
|
||||||
|
|
||||||
int wl_display_add_global(struct wl_display *display, struct wl_object *object, wl_client_connect_func_t func); |
|
||||||
|
|
||||||
void wl_client_destroy(struct wl_client *client); |
|
||||||
void wl_client_post_no_memory(struct wl_client *client); |
|
||||||
void wl_client_post_global(struct wl_client *client, struct wl_object *object); |
|
||||||
|
|
||||||
struct wl_visual { |
|
||||||
struct wl_object object; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_compositor { |
|
||||||
struct wl_object object; |
|
||||||
struct wl_visual argb_visual; |
|
||||||
struct wl_visual premultiplied_argb_visual; |
|
||||||
struct wl_visual rgb_visual; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_resource { |
|
||||||
struct wl_object object; |
|
||||||
void (*destroy)(struct wl_resource *resource, |
|
||||||
struct wl_client *client); |
|
||||||
struct wl_list link; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_buffer { |
|
||||||
struct wl_resource resource; |
|
||||||
struct wl_compositor *compositor; |
|
||||||
struct wl_visual *visual; |
|
||||||
int32_t width, height; |
|
||||||
void (*attach)(struct wl_buffer *buffer, struct wl_surface *surface); |
|
||||||
void (*damage)(struct wl_buffer *buffer, |
|
||||||
struct wl_surface *surface, |
|
||||||
int32_t x, int32_t y, int32_t width, int32_t height); |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_listener { |
|
||||||
struct wl_list link; |
|
||||||
void (*func)(struct wl_listener *listener, |
|
||||||
struct wl_surface *surface, uint32_t time); |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_surface { |
|
||||||
struct wl_resource resource; |
|
||||||
struct wl_client *client; |
|
||||||
struct wl_list destroy_listener_list; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_shell { |
|
||||||
struct wl_object object; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_grab; |
|
||||||
struct wl_grab_interface { |
|
||||||
void (*motion)(struct wl_grab *grab, |
|
||||||
uint32_t time, int32_t x, int32_t y); |
|
||||||
void (*button)(struct wl_grab *grab, |
|
||||||
uint32_t time, int32_t button, int32_t state); |
|
||||||
void (*end)(struct wl_grab *grab, uint32_t time); |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_grab { |
|
||||||
const struct wl_grab_interface *interface; |
|
||||||
struct wl_input_device *input_device; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_input_device { |
|
||||||
struct wl_object object; |
|
||||||
struct wl_compositor *compositor; |
|
||||||
struct wl_surface *pointer_focus; |
|
||||||
struct wl_surface *keyboard_focus; |
|
||||||
struct wl_array keys; |
|
||||||
uint32_t pointer_focus_time; |
|
||||||
uint32_t keyboard_focus_time; |
|
||||||
struct wl_listener pointer_focus_listener; |
|
||||||
struct wl_listener keyboard_focus_listener; |
|
||||||
|
|
||||||
int32_t x, y; |
|
||||||
struct wl_grab *grab; |
|
||||||
struct wl_grab motion_grab; |
|
||||||
uint32_t grab_time; |
|
||||||
int32_t grab_x, grab_y; |
|
||||||
uint32_t grab_button; |
|
||||||
struct wl_listener grab_listener; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_drag_offer { |
|
||||||
struct wl_object object; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_drag { |
|
||||||
struct wl_resource resource; |
|
||||||
struct wl_grab grab; |
|
||||||
struct wl_drag_offer drag_offer; |
|
||||||
struct wl_surface *source; |
|
||||||
struct wl_surface *drag_focus; |
|
||||||
struct wl_client *target; |
|
||||||
int32_t x, y, sx, sy; |
|
||||||
struct wl_array types; |
|
||||||
const char *type; |
|
||||||
uint32_t pointer_focus_time; |
|
||||||
struct wl_listener drag_focus_listener; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_selection_offer { |
|
||||||
struct wl_object object; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_selection { |
|
||||||
struct wl_resource resource; |
|
||||||
struct wl_client *client; |
|
||||||
struct wl_input_device *input_device; |
|
||||||
struct wl_selection_offer selection_offer; |
|
||||||
struct wl_surface *selection_focus; |
|
||||||
struct wl_client *target; |
|
||||||
struct wl_array types; |
|
||||||
struct wl_listener selection_focus_listener; |
|
||||||
}; |
|
||||||
|
|
||||||
void |
|
||||||
wl_client_post_event(struct wl_client *client, |
|
||||||
struct wl_object *sender, |
|
||||||
uint32_t event, ...); |
|
||||||
|
|
||||||
int |
|
||||||
wl_display_set_compositor(struct wl_display *display, |
|
||||||
struct wl_compositor *compositor, |
|
||||||
const struct wl_compositor_interface *implementation); |
|
||||||
|
|
||||||
void |
|
||||||
wl_display_post_frame(struct wl_display *display, uint32_t msecs); |
|
||||||
|
|
||||||
void |
|
||||||
wl_client_add_resource(struct wl_client *client, |
|
||||||
struct wl_resource *resource); |
|
||||||
|
|
||||||
struct wl_display * |
|
||||||
wl_client_get_display(struct wl_client *client); |
|
||||||
|
|
||||||
void |
|
||||||
wl_resource_destroy(struct wl_resource *resource, struct wl_client *client); |
|
||||||
|
|
||||||
void |
|
||||||
wl_input_device_init(struct wl_input_device *device, |
|
||||||
struct wl_compositor *compositor); |
|
||||||
|
|
||||||
void |
|
||||||
wl_input_device_set_pointer_focus(struct wl_input_device *device, |
|
||||||
struct wl_surface *surface, |
|
||||||
uint32_t time, |
|
||||||
int32_t x, int32_t y, |
|
||||||
int32_t sx, int32_t sy); |
|
||||||
|
|
||||||
void |
|
||||||
wl_input_device_set_keyboard_focus(struct wl_input_device *device, |
|
||||||
struct wl_surface *surface, |
|
||||||
uint32_t time); |
|
||||||
|
|
||||||
void |
|
||||||
wl_input_device_end_grab(struct wl_input_device *device, uint32_t time); |
|
||||||
void |
|
||||||
wl_input_device_start_grab(struct wl_input_device *device, |
|
||||||
struct wl_grab *grab, |
|
||||||
uint32_t button, uint32_t time); |
|
||||||
int |
|
||||||
wl_input_device_update_grab(struct wl_input_device *device, |
|
||||||
struct wl_grab *grab, |
|
||||||
struct wl_surface *surface, uint32_t time); |
|
||||||
|
|
||||||
int |
|
||||||
wl_compositor_init(struct wl_compositor *compositor, |
|
||||||
const struct wl_compositor_interface *interface, |
|
||||||
struct wl_display *display); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif |
|
@ -1,10 +0,0 @@ |
|||||||
prefix=@prefix@ |
|
||||||
exec_prefix=${prefix} |
|
||||||
libdir=${exec_prefix}/lib |
|
||||||
includedir=${prefix}/include |
|
||||||
|
|
||||||
Name: Wayland Server |
|
||||||
Description: Server side implementation of the Wayland protocol |
|
||||||
Version: 0.1 |
|
||||||
Cflags: -I${includedir} |
|
||||||
Libs: -L${libdir} -lwayland-server |
|
@ -1,123 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <string.h> |
|
||||||
#include "wayland-util.h" |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_list_init(struct wl_list *list) |
|
||||||
{ |
|
||||||
list->prev = list; |
|
||||||
list->next = list; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_list_insert(struct wl_list *list, struct wl_list *elm) |
|
||||||
{ |
|
||||||
elm->prev = list; |
|
||||||
elm->next = list->next; |
|
||||||
list->next = elm; |
|
||||||
elm->next->prev = elm; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_list_remove(struct wl_list *elm) |
|
||||||
{ |
|
||||||
elm->prev->next = elm->next; |
|
||||||
elm->next->prev = elm->prev; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_list_length(struct wl_list *list) |
|
||||||
{ |
|
||||||
struct wl_list *e; |
|
||||||
int count; |
|
||||||
|
|
||||||
count = 0; |
|
||||||
e = list->next; |
|
||||||
while (e != list) { |
|
||||||
e = e->next; |
|
||||||
count++; |
|
||||||
} |
|
||||||
|
|
||||||
return count; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT int |
|
||||||
wl_list_empty(struct wl_list *list) |
|
||||||
{ |
|
||||||
return list->next == list; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_array_init(struct wl_array *array) |
|
||||||
{ |
|
||||||
memset(array, 0, sizeof *array); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_array_release(struct wl_array *array) |
|
||||||
{ |
|
||||||
free(array->data); |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void * |
|
||||||
wl_array_add(struct wl_array *array, int size) |
|
||||||
{ |
|
||||||
int alloc; |
|
||||||
void *data, *p; |
|
||||||
|
|
||||||
if (array->alloc > 0) |
|
||||||
alloc = array->alloc; |
|
||||||
else |
|
||||||
alloc = 16; |
|
||||||
|
|
||||||
while (alloc < array->size + size) |
|
||||||
alloc *= 2; |
|
||||||
|
|
||||||
if (array->alloc < alloc) { |
|
||||||
if (array->alloc > 0) |
|
||||||
data = realloc(array->data, alloc); |
|
||||||
else |
|
||||||
data = malloc(alloc); |
|
||||||
|
|
||||||
if (data == NULL) |
|
||||||
return 0; |
|
||||||
array->data = data; |
|
||||||
array->alloc = alloc; |
|
||||||
} |
|
||||||
|
|
||||||
p = array->data + array->size; |
|
||||||
array->size += size; |
|
||||||
|
|
||||||
return p; |
|
||||||
} |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
wl_array_copy(struct wl_array *array, struct wl_array *source) |
|
||||||
{ |
|
||||||
array->size = 0; |
|
||||||
wl_array_add(array, source->size); |
|
||||||
memcpy(array->data, source->data, source->size); |
|
||||||
} |
|
@ -1,157 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef WAYLAND_UTIL_H |
|
||||||
#define WAYLAND_UTIL_H |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
#include <inttypes.h> |
|
||||||
|
|
||||||
/* GCC visibility */ |
|
||||||
#if defined(__GNUC__) && __GNUC__ >= 4 |
|
||||||
#define WL_EXPORT __attribute__ ((visibility("default"))) |
|
||||||
#else |
|
||||||
#define WL_EXPORT |
|
||||||
#endif |
|
||||||
|
|
||||||
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) |
|
||||||
#define ALIGN(n, a) ( ((n) + ((a) - 1)) & ~((a) - 1) ) |
|
||||||
#define DIV_ROUNDUP(n, a) ( ((n) + ((a) - 1)) / (a) ) |
|
||||||
|
|
||||||
#define container_of(ptr, type, member) ({ \ |
|
||||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
|
||||||
(type *)( (char *)__mptr - offsetof(type,member) );}) |
|
||||||
|
|
||||||
struct wl_argument { |
|
||||||
uint32_t type; |
|
||||||
void *data; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_message { |
|
||||||
const char *name; |
|
||||||
const char *signature; |
|
||||||
const void **types; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_interface { |
|
||||||
const char *name; |
|
||||||
int version; |
|
||||||
int method_count; |
|
||||||
const struct wl_message *methods; |
|
||||||
int event_count; |
|
||||||
const struct wl_message *events; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_object { |
|
||||||
const struct wl_interface *interface; |
|
||||||
void (**implementation)(void); |
|
||||||
uint32_t id; |
|
||||||
}; |
|
||||||
|
|
||||||
struct wl_hash_table; |
|
||||||
struct wl_hash_table *wl_hash_table_create(void); |
|
||||||
void wl_hash_table_destroy(struct wl_hash_table *ht); |
|
||||||
void *wl_hash_table_lookup(struct wl_hash_table *ht, uint32_t hash); |
|
||||||
int wl_hash_table_insert(struct wl_hash_table *ht, uint32_t hash, void *data); |
|
||||||
void wl_hash_table_remove(struct wl_hash_table *ht, uint32_t hash); |
|
||||||
|
|
||||||
/**
|
|
||||||
* wl_list - linked list |
|
||||||
* |
|
||||||
* The list head is of "struct wl_list" type, and must be initialized |
|
||||||
* using wl_list_init(). All entries in the list must be of the same |
|
||||||
* type. The item type must have a "struct wl_list" member. This |
|
||||||
* member will be initialized by wl_list_insert(). There is no need to |
|
||||||
* call wl_list_init() on the individual item. To query if the list is |
|
||||||
* empty in O(1), use wl_list_empty(). |
|
||||||
* |
|
||||||
* Let's call the list reference "struct wl_list foo_list", the item type as |
|
||||||
* "item_t", and the item member as "struct wl_list link". The following code |
|
||||||
* |
|
||||||
* The following code will initialize a list: |
|
||||||
* |
|
||||||
* wl_list_init(foo_list); |
|
||||||
* wl_list_insert(foo_list, item1); Pushes item1 at the head |
|
||||||
* wl_list_insert(foo_list, item2); Pushes item2 at the head |
|
||||||
* wl_list_insert(item2, item3); Pushes item3 after item2 |
|
||||||
* |
|
||||||
* The list now looks like [item2, item3, item1] |
|
||||||
* |
|
||||||
* Will iterate the list in ascending order: |
|
||||||
* |
|
||||||
* item_t *item; |
|
||||||
* wl_list_for_each(item, foo_list, link) { |
|
||||||
* Do_something_with_item(item); |
|
||||||
* } |
|
||||||
*/ |
|
||||||
struct wl_list { |
|
||||||
struct wl_list *prev; |
|
||||||
struct wl_list *next; |
|
||||||
}; |
|
||||||
|
|
||||||
void wl_list_init(struct wl_list *list); |
|
||||||
void wl_list_insert(struct wl_list *list, struct wl_list *elm); |
|
||||||
void wl_list_remove(struct wl_list *elm); |
|
||||||
int wl_list_length(struct wl_list *list); |
|
||||||
int wl_list_empty(struct wl_list *list); |
|
||||||
|
|
||||||
#define __container_of(ptr, sample, member) \ |
|
||||||
(void *)((char *)(ptr) - \
|
|
||||||
((char *)&(sample)->member - (char *)(sample))) |
|
||||||
|
|
||||||
#define wl_list_for_each(pos, head, member) \ |
|
||||||
for (pos = 0, pos = __container_of((head)->next, pos, member); \
|
|
||||||
&pos->member != (head); \
|
|
||||||
pos = __container_of(pos->member.next, pos, member)) |
|
||||||
|
|
||||||
#define wl_list_for_each_safe(pos, tmp, head, member) \ |
|
||||||
for (pos = 0, tmp = 0, \
|
|
||||||
pos = __container_of((head)->next, pos, member), \
|
|
||||||
tmp = __container_of((pos)->member.next, tmp, member); \
|
|
||||||
&pos->member != (head); \
|
|
||||||
pos = tmp, \
|
|
||||||
tmp = __container_of(pos->member.next, tmp, member)) |
|
||||||
|
|
||||||
#define wl_list_for_each_reverse(pos, head, member) \ |
|
||||||
for (pos = 0, pos = __container_of((head)->prev, pos, member); \
|
|
||||||
&pos->member != (head); \
|
|
||||||
pos = __container_of(pos->member.prev, pos, member)) |
|
||||||
|
|
||||||
struct wl_array { |
|
||||||
uint32_t size; |
|
||||||
uint32_t alloc; |
|
||||||
void *data; |
|
||||||
}; |
|
||||||
|
|
||||||
void wl_array_init(struct wl_array *array); |
|
||||||
void wl_array_release(struct wl_array *array); |
|
||||||
void *wl_array_add(struct wl_array *array, int size); |
|
||||||
void wl_array_copy(struct wl_array *array, struct wl_array *source); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif |
|
Loading…
Reference in new issue