diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7dc7e33f..eeed7899 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,7 +43,7 @@ include: # API changes. If you need new features from ci-templates you must bump # this to the current SHA you require from the ci-templates repo, however # be aware that you may need to account for API changes when doing so. - ref: b791bd48996e3ced9ca13f1c5ee82be8540b8adb + ref: 48c2c583a865bd59be21e8938df247faf460099c file: - '/templates/debian.yml' - '/templates/freebsd.yml' @@ -62,6 +62,13 @@ stages: - "Build and test" - "Other build configurations" +workflow: + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS + when: never + - if: $CI_COMMIT_BRANCH + .ci-rules: rules: - when: on_success @@ -75,7 +82,7 @@ stages: FDO_DISTRIBUTION_EXEC: 'pip3 install --break-system-packages meson~=0.57.2' # bump this tag every time you change something which requires rebuilding the # base image - FDO_DISTRIBUTION_TAG: "2024-03-28.2" + FDO_DISTRIBUTION_TAG: "2025-01-21.1" .debian-x86_64: extends: @@ -94,6 +101,7 @@ stages: - .os-debian variables: BUILD_ARCH: "armv7" + FDO_DISTRIBUTION_PLATFORM: "linux/arm/v7" # Does not inherit .ci-rules as we only want it to run in MR context. @@ -147,7 +155,6 @@ armv7-debian-container_prep: stage: "Base container" variables: GIT_STRATEGY: none - FDO_BASE_IMAGE: "arm32v7/debian:$FDO_DISTRIBUTION_VERSION" # Core build environment. @@ -299,11 +306,11 @@ armv7-release-debian-build: .os-freebsd: variables: BUILD_OS: freebsd - FDO_DISTRIBUTION_VERSION: "13.2" + FDO_DISTRIBUTION_VERSION: "14.3" FDO_DISTRIBUTION_PACKAGES: 'libxslt meson ninja pkgconf expat libffi libepoll-shim libxml2' # bump this tag every time you change something which requires rebuilding the # base image - FDO_DISTRIBUTION_TAG: "2023-08-02.0" + FDO_DISTRIBUTION_TAG: "2025-07-20.0" # Don't build documentation since installing the required tools massively # increases the VM image (and therefore container) size. MESON_ARGS: "--fatal-meson-warnings -Dwerror=true -Ddocumentation=false" diff --git a/cursor/convert_font.c b/cursor/convert_font.c index 74e45fbc..77e34c20 100644 --- a/cursor/convert_font.c +++ b/cursor/convert_font.c @@ -29,7 +29,6 @@ * http://fontforge.org/pcf-format.html */ -#include #include #include #include diff --git a/cursor/wayland-cursor.c b/cursor/wayland-cursor.c index 156f0a80..2e21db73 100644 --- a/cursor/wayland-cursor.c +++ b/cursor/wayland-cursor.c @@ -27,6 +27,7 @@ #include "xcursor.h" #include "wayland-cursor.h" #include "wayland-client.h" +#include #include #include #include @@ -68,11 +69,16 @@ shm_pool_create(struct wl_shm *shm, int size) goto err_close; pool->pool = wl_shm_create_pool(shm, pool->fd, size); + if (!pool->pool) + goto err_unmap; + pool->size = size; pool->used = 0; return pool; +err_unmap: + munmap(pool->data, size); err_close: close(pool->fd); err_free: @@ -279,7 +285,8 @@ wl_cursor_create_from_xcursor_images(struct xcursor_images *images, { struct cursor *cursor; struct cursor_image *image; - int i, size; + size_t size; + int i; cursor = malloc(sizeof *cursor); if (!cursor) @@ -309,7 +316,12 @@ wl_cursor_create_from_xcursor_images(struct xcursor_images *images, image->image.hotspot_y = images->images[i]->yhot; image->image.delay = images->images[i]->delay; - size = image->image.width * image->image.height * 4; + size = (size_t) image->image.width * image->image.height * 4; + if (size > INT_MAX) { + free(image); + break; + } + image->offset = shm_pool_allocate(theme->pool, size); if (image->offset < 0) { free(image); @@ -339,6 +351,8 @@ load_callback(struct xcursor_images *images, void *data) { struct wl_cursor_theme *theme = data; struct wl_cursor *cursor; + struct wl_cursor **p; + size_t s; if (wl_cursor_theme_get_cursor(theme, images->name)) { xcursor_images_destroy(images); @@ -348,15 +362,14 @@ load_callback(struct xcursor_images *images, void *data) cursor = wl_cursor_create_from_xcursor_images(images, theme); if (cursor) { - theme->cursor_count++; - theme->cursors = - realloc(theme->cursors, - theme->cursor_count * sizeof theme->cursors[0]); + s = theme->cursor_count + 1; + p = realloc(theme->cursors, s * sizeof theme->cursors[0]); - if (theme->cursors == NULL) { - theme->cursor_count--; + if (p == NULL) { free(cursor); } else { + theme->cursor_count = s; + theme->cursors = p; theme->cursors[theme->cursor_count - 1] = cursor; } } @@ -384,6 +397,9 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) if (!theme) return NULL; + if (size < 0 || (size > 0 && INT_MAX / size / 4 < size)) + goto err; + if (!name) name = "default"; @@ -393,7 +409,7 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) theme->pool = shm_pool_create(shm, size * size * 4); if (!theme->pool) - goto out_error_pool; + goto err; xcursor_load_theme(name, size, load_callback, theme); @@ -405,7 +421,7 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) return theme; -out_error_pool: +err: free(theme); return NULL; } diff --git a/cursor/xcursor.c b/cursor/xcursor.c index 0d5761f2..6e54cdbd 100644 --- a/cursor/xcursor.c +++ b/cursor/xcursor.c @@ -259,6 +259,8 @@ xcursor_read_file_header(FILE *file) return NULL; if (!xcursor_read_uint(file, &head.ntoc)) return NULL; + if (head.header < XCURSOR_FILE_HEADER_LEN) + return NULL; skip = head.header - XCURSOR_FILE_HEADER_LEN; if (skip) if (fseek(file, skip, SEEK_CUR) == EOF) @@ -571,7 +573,7 @@ xcursor_build_theme_dir(const char *dir, const char *theme) * add space for any needed directory separators, one per component, * and one for the trailing null */ - full_size = 1 + homelen + 1 + dirlen + 1 + themelen + 1; + full_size = (size_t) 1 + homelen + 1 + dirlen + 1 + themelen + 1; full = malloc(full_size); if (!full) return NULL; @@ -686,11 +688,15 @@ load_all_cursors_from_dir(const char *path, int size, void *user_data) { FILE *f; - DIR *dir = opendir(path); + DIR *dir; struct dirent *ent; char *full; struct xcursor_images *images; + if (!path) + return; + + dir = opendir(path); if (!dir) return; @@ -798,14 +804,14 @@ xcursor_load_theme_protected(const char *theme, int size, free(xcursor_path); } -/** Load all the cursor of a theme +/** Load all the cursors of a theme * * This function loads all the cursor images of a given theme and its - * inherited themes. Each cursor is loaded into an struct xcursor_images object + * inherited themes. Each cursor is loaded into a struct xcursor_images object * which is passed to the caller's load callback. If a cursor appears * more than once across all the inherited themes, the load callback * will be called multiple times, with possibly different struct xcursor_images - * object which have the same name. The user is expected to destroy the + * objects which have the same name. The user is expected to destroy the * struct xcursor_images objects passed to the callback with * xcursor_images_destroy(). * diff --git a/doc/publican/sources/Color.xml b/doc/publican/sources/Color.xml new file mode 100644 index 00000000..ceee779e --- /dev/null +++ b/doc/publican/sources/Color.xml @@ -0,0 +1,139 @@ + + +%BOOK_ENTITIES; +]> + + + Color management + +
+ Overview + + + Color management in Wayland considers only displays. All pictures in + Wayland are always display-referred, meaning that the pixel values are + intended as-is for some specific display where they would produce the + light emissions (stimuli) the picture's + author desired. Wayland does not support displaying "raw" camera or + scanner images as they are not display-referred, nor are they even + pictures without complex and subjective processing. + + + Stimuli — the picture itself — are only half of the picture reproduction. + The other half is the environment where a display is viewed. A striking + example is comparing a brightly lit office to a dark movie theater, the + stimuli required to produce a good reading of the picture is greatly + different. Therefore display-referred does not include only the display + but the viewing environment as well. + + + Window systems have been very well capable of operating without any + explicit consideration to color management. This is because there used to + be the implicit assumption of the standard display, the sRGB display, + which all computer monitors implemented, more or less. The viewing + environment was and still is accounted by adjusting the display and/or the + room to produce a workable experience. Pictures are authored on a computer + system by drawing, painting and adjusting the picture until it looks right + on the author's monitor. This implicitly builds the standard display and + environment assumption into the picture data. Deviations from the sRGB + specification were minor enough that they often did not matter if not in a + professional context like the printing industry. Displaying video material + required some more attention to the details, because video and television + standards differ enough from the sRGB display. What really made explicit + color management a hard requirement for entertainment is the coming of + wide color gamut (WCG) and high dynamic range (HDR) materials and + displays. + + + The color management design in Wayland follows the general Wayland design + principles: compositors tell clients what would be the optimal thing to + do, clients tell the compositors what kind of pictures they are actually + producing, and then compositors display those pictures the best they can. + +
+ +
+ Protocol Interfaces + + + Color management interfaces in Wayland and divided into two protocols: + color-management + and + color-representation. + They are designed to work together, but they can also be used + independently when the other one is not needed. + + +
+ Color-management + + + Color management protocol has two main purposes. First, it puts the + responsibility of color management on the compositor. This means that + clients do not necessarily need to care about color management at all, + and can display just fine by using the traditional standard display + assumption even when the actual display is wildly different. Clients + can also choose to target some other assumed display and let the + compositor handle it, or they can explicitly render for the actual + display at hand. Second, when the window system has multiple different + monitors, and a wl_surface happens to span more than one monitor, the + compositor can display the surface content correctly on all spanned + monitors simultaneously, as much as physically possible. + + + Color-management protocol concentrates on colorimetry: when you have a + pixel with RGB values, what stimulus do those values represent. The + stimulus definition follows the CIE 1931 two-degree observer model. Some + core concepts here are color primaries, white point, transfer function, + and dynamic range. The viewing environment is represented in an + extremely simplified way as the reference white luminance. The + connection between pixel RGB values and stimulus plus viewing + environment is recorded in an image description + object. Clients can create image description objects and tag + wl_surfaces with them, to indicate what kind of surface + content there will be. Clients can also ask what image description the + compositor would prefer to have on the wl_surface, and that + preference can change over time, e.g. when the wl_surface + is moved from one + wl_output to another. Following the compositor's preference + may provide advantages in image quality and power consumption. + + + Image description objects can come in two flavors: parametric and + ICC-based. The above was written with parametric image descriptions in + mind, and they have first-class support for HDR. ICC-based image + descriptions are wrapping an ICC profile and have no other data. ICC + profiles are the standard tool for standard dynamic range (SDR) display + color management. This means the capabilities between the two flavors + differ, and one cannot always be replaced by the other. Compositor + support for each flavor is optional. + +
+ +
+ Color-representation + + + Color-representation protocol deals with (potentially sub-sampled) + YCbCr-RGB conversion, quantization range, and the inclusion of alpha in + the RGB color channels, a.k.a. pre-multiplication. There are several + different specifications on how an YCbCr-like (including ICtCp) signal, + with chroma sub-sampling or not, is created from a full-resolution RGB + image. Again, a client can tag a wl_surface with + color-representation metadata to tell the compositor what kind of pixel + data will be displayed through the wl_surface. + + + The main purpose of color-representation is to correctly off-load the + YCbCr-RGB conversion to the compositor, which can then opportunistically + off-load it further to very power-efficient fixed-function circuitry in + a display controller. This can significantly reduce power consumption + when watching videos compared to using a GPU for the same, and on some + embedded hardware platforms it is a hard requirement for processing high + resolution video. + +
+
+
diff --git a/doc/publican/sources/Introduction.xml b/doc/publican/sources/Introduction.xml index 276db2da..f2a82744 100644 --- a/doc/publican/sources/Introduction.xml +++ b/doc/publican/sources/Introduction.xml @@ -87,7 +87,7 @@ Overall, the philosophy of Wayland is to provide clients with a way to - manage windows and how their contents is displayed. Rendering is left + manage windows and how their contents are displayed. Rendering is left to clients, and system wide memory management interfaces are used to pass buffer handles between clients and the compositing manager. diff --git a/doc/publican/sources/Protocol.xml b/doc/publican/sources/Protocol.xml index 89d76d8e..e4087e9f 100644 --- a/doc/publican/sources/Protocol.xml +++ b/doc/publican/sources/Protocol.xml @@ -97,7 +97,9 @@ in the environment). Beginning in Wayland 1.15, implementations can optionally support server socket endpoints located at arbitrary locations in the filesystem by setting WAYLAND_DISPLAY - to the absolute path at which the server endpoint listens. + to the absolute path at which the server endpoint listens. The socket may + also be provided through file descriptor inheritance, in which case + WAYLAND_SOCKET is set. Every message is structured as 32-bit words; values are represented in the @@ -150,9 +152,10 @@ Starts with an unsigned 32-bit length (including null terminator), - followed by the string contents, including terminating null byte, - then padding to a 32-bit boundary. A null value is represented - with a length of 0. + followed by the UTF-8 encoded string contents, including + terminating null byte, then padding to a 32-bit boundary. A null + value is represented with a length of 0. Interior null bytes are + not permitted. diff --git a/doc/publican/sources/Wayland.xml b/doc/publican/sources/Wayland.xml index 0457c15c..7593097e 100644 --- a/doc/publican/sources/Wayland.xml +++ b/doc/publican/sources/Wayland.xml @@ -12,6 +12,7 @@ + diff --git a/doc/publican/sources/meson.build b/doc/publican/sources/meson.build index 52f3a681..a53b3890 100644 --- a/doc/publican/sources/meson.build +++ b/doc/publican/sources/meson.build @@ -54,6 +54,7 @@ publican_sources = [ 'Protocol.xml', 'Xwayland.xml', 'Compositors.xml', + 'Color.xml', 'Client.xml', 'Server.xml' ] diff --git a/egl/meson.build b/egl/meson.build index 5363e808..b72c7a46 100644 --- a/egl/meson.build +++ b/egl/meson.build @@ -22,6 +22,7 @@ if get_option('tests') test( 'wayland-egl symbols check', find_program('wayland-egl-symbols-check'), + depends: wayland_egl, env: [ 'WAYLAND_EGL_LIB=@0@'.format(wayland_egl_shared.full_path()), 'NM=@0@'.format(nm_path) diff --git a/meson.build b/meson.build index e2976afc..ce386a4c 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'wayland', 'c', - version: '1.22.93', + version: '1.24.90', license: 'MIT', meson_version: '>= 0.57.0', default_options: [ @@ -46,6 +46,7 @@ have_funcs = [ 'memfd_create', 'mremap', 'strndup', + 'gettid', ] foreach f: have_funcs config_h.set('HAVE_' + f.underscorify().to_upper(), cc.has_function(f)) @@ -131,7 +132,9 @@ if get_option('scanner') 'wayland-scanner.mk', 'protocol/wayland.xml', 'protocol/wayland.dtd', - ]) + ], + install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'wayland'), + ) install_data( [ 'wayland-scanner.m4' ], diff --git a/protocol/wayland.xml b/protocol/wayland.xml index 97ad2eb2..c6c22019 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -501,8 +501,10 @@ Sent when this wl_buffer is no longer used by the compositor. - The client is now free to reuse or destroy this buffer and its - backing storage. + + For more information on when release events may or may not be sent, + and what consequences it has, please see the description of + wl_surface.attach. If a client receives a release event before the frame callback requested in the same wl_surface.commit that attaches this @@ -1504,7 +1506,8 @@ the delivery of wl_buffer.release events becomes undefined. A well behaved client should not rely on wl_buffer.release events in this case. Alternatively, a client could create multiple wl_buffer objects - from the same backing storage or use wp_linux_buffer_release. + from the same backing storage or use a protocol extension providing + per-commit release notifications. Destroying the wl_buffer after wl_buffer.release does not change the surface contents. Destroying the wl_buffer before wl_buffer.release @@ -1514,9 +1517,15 @@ mutates the underlying buffer storage, the surface contents become undefined immediately. - If wl_surface.attach is sent with a NULL wl_buffer, or the pending - wl_buffer has been destroyed, the following wl_surface.commit will - remove the surface content. + If wl_surface.attach is sent with a NULL wl_buffer, the + following wl_surface.commit will remove the surface content. + + If a pending wl_buffer has been destroyed, the result is not specified. + Many compositors are known to remove the surface content on the following + wl_surface.commit, but this behaviour is not universal. Clients seeking to + maximise compatibility should not destroy pending buffers and should + ensure that they explicitly remove content from surfaces, even after + destroying buffers. @@ -1658,21 +1667,48 @@ etc.) is double-buffered. Protocol requests modify the pending state, as opposed to the active state in use by the compositor. - A commit request atomically creates a content update from the pending - state, even if the pending state has not been touched. The content - update is placed in a queue until it becomes active. After commit, the - new pending state is as documented for each related request. - - When the content update is applied, the wl_buffer is applied before all - other state. This means that all coordinates in double-buffered state - are relative to the newly attached wl_buffers, except for - wl_surface.attach itself. If there is no newly attached wl_buffer, the - coordinates are relative to the previous content update. - All requests that need a commit to become effective are documented to affect double-buffered state. Other interfaces may add further double-buffered surface state. + + A commit request atomically creates a Content Update (CU) from the + pending state, even if the pending state has not been touched. The + content update is placed at the end of a per-surface queue until it + becomes active. After commit, the new pending state is as documented for + each related request. + + A CU is either a Desync Content Update (DCU) or a Sync Content Update + (SCU). If the surface is effectively synchronized at the commit request, + it is a SCU, otherwise a DCU. + + When a surface transitions from effectively synchronized to effectively + desynchronized, all SCUs in its queue which are not reachable by any + DCU become DCUs and dependency edges from outside the queue to these CUs + are removed. + + See wl_subsurface for the definition of 'effectively synchronized' and + 'effectively desynchronized'. + + When a CU is placed in the queue, the CU has a dependency on the CU in + front of it and to the SCU at end of the queue of every direct child + surface if that SCU exists and does not have another dependent. This can + form a directed acyclic graph of CUs with dependencies as edges. + + In addition to surface state, the CU can have constraints that must be + satisfied before it can be applied. Other interfaces may add CU + constraints. + + All DCUs which do not have a SCU in front of themselves in their queue, + are candidates. If the graph that's reachable by a candidate does not + have any unsatisfied constraints, the entire graph must be applied + atomically. + + When a CU is applied, the wl_buffer is applied before all other state. + This means that all coordinates in double-buffered state are relative to + the newly attached wl_buffers, except for wl_surface.attach itself. If + there is no newly attached wl_buffer, the coordinates are relative to + the previous content update. @@ -1826,6 +1862,9 @@ x and y, combined with the new surface size define in which directions the surface's size changes. + The exact semantics of wl_surface.offset are role-specific. Refer to + the documentation of specific roles for more information. + Surface location offset is double-buffered state, see wl_surface.commit. @@ -1874,7 +1913,7 @@ - + A seat is a group of keyboards, pointer and touch devices. This object is published as a global during start up, or when such a @@ -1902,9 +1941,10 @@ - This is emitted whenever a seat gains or loses the pointer, - keyboard or touch capabilities. The argument is a capability - enum containing the complete set of capabilities this seat has. + This is sent on binding to the seat global or whenever a seat gains + or loses the pointer, keyboard or touch capabilities. + The argument is a capability enum containing the complete set of + capabilities this seat has. When the pointer capability is added, a client may create a wl_pointer object using the wl_seat.get_pointer request. This object @@ -1986,9 +2026,9 @@ The same seat names are used for all clients. Thus, the name can be shared across processes to refer to a specific wl_seat global. - The name event is sent after binding to the seat global. This event is - only sent once per seat object, and the name does not change over the - lifetime of the wl_seat global. + The name event is sent after binding to the seat global, and should be sent + before announcing capabilities. This event only sent once per seat object, + and the name does not change over the lifetime of the wl_seat global. Compositors may re-use the same seat name if the wl_seat global is destroyed and re-created later. @@ -2007,7 +2047,7 @@ - + The wl_pointer interface represents one or more input devices, such as mice, which control the pointer location and pointer_focus @@ -2420,7 +2460,7 @@ - + The wl_keyboard interface represents one or more keyboards associated with a seat. @@ -2473,6 +2513,9 @@ the surface argument and the keys currently logically down to the keys in the keys argument. The compositor must not send this event if the wl_keyboard already had an active surface immediately before this event. + + Clients should not use the list of pressed keys to emulate key-press + events. The order of keys in the list is unspecified. @@ -2499,9 +2542,18 @@ Describes the physical state of a key that produced the key event. + + Since version 10, the key can be in a "repeated" pseudo-state which + means the same as "pressed", but is used to signal repetition in the + key event. + + The key may only enter the repeated state after entering the pressed + state and before entering the released state. This event may be + generated multiple times while the key is down. + @@ -2524,6 +2576,11 @@ compositor must not send this event if state is pressed (resp. released) and the key was already logically down (resp. was not logically down) immediately before this event. + + Since version 10, compositors may send key events with the "repeated" + key state when a wl_keyboard.repeat_info event with a rate argument of + 0 has been received. This allows the compositor to take over the + responsibility of key repetition. @@ -2584,7 +2641,7 @@ - + The wl_touch interface represents a touchscreen associated with a seat. @@ -3090,23 +3147,9 @@ hidden, or if a NULL wl_buffer is applied. These rules apply recursively through the tree of surfaces. - The behaviour of a wl_surface.commit request on a sub-surface - depends on the sub-surface's mode. The possible modes are - synchronized and desynchronized, see methods - wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized - mode caches the wl_surface state to be applied when the parent's - state gets applied, and desynchronized mode applies the pending - wl_surface state directly. A sub-surface is initially in the - synchronized mode. - - Sub-surfaces also have another kind of state, which is managed by - wl_subsurface requests, as opposed to wl_surface requests. This - state includes the sub-surface position relative to the parent - surface (wl_subsurface.set_position), and the stacking order of - the parent and its sub-surfaces (wl_subsurface.place_above and - .place_below). This state is applied when the parent surface's - wl_surface state is applied, regardless of the sub-surface's mode. - As the exception, set_sync and set_desync are effective immediately. + A sub-surface can be in one of two modes. The possible modes are + synchronized and desynchronized, see methods wl_subsurface.set_sync and + wl_subsurface.set_desync. The main surface can be thought to be always in desynchronized mode, since it does not have a parent in the sub-surfaces sense. @@ -3118,6 +3161,22 @@ synchronized mode, and then assume that all its child and grand-child sub-surfaces are synchronized, too, without explicitly setting them. + If a surface behaves as in synchronized mode, it is effectively + synchronized, otherwise it is effectively desynchronized. + + A sub-surface is initially in the synchronized mode. + + Sub-surfaces also have another kind of state, which is managed by + wl_subsurface requests, as opposed to wl_surface requests. This + state includes the sub-surface position relative to the parent + surface (wl_subsurface.set_position), and the stacking order of + the parent and its sub-surfaces (wl_subsurface.place_above and + .place_below). + + This state is double-buffered on the parent's surface regardless of the + sub-surface's mode. As the exception, set_sync and set_desync are + effective immediately. + Destroying a sub-surface takes effect immediately. If you need to synchronize the removal of a sub-surface to the parent surface update, unmap the sub-surface first by attaching a NULL wl_buffer, update parent, @@ -3199,44 +3258,47 @@ Change the commit behaviour of the sub-surface to synchronized - mode, also described as the parent dependent mode. + mode. - In synchronized mode, wl_surface.commit on a sub-surface will - accumulate the committed state in a cache, but the state will - not be applied and hence will not change the compositor output. - The cached state is applied to the sub-surface immediately after - the parent surface's state is applied. This ensures atomic - updates of the parent and all its synchronized sub-surfaces. - Applying the cached state will invalidate the cache, so further - parent surface commits do not (re-)apply old state. - - See wl_subsurface for the recursive effect of this mode. + See wl_subsurface and wl_surface.commit for more information. Change the commit behaviour of the sub-surface to desynchronized - mode, also described as independent or freely running mode. + mode. - In desynchronized mode, wl_surface.commit on a sub-surface will - apply the pending state directly, without caching, as happens - normally with a wl_surface. Calling wl_surface.commit on the - parent surface has no effect on the sub-surface's wl_surface - state. This mode allows a sub-surface to be updated on its own. - - If cached state exists when wl_surface.commit is called in - desynchronized mode, the pending state is added to the cached - state, and applied as a whole. This invalidates the cache. - - Note: even if a sub-surface is set to desynchronized, a parent - sub-surface may override it to behave as synchronized. For details, - see wl_subsurface. - - If a surface's parent surface behaves as desynchronized, then - the cached state is applied on set_desync. + See wl_subsurface and wl_surface.commit for more information. + + + This global fixes problems with other core-protocol interfaces that + cannot be fixed in these interfaces themselves. + + + + + + + + + This request destroys a wl_registry object. + + The client should no longer use the wl_registry after making this + request. + + The compositor will emit a wl_display.delete_id event with the object ID + of the registry and will no longer emit any events on the registry. The + client should re-use the object ID once it receives the + wl_display.delete_id event. + + + + + diff --git a/src/connection.c b/src/connection.c index 8870fd2d..2d1e8d1d 100644 --- a/src/connection.c +++ b/src/connection.c @@ -26,7 +26,8 @@ #define _GNU_SOURCE -#include +#include "../config.h" + #include #include #include @@ -75,7 +76,8 @@ struct wl_connection { static inline size_t size_pot(uint32_t size_bits) { - assert(size_bits < 8 * sizeof(size_t)); + if (!(size_bits < 8 * sizeof(size_t))) + wl_abort("Too many bits for size_t\n"); return ((size_t)1) << size_bits; } @@ -260,7 +262,7 @@ ring_buffer_ensure_space(struct wl_ring_buffer *b, size_t count) * allowed). */ if (net_size > size_pot(size_bits)) { - wl_log("Data too big for buffer (%d + %zd > %zd).\n", + wl_log("Data too big for buffer (%zu + %zu > %zu).\n", ring_buffer_size(b), count, size_pot(size_bits)); errno = E2BIG; return -1; @@ -928,7 +930,7 @@ wl_connection_demarshal(struct wl_connection *connection, for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); - if (arg.type != WL_ARG_FD && p + 1 > end) { + if (arg.type != WL_ARG_FD && p >= end) { wl_log("message too short, " "object (%d), message %s(%s)\n", closure->sender_id, message->name, @@ -975,7 +977,7 @@ wl_connection_demarshal(struct wl_connection *connection, s = (char *) p; - if (length > 0 && s[length - 1] != '\0') { + if (s[length - 1] != '\0') { wl_log("string not nul-terminated, " "message %s(%s)\n", message->name, message->signature); @@ -983,6 +985,14 @@ wl_connection_demarshal(struct wl_connection *connection, goto err; } + if (strlen(s) != length - 1) { + wl_log("string has embedded nul at offset %zu, " + "message %s(%s)\n", strlen(s), + message->name, message->signature); + errno = EINVAL; + goto err; + } + closure->args[i].s = s; p = next; break; @@ -1221,6 +1231,11 @@ wl_closure_invoke(struct wl_closure *closure, uint32_t flags, count + 2, &ffi_type_void, ffi_types); implementation = target->implementation; + if (!implementation) { + wl_abort("Implementation of resource %d of %s is NULL\n", + target->id, target->interface->name); + } + if (!implementation[opcode]) { wl_abort("listener function for opcode %u of %s is NULL\n", opcode, target->interface->name); @@ -1343,7 +1358,7 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer, if (arg.type == WL_ARG_FD) continue; - if (p + 1 > end) + if (p >= end) goto overflow; switch (arg.type) { @@ -1371,7 +1386,7 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer, size = strlen(closure->args[i].s) + 1; *p++ = size; - if (p + div_roundup(size, sizeof *p) > end) + if (div_roundup(size, sizeof *p) > (uint32_t)(end - p)) goto overflow; memcpy(p, closure->args[i].s, size); @@ -1386,7 +1401,7 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer, size = closure->args[i].a->size; *p++ = size; - if (p + div_roundup(size, sizeof *p) > end) + if (div_roundup(size, sizeof *p) > (uint32_t)(end - p)) goto overflow; if (size != 0) @@ -1478,11 +1493,56 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection) return result; } +bool +wl_check_env_token(const char *env, const char *token) +{ + const char *ptr = env; + size_t token_len; + + if (env == NULL) + return false; + + token_len = strlen(token); + + // Scan the string for comma-separated tokens and look for a match. + while (true) { + const char *end; + size_t len; + + // Skip over any leading separators. + while (*ptr == ',') + ptr++; + + if (*ptr == '\x00') + return false; + + end = strchr(ptr + 1, ','); + + // If there isn't another separarator, then the rest of the string + // is one token. + if (end == NULL) + return (strcmp(ptr, token) == 0); + + len = end - ptr; + if (len == token_len && memcmp(ptr, token, len) == 0) { + return true; + } + + // Skip to the next token. + ptr += len; + } + + return false; +} + void wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg), - const char *queue_name) + const char *queue_name, int color) { +#if defined(HAVE_GETTID) + static int include_tid = -1; +#endif // defined(HAVE_GETTID) int i; struct argument_details arg; const char *signature = closure->message->signature; @@ -1499,17 +1559,40 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, clock_gettime(CLOCK_REALTIME, &tp); time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); + fprintf(f, "%s[%7u.%03u] ", + color ? WL_DEBUG_COLOR_GREEN : "", + time / 1000, time % 1000); - fprintf(f, "[%7u.%03u] ", time / 1000, time % 1000); +#if defined(HAVE_GETTID) + if (include_tid < 0) { + include_tid = wl_check_env_token(getenv("WAYLAND_DEBUG"), "thread_id"); + } - if (queue_name) - fprintf(f, "{%s} ", queue_name); + if (include_tid) { + fprintf(f, "%sTID#%d ", + color ? WL_DEBUG_COLOR_CYAN : "", + (int) gettid()); + } +#endif - fprintf(f, "%s%s%s#%u.%s(", + if (queue_name) { + fprintf(f, "%s{%s} ", + color ? WL_DEBUG_COLOR_YELLOW : "", + queue_name); + } + + fprintf(f, "%s%s%s%s%s%s%s#%u%s.%s%s(", + color ? WL_DEBUG_COLOR_RED : "", discarded ? "discarded " : "", + color ? WL_DEBUG_COLOR_RESET : "", send ? " -> " : "", - target->interface->name, target->id, - closure->message->name); + color ? WL_DEBUG_COLOR_BLUE : "", + target->interface->name, + color ? WL_DEBUG_COLOR_MAGENTA : "", + target->id, + color ? WL_DEBUG_COLOR_CYAN : "", + closure->message->name, + color ? WL_DEBUG_COLOR_RESET : ""); for (i = 0; i < closure->count; i++) { signature = get_next_argument(signature, &arg); @@ -1574,7 +1657,7 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, } } - fprintf(f, ")\n"); + fprintf(f, ")%s\n", color ? WL_DEBUG_COLOR_RESET : ""); if (fclose(f) == 0) { fprintf(stderr, "%s", buffer); diff --git a/src/event-loop.c b/src/event-loop.c index 45222f71..89294fd9 100644 --- a/src/event-loop.c +++ b/src/event-loop.c @@ -23,7 +23,6 @@ * SOFTWARE. */ -#include #include #include #include @@ -39,6 +38,7 @@ #include #include #include +#include "timespec-util.h" #include "wayland-util.h" #include "wayland-private.h" #include "wayland-server-core.h" @@ -447,7 +447,8 @@ wl_timer_heap_disarm(struct wl_timer_heap *timers, struct wl_event_source_timer *last_end_evt; int old_source_idx; - assert(source->heap_idx >= 0); + if (!(source->heap_idx >= 0)) + wl_abort("Timer has already been disarmed\n"); old_source_idx = source->heap_idx; source->heap_idx = -1; @@ -476,7 +477,8 @@ wl_timer_heap_arm(struct wl_timer_heap *timers, struct wl_event_source_timer *source, struct timespec deadline) { - assert(source->heap_idx == -1); + if (!(source->heap_idx == -1)) + wl_abort("Timer is already armed\n"); source->deadline = deadline; timers->data[timers->active] = source; @@ -972,57 +974,6 @@ wl_event_loop_dispatch_idle(struct wl_event_loop *loop) } } -static int -timespec_to_ms(struct timespec value) -{ - return (value.tv_sec * 1000) + (value.tv_nsec / 1000000); -} - -static struct timespec -ms_to_timespec(int ms) -{ - struct timespec val; - val.tv_sec = ms / 1000; - val.tv_nsec = (ms % 1000) * 1000000; - return val; -} - -static struct timespec -timespec_normalize(struct timespec value) -{ - struct timespec result = value; - - while (result.tv_nsec >= 1000000000) { - result.tv_nsec -= 1000000000; - result.tv_sec++; - } - - while (result.tv_nsec < 0) { - result.tv_nsec += 1000000000; - result.tv_sec--; - } - - return result; -} - -static struct timespec -timespec_add(struct timespec a, struct timespec b) -{ - struct timespec result; - result.tv_sec = a.tv_sec + b.tv_sec; - result.tv_nsec = a.tv_nsec + b.tv_nsec; - return timespec_normalize(result); -} - -static struct timespec -timespec_sub(struct timespec a, struct timespec b) -{ - struct timespec result; - result.tv_sec = a.tv_sec - b.tv_sec; - result.tv_nsec = a.tv_nsec - b.tv_nsec; - return timespec_normalize(result); -} - /** Wait for events and dispatch them * * \param loop The event loop whose sources to wait for. @@ -1051,13 +1002,15 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout) int i, count; bool has_timers = false; bool use_timeout = timeout > 0; - struct timespec now, end; + struct timespec now; + struct timespec deadline = {0}; + struct timespec result; wl_event_loop_dispatch_idle(loop); if (use_timeout) { clock_gettime(CLOCK_MONOTONIC, &now); - end = timespec_add(now, ms_to_timespec(timeout)); + timespec_add_msec(&deadline, &now, timeout); } while (true) { @@ -1069,7 +1022,8 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout) if (use_timeout) { clock_gettime(CLOCK_MONOTONIC, &now); - timeout = timespec_to_ms(timespec_sub(end, now)); + timespec_sub(&result, &deadline, &now); + timeout = timespec_to_msec(&result); if (timeout <= 0) { /* too late */ count = 0; diff --git a/src/meson.build b/src/meson.build index 5d04334e..984e34ab 100644 --- a/src/meson.build +++ b/src/meson.build @@ -212,6 +212,7 @@ if get_option('libraries') description: 'Server side implementation of the Wayland protocol', version: meson.project_version(), filebase: 'wayland-server', + libraries: mathlib_dep, variables: [ 'datarootdir=' + join_paths('${prefix}', get_option('datadir')), 'pkgdatadir=' + join_paths('${pc_sysrootdir}${datarootdir}', meson.project_name()) @@ -251,6 +252,7 @@ if get_option('libraries') description: 'Wayland client side library', version: meson.project_version(), filebase: 'wayland-client', + libraries: mathlib_dep, variables: [ 'datarootdir=' + join_paths('${prefix}', get_option('datadir')), 'pkgdatadir=' + join_paths('${pc_sysrootdir}${datarootdir}', meson.project_name()) diff --git a/src/scanner.c b/src/scanner.c index 95524513..1b71e60c 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -1378,6 +1378,58 @@ emit_event_wrappers(struct wl_list *message_list, struct interface *interface) } } +static void +emit_validator(struct interface *interface, struct enumeration *e) +{ + struct entry *entry; + + printf("#ifndef %s_%s_ENUM_IS_VALID\n", + interface->uppercase_name, e->uppercase_name); + printf("#define %s_%s_ENUM_IS_VALID\n", + interface->uppercase_name, e->uppercase_name); + + printf("/**\n" + " * @ingroup iface_%s\n" + " * Validate a %s %s value.\n" + " *\n" + " * @return true on success, false on error.\n" + " * @ref %s_%s\n" + " */\n" + "static inline bool\n" + "%s_%s_is_valid(uint32_t value, uint32_t version) {\n", + interface->name, interface->name, e->name, + interface->name, e->name, + interface->name, e->name); + + if (e->bitfield) { + printf(" uint32_t valid = 0;\n"); + wl_list_for_each(entry, &e->entry_list, link) { + printf(" if (version >= %d)\n" + " valid |= %s_%s_%s;\n", + entry->since, + interface->uppercase_name, e->uppercase_name, + entry->uppercase_name); + } + printf(" return (value & ~valid) == 0;\n"); + } else { + printf(" switch (value) {\n"); + wl_list_for_each(entry, &e->entry_list, link) { + printf(" case %s%s_%s_%s:\n" + " return version >= %d;\n", + entry->value[0] == '-' ? "(uint32_t)" : "", + interface->uppercase_name, e->uppercase_name, + entry->uppercase_name, entry->since); + } + printf(" default:\n" + " return false;\n" + " }\n"); + } + printf("}\n"); + + printf("#endif /* %s_%s_ENUM_IS_VALID */\n\n", + interface->uppercase_name, e->uppercase_name); +} + static void emit_enumerations(struct interface *interface, bool with_validators) { @@ -1439,35 +1491,11 @@ emit_enumerations(struct interface *interface, bool with_validators) } - if (with_validators) { - printf("/**\n" - " * @ingroup iface_%s\n" - " * Validate a %s %s value.\n" - " *\n" - " * @return true on success, false on error.\n" - " * @ref %s_%s\n" - " */\n" - "static inline bool\n" - "%s_%s_is_valid(uint32_t value, uint32_t version) {\n" - " switch (value) {\n", - interface->name, interface->name, e->name, - interface->name, e->name, - interface->name, e->name); - wl_list_for_each(entry, &e->entry_list, link) { - printf(" case %s%s_%s_%s:\n" - " return version >= %d;\n", - entry->value[0] == '-' ? "(uint32_t)" : "", - interface->uppercase_name, e->uppercase_name, - entry->uppercase_name, entry->since); - } - printf(" default:\n" - " return false;\n" - " }\n" - "}\n"); - } - printf("#endif /* %s_%s_ENUM */\n\n", interface->uppercase_name, e->uppercase_name); + + if (with_validators) + emit_validator(interface, e); } } diff --git a/src/timespec-util.h b/src/timespec-util.h new file mode 100644 index 00000000..d3c53bd7 --- /dev/null +++ b/src/timespec-util.h @@ -0,0 +1,311 @@ +/* + * Copyright © 2014 - 2015 Collabora, Ltd. + * + * 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. + */ + +#ifndef TIMESPEC_UTIL_H +#define TIMESPEC_UTIL_H + +#include +#include +#include +#include + +#define NSEC_PER_SEC 1000000000 + +/* Subtract timespecs + * + * \param r[out] result: a - b + * \param a[in] operand + * \param b[in] operand + */ +static inline void +timespec_sub(struct timespec *r, + const struct timespec *a, const struct timespec *b) +{ + r->tv_sec = a->tv_sec - b->tv_sec; + r->tv_nsec = a->tv_nsec - b->tv_nsec; + if (r->tv_nsec < 0) { + r->tv_sec--; + r->tv_nsec += NSEC_PER_SEC; + } +} + +/* Add a nanosecond value to a timespec + * + * \param r[out] result: a + b + * \param a[in] base operand as timespec + * \param b[in] operand in nanoseconds + */ +static inline void +timespec_add_nsec(struct timespec *r, const struct timespec *a, int64_t b) +{ + r->tv_sec = a->tv_sec + (b / NSEC_PER_SEC); + r->tv_nsec = a->tv_nsec + (b % NSEC_PER_SEC); + + if (r->tv_nsec >= NSEC_PER_SEC) { + r->tv_sec++; + r->tv_nsec -= NSEC_PER_SEC; + } else if (r->tv_nsec < 0) { + r->tv_sec--; + r->tv_nsec += NSEC_PER_SEC; + } +} + +/* Add a millisecond value to a timespec + * + * \param r[out] result: a + b + * \param a[in] base operand as timespec + * \param b[in] operand in milliseconds + */ +static inline void +timespec_add_msec(struct timespec *r, const struct timespec *a, int64_t b) +{ + timespec_add_nsec(r, a, b * 1000000); +} + +/* Convert timespec to nanoseconds + * + * \param a timespec + * \return nanoseconds + */ +static inline int64_t +timespec_to_nsec(const struct timespec *a) +{ + return (int64_t)a->tv_sec * NSEC_PER_SEC + a->tv_nsec; +} + +/* Subtract timespecs and return result in nanoseconds + * + * \param a[in] operand + * \param b[in] operand + * \return to_nanoseconds(a - b) + */ +static inline int64_t +timespec_sub_to_nsec(const struct timespec *a, const struct timespec *b) +{ + struct timespec r; + timespec_sub(&r, a, b); + return timespec_to_nsec(&r); +} + +/* Convert timespec to milliseconds + * + * \param a timespec + * \return milliseconds + * + * Rounding to integer milliseconds happens always down (floor()). + */ +static inline int64_t +timespec_to_msec(const struct timespec *a) +{ + return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; +} + +/* Subtract timespecs and return result in milliseconds + * + * \param a[in] operand + * \param b[in] operand + * \return to_milliseconds(a - b) + */ +static inline int64_t +timespec_sub_to_msec(const struct timespec *a, const struct timespec *b) +{ + return timespec_sub_to_nsec(a, b) / 1000000; +} + +/* Convert timespec to microseconds + * + * \param a timespec + * \return microseconds + * + * Rounding to integer microseconds happens always down (floor()). + */ +static inline int64_t +timespec_to_usec(const struct timespec *a) +{ + return (int64_t)a->tv_sec * 1000000 + a->tv_nsec / 1000; +} + +/* Convert timespec to protocol data + * + * \param a timespec + * \param tv_sec_hi[out] the high bytes of the seconds part + * \param tv_sec_lo[out] the low bytes of the seconds part + * \param tv_nsec[out] the nanoseconds part + * + * The input timespec must be normalized (the nanoseconds part should + * be less than 1 second) and non-negative. + */ +static inline void +timespec_to_proto(const struct timespec *a, uint32_t *tv_sec_hi, + uint32_t *tv_sec_lo, uint32_t *tv_nsec) +{ + assert(a->tv_sec >= 0); + assert(a->tv_nsec >= 0 && a->tv_nsec < NSEC_PER_SEC); + + uint64_t sec64 = a->tv_sec; + + *tv_sec_hi = sec64 >> 32; + *tv_sec_lo = sec64 & 0xffffffff; + *tv_nsec = a->tv_nsec; +} + +/* Convert nanoseconds to timespec + * + * \param a timespec + * \param b nanoseconds + */ +static inline void +timespec_from_nsec(struct timespec *a, int64_t b) +{ + a->tv_sec = b / NSEC_PER_SEC; + a->tv_nsec = b % NSEC_PER_SEC; +} + +/* Convert microseconds to timespec + * + * \param a timespec + * \param b microseconds + */ +static inline void +timespec_from_usec(struct timespec *a, int64_t b) +{ + timespec_from_nsec(a, b * 1000); +} + +/* Convert milliseconds to timespec + * + * \param a timespec + * \param b milliseconds + */ +static inline void +timespec_from_msec(struct timespec *a, int64_t b) +{ + timespec_from_nsec(a, b * 1000000); +} + +/* Convert protocol data to timespec + * + * \param a[out] timespec + * \param tv_sec_hi the high bytes of seconds part + * \param tv_sec_lo the low bytes of seconds part + * \param tv_nsec the nanoseconds part + */ +static inline void +timespec_from_proto(struct timespec *a, uint32_t tv_sec_hi, + uint32_t tv_sec_lo, uint32_t tv_nsec) +{ + a->tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo; + a->tv_nsec = tv_nsec; +} + +/* Check if a timespec is zero + * + * \param a timespec + * \return whether the timespec is zero + */ +static inline bool +timespec_is_zero(const struct timespec *a) +{ + return a->tv_sec == 0 && a->tv_nsec == 0; +} + +/* Check if two timespecs are equal + * + * \param a[in] timespec to check + * \param b[in] timespec to check + * \return whether timespecs a and b are equal + */ +static inline bool +timespec_eq(const struct timespec *a, const struct timespec *b) +{ + return a->tv_sec == b->tv_sec && + a->tv_nsec == b->tv_nsec; +} + +/* Convert milli-Hertz to nanoseconds + * + * \param mhz frequency in mHz, not zero + * \return period in nanoseconds + */ +static inline int64_t +millihz_to_nsec(uint32_t mhz) +{ + assert(mhz > 0); + return 1000000000000LL / mhz; +} + +/** + * Checks whether a timespec value is after another + * + * \param a[in] timespec to compare + * \param b[in] timespec to compare + * \return whether a is after b + */ +static inline bool +timespec_after(const struct timespec *a, const struct timespec *b) +{ + return (a->tv_sec == b->tv_sec) ? + (a->tv_nsec > b->tv_nsec) : + (a->tv_sec > b->tv_sec); +} + +/** + * Add timespecs + * + * \param r[out] result: a + b + * \param a[in] operand + * \param b[in] operand + */ +static inline void +timespec_add(struct timespec *r, + const struct timespec *a, const struct timespec *b) +{ + r->tv_sec = a->tv_sec + b->tv_sec; + r->tv_nsec = a->tv_nsec + b->tv_nsec; + if (r->tv_nsec > NSEC_PER_SEC) { + r->tv_sec++; + r->tv_nsec -= NSEC_PER_SEC; + } +} + +/** + * Saturating timespec subtraction + * + * \param r[out] result: max(a - b, 0) + * \param a[in] operand + * \param b[in] operand + */ +static inline void +timespec_sub_saturate(struct timespec *r, + const struct timespec *a, const struct timespec *b) +{ + timespec_sub(r, a, b); + if (r->tv_sec < 0) { + r->tv_sec = 0; + r->tv_nsec = 0; + } +} + +#endif /* TIMESPEC_UTIL_H */ diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h index a4ca4e5d..e0523e49 100644 --- a/src/wayland-client-core.h +++ b/src/wayland-client-core.h @@ -34,6 +34,8 @@ extern "C" { #endif +struct timespec; + /** \class wl_proxy * * \brief Represents a protocol object on the client side. @@ -219,6 +221,9 @@ wl_proxy_get_tag(struct wl_proxy *proxy); const char * wl_proxy_get_class(struct wl_proxy *proxy); +const struct wl_interface * +wl_proxy_get_interface(struct wl_proxy *proxy); + struct wl_display * wl_proxy_get_display(struct wl_proxy *proxy); @@ -250,13 +255,29 @@ int wl_display_dispatch_queue(struct wl_display *display, struct wl_event_queue *queue); +int +wl_display_dispatch_timeout(struct wl_display *display, + const struct timespec *timeout); + +int +wl_display_dispatch_queue_timeout(struct wl_display *display, + struct wl_event_queue *queue, + const struct timespec *timeout); + int wl_display_dispatch_queue_pending(struct wl_display *display, struct wl_event_queue *queue); +int +wl_display_dispatch_queue_pending_single(struct wl_display *display, + struct wl_event_queue *queue); + int wl_display_dispatch_pending(struct wl_display *display); +int +wl_display_dispatch_pending_single(struct wl_display *display); + int wl_display_get_error(struct wl_display *display); diff --git a/src/wayland-client.c b/src/wayland-client.c index 9cf27939..ed686b5c 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -46,6 +45,7 @@ #include "wayland-os.h" #include "wayland-client.h" #include "wayland-private.h" +#include "timespec-util.h" /** \cond */ @@ -115,6 +115,7 @@ struct wl_display { /** \endcond */ static int debug_client = 0; +static int debug_color = 0; /** * This helper function wakes up all threads that are @@ -235,13 +236,16 @@ wl_event_queue_init(struct wl_event_queue *queue, static void wl_proxy_unref(struct wl_proxy *proxy) { - assert(proxy->refcount > 0); + if (!(proxy->refcount > 0)) + wl_abort("Proxy requested for unref has no references\n"); if (--proxy->refcount > 0) return; /* If we get here, the client must have explicitly requested * deletion. */ - assert(proxy->flags & WL_PROXY_FLAG_DESTROYED); + if (!(proxy->flags & WL_PROXY_FLAG_DESTROYED)) + wl_abort("Proxy with no references not yet explicitly" + "destroyed\n"); free(proxy); } @@ -671,6 +675,9 @@ wl_proxy_add_listener(struct wl_proxy *proxy, * This function is useful in clients with multiple listeners on the same * interface to allow the identification of which code to execute. * + * If \ref wl_proxy_add_dispatcher was used, this function returns the + * dispatcher_data pointer instead. + * * \memberof wl_proxy */ WL_EXPORT const void * @@ -914,21 +921,29 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode, closure = wl_closure_marshal(&proxy->object, opcode, args, message); if (closure == NULL) { - wl_log("Error marshalling request: %s\n", strerror(errno)); + wl_log("Error marshalling request for %s.%s: %s\n", + proxy->object.interface->name, message->name, + strerror(errno)); display_fatal_error(proxy->display, errno); goto err_unlock; } if (debug_client) { struct wl_event_queue *queue; + const char *queue_name = NULL; queue = wl_proxy_get_queue(proxy); + if (queue) + queue_name = wl_event_queue_get_name(queue); + wl_closure_print(closure, &proxy->object, true, false, NULL, - wl_event_queue_get_name(queue)); + queue_name, debug_color); } if (wl_closure_send(closure, proxy->display->connection)) { - wl_log("Error sending request: %s\n", strerror(errno)); + wl_log("Error sending request for %s.%s: %s\n", + proxy->object.interface->name, message->name, + strerror(errno)); display_fatal_error(proxy->display, errno); } @@ -1172,7 +1187,8 @@ connect_to_socket(const char *name) "%s", name) + 1; } - assert(name_size > 0); + if (!(name_size > 0)) + wl_abort("Error assigning path name for socket connection\n"); if (name_size > (int)sizeof addr.sun_path) { if (!path_is_absolute) { wl_log("error: socket path \"%s/%s\" plus null terminator" @@ -1214,10 +1230,23 @@ wl_display_connect_to_fd(int fd) { struct wl_display *display; const char *debug; + const char *no_color; + const char *force_color; + no_color = getenv("NO_COLOR"); + force_color = getenv("FORCE_COLOR"); debug = getenv("WAYLAND_DEBUG"); - if (debug && (strstr(debug, "client") || strstr(debug, "1"))) + if (debug && (wl_check_env_token(debug, "client") || wl_check_env_token(debug, "1"))) { debug_client = 1; + if (isatty(fileno(stderr))) + debug_color = 1; + } + + if (force_color && force_color[0] != '\0') + debug_color = 1; + + if (no_color && no_color[0] != '\0') + debug_color = 0; display = zalloc(sizeof *display); if (display == NULL) { @@ -1549,6 +1578,28 @@ queue_event(struct wl_display *display, int len) id = p[0]; opcode = p[1] & 0xffff; size = p[1] >> 16; + + /* + * If the message is larger than the maximum size of the + * connection buffer, the connection buffer will fill to + * its max size and stay there, with no message ever + * successfully being processed. If the user of + * libwayland-client uses a level-triggered event loop, + * this will cause the client to enter a loop that + * consumes CPU. To avoid this, immediately drop the + * connection. Since the maximum size of a message should + * not depend on the max buffer size chosen by the client, + * always compare the message size against the + * limit enforced by libwayland 1.22 and below (4096), + * rather than the actual value the client chose. + */ + if (size > WL_MAX_MESSAGE_SIZE) { + wl_log("Message length %u exceeds limit %d\n", + size, WL_MAX_MESSAGE_SIZE); + errno = E2BIG; + return -1; + } + if (len < size) return 0; @@ -1563,12 +1614,16 @@ queue_event(struct wl_display *display, int len) if (debug_client) { clock_gettime(CLOCK_REALTIME, &tp); time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); - - fprintf(stderr, "[%7u.%03u] discarded [%s]#%d.[event %d]" + fprintf(stderr, "%s[%7u.%03u] %sdiscarded %s[%s]%s#%u%s.[event %d]%s" "(%d fd, %d byte)\n", + debug_color ? WL_DEBUG_COLOR_GREEN : "", time / 1000, time % 1000, + debug_color ? WL_DEBUG_COLOR_RED : "", + debug_color ? WL_DEBUG_COLOR_BLUE : "", zombie ? "zombie" : "unknown", - id, opcode, + debug_color ? WL_DEBUG_COLOR_MAGENTA : "", id, + debug_color ? WL_DEBUG_COLOR_BLUE : "", opcode, + debug_color ? WL_DEBUG_COLOR_RESET : "", num_zombie_fds, size); } if (num_zombie_fds > 0) @@ -1653,7 +1708,7 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue) !(proxy->dispatcher || proxy->object.implementation); wl_closure_print(closure, &proxy->object, false, discarded, - id_from_object, queue->name); + id_from_object, queue->name, debug_color); } if (proxy_destroyed) { @@ -1827,6 +1882,34 @@ err: return -1; } + +static int +dispatch_queue_single(struct wl_display *display, struct wl_event_queue *queue) +{ + if (display->last_error) + goto err; + + while (!wl_list_empty(&display->display_queue.event_list)) { + dispatch_event(display, &display->display_queue); + if (display->last_error) + goto err; + } + + if (!wl_list_empty(&queue->event_list)) { + dispatch_event(display, queue); + if (display->last_error) + goto err; + return 1; + } else { + return 0; + } + +err: + errno = display->last_error; + + return -1; +} + /** Prepare to read events from the display's file descriptor to a queue * * \param display The display context object @@ -1942,20 +2025,142 @@ wl_display_cancel_read(struct wl_display *display) } static int -wl_display_poll(struct wl_display *display, short int events) +wl_display_poll(struct wl_display *display, + short int events, + const struct timespec *timeout) { int ret; struct pollfd pfd[1]; + struct timespec now; + struct timespec deadline = {0}; + struct timespec result; + struct timespec *remaining_timeout = NULL; + + if (timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timespec_add(&deadline, &now, timeout); + } pfd[0].fd = display->fd; pfd[0].events = events; do { - ret = poll(pfd, 1, -1); + if (timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timespec_sub_saturate(&result, &deadline, &now); + remaining_timeout = &result; + } + ret = ppoll(pfd, 1, remaining_timeout, NULL); } while (ret == -1 && errno == EINTR); return ret; } +/** Dispatch events in an event queue with a timeout + * + * \param display The display context object + * \param queue The event queue to dispatch + * \param timeout A timeout describing how long the call should block trying to + * dispatch events + * \return The number of dispatched events on success, -1 on failure + * + * This function behaves identical to wl_display_dispatch_queue() except + * that it also takes a timeout and returns 0 if the timeout elapsed. + * + * Passing NULL as a timeout means an infinite timeout. An empty timespec + * causes wl_display_dispatch_queue_timeout() to return immediately even if no + * events have been dispatched. + * + * If a timeout is passed to wl_display_dispatch_queue_timeout() it is updated + * to the remaining time. + * + * \sa wl_display_dispatch_queue() + * + * \memberof wl_display + */ +WL_EXPORT int +wl_display_dispatch_queue_timeout(struct wl_display *display, + struct wl_event_queue *queue, + const struct timespec *timeout) +{ + int ret; + struct timespec now; + struct timespec deadline = {0}; + struct timespec result; + struct timespec *remaining_timeout = NULL; + + if (timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timespec_add(&deadline, &now, timeout); + } + + if (wl_display_prepare_read_queue(display, queue) == -1) + return wl_display_dispatch_queue_pending(display, queue); + + while (true) { + ret = wl_display_flush(display); + + if (ret != -1 || errno != EAGAIN) + break; + + if (timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timespec_sub_saturate(&result, &deadline, &now); + remaining_timeout = &result; + } + ret = wl_display_poll(display, POLLOUT, remaining_timeout); + + if (ret <= 0) { + wl_display_cancel_read(display); + return ret; + } + } + + /* Don't stop if flushing hits an EPIPE; continue so we can read any + * protocol error that may have triggered it. */ + if (ret < 0 && errno != EPIPE) { + wl_display_cancel_read(display); + return -1; + } + + while (true) { + if (timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timespec_sub_saturate(&result, &deadline, &now); + remaining_timeout = &result; + } + + ret = wl_display_poll(display, POLLIN, remaining_timeout); + if (ret <= 0) { + wl_display_cancel_read(display); + break; + } + + ret = wl_display_read_events(display); + if (ret == -1) + break; + + ret = wl_display_dispatch_queue_pending(display, queue); + if (ret != 0) + break; + + /* We managed to read data from the display but there is no + * complete event to dispatch yet. Try reading again. */ + if (wl_display_prepare_read_queue(display, queue) == -1) + return wl_display_dispatch_queue_pending(display, queue); + } + + return ret; +} + +WL_EXPORT int +wl_display_dispatch_timeout(struct wl_display *display, + const struct timespec *timeout) +{ + return wl_display_dispatch_queue_timeout(display, + &display->default_queue, + timeout); +} + /** Dispatch events in an event queue * * \param display The display context object @@ -1987,8 +2192,8 @@ wl_display_poll(struct wl_display *display, short int events) * \note Since Wayland 1.5 the display has an extra queue * for its own events (i. e. delete_id). This queue is dispatched always, * no matter what queue we passed as an argument to this function. - * That means that this function can return non-0 value even when it - * haven't dispatched any event for the given queue. + * That means that this function can return even when it has not dispatched any + * event for the given queue. * * \sa wl_display_dispatch(), wl_display_dispatch_pending(), * wl_display_dispatch_queue_pending(), wl_display_prepare_read_queue() @@ -2001,37 +2206,10 @@ wl_display_dispatch_queue(struct wl_display *display, { int ret; - if (wl_display_prepare_read_queue(display, queue) == -1) - return wl_display_dispatch_queue_pending(display, queue); + ret = wl_display_dispatch_queue_timeout(display, queue, NULL); + assert(ret == -1 || ret > 0); - while (true) { - ret = wl_display_flush(display); - - if (ret != -1 || errno != EAGAIN) - break; - - if (wl_display_poll(display, POLLOUT) == -1) { - wl_display_cancel_read(display); - return -1; - } - } - - /* Don't stop if flushing hits an EPIPE; continue so we can read any - * protocol error that may have triggered it. */ - if (ret < 0 && errno != EPIPE) { - wl_display_cancel_read(display); - return -1; - } - - if (wl_display_poll(display, POLLIN) == -1) { - wl_display_cancel_read(display); - return -1; - } - - if (wl_display_read_events(display) == -1) - return -1; - - return wl_display_dispatch_queue_pending(display, queue); + return ret; } /** Dispatch pending events in an event queue @@ -2062,6 +2240,34 @@ wl_display_dispatch_queue_pending(struct wl_display *display, return ret; } +/** Dispatch at most one pending event in an event queue + * + * \param display The display context object + * \param queue The event queue to dispatch + * \return The number of dispatched events (0 or 1) on success or -1 on failure + * + * Dispatch at most one pending event for objects assigned to the given + * event queue. On failure -1 is returned and errno set appropriately. + * If there are no events queued, this function returns immediately. + * + * \memberof wl_display + * \since 1.25.0 + */ +WL_EXPORT int +wl_display_dispatch_queue_pending_single(struct wl_display *display, + struct wl_event_queue *queue) +{ + int ret; + + pthread_mutex_lock(&display->mutex); + + ret = dispatch_queue_single(display, queue); + + pthread_mutex_unlock(&display->mutex); + + return ret; +} + /** Process incoming events * * \param display The display context object @@ -2122,6 +2328,25 @@ wl_display_dispatch_pending(struct wl_display *display) &display->default_queue); } +/** Dispatch at most one pending event in the default event queue. + * + * \param display The display context object + * \return The number of dispatched events (0 or 1) on success or -1 on failure + * + * Dispatch at most one pending event for objects assigned to the default + * event queue. On failure -1 is returned and errno set appropriately. + * If there are no events queued, this function returns immediately. + * + * \memberof wl_display + * \since 1.25.0 + */ +WL_EXPORT int +wl_display_dispatch_pending_single(struct wl_display *display) +{ + return wl_display_dispatch_queue_pending_single(display, + &display->default_queue); +} + /** Retrieve the last error that occurred on a display * * \param display The display context object @@ -2403,6 +2628,20 @@ wl_proxy_get_class(struct wl_proxy *proxy) return proxy->object.interface->name; } +/** Get the interface of a proxy object + * + * \param proxy The proxy object + * \return The interface of the object associated with the proxy + * + * \memberof wl_proxy + * \since 1.24 + */ +WL_EXPORT const struct wl_interface * +wl_proxy_get_interface(struct wl_proxy *proxy) +{ + return proxy->object.interface; +} + /** Get the display of a proxy object * * \param proxy The proxy object @@ -2453,7 +2692,9 @@ wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue) wl_list_remove(&proxy->queue_link); if (queue) { - assert(proxy->display == queue->display); + if (!(proxy->display == queue->display)) + wl_abort("Proxy and queue point to different " + "wl_displays"); proxy->queue = queue; } else { proxy->queue = &proxy->display->default_queue; @@ -2581,7 +2822,8 @@ wl_proxy_wrapper_destroy(void *proxy_wrapper) wl_abort("Tried to destroy non-wrapper proxy with " "wl_proxy_wrapper_destroy\n"); - assert(wrapper->refcount == 1); + if (!(wrapper->refcount == 1)) + wl_abort("Expected proxy wrapper's refcount to be 1\n"); pthread_mutex_lock(&wrapper->display->mutex); diff --git a/src/wayland-private.h b/src/wayland-private.h index fe9120af..d0e4cfc6 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -49,6 +49,17 @@ #define WL_CLOSURE_MAX_ARGS 20 #define WL_BUFFER_DEFAULT_SIZE_POT 12 #define WL_BUFFER_DEFAULT_MAX_SIZE (1 << WL_BUFFER_DEFAULT_SIZE_POT) +#if WL_BUFFER_DEFAULT_MAX_SIZE < WL_MAX_MESSAGE_SIZE +# error default buffer cannot hold maximum-sized message +#endif + +#define WL_DEBUG_COLOR_RESET "\e[0m" +#define WL_DEBUG_COLOR_RED "\e[31m" +#define WL_DEBUG_COLOR_GREEN "\e[32m" +#define WL_DEBUG_COLOR_YELLOW "\e[33m" +#define WL_DEBUG_COLOR_BLUE "\e[34m" +#define WL_DEBUG_COLOR_MAGENTA "\e[35m" +#define WL_DEBUG_COLOR_CYAN "\e[36m" /** * Argument types used in signatures. @@ -226,11 +237,14 @@ wl_closure_send(struct wl_closure *closure, struct wl_connection *connection); int wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection); +bool +wl_check_env_token(const char *env, const char *token); + void wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg), - const char *queue_name); + const char *queue_name, int color); void wl_closure_destroy(struct wl_closure *closure); diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index 63d0f02d..c2dcc218 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -227,7 +227,7 @@ typedef void (*wl_global_bind_func_t)(struct wl_client *client, void *data, uint32_t version, uint32_t id); uint32_t -wl_display_get_serial(struct wl_display *display); +wl_display_get_serial(const struct wl_display *display); uint32_t wl_display_next_serial(struct wl_display *display); @@ -324,7 +324,7 @@ void wl_client_flush(struct wl_client *client); void -wl_client_get_credentials(struct wl_client *client, +wl_client_get_credentials(const struct wl_client *client, pid_t *pid, uid_t *uid, gid_t *gid); int @@ -550,7 +550,10 @@ void wl_resource_queue_event_array(struct wl_resource *resource, uint32_t opcode, union wl_argument *args); -/* msg is a printf format string, variable args are its args. */ +void +wl_resource_post_error_vargs(struct wl_resource *resource, + uint32_t code, const char *msg, va_list argp); + void wl_resource_post_error(struct wl_resource *resource, uint32_t code, const char *msg, ...) WL_PRINTF(3, 4); @@ -583,7 +586,7 @@ void wl_resource_destroy(struct wl_resource *resource); uint32_t -wl_resource_get_id(struct wl_resource *resource); +wl_resource_get_id(const struct wl_resource *resource); struct wl_list * wl_resource_get_link(struct wl_resource *resource); @@ -604,7 +607,7 @@ void * wl_resource_get_user_data(struct wl_resource *resource); int -wl_resource_get_version(struct wl_resource *resource); +wl_resource_get_version(const struct wl_resource *resource); void wl_resource_set_destructor(struct wl_resource *resource, @@ -614,8 +617,12 @@ int wl_resource_instance_of(struct wl_resource *resource, const struct wl_interface *interface, const void *implementation); + const char * -wl_resource_get_class(struct wl_resource *resource); +wl_resource_get_class(const struct wl_resource *resource); + +const struct wl_interface * +wl_resource_get_interface(struct wl_resource *resource); void wl_resource_add_destroy_listener(struct wl_resource *resource, @@ -651,16 +658,22 @@ void * wl_shm_buffer_get_data(struct wl_shm_buffer *buffer); int32_t -wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer); +wl_shm_buffer_get_stride(const struct wl_shm_buffer *buffer); uint32_t -wl_shm_buffer_get_format(struct wl_shm_buffer *buffer); +wl_shm_buffer_get_format(const struct wl_shm_buffer *buffer); int32_t -wl_shm_buffer_get_width(struct wl_shm_buffer *buffer); +wl_shm_buffer_get_width(const struct wl_shm_buffer *buffer); int32_t -wl_shm_buffer_get_height(struct wl_shm_buffer *buffer); +wl_shm_buffer_get_height(const struct wl_shm_buffer *buffer); + +struct wl_shm_buffer * +wl_shm_buffer_ref(struct wl_shm_buffer *buffer); + +void +wl_shm_buffer_unref(struct wl_shm_buffer *buffer); struct wl_shm_pool * wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer); @@ -674,10 +687,11 @@ wl_display_init_shm(struct wl_display *display); uint32_t * wl_display_add_shm_format(struct wl_display *display, uint32_t format); +WL_DEPRECATED struct wl_shm_buffer * wl_shm_buffer_create(struct wl_client *client, uint32_t id, int32_t width, int32_t height, - int32_t stride, uint32_t format) WL_DEPRECATED; + int32_t stride, uint32_t format); void wl_log_set_handler_server(wl_log_func_t handler); diff --git a/src/wayland-server.c b/src/wayland-server.c index 2e185634..c81d98f1 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -150,6 +149,7 @@ struct wl_protocol_logger { }; static int debug_server = 0; +static int debug_color = 0; static void log_closure(struct wl_resource *resource, @@ -161,7 +161,7 @@ log_closure(struct wl_resource *resource, struct wl_protocol_logger_message message; if (debug_server) - wl_closure_print(closure, object, send, false, NULL, NULL); + wl_closure_print(closure, object, send, false, NULL, NULL, debug_color); if (!wl_list_empty(&display->protocol_loggers)) { message.resource = resource; @@ -288,7 +288,16 @@ wl_resource_queue_event(struct wl_resource *resource, uint32_t opcode, ...) wl_resource_queue_event_array(resource, opcode, args); } -static void +/** Post a protocol error + * + * \param resource The resource object + * \param code The error code + * \param msg The error message format string + * \param argp The format string argument list + * + * \memberof wl_resource + */ +WL_EXPORT void wl_resource_post_error_vargs(struct wl_resource *resource, uint32_t code, const char *msg, va_list argp) { @@ -310,9 +319,17 @@ wl_resource_post_error_vargs(struct wl_resource *resource, wl_resource_post_event(client->display_resource, WL_DISPLAY_ERROR, resource, code, buffer); client->error = true; - } +/** Post a protocol error + * + * \param resource The resource object + * \param code The error code + * \param msg The error message format string + * \param ... The format string arguments + * + * \memberof wl_resource + */ WL_EXPORT void wl_resource_post_error(struct wl_resource *resource, uint32_t code, const char *msg, ...) @@ -381,6 +398,29 @@ wl_client_connection_data(int fd, uint32_t mask, void *data) wl_connection_copy(connection, p, sizeof p); opcode = p[1] & 0xffff; size = p[1] >> 16; + + /* + * If the message is larger than the maximum size of the + * connection buffer, the connection buffer will fill to + * its max size and stay there, with no message ever + * successfully being processed. Since libwayland-server + * uses level-triggered epoll, it will cause the server to + * enter a loop that consumes CPU. To avoid this, + * immediately disconnect the client with a protocol + * error. Since the maximum size of a message should not + * depend on the buffer size chosen by the compositor, + * always compare the message size against the + * limit enforced by libwayland 1.22 and below (4096), + * rather than the actual value the compositor chose. + */ + if (size > WL_MAX_MESSAGE_SIZE) { + wl_resource_post_error(client->display_resource, + WL_DISPLAY_ERROR_INVALID_METHOD, + "message length %u exceeds %d", + size, WL_MAX_MESSAGE_SIZE); + break; + } + if (len < size) break; @@ -603,7 +643,7 @@ err_client: * \memberof wl_client */ WL_EXPORT void -wl_client_get_credentials(struct wl_client *client, +wl_client_get_credentials(const struct wl_client *client, pid_t *pid, uid_t *uid, gid_t *gid) { if (pid) @@ -783,7 +823,7 @@ wl_resource_destroy(struct wl_resource *resource) } WL_EXPORT uint32_t -wl_resource_get_id(struct wl_resource *resource) +wl_resource_get_id(const struct wl_resource *resource) { return resource->object.id; } @@ -837,7 +877,7 @@ wl_resource_get_user_data(struct wl_resource *resource) } WL_EXPORT int -wl_resource_get_version(struct wl_resource *resource) +wl_resource_get_version(const struct wl_resource *resource) { return resource->version; } @@ -884,11 +924,25 @@ wl_resource_get_destroy_listener(struct wl_resource *resource, * \memberof wl_resource */ WL_EXPORT const char * -wl_resource_get_class(struct wl_resource *resource) +wl_resource_get_class(const struct wl_resource *resource) { return resource->object.interface->name; } +/** Get the interface of a resource object + * + * \param resource The resource object + * \return The interface of the object associated with the resource + * + * \memberof wl_resource + * \since 1.24 + */ +WL_EXPORT const struct wl_interface * +wl_resource_get_interface(struct wl_resource *resource) +{ + return resource->object.interface; +} + /** * Add a listener to be called at the beginning of wl_client destruction * @@ -1138,10 +1192,23 @@ wl_display_create(void) { struct wl_display *display; const char *debug; + const char *no_color; + const char *force_color; + no_color = getenv("NO_COLOR"); + force_color = getenv("FORCE_COLOR"); debug = getenv("WAYLAND_DEBUG"); - if (debug && (strstr(debug, "server") || strstr(debug, "1"))) + if (debug && (wl_check_env_token(debug, "server") || wl_check_env_token(debug, "1"))) { debug_server = 1; + if (isatty(fileno(stderr))) + debug_color = 1; + } + + if (force_color && force_color[0] != '\0') + debug_color = 1; + + if (no_color && no_color[0] != '\0') + debug_color = 0; display = zalloc(sizeof *display); if (display == NULL) @@ -1480,7 +1547,7 @@ wl_global_set_user_data(struct wl_global *global, void *data) * \memberof wl_display */ WL_EXPORT uint32_t -wl_display_get_serial(struct wl_display *display) +wl_display_get_serial(const struct wl_display *display) { return display->serial; } @@ -1517,7 +1584,8 @@ wl_display_terminate(struct wl_display *display) display->run = false; ret = write(display->terminate_efd, &terminate, sizeof(terminate)); - assert (ret >= 0 || errno == EAGAIN); + if (ret < 0 && errno != EAGAIN) + wl_abort("Write failed at shutdown\n"); } WL_EXPORT void @@ -1714,7 +1782,8 @@ wl_socket_init_for_display_name(struct wl_socket *s, const char *name) name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, "%s%s%s", runtime_dir, separator, name) + 1; - assert(name_size > 0); + if (!(name_size > 0)) + wl_abort("Error assigning path name for socket address\n"); if (name_size > (int)sizeof s->addr.sun_path) { wl_log("error: socket path \"%s%s%s\" plus null terminator" " exceeds 108 bytes\n", runtime_dir, separator, name); @@ -1762,6 +1831,24 @@ _wl_display_add_socket(struct wl_display *display, struct wl_socket *s) return 0; } + +/** Automatically pick a Wayland display socket for the clients to connect to. + * + * \param display Wayland display to which the socket should be added. + * \return The socket name if success. NULL if failed. + * + * This adds a Unix socket to Wayland display which can be used by clients to + * connect to Wayland display. The name of the socket is chosen automatically + * as the first available name in the sequence "wayland-0", "wayland-1", + * "wayland-2", ..., "wayland-32". + * + * The string returned by this function is owned by the library and should + * not be freed. + * + * \sa wl_display_add_socket + * + * \memberof wl_display + */ WL_EXPORT const char * wl_display_add_socket_auto(struct wl_display *display) { @@ -2042,7 +2129,7 @@ wl_log_set_handler_server(wl_log_func_t handler) * \param func The function to call to log a new protocol message * \param user_data The user data pointer to pass to \a func * - * \return The protol logger object on success, NULL on failure. + * \return The protocol logger object on success, NULL on failure. * * \sa wl_protocol_logger_destroy * @@ -2483,9 +2570,10 @@ wl_priv_signal_final_emit(struct wl_priv_signal *signal, void *data) /** \cond */ /* Deprecated functions below. */ +WL_DEPRECATED uint32_t wl_client_add_resource(struct wl_client *client, - struct wl_resource *resource) WL_DEPRECATED; + struct wl_resource *resource); WL_EXPORT uint32_t wl_client_add_resource(struct wl_client *client, @@ -2514,11 +2602,12 @@ wl_client_add_resource(struct wl_client *client, return resource->object.id; } +WL_DEPRECATED struct wl_resource * wl_client_add_object(struct wl_client *client, const struct wl_interface *interface, const void *implementation, - uint32_t id, void *data) WL_DEPRECATED; + uint32_t id, void *data); WL_EXPORT struct wl_resource * wl_client_add_object(struct wl_client *client, @@ -2537,10 +2626,11 @@ wl_client_add_object(struct wl_client *client, return resource; } +WL_DEPRECATED struct wl_resource * wl_client_new_object(struct wl_client *client, const struct wl_interface *interface, - const void *implementation, void *data) WL_DEPRECATED; + const void *implementation, void *data); WL_EXPORT struct wl_resource * wl_client_new_object(struct wl_client *client, @@ -2599,10 +2689,11 @@ wl_client_get_user_data(struct wl_client *client) return client->data; } +WL_DEPRECATED struct wl_global * wl_display_add_global(struct wl_display *display, const struct wl_interface *interface, - void *data, wl_global_bind_func_t bind) WL_DEPRECATED; + void *data, wl_global_bind_func_t bind); WL_EXPORT struct wl_global * wl_display_add_global(struct wl_display *display, @@ -2612,9 +2703,10 @@ wl_display_add_global(struct wl_display *display, return wl_global_create(display, interface, interface->version, data, bind); } +WL_DEPRECATED void wl_display_remove_global(struct wl_display *display, - struct wl_global *global) WL_DEPRECATED; + struct wl_global *global); WL_EXPORT void wl_display_remove_global(struct wl_display *display, struct wl_global *global) diff --git a/src/wayland-server.h b/src/wayland-server.h index 1be565f2..48fab1dd 100644 --- a/src/wayland-server.h +++ b/src/wayland-server.h @@ -70,30 +70,35 @@ struct wl_resource { void *data; }; +WL_DEPRECATED uint32_t wl_client_add_resource(struct wl_client *client, - struct wl_resource *resource) WL_DEPRECATED; + struct wl_resource *resource); +WL_DEPRECATED struct wl_resource * wl_client_add_object(struct wl_client *client, const struct wl_interface *interface, const void *implementation, - uint32_t id, void *data) WL_DEPRECATED; + uint32_t id, void *data); +WL_DEPRECATED struct wl_resource * wl_client_new_object(struct wl_client *client, const struct wl_interface *interface, - const void *implementation, void *data) WL_DEPRECATED; + const void *implementation, void *data); +WL_DEPRECATED struct wl_global * wl_display_add_global(struct wl_display *display, const struct wl_interface *interface, void *data, - wl_global_bind_func_t bind) WL_DEPRECATED; + wl_global_bind_func_t bind); +WL_DEPRECATED void wl_display_remove_global(struct wl_display *display, - struct wl_global *global) WL_DEPRECATED; + struct wl_global *global); #endif diff --git a/src/wayland-shm.c b/src/wayland-shm.c index 0a11736a..3ac4add2 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -85,6 +84,10 @@ struct wl_shm_pool { */ struct wl_shm_buffer { struct wl_resource *resource; + int internal_refcount; + int external_refcount; + struct wl_client *client; + struct wl_listener client_destroy_listener; int32_t width, height; int32_t stride; uint32_t format; @@ -144,12 +147,16 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external) { if (external) { pool->external_refcount--; - assert(pool->external_refcount >= 0); + if (pool->external_refcount < 0) + wl_abort("Requested to unref an external reference to " + "pool but none found\n"); if (pool->external_refcount == 0) shm_pool_finish_resize(pool); } else { pool->internal_refcount--; - assert(pool->internal_refcount >= 0); + if (pool->internal_refcount < 0) + wl_abort("Requested to unref an internal reference to " + "pool but none found\n"); } if (pool->internal_refcount + pool->external_refcount > 0) @@ -162,13 +169,38 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external) free(pool); } +static void +shm_buffer_unref(struct wl_shm_buffer *buffer, bool external) +{ + if (external) { + buffer->external_refcount--; + if (buffer->external_refcount < 0) { + wl_abort("Requested to unref an external reference to " + "buffer but none found\n"); + } + } else { + buffer->internal_refcount--; + if (buffer->internal_refcount < 0) { + wl_abort("Requested to unref an internal reference to " + "buffer but none found\n"); + } + } + + if (buffer->internal_refcount + buffer->external_refcount > 0) + return; + + if (buffer->client) + wl_list_remove(&buffer->client_destroy_listener.link); + shm_pool_unref(buffer->pool, false); + free(buffer); +} + static void destroy_buffer(struct wl_resource *resource) { struct wl_shm_buffer *buffer = wl_resource_get_user_data(resource); - shm_pool_unref(buffer->pool, false); - free(buffer); + shm_buffer_unref(buffer, false); } static void @@ -202,6 +234,17 @@ format_is_supported(struct wl_client *client, uint32_t format) return false; } + +static void +shm_buffer_client_destroy_notify(struct wl_listener *listener, void *data) +{ + struct wl_shm_buffer *buffer = + wl_container_of(listener, buffer, client_destroy_listener); + + buffer->client = NULL; + wl_list_remove(&buffer->client_destroy_listener.link); +} + static void shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, int32_t offset, @@ -234,6 +277,14 @@ shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource, return; } + buffer->client = client; + buffer->client_destroy_listener.notify = + shm_buffer_client_destroy_notify; + wl_client_add_destroy_listener(buffer->client, + &buffer->client_destroy_listener); + + buffer->internal_refcount = 1; + buffer->external_refcount = 0; buffer->width = width; buffer->height = height; buffer->format = format; @@ -441,7 +492,7 @@ wl_shm_buffer_get(struct wl_resource *resource) } WL_EXPORT int32_t -wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer) +wl_shm_buffer_get_stride(const struct wl_shm_buffer *buffer) { return buffer->stride; } @@ -458,8 +509,8 @@ wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer) * SIGBUS signals. This can happen if the client claims that the * buffer is larger than it is or if something truncates the * underlying file. To prevent this signal from causing the compositor - * to crash you should call wl_shm_buffer_begin_access and - * wl_shm_buffer_end_access around code that reads from the memory. + * to crash you should call wl_shm_buffer_begin_access() and + * wl_shm_buffer_end_access() around code that reads from the memory. * * \memberof wl_shm_buffer */ @@ -475,23 +526,62 @@ wl_shm_buffer_get_data(struct wl_shm_buffer *buffer) } WL_EXPORT uint32_t -wl_shm_buffer_get_format(struct wl_shm_buffer *buffer) +wl_shm_buffer_get_format(const struct wl_shm_buffer *buffer) { return buffer->format; } WL_EXPORT int32_t -wl_shm_buffer_get_width(struct wl_shm_buffer *buffer) +wl_shm_buffer_get_width(const struct wl_shm_buffer *buffer) { return buffer->width; } WL_EXPORT int32_t -wl_shm_buffer_get_height(struct wl_shm_buffer *buffer) +wl_shm_buffer_get_height(const struct wl_shm_buffer *buffer) { return buffer->height; } +/** Reference a shm_buffer + * + * \param buffer The buffer object + * + * Returns a pointer to the buffer and increases the refcount. + * + * The compositor must remember to call wl_shm_buffer_unref() when + * it no longer needs the reference to ensure proper destruction + * of the buffer. + * + * \memberof wl_shm_buffer + * \sa wl_shm_buffer_unref + */ +WL_EXPORT struct wl_shm_buffer * +wl_shm_buffer_ref(struct wl_shm_buffer *buffer) +{ + buffer->external_refcount++; + return buffer; +} + +/** Unreference a shm_buffer + * + * \param buffer The buffer object + * + * Drops a reference to a buffer object. + * + * This is only necessary if the compositor has explicitly + * taken a reference with wl_shm_buffer_ref(), otherwise + * the buffer will be automatically destroyed when appropriate. + * + * \memberof wl_shm_buffer + * \sa wl_shm_buffer_ref + */ +WL_EXPORT void +wl_shm_buffer_unref(struct wl_shm_buffer *buffer) +{ + shm_buffer_unref(buffer, true); +} + /** Get a reference to a shm_buffer's shm_pool * * \param buffer The buffer object @@ -499,7 +589,7 @@ wl_shm_buffer_get_height(struct wl_shm_buffer *buffer) * Returns a pointer to a buffer's shm_pool and increases the * shm_pool refcount. * - * The compositor must remember to call wl_shm_pool_unref when + * The compositor must remember to call wl_shm_pool_unref() when * it no longer needs the reference to ensure proper destruction * of the pool. * @@ -509,9 +599,6 @@ wl_shm_buffer_get_height(struct wl_shm_buffer *buffer) WL_EXPORT struct wl_shm_pool * wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer) { - assert(buffer->pool->internal_refcount + - buffer->pool->external_refcount); - buffer->pool->external_refcount++; return buffer->pool; } @@ -613,7 +700,7 @@ init_sigbus_data_key(void) * In order to make the compositor robust against clients that change * the size of the underlying file or lie about its size, you should * protect access to the buffer by calling this function before - * reading from the memory and call wl_shm_buffer_end_access + * reading from the memory and call wl_shm_buffer_end_access() * afterwards. This will install a signal handler for SIGBUS which * will prevent the compositor from crashing. * @@ -624,15 +711,15 @@ init_sigbus_data_key(void) * * If a SIGBUS signal is received for an address within the range of * the SHM pool of the given buffer then the client will be sent an - * error event when wl_shm_buffer_end_access is called. If the signal + * error event when wl_shm_buffer_end_access() is called. If the signal * is for an address outside that range then the signal handler will * reraise the signal which would will likely cause the compositor to * terminate. * * It is safe to nest calls to these functions as long as the nested - * calls are all accessing the same buffer. The number of calls to - * wl_shm_buffer_end_access must match the number of calls to - * wl_shm_buffer_begin_access. These functions are thread-safe and it + * calls are all accessing the same pool. The number of calls to + * wl_shm_buffer_end_access() must match the number of calls to + * wl_shm_buffer_begin_access(). These functions are thread-safe and it * is allowed to simultaneously access different buffers or the same * buffer from multiple threads. * @@ -658,18 +745,19 @@ wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer) pthread_setspecific(wl_shm_sigbus_data_key, sigbus_data); } - assert(sigbus_data->current_pool == NULL || - sigbus_data->current_pool == pool); + if (!(sigbus_data->current_pool == NULL || + sigbus_data->current_pool == pool)) + wl_abort("Incorrect pool passed for current thread\n"); sigbus_data->current_pool = pool; sigbus_data->access_count++; } -/** Ends the access to a buffer started by wl_shm_buffer_begin_access +/** Ends the access to a buffer started by wl_shm_buffer_begin_access() * * \param buffer The SHM buffer * - * This should be called after wl_shm_buffer_begin_access once the + * This should be called after wl_shm_buffer_begin_access() once the * buffer is no longer being accessed. If a SIGBUS signal was * generated in-between these two calls then the resource for the * given buffer will be sent an error. @@ -686,13 +774,22 @@ wl_shm_buffer_end_access(struct wl_shm_buffer *buffer) return; sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key); - assert(sigbus_data && sigbus_data->access_count >= 1); + if (!(sigbus_data && sigbus_data->access_count >= 1)) + wl_abort("sigbus_data is NULL or wl_shm_buffer_begin_access " + "wasn't called before\n"); if (--sigbus_data->access_count == 0) { if (sigbus_data->fallback_mapping_used) { - wl_resource_post_error(buffer->resource, - WL_SHM_ERROR_INVALID_FD, - "error accessing SHM buffer"); + if (buffer->resource) { + wl_resource_post_error(buffer->resource, + WL_SHM_ERROR_INVALID_FD, + "error accessing SHM buffer"); + } else if (buffer->client) { + wl_client_post_implementation_error(buffer->client, + "Error accessing SHM buffer of a " + "wl_buffer resource which has " + "already been destroyed"); + } sigbus_data->fallback_mapping_used = 0; } diff --git a/src/wayland-util.h b/src/wayland-util.h index 929a34f3..98c72fde 100644 --- a/src/wayland-util.h +++ b/src/wayland-util.h @@ -48,7 +48,7 @@ extern "C" { #endif /** Deprecated attribute */ -#if __STDC_VERSION__ >= 202311L +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || (defined(__cplusplus) && __cplusplus >= 201402L) #define WL_DEPRECATED [[deprecated]] #elif defined(__GNUC__) && __GNUC__ >= 4 #define WL_DEPRECATED __attribute__ ((deprecated)) @@ -70,7 +70,7 @@ extern "C" { #define WL_PRINTF(x, y) #endif -#if __STDC_VERSION__ >= 202311L +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L #define WL_TYPEOF(expr) typeof(expr) #else #define WL_TYPEOF(expr) __typeof__(expr) @@ -90,6 +90,14 @@ extern "C" { */ struct wl_object; +/** + * The maximum size of a protocol message. + * + * If a message size exceeds this value, the connection will be dropped. + * Servers will send an invalid_method error before disconnecting. + */ +#define WL_MAX_MESSAGE_SIZE 4096 + /** * Protocol message signature * @@ -635,7 +643,7 @@ wl_fixed_to_double(wl_fixed_t f) static inline wl_fixed_t wl_fixed_from_double(double d) { - return (wl_fixed_t) (d * 256.0); + return (wl_fixed_t) (round(d * 256.0)); } /** diff --git a/tests/connection-test.c b/tests/connection-test.c index dde5d89c..aed97a0a 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -686,7 +686,7 @@ TEST(connection_marshal_big_enough) TEST(connection_marshal_unbounded_boundary_size) { - /* A string of lenth 8178 requires a buffer size of exactly 2^13. */ + /* A string of length 8178 requires a buffer size of exactly 2^13. */ struct marshal_data data; char *big_string = malloc(8178); assert(big_string); diff --git a/tests/data/example-server.h b/tests/data/example-server.h index b0d9226d..0ddc73db 100644 --- a/tests/data/example-server.h +++ b/tests/data/example-server.h @@ -901,6 +901,10 @@ enum wl_display_error { */ WL_DISPLAY_ERROR_NO_MEMORY = 2, }; +#endif /* WL_DISPLAY_ERROR_ENUM */ + +#ifndef WL_DISPLAY_ERROR_ENUM_IS_VALID +#define WL_DISPLAY_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_display * Validate a wl_display error value. @@ -921,7 +925,7 @@ wl_display_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_DISPLAY_ERROR_ENUM */ +#endif /* WL_DISPLAY_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_display @@ -1194,6 +1198,10 @@ enum wl_shm_error { */ WL_SHM_ERROR_INVALID_FD = 2, }; +#endif /* WL_SHM_ERROR_ENUM */ + +#ifndef WL_SHM_ERROR_ENUM_IS_VALID +#define WL_SHM_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_shm * Validate a wl_shm error value. @@ -1214,7 +1222,7 @@ wl_shm_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SHM_ERROR_ENUM */ +#endif /* WL_SHM_ERROR_ENUM_IS_VALID */ #ifndef WL_SHM_FORMAT_ENUM #define WL_SHM_FORMAT_ENUM @@ -1466,6 +1474,10 @@ enum wl_shm_format { */ WL_SHM_FORMAT_YVU444 = 0x34325659, }; +#endif /* WL_SHM_FORMAT_ENUM */ + +#ifndef WL_SHM_FORMAT_ENUM_IS_VALID +#define WL_SHM_FORMAT_ENUM_IS_VALID /** * @ingroup iface_wl_shm * Validate a wl_shm format value. @@ -1596,7 +1608,7 @@ wl_shm_format_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SHM_FORMAT_ENUM */ +#endif /* WL_SHM_FORMAT_ENUM_IS_VALID */ /** * @ingroup iface_wl_shm @@ -1706,6 +1718,10 @@ enum wl_data_offer_error { */ WL_DATA_OFFER_ERROR_INVALID_OFFER = 3, }; +#endif /* WL_DATA_OFFER_ERROR_ENUM */ + +#ifndef WL_DATA_OFFER_ERROR_ENUM_IS_VALID +#define WL_DATA_OFFER_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_data_offer * Validate a wl_data_offer error value. @@ -1728,7 +1744,7 @@ wl_data_offer_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_DATA_OFFER_ERROR_ENUM */ +#endif /* WL_DATA_OFFER_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_data_offer @@ -1940,6 +1956,10 @@ enum wl_data_source_error { */ WL_DATA_SOURCE_ERROR_INVALID_SOURCE = 1, }; +#endif /* WL_DATA_SOURCE_ERROR_ENUM */ + +#ifndef WL_DATA_SOURCE_ERROR_ENUM_IS_VALID +#define WL_DATA_SOURCE_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_data_source * Validate a wl_data_source error value. @@ -1958,7 +1978,7 @@ wl_data_source_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_DATA_SOURCE_ERROR_ENUM */ +#endif /* WL_DATA_SOURCE_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_data_source @@ -2130,6 +2150,10 @@ enum wl_data_device_error { */ WL_DATA_DEVICE_ERROR_ROLE = 0, }; +#endif /* WL_DATA_DEVICE_ERROR_ENUM */ + +#ifndef WL_DATA_DEVICE_ERROR_ENUM_IS_VALID +#define WL_DATA_DEVICE_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_data_device * Validate a wl_data_device error value. @@ -2146,7 +2170,7 @@ wl_data_device_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_DATA_DEVICE_ERROR_ENUM */ +#endif /* WL_DATA_DEVICE_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_data_device @@ -2387,6 +2411,10 @@ enum wl_data_device_manager_dnd_action { */ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK = 4, }; +#endif /* WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM */ + +#ifndef WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM_IS_VALID +#define WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM_IS_VALID /** * @ingroup iface_wl_data_device_manager * Validate a wl_data_device_manager dnd_action value. @@ -2396,20 +2424,18 @@ enum wl_data_device_manager_dnd_action { */ static inline bool wl_data_device_manager_dnd_action_is_valid(uint32_t value, uint32_t version) { - switch (value) { - case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: - return version >= 1; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: - return version >= 1; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: - return version >= 1; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK: - return version >= 1; - default: - return false; - } + uint32_t valid = 0; + if (version >= 1) + valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (version >= 1) + valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (version >= 1) + valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + if (version >= 1) + valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + return (value & ~valid) == 0; } -#endif /* WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM */ +#endif /* WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM_IS_VALID */ /** * @ingroup iface_wl_data_device_manager @@ -2456,6 +2482,10 @@ enum wl_shell_error { */ WL_SHELL_ERROR_ROLE = 0, }; +#endif /* WL_SHELL_ERROR_ENUM */ + +#ifndef WL_SHELL_ERROR_ENUM_IS_VALID +#define WL_SHELL_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_shell * Validate a wl_shell error value. @@ -2472,7 +2502,7 @@ wl_shell_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SHELL_ERROR_ENUM */ +#endif /* WL_SHELL_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_shell @@ -2551,6 +2581,10 @@ enum wl_shell_surface_resize { */ WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT = 10, }; +#endif /* WL_SHELL_SURFACE_RESIZE_ENUM */ + +#ifndef WL_SHELL_SURFACE_RESIZE_ENUM_IS_VALID +#define WL_SHELL_SURFACE_RESIZE_ENUM_IS_VALID /** * @ingroup iface_wl_shell_surface * Validate a wl_shell_surface resize value. @@ -2560,30 +2594,28 @@ enum wl_shell_surface_resize { */ static inline bool wl_shell_surface_resize_is_valid(uint32_t value, uint32_t version) { - switch (value) { - case WL_SHELL_SURFACE_RESIZE_NONE: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_TOP: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_BOTTOM: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_LEFT: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_TOP_LEFT: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_RIGHT: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_TOP_RIGHT: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT: - return version >= 1; - default: - return false; - } + uint32_t valid = 0; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_NONE; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_TOP; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_BOTTOM; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_LEFT; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_TOP_LEFT; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_RIGHT; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_TOP_RIGHT; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT; + return (value & ~valid) == 0; } -#endif /* WL_SHELL_SURFACE_RESIZE_ENUM */ +#endif /* WL_SHELL_SURFACE_RESIZE_ENUM_IS_VALID */ #ifndef WL_SHELL_SURFACE_TRANSIENT_ENUM #define WL_SHELL_SURFACE_TRANSIENT_ENUM @@ -2600,6 +2632,10 @@ enum wl_shell_surface_transient { */ WL_SHELL_SURFACE_TRANSIENT_INACTIVE = 0x1, }; +#endif /* WL_SHELL_SURFACE_TRANSIENT_ENUM */ + +#ifndef WL_SHELL_SURFACE_TRANSIENT_ENUM_IS_VALID +#define WL_SHELL_SURFACE_TRANSIENT_ENUM_IS_VALID /** * @ingroup iface_wl_shell_surface * Validate a wl_shell_surface transient value. @@ -2609,14 +2645,12 @@ enum wl_shell_surface_transient { */ static inline bool wl_shell_surface_transient_is_valid(uint32_t value, uint32_t version) { - switch (value) { - case WL_SHELL_SURFACE_TRANSIENT_INACTIVE: - return version >= 1; - default: - return false; - } + uint32_t valid = 0; + if (version >= 1) + valid |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE; + return (value & ~valid) == 0; } -#endif /* WL_SHELL_SURFACE_TRANSIENT_ENUM */ +#endif /* WL_SHELL_SURFACE_TRANSIENT_ENUM_IS_VALID */ #ifndef WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM #define WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM @@ -2646,6 +2680,10 @@ enum wl_shell_surface_fullscreen_method { */ WL_SHELL_SURFACE_FULLSCREEN_METHOD_FILL = 3, }; +#endif /* WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM */ + +#ifndef WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM_IS_VALID +#define WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM_IS_VALID /** * @ingroup iface_wl_shell_surface * Validate a wl_shell_surface fullscreen_method value. @@ -2668,7 +2706,7 @@ wl_shell_surface_fullscreen_method_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM */ +#endif /* WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM_IS_VALID */ /** * @ingroup iface_wl_shell_surface @@ -3000,6 +3038,10 @@ enum wl_surface_error { */ WL_SURFACE_ERROR_INVALID_TRANSFORM = 1, }; +#endif /* WL_SURFACE_ERROR_ENUM */ + +#ifndef WL_SURFACE_ERROR_ENUM_IS_VALID +#define WL_SURFACE_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_surface * Validate a wl_surface error value. @@ -3018,7 +3060,7 @@ wl_surface_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SURFACE_ERROR_ENUM */ +#endif /* WL_SURFACE_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_surface @@ -3477,6 +3519,10 @@ enum wl_seat_capability { */ WL_SEAT_CAPABILITY_TOUCH = 4, }; +#endif /* WL_SEAT_CAPABILITY_ENUM */ + +#ifndef WL_SEAT_CAPABILITY_ENUM_IS_VALID +#define WL_SEAT_CAPABILITY_ENUM_IS_VALID /** * @ingroup iface_wl_seat * Validate a wl_seat capability value. @@ -3486,18 +3532,16 @@ enum wl_seat_capability { */ static inline bool wl_seat_capability_is_valid(uint32_t value, uint32_t version) { - switch (value) { - case WL_SEAT_CAPABILITY_POINTER: - return version >= 1; - case WL_SEAT_CAPABILITY_KEYBOARD: - return version >= 1; - case WL_SEAT_CAPABILITY_TOUCH: - return version >= 1; - default: - return false; - } + uint32_t valid = 0; + if (version >= 1) + valid |= WL_SEAT_CAPABILITY_POINTER; + if (version >= 1) + valid |= WL_SEAT_CAPABILITY_KEYBOARD; + if (version >= 1) + valid |= WL_SEAT_CAPABILITY_TOUCH; + return (value & ~valid) == 0; } -#endif /* WL_SEAT_CAPABILITY_ENUM */ +#endif /* WL_SEAT_CAPABILITY_ENUM_IS_VALID */ /** * @ingroup iface_wl_seat @@ -3621,6 +3665,10 @@ enum wl_pointer_error { */ WL_POINTER_ERROR_ROLE = 0, }; +#endif /* WL_POINTER_ERROR_ENUM */ + +#ifndef WL_POINTER_ERROR_ENUM_IS_VALID +#define WL_POINTER_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_pointer * Validate a wl_pointer error value. @@ -3637,7 +3685,7 @@ wl_pointer_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_POINTER_ERROR_ENUM */ +#endif /* WL_POINTER_ERROR_ENUM_IS_VALID */ #ifndef WL_POINTER_BUTTON_STATE_ENUM #define WL_POINTER_BUTTON_STATE_ENUM @@ -3658,6 +3706,10 @@ enum wl_pointer_button_state { */ WL_POINTER_BUTTON_STATE_PRESSED = 1, }; +#endif /* WL_POINTER_BUTTON_STATE_ENUM */ + +#ifndef WL_POINTER_BUTTON_STATE_ENUM_IS_VALID +#define WL_POINTER_BUTTON_STATE_ENUM_IS_VALID /** * @ingroup iface_wl_pointer * Validate a wl_pointer button_state value. @@ -3676,7 +3728,7 @@ wl_pointer_button_state_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_POINTER_BUTTON_STATE_ENUM */ +#endif /* WL_POINTER_BUTTON_STATE_ENUM_IS_VALID */ #ifndef WL_POINTER_AXIS_ENUM #define WL_POINTER_AXIS_ENUM @@ -3696,6 +3748,10 @@ enum wl_pointer_axis { */ WL_POINTER_AXIS_HORIZONTAL_SCROLL = 1, }; +#endif /* WL_POINTER_AXIS_ENUM */ + +#ifndef WL_POINTER_AXIS_ENUM_IS_VALID +#define WL_POINTER_AXIS_ENUM_IS_VALID /** * @ingroup iface_wl_pointer * Validate a wl_pointer axis value. @@ -3714,7 +3770,7 @@ wl_pointer_axis_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_POINTER_AXIS_ENUM */ +#endif /* WL_POINTER_AXIS_ENUM_IS_VALID */ #ifndef WL_POINTER_AXIS_SOURCE_ENUM #define WL_POINTER_AXIS_SOURCE_ENUM @@ -3762,6 +3818,10 @@ enum wl_pointer_axis_source { * @ingroup iface_wl_pointer */ #define WL_POINTER_AXIS_SOURCE_WHEEL_TILT_SINCE_VERSION 6 +#endif /* WL_POINTER_AXIS_SOURCE_ENUM */ + +#ifndef WL_POINTER_AXIS_SOURCE_ENUM_IS_VALID +#define WL_POINTER_AXIS_SOURCE_ENUM_IS_VALID /** * @ingroup iface_wl_pointer * Validate a wl_pointer axis_source value. @@ -3784,7 +3844,7 @@ wl_pointer_axis_source_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_POINTER_AXIS_SOURCE_ENUM */ +#endif /* WL_POINTER_AXIS_SOURCE_ENUM_IS_VALID */ /** * @ingroup iface_wl_pointer @@ -4045,6 +4105,10 @@ enum wl_keyboard_keymap_format { */ WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 = 1, }; +#endif /* WL_KEYBOARD_KEYMAP_FORMAT_ENUM */ + +#ifndef WL_KEYBOARD_KEYMAP_FORMAT_ENUM_IS_VALID +#define WL_KEYBOARD_KEYMAP_FORMAT_ENUM_IS_VALID /** * @ingroup iface_wl_keyboard * Validate a wl_keyboard keymap_format value. @@ -4063,7 +4127,7 @@ wl_keyboard_keymap_format_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_KEYBOARD_KEYMAP_FORMAT_ENUM */ +#endif /* WL_KEYBOARD_KEYMAP_FORMAT_ENUM_IS_VALID */ #ifndef WL_KEYBOARD_KEY_STATE_ENUM #define WL_KEYBOARD_KEY_STATE_ENUM @@ -4083,6 +4147,10 @@ enum wl_keyboard_key_state { */ WL_KEYBOARD_KEY_STATE_PRESSED = 1, }; +#endif /* WL_KEYBOARD_KEY_STATE_ENUM */ + +#ifndef WL_KEYBOARD_KEY_STATE_ENUM_IS_VALID +#define WL_KEYBOARD_KEY_STATE_ENUM_IS_VALID /** * @ingroup iface_wl_keyboard * Validate a wl_keyboard key_state value. @@ -4101,7 +4169,7 @@ wl_keyboard_key_state_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_KEYBOARD_KEY_STATE_ENUM */ +#endif /* WL_KEYBOARD_KEY_STATE_ENUM_IS_VALID */ /** * @ingroup iface_wl_keyboard @@ -4427,6 +4495,10 @@ enum wl_output_subpixel { */ WL_OUTPUT_SUBPIXEL_VERTICAL_BGR = 5, }; +#endif /* WL_OUTPUT_SUBPIXEL_ENUM */ + +#ifndef WL_OUTPUT_SUBPIXEL_ENUM_IS_VALID +#define WL_OUTPUT_SUBPIXEL_ENUM_IS_VALID /** * @ingroup iface_wl_output * Validate a wl_output subpixel value. @@ -4453,7 +4525,7 @@ wl_output_subpixel_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_OUTPUT_SUBPIXEL_ENUM */ +#endif /* WL_OUTPUT_SUBPIXEL_ENUM_IS_VALID */ #ifndef WL_OUTPUT_TRANSFORM_ENUM #define WL_OUTPUT_TRANSFORM_ENUM @@ -4507,6 +4579,10 @@ enum wl_output_transform { */ WL_OUTPUT_TRANSFORM_FLIPPED_270 = 7, }; +#endif /* WL_OUTPUT_TRANSFORM_ENUM */ + +#ifndef WL_OUTPUT_TRANSFORM_ENUM_IS_VALID +#define WL_OUTPUT_TRANSFORM_ENUM_IS_VALID /** * @ingroup iface_wl_output * Validate a wl_output transform value. @@ -4537,7 +4613,7 @@ wl_output_transform_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_OUTPUT_TRANSFORM_ENUM */ +#endif /* WL_OUTPUT_TRANSFORM_ENUM_IS_VALID */ #ifndef WL_OUTPUT_MODE_ENUM #define WL_OUTPUT_MODE_ENUM @@ -4558,6 +4634,10 @@ enum wl_output_mode { */ WL_OUTPUT_MODE_PREFERRED = 0x2, }; +#endif /* WL_OUTPUT_MODE_ENUM */ + +#ifndef WL_OUTPUT_MODE_ENUM_IS_VALID +#define WL_OUTPUT_MODE_ENUM_IS_VALID /** * @ingroup iface_wl_output * Validate a wl_output mode value. @@ -4567,16 +4647,14 @@ enum wl_output_mode { */ static inline bool wl_output_mode_is_valid(uint32_t value, uint32_t version) { - switch (value) { - case WL_OUTPUT_MODE_CURRENT: - return version >= 1; - case WL_OUTPUT_MODE_PREFERRED: - return version >= 1; - default: - return false; - } + uint32_t valid = 0; + if (version >= 1) + valid |= WL_OUTPUT_MODE_CURRENT; + if (version >= 1) + valid |= WL_OUTPUT_MODE_PREFERRED; + return (value & ~valid) == 0; } -#endif /* WL_OUTPUT_MODE_ENUM */ +#endif /* WL_OUTPUT_MODE_ENUM_IS_VALID */ /** * @ingroup iface_wl_output @@ -4744,6 +4822,10 @@ enum wl_subcompositor_error { */ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE = 0, }; +#endif /* WL_SUBCOMPOSITOR_ERROR_ENUM */ + +#ifndef WL_SUBCOMPOSITOR_ERROR_ENUM_IS_VALID +#define WL_SUBCOMPOSITOR_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_subcompositor * Validate a wl_subcompositor error value. @@ -4760,7 +4842,7 @@ wl_subcompositor_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SUBCOMPOSITOR_ERROR_ENUM */ +#endif /* WL_SUBCOMPOSITOR_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_subcompositor @@ -4815,6 +4897,10 @@ enum wl_subsurface_error { */ WL_SUBSURFACE_ERROR_BAD_SURFACE = 0, }; +#endif /* WL_SUBSURFACE_ERROR_ENUM */ + +#ifndef WL_SUBSURFACE_ERROR_ENUM_IS_VALID +#define WL_SUBSURFACE_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_subsurface * Validate a wl_subsurface error value. @@ -4831,7 +4917,7 @@ wl_subsurface_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SUBSURFACE_ERROR_ENUM */ +#endif /* WL_SUBSURFACE_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_subsurface diff --git a/tests/data/small-client-core.h b/tests/data/small-client-core.h index 0e722441..03f889c8 100644 --- a/tests/data/small-client-core.h +++ b/tests/data/small-client-core.h @@ -106,6 +106,29 @@ enum intf_A_foo { #define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2 #endif /* INTF_A_FOO_ENUM */ +#ifndef INTF_A_BAR_ENUM +#define INTF_A_BAR_ENUM +enum intf_A_bar { + /** + * this is the first + */ + INTF_A_BAR_FIRST = 0x01, + /** + * this is the second + */ + INTF_A_BAR_SECOND = 0x02, + /** + * this is the third + * @since 2 + */ + INTF_A_BAR_THIRD = 0x04, +}; +/** + * @ingroup iface_intf_A + */ +#define INTF_A_BAR_THIRD_SINCE_VERSION 2 +#endif /* INTF_A_BAR_ENUM */ + /** * @ingroup iface_intf_A * @struct intf_A_listener diff --git a/tests/data/small-client.h b/tests/data/small-client.h index ad435923..0d5b6055 100644 --- a/tests/data/small-client.h +++ b/tests/data/small-client.h @@ -106,6 +106,29 @@ enum intf_A_foo { #define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2 #endif /* INTF_A_FOO_ENUM */ +#ifndef INTF_A_BAR_ENUM +#define INTF_A_BAR_ENUM +enum intf_A_bar { + /** + * this is the first + */ + INTF_A_BAR_FIRST = 0x01, + /** + * this is the second + */ + INTF_A_BAR_SECOND = 0x02, + /** + * this is the third + * @since 2 + */ + INTF_A_BAR_THIRD = 0x04, +}; +/** + * @ingroup iface_intf_A + */ +#define INTF_A_BAR_THIRD_SINCE_VERSION 2 +#endif /* INTF_A_BAR_ENUM */ + /** * @ingroup iface_intf_A * @struct intf_A_listener diff --git a/tests/data/small-server-core.h b/tests/data/small-server-core.h index e696cde7..4248d455 100644 --- a/tests/data/small-server-core.h +++ b/tests/data/small-server-core.h @@ -107,6 +107,10 @@ enum intf_A_foo { * @ingroup iface_intf_A */ #define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2 +#endif /* INTF_A_FOO_ENUM */ + +#ifndef INTF_A_FOO_ENUM_IS_VALID +#define INTF_A_FOO_ENUM_IS_VALID /** * @ingroup iface_intf_A * Validate a intf_A foo value. @@ -131,7 +135,52 @@ intf_A_foo_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* INTF_A_FOO_ENUM */ +#endif /* INTF_A_FOO_ENUM_IS_VALID */ + +#ifndef INTF_A_BAR_ENUM +#define INTF_A_BAR_ENUM +enum intf_A_bar { + /** + * this is the first + */ + INTF_A_BAR_FIRST = 0x01, + /** + * this is the second + */ + INTF_A_BAR_SECOND = 0x02, + /** + * this is the third + * @since 2 + */ + INTF_A_BAR_THIRD = 0x04, +}; +/** + * @ingroup iface_intf_A + */ +#define INTF_A_BAR_THIRD_SINCE_VERSION 2 +#endif /* INTF_A_BAR_ENUM */ + +#ifndef INTF_A_BAR_ENUM_IS_VALID +#define INTF_A_BAR_ENUM_IS_VALID +/** + * @ingroup iface_intf_A + * Validate a intf_A bar value. + * + * @return true on success, false on error. + * @ref intf_A_bar + */ +static inline bool +intf_A_bar_is_valid(uint32_t value, uint32_t version) { + uint32_t valid = 0; + if (version >= 1) + valid |= INTF_A_BAR_FIRST; + if (version >= 1) + valid |= INTF_A_BAR_SECOND; + if (version >= 2) + valid |= INTF_A_BAR_THIRD; + return (value & ~valid) == 0; +} +#endif /* INTF_A_BAR_ENUM_IS_VALID */ /** * @ingroup iface_intf_A diff --git a/tests/data/small-server.h b/tests/data/small-server.h index 009d9cdd..743fc8d7 100644 --- a/tests/data/small-server.h +++ b/tests/data/small-server.h @@ -107,6 +107,10 @@ enum intf_A_foo { * @ingroup iface_intf_A */ #define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2 +#endif /* INTF_A_FOO_ENUM */ + +#ifndef INTF_A_FOO_ENUM_IS_VALID +#define INTF_A_FOO_ENUM_IS_VALID /** * @ingroup iface_intf_A * Validate a intf_A foo value. @@ -131,7 +135,52 @@ intf_A_foo_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* INTF_A_FOO_ENUM */ +#endif /* INTF_A_FOO_ENUM_IS_VALID */ + +#ifndef INTF_A_BAR_ENUM +#define INTF_A_BAR_ENUM +enum intf_A_bar { + /** + * this is the first + */ + INTF_A_BAR_FIRST = 0x01, + /** + * this is the second + */ + INTF_A_BAR_SECOND = 0x02, + /** + * this is the third + * @since 2 + */ + INTF_A_BAR_THIRD = 0x04, +}; +/** + * @ingroup iface_intf_A + */ +#define INTF_A_BAR_THIRD_SINCE_VERSION 2 +#endif /* INTF_A_BAR_ENUM */ + +#ifndef INTF_A_BAR_ENUM_IS_VALID +#define INTF_A_BAR_ENUM_IS_VALID +/** + * @ingroup iface_intf_A + * Validate a intf_A bar value. + * + * @return true on success, false on error. + * @ref intf_A_bar + */ +static inline bool +intf_A_bar_is_valid(uint32_t value, uint32_t version) { + uint32_t valid = 0; + if (version >= 1) + valid |= INTF_A_BAR_FIRST; + if (version >= 1) + valid |= INTF_A_BAR_SECOND; + if (version >= 2) + valid |= INTF_A_BAR_THIRD; + return (value & ~valid) == 0; +} +#endif /* INTF_A_BAR_ENUM_IS_VALID */ /** * @ingroup iface_intf_A diff --git a/tests/data/small.xml b/tests/data/small.xml index ac527795..ab297490 100644 --- a/tests/data/small.xml +++ b/tests/data/small.xml @@ -58,5 +58,12 @@ + + + + + + + diff --git a/tests/display-test.c b/tests/display-test.c index c2def444..fe78b521 100644 --- a/tests/display-test.c +++ b/tests/display-test.c @@ -924,7 +924,7 @@ TEST(versions) } static void -check_error_on_destroyed_object(void *data) +check_error_on_destroyed_object(void) { struct client *c; struct wl_seat *seat; @@ -1043,7 +1043,7 @@ TEST(filtered_global_is_hidden) 1, d, bind_data_offer); wl_display_set_global_filter(d->wl_display, global_filter, NULL); - client_create_noarg(d, get_globals); + client_create(d, get_globals, NULL); display_run(d); wl_global_destroy(g); @@ -1052,13 +1052,13 @@ TEST(filtered_global_is_hidden) } static void -get_dynamic_globals(void *data) +get_dynamic_globals(void) { struct client *c = client_connect(); struct wl_registry *registry; registry = wl_display_get_registry(c->wl_display); - wl_registry_add_listener(registry, ®istry_listener_filtered, data); + wl_registry_add_listener(registry, ®istry_listener_filtered, NULL); wl_display_roundtrip(c->wl_display); /* Wait for the server to create a new global */ @@ -1206,7 +1206,7 @@ static const struct wl_registry_listener zombie_fd_registry_listener = { }; static void -zombie_client(void *data) +zombie_client(void) { struct client *c = client_connect(); struct wl_registry *registry; @@ -1376,7 +1376,7 @@ static const struct wl_registry_listener double_zombie_fd_registry_listener = { }; static void -double_zombie_client(void *data) +double_zombie_client(void) { struct client *c = client_connect(); struct wl_registry *registry; @@ -1436,7 +1436,7 @@ static const struct wl_registry_listener bind_interface_mismatch_registry_listen }; static void -registry_bind_interface_mismatch_client(void *data) +registry_bind_interface_mismatch_client(void) { struct client *c = client_connect(); struct wl_registry *registry; @@ -1598,7 +1598,7 @@ static const struct wl_registry_listener global_remove_before_registry_listener }; static void -global_remove_before_client(void *data) +global_remove_before_client(void) { struct client *c = client_connect(); struct wl_registry *registry; @@ -1648,7 +1648,7 @@ static const struct wl_registry_listener global_remove_after_registry_listener = }; static void -global_remove_after_client(void *data) +global_remove_after_client(void) { struct client *c = client_connect(); struct wl_registry *registry; @@ -1695,6 +1695,75 @@ TEST(global_remove) display_destroy(d); } +static void +dispatch_single_read_events(struct wl_display *d) +{ + if (wl_display_prepare_read(d) < 0) { + return; + } + + int ret = 0; + do { + ret = wl_display_flush(d); + } while (ret < 0 && (errno == EINTR || errno == EAGAIN)); + assert(ret >= 0); + + struct pollfd pfd[1]; + pfd[0].fd = wl_display_get_fd(d); + pfd[0].events = POLLIN; + + do { + ret = poll(pfd, 1, -1); + } while (ret < 0 && errno == EINTR); + assert(ret > 0); + + wl_display_read_events(d); +} + +static void +dispatch_single_client(void) +{ + struct client *c = client_connect(); + + assert(wl_display_dispatch_pending_single(c->wl_display) == 0); + + struct wl_registry *registry = wl_display_get_registry(c->wl_display); + + dispatch_single_read_events(c->wl_display); + + // [1815110.061] {Default Queue} wl_registry#3.global(1, "test", 1) + assert(wl_display_dispatch_pending_single(c->wl_display) == 1); + + dispatch_single_read_events(c->wl_display); + + // [1815110.067] {Default Queue} wl_registry#3.global(2, "wl_seat", 1) + assert(wl_display_dispatch_pending_single(c->wl_display) == 1); + + // No more events + assert(wl_display_dispatch_pending_single(c->wl_display) == 0); + + wl_registry_destroy(registry); + + client_disconnect(c); +} + +TEST(dispatch_single) +{ + struct display *d = display_create(); + + struct wl_global *global = wl_global_create(d->wl_display, + &wl_seat_interface, + 1, d, bind_seat); + + client_create_noarg(d, dispatch_single_client); + + display_run(d); + + wl_global_destroy(global); + + display_destroy(d); +} + static void terminate_display(void *arg) { diff --git a/tests/enum-validator-test.c b/tests/enum-validator-test.c index 92037cff..8fb05b43 100644 --- a/tests/enum-validator-test.c +++ b/tests/enum-validator-test.c @@ -10,4 +10,19 @@ main(int argc, char *argv[]) { assert(intf_A_foo_is_valid(INTF_A_FOO_THIRD, 2)); assert(intf_A_foo_is_valid(INTF_A_FOO_NEGATIVE, 2)); + + assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST, 1)); + assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST, 2)); + assert(intf_A_bar_is_valid(INTF_A_BAR_SECOND, 1)); + assert(intf_A_bar_is_valid(INTF_A_BAR_SECOND, 2)); + assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_SECOND, 1)); + assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_SECOND, 2)); + + assert(!intf_A_bar_is_valid(INTF_A_BAR_THIRD, 1)); + assert(!intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_THIRD, 1)); + assert(intf_A_bar_is_valid(INTF_A_BAR_THIRD, 2)); + assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_THIRD, 2)); + + assert(!intf_A_bar_is_valid(0xFF, 1)); + assert(!intf_A_bar_is_valid(0xFF, 2)); } diff --git a/tests/meson.build b/tests/meson.build index 2c22b82a..6ada5202 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -54,7 +54,7 @@ tests_protocol_c = custom_target( output: 'tests-protocol.c' ) -executable( +exec_fd_leak_checker = executable( 'exec-fd-leak-checker', 'exec-fd-leak-checker.c', dependencies: test_runner_dep @@ -96,78 +96,116 @@ if get_option('scanner') endif tests = { - 'array-test': [], - 'client-test': [ wayland_server_protocol_h ], - 'display-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - tests_server_protocol_h, - tests_client_protocol_c, - tests_protocol_c, - ], - 'connection-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'event-loop-test': [ wayland_server_protocol_h ], - 'fixed-test': [], - 'interface-test': [ wayland_client_protocol_h ], - 'list-test': [], - 'map-test': [], - 'sanity-test' : [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'socket-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'queue-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'signal-test': [ wayland_server_protocol_h ], - 'newsignal-test': [ - # wayland-server.c is needed here to access wl_priv_* functions - files('../src/wayland-server.c'), - wayland_server_protocol_h, - ], - 'resources-test': [ wayland_server_protocol_h ], - 'message-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'compositor-introspection-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'protocol-logger-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'headers-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - 'headers-protocol-test.c', - wayland_client_protocol_core_h, - wayland_server_protocol_core_h, - 'headers-protocol-core-test.c', - ], - 'os-wrappers-test': [], - 'proxy-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'enum-validator-test': [], + 'array-test': {}, + 'client-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'display-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + tests_server_protocol_h, + tests_client_protocol_c, + tests_protocol_c, + ], + }, + 'connection-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'event-loop-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'fixed-test': {}, + 'interface-test': { + 'extra_sources': [ wayland_client_protocol_h ], + }, + 'list-test': {}, + 'map-test': {}, + 'sanity-test' : { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + 'runtime_deps': [ exec_fd_leak_checker ], + }, + 'socket-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'queue-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'signal-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'newsignal-test': { + 'extra_sources': [ + # wayland-server.c is needed here to access wl_priv_* functions + files('../src/wayland-server.c'), + wayland_server_protocol_h, + ], + }, + 'resources-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'message-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'compositor-introspection-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'protocol-logger-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'headers-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + 'headers-protocol-test.c', + wayland_client_protocol_core_h, + wayland_server_protocol_core_h, + 'headers-protocol-core-test.c', + ], + }, + 'os-wrappers-test': { + 'runtime_deps': [ exec_fd_leak_checker ], + }, + 'proxy-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'enum-validator-test': {}, } -foreach test_name, test_extra_sources: tests +foreach test_name, test_extras : tests + test_extra_sources = test_extras.get('extra_sources', []) + test_runtime_deps = test_extras.get('runtime_deps', []) test_sources = [ test_name + '.c' ] + test_extra_sources test_deps = [test_runner_dep, epoll_dep] bin = executable(test_name, test_sources, dependencies: test_deps) test( test_name, bin, + depends: test_runtime_deps, env: [ 'TEST_SRC_DIR=@0@'.format(meson.current_source_dir()), 'TEST_BUILD_DIR=@0@'.format(meson.current_build_dir()), diff --git a/tests/queue-test.c b/tests/queue-test.c index cb61a85b..7dfdd306 100644 --- a/tests/queue-test.c +++ b/tests/queue-test.c @@ -40,6 +40,7 @@ #include "wayland-server.h" #include "test-runner.h" #include "test-compositor.h" +#include "../src/timespec-util.h" #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) @@ -571,6 +572,68 @@ client_test_queue_names(void) wl_display_disconnect(display); } +static void +dispatch_timeout_sync_callback(void *data, struct wl_callback *callback, + uint32_t serial) +{ + bool *done = data; + + *done = true; + wl_callback_destroy(callback); +} + +static const struct wl_callback_listener dispatch_timeout_sync_listener = { + dispatch_timeout_sync_callback +}; + +static void +client_test_queue_dispatch_simple(void) +{ + struct wl_display *display; + struct timespec timeout; + struct wl_callback *callback; + bool done = false; + int ret = 0; + + display = wl_display_connect(NULL); + assert(display); + + callback = wl_display_sync(display); + assert(callback != NULL); + wl_callback_add_listener(callback, &dispatch_timeout_sync_listener, &done); + + timespec_from_msec(&timeout, 1000); + + while (!done) { + ret = wl_display_dispatch_timeout(display, &timeout); + assert(ret > 0); + } + + wl_display_disconnect(display); + + exit(0); +} + +static void +client_test_queue_dispatch_timeout(void) +{ + struct wl_display *display; + struct timespec timeout; + int ret = 0; + + display = wl_display_connect(NULL); + assert(display); + + timespec_from_msec(&timeout, 100); + + ret = wl_display_dispatch_timeout(display, &timeout); + assert(ret == 0); + + wl_display_disconnect(display); + + exit(0); +} + static void dummy_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) @@ -709,3 +772,27 @@ TEST(queue_names) display_destroy(d); } + +TEST(queue_dispatch_simple) +{ + struct display *d = display_create(); + + test_set_timeout(2); + + client_create_noarg(d, client_test_queue_dispatch_simple); + display_run(d); + + display_destroy(d); +} + +TEST(queue_dispatch_timeout) +{ + struct display *d = display_create(); + + test_set_timeout(2); + + client_create_noarg(d, client_test_queue_dispatch_timeout); + display_run(d); + + display_destroy(d); +} diff --git a/tests/test-compositor.c b/tests/test-compositor.c index 8648fb69..8ec0631b 100644 --- a/tests/test-compositor.c +++ b/tests/test-compositor.c @@ -507,7 +507,7 @@ static const struct wl_registry_listener registry_listener = NULL }; -struct client *client_connect() +struct client *client_connect(void) { struct wl_registry *reg; struct client *c = calloc(1, sizeof *c); diff --git a/tests/test-compositor.h b/tests/test-compositor.h index 3fb390c2..662a81c3 100644 --- a/tests/test-compositor.h +++ b/tests/test-compositor.h @@ -118,5 +118,17 @@ struct client_info *client_create_with_name(struct display *d, void *data, const char *name); #define client_create(d, c, data) client_create_with_name((d), (c), data, (#c)) +static inline void noarg_cb(void *data) +{ + void (*cb)(void) = data; + cb(); +} +static inline struct client_info *client_create_with_name_noarg(struct display *d, + void (*client_main)(void), + const char *name) +{ + return client_create_with_name(d, noarg_cb, client_main, name); +} + #define client_create_noarg(d, c) \ - client_create_with_name((d), (void(*)(void *)) (c), NULL, (#c)) + client_create_with_name_noarg((d), (c), (#c))