Compare commits

...

613 Commits

Author SHA1 Message Date
Nikita Tokarchuk be9f5bd993 Revert "backend-wayland: Stop supporting wl_shell" 5 months ago
Nikita Tokarchuk 419d6a9cf5 Revert "backend-wayland: restructure wayland_output_resize_surface()" 5 months ago
Nikita Tokarchuk 996dd5caa5 Revert "backend-wayland: fix pixman buffer size" 5 months ago
Nikita Tokarchuk 0cc7369023 11.0.3 5 months ago
Marius Vlad 742ad74bc0 build: bump to version 11.0.3 for the point release 1 year ago
Marius Vlad 263702cf7d backend-drm/meson.build: Require at least mesa 21.1.1 1 year ago
Liu, Kai1 38eb0a96e0 xwm: WM_TRANSIENT_FOR should not point to override-redirect window 1 year ago
Michael Tretter a5d52075a0 backend-drm: schedule connector disable for detached head 1 year ago
Marius Vlad 78852bd350 build: bump to version 11.0.2 for the point release 2 years ago
Sergio Gómez a627a4be50 libweston/input: Fix assert for valid confine region 2 years ago
Sergio Gómez 072c56723c libweston: Add assert for valid confine region in maybe_warp_confined_pointer() 2 years ago
Sergio Gómez 21e46364c0 libweston: Add view unmap listener to pointer constraints 2 years ago
Sergio Gómez 0bd68d9ad6 libweston/input: Remove redundant surface destroy listener in constraints 2 years ago
Michael Olbrich ff13a90eea desktop-shell: avoid crashes when a surface disappears during resize 2 years ago
Michael Olbrich 5ad870f505 libweston: clear parent_view when the parent view is destroyed 2 years ago
Alexandros Frantzis 2d66d01cf5 xwayland: Handle shell hint for client to choose dimensions 2 years ago
Leandro Ribeiro d5a3ec5e58 desktop-shell: do not forget to reset pending config size after resizes 2 years ago
Marius Vlad df70b81ed7 backend-drm: Do not overwrite plane's index when creating virtual plane 2 years ago
Marius Vlad 7047926834 pipewire-plugin: Check virtual outputs/remoting instance 2 years ago
Marius Vlad 0849a9b3c8 pipewire: Destroy the pipewire outputs at shutdown 2 years ago
Marius Vlad 597437a096 pipewire: Fix memleak upon compositor shutdown 2 years ago
Marius Vlad eaa777b914 pipewire: Follow-up with remoting pluging when releasing the head 2 years ago
Marius Vlad 17f5a44f3b remoting-plugin: Check virtual outputs/remoting instance 2 years ago
Marius Vlad 0ba5b694d3 remoting-plugin: Release and detach the head 2 years ago
Marius Vlad 783b144f2e build: bump to version 11.0.1 for the point release 2 years ago
Derek Foreman 995fb60fde xwm: Propagate selection ownership immediately 2 years ago
Derek Foreman f5fafa05fc xwm: Don't crash when setting selection with no seat 2 years ago
Marius Vlad ac05950098 ivi-shell: Move out weston_desktop_shell at the end 2 years ago
Marius Vlad 097ed47292 hmi-controller: Add missing removal of destroy listener 2 years ago
Alexandros Frantzis ad7c5162bc kiosk-shell: Don't use a modifier for surface activation bindings 2 years ago
Michael Tretter e7cf894fa2 ivi-shell: fix cleanup of desktop surfaces 2 years ago
Michael Tretter 72a6929467 ivi-shell: fix free in get_layers_under_surface 2 years ago
Alexandros Frantzis 7a8392d2fe kiosk-shell: Update view transform after activation. 2 years ago
Paul Kocialkowski 7678ec9209 screenshooter: Add SHM buffer destroy listener to avoid invalid memcpy 2 years ago
Derek Foreman 5517953ed0 xwm: Check size hints in weston_wm_window_is_positioned() 2 years ago
Marius Vlad 00a78294b1 compositor/shared: Suppress write(2) warnings 2 years ago
Marius Vlad 715eb67cd8 backend-rdp/rdpclip: Avoid printing negative index 2 years ago
Marius Vlad 0da83cc1d8 doc/sphinx: Make doxygen warn as error depend on meson werror flag 2 years ago
vanfanel 24ee61445c Don't change the max_bpc connector prop if mode=current. 2 years ago
Michael Olbrich 870db9703c backend-wayland: always propagate touch frame event 2 years ago
Michael Olbrich cf1ca2c300 input: send touch frame event after up event 2 years ago
Simon Ser d23a69272f build: bump to version 11.0.0 for the official release 2 years ago
Simon Ser 3dc6a682e4 build: bump to version 10.0.94 for the RC2 release 2 years ago
Alexandros Frantzis 0669d4de4f libweston: Skip views without a layer assignment in output_mask calculations 2 years ago
Marius Vlad b87418e4c4 clients/eventdemo: Remove duplicated param entries 2 years ago
Pekka Paalanen 259bd17822 doc: remove directives deprecated in Doxygen 1.9.5 2 years ago
Derek Foreman 11ba13d717 clients: Fix cursors when compositor gives wl_seat before wl_compositor 2 years ago
Simon Ser a6b8f0f89c build: bump to version 10.0.93 for the RC1 release 2 years ago
Marius Vlad 1aa935e6d8 libweston/input: Assert if we're still having a notify listener installed 2 years ago
Marius Vlad d6ab6da988 libweston/backend-x11: Tracking previous events over multiple calls 2 years ago
Michael Olbrich 4cde507be6 backend-drm: fix plane sorting 2 years ago
Simon Ser 4990e28ff2 build: bump to version 10.0.92 for the beta release 2 years ago
Marius Vlad 32791eae1f simple-egl: Update buffer_size dimensions when starting as maximized 2 years ago
Erik Kurzinger 8f1ca8204a clients/simple-egl: call eglSwapInterval after eglMakeCurrent 2 years ago
Derek Foreman 646cc1b389 clients: Set the hotspot with attach if we already have a valid cursor 2 years ago
Derek Foreman ebbe30df3c Revert "clients/window: atomically update pointer cursor" 2 years ago
Derek Foreman 8b0125d601 Revert "clients/window: Fix animated cursors" 2 years ago
Simon Ser aa2b615d30 build: bump to version 10.0.91 for the alpha release 2 years ago
Marius Vlad c33e8d2c10 desktop-shell: Handle tiled orientation in various circumstances 2 years ago
Marius Vlad 5bcbe92d51 desktops-shell: Add tiled orientation support using key-bindings 2 years ago
Marius Vlad 37a3025d89 libweston/desktop/xdg-shell: Add tiled orientation states 2 years ago
Michael Olbrich 6275a0fb32 backend-drm: delay mode switches until the last commit is completed 2 years ago
Michael Tretter a2684005b6 doc: update and move IVI-shell README to doc 2 years ago
Michael Tretter 7d16485efd ivi-shell: remove dysfunctional link 2 years ago
Michael Tretter b282fe3a73 ivi-shell: remove unused definition ivi_layout_screen 2 years ago
Marius Vlad 478b24cae0 desktops-shell: Re-use helper for modifier retrieval 2 years ago
Marius Vlad f7ba35f5fc kiosk-shell: Enable debug keybindings 2 years ago
Derek Foreman dac2f146ea xwm: Perform a roundtrip to send a deferred WM_TAKE_FOCUS 2 years ago
Derek Foreman ae4209978c xwayland: Don't focus an already focused xwayland window 2 years ago
Hideyuki Nagase 5afe6c5b39 xwm: Change event mask for WM_TAKE_FOCUS 2 years ago
Hideyuki Nagase 55b2bf9393 xwayland: Respect client WM_TAKE_FOCUS setting 2 years ago
Thomas Petazzoni 6e529cb6ab compositor/main.c: use pixman renderer by default when gl-renderer not enabled 2 years ago
Derek Foreman 6ee486ff95 libweston: Don't send output_changed signal when moving disabled outputs 2 years ago
Derek Foreman 7e7198bd88 libweston: Check output placement 2 years ago
Derek Foreman 8409b74ec2 libweston: Don't move outputs during enable 2 years ago
Michael Olbrich 3b3fdc52c3 backend-drm: improve atomic commit failure handling 2 years ago
Pekka Paalanen d4eafbaa98 backend-wayland: fix pixman buffer size 2 years ago
Derek Foreman 214d48bbab compositor: Fix use after free at shutdown 2 years ago
Pekka Paalanen 851b16f00c gl-renderer: use pixel_format_info in read_pixels 2 years ago
Pekka Paalanen b966fd07ea libweston: change read_format to struct pixel_format_info 2 years ago
Pekka Paalanen 03c229f4ce screen-share: use read_format consistently 2 years ago
Pekka Paalanen d2aa62a074 libweston: add pixel_format_get_info_by_pixman() 2 years ago
Derek Foreman 0aac3dd343 xwm: Don't send synthetic ConfigureNotify to windows that were mapped O-R 2 years ago
Pekka Paalanen 3387afd56b fullscreen-shell: fix black output 2 years ago
Marius Vlad f9e52eb0d7 desktop-shell: Always update the shsurf's output to that of the view 2 years ago
Pekka Paalanen aac8eefc44 backend-x11: use shorthand for current_mode 2 years ago
Pekka Paalanen 9e1c96bce7 backend-wayland: restructure wayland_output_resize_surface() 2 years ago
Pekka Paalanen 8b6c3fe0ad backend-headless: choose pixel format by drm_fourcc 2 years ago
Pekka Paalanen dd706d5953 backend-headless: let pixman allocate the image 2 years ago
Pekka Paalanen 7323ddec62 pixman-renderer: let pixman allocate shadow 2 years ago
Derek Foreman d6b112c857 xwayland: Only prevent focus change to inactive toplevels 2 years ago
Marius Vlad 8a610ffe41 compositor/text-backend: Avoid a potential UAF 2 years ago
Jonas Ådahl 5ffa1962a5 compositor: Add support for wl_surface.offset() 2 years ago
Derek Foreman 4564a40cb0 rdp: Move peer list from output to backend 2 years ago
Michael Olbrich 27d2a4cfab libweston: don't reset the plane for views from other outputs 2 years ago
Derek Foreman 2badd284a5 compositor: Load xwayland module first 2 years ago
Derek Foreman 0972c6b2da compositor: Remove deprecated xwayland loading method 2 years ago
Marius Vlad 78ccc99d0a libweston: Remove runtime render switching 2 years ago
Marius Vlad 6744a6278e clients/window: Bump xdg-shell version to latest 2 years ago
Daniel Stone a8048c5c1c libweston: Properly namespace solid_buffer_values 2 years ago
Joshua Watt a09f02d43a libweston: Compute output protection when head is attached 2 years ago
Daniel Stone b047f989a5 xdg-shell: Implement xdg-shell v5 capabilities event 2 years ago
Daniel Stone 1541c44777 libweston-desktop: Add shell capability queries 2 years ago
Daniel Stone 28caa08be6 Implement wp_single_pixel_buffer_v1 protocol 2 years ago
Daniel Stone 0dcd000b3a build: Separate unstable and version for wayland-protocols 2 years ago
Pekka Paalanen 9358706743 README: establish no-malloc-failures policy 2 years ago
Pekka Paalanen fc26c749df shared/xalloc.h: do not rely on zalloc() 2 years ago
Pekka Paalanen c95feefbc0 clients/simple-touch: use xzalloc() for buffer 2 years ago
Pekka Paalanen 9229a45116 shared: rewrite fail_on_null() as abort_oom_if_null() 2 years ago
Pekka Paalanen cbbf0e59a5 ivi-shell: replace MEM_ALLOC() with mostly xcalloc() 2 years ago
Pekka Paalanen 27cf50462b README: drop note about a cairo build option 2 years ago
Derek Foreman c79e1126b6 xwayland: give Xwayland its own session 2 years ago
Daniel Stone 53f895b476 wet_process: Do not weston_log() after fork() 2 years ago
Daniel Stone 9ab97ebd72 wet_process: Use custom_env when forking clients 2 years ago
Daniel Stone 8aa4571240 wet_process: Inline child_client_exec() 2 years ago
Daniel Stone c0a76716c1 wet_process: Use fdstr when executing clients 2 years ago
Daniel Stone a3175727cb wet_process: Rearrange fork() if tree to case statement 2 years ago
Daniel Stone 8b238905d7 xwayland: Use os_socketpair_cloexec() 2 years ago
Daniel Stone 5dbe001661 xwayland: Use custom-env arg handling 2 years ago
Daniel Stone 965d90cbaa desktop-shell: Use custom_env to launch panel clients 2 years ago
Daniel Stone 2cdb473690 custom-env: Add helper to parse combined env/arg strings 2 years ago
Daniel Stone e568a025e1 custom-env: Add support for argument array 2 years ago
Daniel Stone 2a9cae17d8 custom-env: Add tests for environment handling 2 years ago
Daniel Stone fafe5f0fc2 custom-env: Prepare for handling args as well as environment 2 years ago
Daniel Stone 3af823b69b process-util: Assert we don't finalize twice 2 years ago
Daniel Stone b685e075cd process-util: Move Xwayland fork helpers to shared 2 years ago
Marius Vlad 7fd22ae44d libweston/compositor: Check whether flushing is allowed 2 years ago
Marius Vlad 50f98b1006 backend-drm/state-propose: Amend an older comment 2 years ago
Pekka Paalanen 764c2aff8f xwayland: do not check execve() return value 2 years ago
Pekka Paalanen 1bd92dac01 xwayland: do not use setenv() after fork() 2 years ago
Pekka Paalanen d1b01ffb9a xwayland: use execv() 2 years ago
Pekka Paalanen a3d7199bea xwayland: use pipe2() 2 years ago
Pekka Paalanen 4c0bdbfde9 xwayland: do not snprintf() after fork() 2 years ago
Pekka Paalanen 99b2b958f9 shared: introduce os_fd_clear_cloexec() 2 years ago
Pekka Paalanen 0260b8a0b5 shared: fcntl uses int, not long 2 years ago
Pekka Paalanen 77cf8cb006 xwayland: do not weston_log() after fork() 2 years ago
Pekka Paalanen 71b40fc76b xwayland: move config reading up 2 years ago
Pekka Paalanen e88a622434 Revert "xwayland: Don't dup() displayfd pipe" 2 years ago
Pekka Paalanen ff94ba33cd compositor: fix shutdown when xwayland failed to start 2 years ago
Pekka Paalanen 00641368e2 compositor: deprecate cms-static and cms-colord plugins 2 years ago
Daniel Stone 18897253d4 xwayland: Add compositor destroy listener to free allocation 3 years ago
Daniel Stone 5b11f4066a xwayland: Allow for old WM_NORMAL_HINTS 3 years ago
Daniel Stone 4aa885d4af xwayland: Don't dup() displayfd pipe 3 years ago
Daniel Stone 23c8dc7b27 tests: Check requirements after setting up args 3 years ago
Daniel Stone f52231660e tests: Use memstream for config-parser test 3 years ago
Daniel Stone 759712ba05 zuc: Delete support for forking tests 3 years ago
Daniel Stone 6c8ae362bb CI: Never unload llvmpipe DSO whilst testing 3 years ago
Daniel Stone c5ed892b1b CI: Disable ASan fast unwinding for suppressions 3 years ago
Daniel Stone 6a06a06980 CI: Skip certain fontconfig leaks 3 years ago
Michael Olbrich f5a4fb5abc backend-drm: make sure all buffers are released when an output is removed 3 years ago
Michael Olbrich 158c3ef0dd compositor: destroy the layout after the compositor 3 years ago
Daniel Stone b923802113 xwayland: Refactor argument string construction 3 years ago
Marius Vlad e5f6e512ce doc/sphinx/doxygen.ini.in: Remove CLASS_DIAGRAM 3 years ago
Marius Vlad afa494014f doc/sphinx/doxygen.ini.in: Remove DOCBOOK_PROGRAMLISTING 3 years ago
Marius Vlad bd50e257e6 doc/sphinx/doxygen.ini.in: Remove RTF generation 3 years ago
Marius Vlad c2c7644fd0 docs/sphinx/doxygen.ini.in: Remove LaTeX generation 3 years ago
Marius Vlad 7932664b3b weston-log: Fix documentation for weston_log_subscription_create 3 years ago
Pekka Paalanen aa4f7d3a63 tests/color-icc-output: add blending test 3 years ago
Pekka Paalanen 2c0ff9a3b4 tests/color_util: expose color_float_apply_curve() 3 years ago
Michel Dänzer 77fb2f56af clients/presentation-shm: Bind to xdg_wm_base version 1 3 years ago
Pekka Paalanen b5467ba258 tests/color-icc-output: use two-norm tolerance 3 years ago
Pekka Paalanen 3acb1c4793 tests/color-icc-output: compare_float() to rgb_diff_stat 3 years ago
Pekka Paalanen baf7ab5795 tests/alpha-blending: use two_norm tolerance 3 years ago
Pekka Paalanen a0584e64cf tests/alpha-blending: replace compare_float() with rgb_diff_stat 3 years ago
Pekka Paalanen be281478dc tests/color_util: doc rgb_diff_stat and scalar_stat 3 years ago
Pekka Paalanen e103ef4d0d tests: add rgb_diff_stat dumps 3 years ago
Pekka Paalanen 3f60542405 tests/color_util: make rgb_diff_stat pos explicit 3 years ago
Pekka Paalanen 912ea2cb20 tests: add scalar_stat dumps 3 years ago
Pekka Paalanen f31d26669d tests/color_util: constify *_stat_update() 3 years ago
Pekka Paalanen 9026293bff tests: change rgb_diff_stat printing 3 years ago
Pekka Paalanen 0d385ffacb tests/alpha-blending: move unpremult to color_util 3 years ago
Daniel Stone 213195c4db tests: Don't leak args when skipping tests 3 years ago
Daniel Stone 2ebdf0a7f3 subsurface-shot-test: Don't leak replaced buffer 3 years ago
Daniel Stone 5374d55f6a safe-signal-test: Fix leak 3 years ago
Daniel Stone c55a14206d cairo-util: Clean up more Cairo detritus; almost all of it 3 years ago
Daniel Stone 29c3422e05 cairo-util: Don't leak Pango objects 3 years ago
Daniel Stone d43931080e weston-terminal: Don't leak Cairo fonts 3 years ago
Daniel Stone fc4fb9fb92 weston-terminal: Make exit path a little more obvious 3 years ago
Daniel Stone f9e54ab2f8 weston-terminal: Fix some egregious memory leaks 3 years ago
Daniel Stone 6bfbfb2e10 toytoolkit: Delete remnants of EGL support 3 years ago
Daniel Stone 01c57eca43 backend-drm: Don't leak drm_device on shutdown 3 years ago
Daniel Stone 97f664815d backend-wayland: Don't leak parent output trackers 3 years ago
Derek Foreman cf5aca5a0d xwm: Generate more synthetic ConfigureNotify events 3 years ago
Derek Foreman ea9a01f2e3 xwm: Prepare send_configurenotify for non-fullscreen use 3 years ago
Derek Foreman 23e3a3285a libweston-desktop: Add get_position 3 years ago
Derek Foreman d615abdffd shells: Add libweston-desktop API to query position and add to shells 3 years ago
Daniel Stone ed97387a4e tests: Use test-desktop-shell for devices-test 3 years ago
Daniel Stone 450ec38d79 noop-renderer: Make sure buffer access doesn't get optimised out 3 years ago
Daniel Stone 19278569a3 noop-renderer: weston_buffer properties are set by the core 3 years ago
Hideyuki Nagase e4100f856d xwayland: Change layer for xwayland override redirect windows 3 years ago
Derek Foreman 0df0dccc84 shared: Make xalloc.h stand alone 3 years ago
Daniel Stone 9336263d9b Move libweston-desktop into libweston 3 years ago
Daniel Stone 3ed3700ca3 kiosk-shell: Don't link desktop-shell protocols 3 years ago
Daniel Stone 0774a321c5 scene-graph: Print when surface/view is not mapped 3 years ago
Michael Olbrich 10403a85ec libweston: disable a pending idle_repaint_source when the output is removed 3 years ago
Daniel Stone 61d8238874 desktop-shell: Remove multiple workspace support 3 years ago
Michael Olbrich 48e8c158ea compositor: only reflow the outputs if the shell did not move them 3 years ago
Marius Vlad 59a72dcf63 shared/xcb-xwayland: Add missing atoms 3 years ago
Michael Olbrich 2929b6c483 backend-drm: check that outputs are in fact ours 3 years ago
Derek Foreman af51618708 xwayland/window-manager: Add support for _NET_FRAME_EXTENTS 3 years ago
Derek Foreman 769e4376c6 shared/frame: Provide a function to get decoration sizes and use it 3 years ago
Hideyuki Nagase d902088bfc xwayland: support minimizing 3 years ago
Marius Vlad cc69dc447e clients/window: Defer closing of window 3 years ago
Pekka Paalanen 57d32722a2 gl-renderer: simplify main() in frag 3 years ago
Pekka Paalanen 932c374779 gl-renderer: move undo-premult to color_pipeline() 3 years ago
Pekka Paalanen 924b94bc94 gl-renderer: call it view_alpha in frag 3 years ago
Marius Vlad 49d6532254 shared/xcb-xwayland: Split into common helpers 3 years ago
Derek Foreman 107d69f10c xwayland: Stop drawing shadows on maximized windows 3 years ago
Derek Foreman 8763f3800e xwayland: Update net_wm state when we change it. 3 years ago
Derek Foreman 93b58c5648 xwayland: Don't move window in response to geometry change if state changed 3 years ago
Derek Foreman 40e76fe19d xwayland: Set non zero default saved window size 3 years ago
Ivan Nikolaenko 0d3e438d08 build: fix possible race/error for some backends 3 years ago
Michael Olbrich 78933093a1 backend-drm: check that outputs are in fact ours 3 years ago
Michael Olbrich 3e44a6eb3d backend-drm: don't try to disable planes on session deactivation 3 years ago
Daniel Stone f962b48958 compositor: Only create paint nodes for mapped surfaces/views 3 years ago
Daniel Stone f1fe6ec776 xdg-shell: Mark xdg_popup surfaces as mapped 3 years ago
Daniel Stone c0ff9ed24a test-desktop-shell: Mark weston_curtain views as mapped 3 years ago
Daniel Stone 19744a5207 weston-curtain: Always mark surface as mapped 3 years ago
Daniel Stone af7dcdddac desktop-shell: Map surfaces in map() 3 years ago
Daniel Stone 0c69688aa2 libweston: Add weston_surface_map() wrapper 3 years ago
Daniel Stone 51fe874ad4 libweston: Use weston_surface_has_content() in core compositor 3 years ago
Daniel Stone dd6b5a190e data-device: Use weston_surface_has_content() 3 years ago
Daniel Stone 888d08d8a5 desktop-shell: Use weston_surface_has_content() 3 years ago
Daniel Stone d211e3173c xdg-shell: Use weston_surface_has_content() 3 years ago
Daniel Stone 13ead893e2 Add weston_surface_has_content() 3 years ago
Pekka Paalanen f212a703cf tests/alpha-blend: use image-iter.h 3 years ago
Pekka Paalanen 67331be0cd tests/internal-screenshot: use image-iter.h 3 years ago
Pekka Paalanen 884c5f80e8 tests/yuv-buffer: use image-iter.h for rgb_image 3 years ago
Pekka Paalanen 94589497a1 tests/client-helper: use image_header_from() more 3 years ago
Pekka Paalanen 791a6be216 tests: pass image_header to image_check_get_roi() 3 years ago
Pekka Paalanen 4eb70a602b tests/client-helper: use image-iter.h 3 years ago
Pekka Paalanen 9b82bfae9e tests/color-icc-output: extract image-iter.h 3 years ago
Pekka Paalanen 8de94ec9c9 compositor: add weston.ini option max-bpc 3 years ago
Pekka Paalanen d24adbbe25 backend-drm: set connector max bpc 3 years ago
Pekka Paalanen 4d2ea5dd0b tests: move set_opaque_rect() to client helpers 3 years ago
Pekka Paalanen b878357dfd tests: remove skip() as unused 3 years ago
Robert Mader f7541d9e42 clients/simple-egl: Fix angle reset on benchmark interval 3 years ago
Hideyuki Nagase ae9643f729 build: enable configuration of RDP backend as a default 3 years ago
Pekka Paalanen 731a2fd45b tests/color-icc-output: move gen_ramp_rgb() in the file 3 years ago
Pekka Paalanen cb38c9c84d tests: rename shaper_matrix_and_cLUT to opaque_pixel_conversion 3 years ago
Pekka Paalanen 6393e43357 tests: rename color-shaper-matrix-test.c to color-icc-output-test.c 3 years ago
Philipp Zabel 4938f8f6e5 compositor: stop creating outputs without head 3 years ago
Philipp Zabel c6e47d177a libweston: consolidate weston_compositor_create_output(_with_head) 3 years ago
Philipp Zabel 060ef82d93 backend-x11: check that outputs and heads are in fact ours 3 years ago
Philipp Zabel 69c4cec4f1 backend-wayland: check that outputs and heads are in fact ours 3 years ago
Philipp Zabel 5b41ffa9da backend-rdp: check that outputs and heads are in fact ours 3 years ago
Philipp Zabel 5159af0607 backend-headless: check that outputs and heads are in fact ours 3 years ago
Philipp Zabel ffc011d6a3 backend-drm: check that outputs and heads are in fact ours 3 years ago
Philipp Zabel aab722bb17 backend-drm: prepare virtual output API for heterogeneous outputs 3 years ago
Philipp Zabel 54d7682ee8 libweston: add opaque backend_id pointer to struct weston_head 3 years ago
Derek Foreman 982e59a942 rdp: Stop using deprecated functions 3 years ago
Derek Foreman 5014eb03a3 rdp: Update to new FreeRDP structure layout 3 years ago
Derek Foreman da386c827e rdp: Update to newer FreeRDP release 3 years ago
Derek Foreman 2afb812d1e shared/cairo-util: Hold onto our pattern reference until we're done 3 years ago
Luigi Santivetti 8b654c47eb Revert "backend-drm: add HDR_OUTPUT_METADATA definitions" 3 years ago
Luigi Santivetti a62bf5ff48 drm-backend: stop parsing IN_FORMATS blobs, use libdrm instead 3 years ago
Luigi Santivetti 08a821f291 gitlab-ci: build libdrm version 2.4.108 from source 3 years ago
Derek Foreman 0f4b411091 ci: Fix cobertura syntax 3 years ago
Marius Vlad 7ceda8cbba gl-renderer: Ensure gl_buffer_state is present for direct-display 3 years ago
Vitaly Prosyak 0c5860fafb tests/color-shaper-matrix: add creation and usage cLUT profiles 3 years ago
Pekka Paalanen 276d1ae024 tests/color-shaper-matrix: add ref image index 3 years ago
Vitaly Prosyak 6478859b4f tests/color-shaper-matrix: prepare for cLUT type profiles 3 years ago
Pekka Paalanen 062b6646ff tests/color-shaper-matrix: fix realpath() leak 3 years ago
Pekka Paalanen 5921a00b38 tests/lcms_util: add SetTextTags() 3 years ago
Pekka Paalanen 44c3079567 tests: add lcms-util with MPE curves 3 years ago
Pekka Paalanen 0225453fb1 tests/color_util: add transfer_fn_name() 3 years ago
Pekka Paalanen 142d8e5125 tests/color_util: add RGB diff stat 3 years ago
Pekka Paalanen c76e4abb60 tests/color_util: add lcmsMAT3_invert() 3 years ago
Pekka Paalanen 141cd3021e tests/color_util: add transfer_fn_invert() 3 years ago
Pekka Paalanen fa477d2407 tests/color_util: add TRANSFER_FN_IDENTITY 3 years ago
Pekka Paalanen 6fa7ab5d5f tests/color_util: prettify arr_curves 3 years ago
Pekka Paalanen 8adbd3d802 tests/color_util: streamline sRGB_linearize/delinearize 3 years ago
Pekka Paalanen 53b1268018 tests/color_util: refactor into color_float_apply_matrix() 3 years ago
Pekka Paalanen c8195289a7 tests/color_util: refactor into color_float_apply_curve() 3 years ago
Pekka Paalanen 85738af912 tests/color_util: protect header from re-reading 3 years ago
Pekka Paalanen 7fa9b15348 build: consolidate lcms2 dependencies 3 years ago
Pekka Paalanen 6234cb98d1 gl-renderer: fix performance regression in frag 3 years ago
Michael Olbrich 81912dc2a6 compositor: improve opacity handling for scaled surfaces 3 years ago
Michael Olbrich e2426960d4 compositor: set transform.opaque for surfaces without alpha channel 3 years ago
Michael Tretter dfceb60274 backend-drm: explicitly pass device to initialization 3 years ago
Michael Tretter deebfd99e3 backend-drm: get the drm device from the output 3 years ago
Michael Tretter 6e36787dfd backend-drm: handle hotplug events per drm device 3 years ago
Michael Tretter d990c6a939 backend-drm: ignore heads from other devices 3 years ago
Michael Tretter 345e705e33 backend-drm: move drm objects from backend to drm device 3 years ago
Michael Tretter c4685d9463 backend-drm: attach device to pending state 3 years ago
Michael Tretter 615a37dc88 backend-drm: make dma-buf feedback device specific 3 years ago
Michael Tretter 101c0f6b8b backend-drm: get the fb using the device instead of the backend 3 years ago
Michael Tretter d89fcf10cb backend-drm: pass device through atomic commit handler 3 years ago
Michael Tretter 0d967bd7f4 backend-drm: extract device from backend 3 years ago
Michael Tretter 2860933ded backend-drm: cleanup debugging 3 years ago
Michael Tretter 00b74293e8 backend-drm: use pixel format to print gbm format 3 years ago
Michael Olbrich 83d1eafd81 backend-drm: virtual: use the DRM fd from the fb 3 years ago
Derek Foreman c0cafde80f drm: Remove destroy listener from list when fired 3 years ago
Derek Foreman cc924e8131 libweston-desktop/xwayland: Use correct geometry 3 years ago
Pekka Paalanen fb7b1a4125 Revert "build: add test-gl-renderer option" 3 years ago
Hideyuki Nagase 8508f93f2b rdp: Update cursor position on most mouse messages 3 years ago
Pekka Paalanen ee085015d0 build: drop unused option rdp-thread-check 3 years ago
Marius Vlad b0257e0ffc backend-drm: Add GBM_BO_HANDLE as a failure reason 3 years ago
Derek Foreman aa507417c2 xwm: Fix pasting in some cases 3 years ago
Pekka Paalanen 892421a93e tests: add matrix-test for CI 3 years ago
Pekka Paalanen 8bbd1a995b libweston: remove UNIT_TEST 3 years ago
Pekka Paalanen a1e5d46d91 tests: delete the manual matrix-test 3 years ago
Robert Mader 62ab6891db clients/simple-egl: Handle buffer scale and transform 3 years ago
Robert Mader 0b2369bb4a clients/simple-egl: Rename geometry to buffer_size 3 years ago
Robert Mader 009625c297 clients/simple-egl: Rename buffer_size to buffer_bpp 3 years ago
Robert Mader 4090f0eb6f clients/simple-egl: Use INT32_MAX for opaque region 3 years ago
Pekka Paalanen e67a0cb57c gl-renderer: fix double-alloc of gl_buffer_state 3 years ago
Marius Vlad 7412a01437 backend-drm: Retrieve reason if dmabuf import failed 3 years ago
Pekka Paalanen 8ebebb20ef drm-backend: add color_outcome / HDR metadata serial 3 years ago
Pekka Paalanen c217453c85 backend-drm: forward HDR metadata 3 years ago
Pekka Paalanen c4fedd503f backend-drm: move code to kms-color.c 3 years ago
Pekka Paalanen ccb4c383d7 tests: add color-metadata-errors test 3 years ago
Pekka Paalanen e13e64c4e0 tests: add color-metadata-parsing 3 years ago
Pekka Paalanen e108c1a2fe color-lcms: color characteristics into HDR metadata 3 years ago
Pekka Paalanen cea53a90d4 libweston: add HDR metadata to weston_output 3 years ago
Pekka Paalanen 518d72a37b compositor: add color_characteristics weston.ini option 3 years ago
Pekka Paalanen 3696d9b6a1 libweston: add basic output color characteristics API 3 years ago
Derek Foreman c8db957a0b rdp: Add audio support 3 years ago
Derek Foreman f8ca784737 rdp: Fix some accidental style errors in new clipboard code 3 years ago
Derek Foreman 2df71c6dd7 rdp: Make thread checks unconfigurable 3 years ago
Robert Mader 572ad2d8a9 clients/simple-dmabuf-*: Use gbm_bo_create_with_modifiers2 3 years ago
Robert Mader 46a6b5b448 clients/simple-dmabuf-feedback: Support multi-tranche feedbacks 3 years ago
Daniel Stone a55bd6798e clients: Delete gears 3 years ago
Daniel Stone dfaba9f107 gl-renderer: Use common value for maximum plane index 3 years ago
Daniel Stone c3d84293b9 gl-renderer: Fix plane count for legacy YUV420 images 3 years ago
Daniel Stone f36d77a199 gl-renderer: Don't use TEXTURE_EXTERNAL for multi-planar formats 3 years ago
Daniel Stone 67fc71214d gl-renderer: Add comment for yuv_format_descriptor 3 years ago
Daniel Stone 18a31a6af8 gl-renderer: Remove special-cased YUV SHM formats 3 years ago
Daniel Stone 1a86963d51 gl-renderer: Get YUV subsampling from pixel-formats 3 years ago
Daniel Stone 1654813903 gl-renderer: Get YUV plane count from pixel-formats 3 years ago
Daniel Stone 727c4ef6fb pixel-formats: Add R8 and GR88 3 years ago
Daniel Stone 1db2fbef61 pixel-formats: Add internal-only format flag 3 years ago
Daniel Stone 32ee42d261 gl-renderer: Remove useless texture_type enum 3 years ago
Daniel Stone 820f3ae866 gl-renderer: Add support for WL_SHM_FORMAT_YUV444 3 years ago
Daniel Stone c2cfadfce9 gl-renderer: Use hsub and vsub from pixel-formats 3 years ago
Daniel Stone 742f10f32c gl-renderer: Use vsub for y offset in SHM 3 years ago
Daniel Stone 4161948da9 pixel-formats: Add hsub and vsub helpers 3 years ago
Daniel Stone e08df66bd3 gl-renderer: Use pixel-formats data for RGB formats 3 years ago
Daniel Stone 2ade128ae2 pixel-formats: Fill in bpp everywhere 3 years ago
Daniel Stone c54eace91d pixel-formats: Rename addfb_legacy_depth 3 years ago
Daniel Stone d37d73a9a0 gl-renderer: Use DRM format codes instead of SHM 3 years ago
Daniel Stone 2c40260397 gl-renderer: Remove unsupported-SHM-format fallback 3 years ago
Daniel Stone badd774c28 gl-renderer: Use pixel-formats GL format for single-planar formats 3 years ago
Daniel Stone 38f933dd46 gl-renderer: Move GL compatibility workarounds out of per-format 3 years ago
Daniel Stone 2e6827d70e gl-renderer: Ensure SHM buffer format stays the same 3 years ago
Daniel Stone d696f8df1f gl-renderer: Use ARRAY_COPY for buffer state 3 years ago
Daniel Stone 89d0d90306 pixel-formats: Add GL types for 16bpc formats 3 years ago
Hideyuki Nagase 297ad403d6 rdp: Add clipboard redirection support 3 years ago
Hideyuki Nagase 252771d9aa rdp: add virtual channel support 3 years ago
Hideyuki Nagase 3bdc29b934 rdp: Add cross thread work queues 3 years ago
Pekka Paalanen e884e7c7b8 gl_renderer: log EGL features 3 years ago
Pekka Paalanen 2f115047de gl-renderer: log rendering device 3 years ago
Pekka Paalanen f3bf7a0d5a gl-renderer: add error messages for missing EGL platforms 3 years ago
Pekka Paalanen b383f52d31 gl_renderer: print more GL ES feature flags 3 years ago
Pekka Paalanen 092115786e gl-renderer: move extension lists away from log 3 years ago
Pekka Paalanen ccb0d4f7ce gl-renderer: pass gr to gl_renderer_log_extensions() 3 years ago
Marius Vlad 2327daf96b desktop-shell: Handle weston_curtain destruction 3 years ago
Daniel Stone 4815936630 gl-renderer: Allocate textures per-buffer, not per-surface 3 years ago
Daniel Stone 62c0f1621c gl-renderer: Delete egl_image wrapper 3 years ago
Daniel Stone 3297d10287 gl-renderer: Cache gl_buffer_state on the weston_buffer 3 years ago
Daniel Stone acc3762506 gl-renderer: Store dmabuf buffer state in weston_buffer 3 years ago
Daniel Stone 56dc4b8aaa gl-renderer: Remove unused dmabuf import_type 3 years ago
Daniel Stone 8b167a1703 gl-renderer: Store EGL buffer state in weston_buffer 3 years ago
Daniel Stone 8544a4d09b weston_buffer: Move direct_display out of gl-renderer 3 years ago
Daniel Stone 57c34139d3 gl-renderer: Don't modify buffer_state in attach 3 years ago
Daniel Stone c9253c0012 renderer: Set surface->is_opaque in the core 3 years ago
Daniel Stone 193de3c2cf renderer: Remove get_content_size hook 3 years ago
Daniel Stone 21c65d7c9b gl-renderer: Remove gl_buffer_state.buffer_type 3 years ago
Daniel Stone 5fdb5fdd90 gl-renderer: Don't match texture width to input pitch 3 years ago
Daniel Stone 907c9d1ffd gl-renderer: Clarify comment 3 years ago
Daniel Stone 90dbf4522d gl-renderer: Remove gl_buffer_state.y_inverted 3 years ago
Daniel Stone c6af9c8581 gl-renderer: Remove gl_buffer_state.height 3 years ago
Daniel Stone 70874428d6 gl-renderer: Make attach_shm return early on cache hit 3 years ago
Daniel Stone 1a65c1b8b1 gl-renderer: Shift buffer reference later in attach 3 years ago
Daniel Stone 77e1a04220 gl-renderer: Remove extraneous parameters from attach 3 years ago
Daniel Stone 0cdf576c92 gl-renderer: Convert attach to flat-return style 3 years ago
Daniel Stone 428ae215e8 gl-renderer: Add return value to attach handlers 3 years ago
Daniel Stone bb624754f1 gl-renderer: Don't leak EGLImages on import fail 3 years ago
Daniel Stone 4519461439 gl-renderer: Move EGL buffer error checks into attach_egl 3 years ago
Daniel Stone 8f56743590 gl-renderer: Change surface_set_color to attach_solid 3 years ago
Daniel Stone 72fc647a96 gl-renderer: Split buffer state away from surface state 3 years ago
Daniel Stone 0c65b23848 libweston: Move renderer interface to internal header 3 years ago
Robert Mader 564828fb96 rdp: Silence compiler warning 3 years ago
Robert Mader 2a2eeb6a33 libweston: Silence compiler warning 3 years ago
Marius Vlad ab42159bf3 desktop-shell: Add missing weston_view_destroy() 3 years ago
Marius Vlad 9b0b5b57dd noop-renderer: Remove volatile and use compiler attribute 3 years ago
Derek Foreman db06aea171 desktop-shell: Check height instead of checking width a second time 3 years ago
Marius Vlad 299f87f073 desktop-shell: Clarify weston_view destruction at tear down 3 years ago
Marius Vlad d03f01377a desktop-shell: Check for a valid desktop_surface 3 years ago
Marius Vlad c41cdcabb4 desktop-shell: Migrate surface_unlink_view 3 years ago
Marius Vlad be5b6f9cdc desktop-shell: Rename destroy_layer functions 3 years ago
Marius Vlad 9cf602840d desktop-shell: Create a distinct view for the fade-out close anim 3 years ago
Marius Vlad bd8314078d libweston, desktop-shell: Add a wrapper for weston_surface reference 3 years ago
Marius Vlad d3ed2eb345 libweston: Assert if ref-count balance is wrong 3 years ago
Marius Vlad 0d8e94af61 libweston: Rename weston_surface_destroy() to weston_surface_unref() 3 years ago
Robert Mader 53a221ccaa libweston/linux-dmabuf: create surface feedback on demand 3 years ago
Robert Mader 34f7e01c2b clients/simple-dmabuf-feedback: use time instead of redraws 3 years ago
Robert Mader 29d480813a backend-drm: Add failure reasons for failing gbm_bo_import 3 years ago
Marius Vlad 70353dace3 desktop-shell: Refuse to set a surface to maximized 3 years ago
Erik Faye-Lund fed2ee51f2 simple-egl: clean up unused callback 3 years ago
Hideyuki Nagase 8a776be925 rdp: Add US international keyboard layout 3 years ago
Hideyuki Nagase e3b95f2d27 rdp: Add hebrew standard layout 3 years ago
Pekka Paalanen e3b6d04017 color-lcms: refactor away setup_seach_param() 3 years ago
Pekka Paalanen dfba19abde color: simplify color manager API with weston_output_color_outcome 3 years ago
Pekka Paalanen 6c0524fd80 libweston: add struct weston_output_color_outcome 3 years ago
Simon Ser 6122765203 clients/simple-dmabuf-feedback: prettify output 3 years ago
Pekka Paalanen e6a9e3c4ee backend-drm: default to XRGB2101010 for HDR 3 years ago
Pekka Paalanen 33d553f833 compositor: add eotf-mode weston.ini option 3 years ago
Pekka Paalanen 5151f9fe9e backend-drm: program HDR_OUTPUT_METADATA 3 years ago
Pekka Paalanen 1d17e4991f backend-drm: check for HDR_OUTPUT_METADATA 3 years ago
Pekka Paalanen 6914064066 backend-drm: add HDR_OUTPUT_METADATA definitions 3 years ago
Pekka Paalanen 271c11e9dc color-lcms: todo for eotf_mode 3 years ago
Pekka Paalanen 46c0383c14 color-noop: supports only SDR EOTF mode 3 years ago
Pekka Paalanen 1e9b1a1047 backend-headless: support all EOTF modes 3 years ago
Pekka Paalanen 5f9b68d68f libweston: introduce weston_eotf_mode 3 years ago
Marius Vlad 054aaa5a8b simple-egl: Move set_fullscreen/set_maximized before initial commit 3 years ago
Marius Vlad 0277046a1d simple-egl: Defer EGL surface/window creation 3 years ago
Marius Vlad c15699b7f8 simple-egl: Remove uneeded check 3 years ago
Marius Vlad cc877d4b77 libweston-desktop: Replace buffer with geometry 3 years ago
Marius Vlad 01ef3746a2 simple-egl: Add start as maximized 3 years ago
Marius Vlad 69a59359fa pixman-renderer: Unref the pixman image when wl_shm_buffer is gone 3 years ago
Hideyuki Nagase b6fc6b2a8d rdp: sync keylocks on synchronize event 3 years ago
Hideyuki Nagase 6515df1333 rdp: Support using Japanese layouts with US keyboards 3 years ago
Hideyuki Nagase 5d939bc636 rdp: Korean keyboard support 3 years ago
Hideyuki Nagase 4d5605b3a0 rdp: refactor xkbRuleNames code 3 years ago
Hideyuki Nagase a29bcb7031 rdp: change japanese keyboard input from kana to alphabetical 3 years ago
Hideyuki Nagase e3a4552ecc rdp: Fix Farsi keyboard map 3 years ago
Hideyuki Nagase 08f5edfe78 rdp: Fix Brazilian keyboard map 3 years ago
Hideyuki Nagase d2a8165bb6 rdp: Add Persian keyboard map 3 years ago
Daniel Stone 06472fb136 desktop-shell: Delete Exposay 3 years ago
Derek Foreman 2eb5912960 rdp: Don't bother trying to pick an optimal keyboard model name 3 years ago
Hideyuki Nagase 806e824809 rdp: Add horizontal scroll support 3 years ago
Hideyuki Nagase 4e907a67e5 rdp: Add high precision scrolling 3 years ago
Hideyuki Nagase ce09c7835c rdp: refactor scrollwheel code 3 years ago
Hideyuki Nagase cf5ddd05cb rdp: Allow configuring the refresh rate 3 years ago
Hideyuki Nagase bd214edf26 rdp: Calculate frame times from mode refresh rate 3 years ago
Brenton DeGeer 2f9319cef6 rdp: Allow specifying a listener fd on the command line 3 years ago
Hideyuki Nagase 13e62c9d18 rdp: validate button state 3 years ago
Hideyuki Nagase 873ec15412 rdp: Fix up xf_extendedMouseEvent 3 years ago
Hideyuki Nagase 7f10997d92 rdp: add mouse input debug 3 years ago
Simon Ser 778c0683c0 clients/simple-dmabuf-feedback: use presentation-time 3 years ago
Leandro Ribeiro 7724c5ea38 clients/simple-dmabuf-feedback: do not use buffer before compositor's response 3 years ago
Marius Vlad 509398dc25 desktop-shell: Avoid spurious configure events for xdg-shell activation 3 years ago
Daniel Stone b5605ccd26 libweston: Remove weston_surface_set_color 3 years ago
Daniel Stone 4d426ab6b1 shell: Explicitly use solid weston_buffers 3 years ago
Daniel Stone 82b646728c backend-drm: Handle solid-colour buffers in state propose 3 years ago
Daniel Stone b38b735e20 backend-drm: Remove Pixman conditional for keep_buffer 3 years ago
Daniel Stone 493a4c013e noop-render: Allow solid-color buffers 3 years ago
Daniel Stone d82d74e713 pixman-renderer: Support solid-colour weston_buffers 3 years ago
Daniel Stone 465f4a250c gl-renderer: Support solid-colour weston_buffers 3 years ago
Daniel Stone 43715ff0c0 weston_buffer: Add solid buffer type 3 years ago
Daniel Stone 7a27f6cbe4 compositor: Downgrade rather than drop buffer reference when copied 3 years ago
Daniel Stone a42908204f weston_buffer: Separate buffer release from lifetime 3 years ago
Daniel Stone fdc7b9c352 weston_buffer: Add mode to weston_buffer_reference 3 years ago
Daniel Stone 7e90433079 weston_buffer: Hold lifetime for resource/backend usage 3 years ago
Daniel Stone d2a858e879 gl-renderer: Prepare for buffer to outlive resource 3 years ago
Daniel Stone 7b3efabd88 weston_buffer: Prepare for buffer to outlive resource 3 years ago
Daniel Stone e9c792ed64 backend-drm: More failure reasons 3 years ago
Daniel Stone 231a67ff8c drm-backend: Refactor unpleasant keep_buffer if tree 3 years ago
Daniel Stone 2dcfe723be backend-drm: Make use of weston_buffer format and type 3 years ago
Daniel Stone 1d9c62b50d weston_buffer: Print more buffer information in scene-graph 3 years ago
Daniel Stone 0a8802404c scene-graph: Use weston_buffer's format/modifier info to print 3 years ago
Daniel Stone 7506cf5240 gl-renderer: Simplify surface->is_opaque 3 years ago
Daniel Stone 4f88b2655e weston_buffer: Change y_inverted to explicit origin enum 3 years ago
Daniel Stone 6dcf3eac1f weston_buffer: Add pixel format and modifier info 3 years ago
Daniel Stone fef8bb275c weston_buffer: Make use of weston_buffer->type 3 years ago
Daniel Stone 34cd0d114f weston_buffer: Add type field 3 years ago
Daniel Stone 1d5f8af82e gl-renderer: Add hook to fill weston_buffer for EGL 3 years ago
Daniel Stone f49d6f47f3 gl-renderer: Reject unknown-format dmabufs 3 years ago
Daniel Stone ca9bb01fe6 renderers: Set buffer properties earlier 3 years ago
Daniel Stone fec0400886 gl-renderer: Drop unnecessary NULL check 3 years ago
Daniel Stone 12675ed19f renderer: Add buffer to flush_damage 3 years ago
Daniel Stone f8ac6f940f gl-renderer: Remove outdated comment 3 years ago
Daniel Stone efd6aae915 gl-renderer: Remove unnecessary dmabuf conditional 3 years ago
Hideyuki Nagase 6129cbd880 rdp: Improved rdp logging infrastructure 3 years ago
Derek Foreman 04e0558327 rdp: Allow disabling RemoteFX codec 3 years ago
Simon Ser 4887f1a7aa build: add Meson fallback for wayland-protocols 3 years ago
Marius Vlad 0a8e3cbc4a screen-share: Document that --no-config option should be passed 3 years ago
Marius Vlad af18a0c4c9 screen-share: Avoid bit-shifting large values 3 years ago
Marius Vlad 08ee337e4d screen-share: Add a compositor destroy listener 3 years ago
Marius Vlad 9e20730e04 screen-share: Start screen sharing even if no pointer is found 3 years ago
Marius Vlad 3bedb70454 touch-calibration: Clean-up if touch calibrator has been enabled 3 years ago
Hideyuki Nagase 8aa1c30bf1 xwayland: Honour the XCURSOR_THEME environment variable 3 years ago
Weng Xuetian e99ed2ad36
Defer launch input method with wl_event_loop_add_idle. 3 years ago
Hideyuki Nagase 8fb529bc31 input: Fix bug in idle inhibition 3 years ago
Dominique Martinet c2f4201ed2 xwayland: use -displayfd instead of USR1 to signal readiness 3 years ago
Pekka Paalanen e1a111e1b7 man: add section delimiters in weston.ini 3 years ago
Pekka Paalanen 9203910b9a man: clean up weston.ini mark-up 3 years ago
Michael Olbrich 924e79f4f2 ivi-shell: emit created notification earlier for desktop surfaces 3 years ago
Maciej Pijanowski cd7801aa95 screen-share: use compositor->read_format with renderer->read_pixels() 3 years ago
shierote f2d6d21eec libweston: correct argument name in the handler of wl_data_source.accept 3 years ago
Hideyuki Nagase 87bded8b54 rdp: split off rdp.h 3 years ago
Hideyuki Nagase 516d2c0207 rdp: fix leak when listener implantation fails 3 years ago
James Hilliard 23205b6b56 protocol/meson.build: install content-protection protocol xml 3 years ago
Derek Foreman ca979aa219 desktop-shell: Fix incorrect use of black_surface_get_label 3 years ago
Derek Foreman bb1d19dc5e compositor: Launch clients in their own session 3 years ago
Derek Foreman f32bcfef42 compositor: Use sigaction to trap SIGINT 3 years ago
Derek Foreman 2f71b3c3de compositor: Stop trapping SIGQUIT 3 years ago
Derek Foreman 0ff4e478cd rdp: Fix comment regarding meaning of 120 3 years ago
Derek Foreman 869cab4938 xwayland: Simplify HAVE_XWAYLAND_LISTENFD usage 3 years ago
Sören Meier edef874696 libbacklight: Fix backlight never gets initialized 3 years ago
Michael Tretter 6ee6e76a0c compositor: remove repaint_data from compositor 3 years ago
Michael Tretter c448b938f7 backend-drm: always get pending_state from backend 3 years ago
Daniel Stone c3415aed23 fullscreen-shell: Use weston_curtain for black view 3 years ago
Daniel Stone b94d69b926 fullscreen-shell: Link with shell-utils helpers 3 years ago
Daniel Stone 577a832f41 test/desktop-shell: Use weston_curtain 3 years ago
Daniel Stone 6cb2526b67 Move shell-utils to its own directory 3 years ago
Daniel Stone 15a553053a desktop-shell: Reuse curtains for fades 3 years ago
Daniel Stone dc0f73bcac shell: Encapsulate weston_curtain in its own struct 3 years ago
Daniel Stone e031397e09 desktop-shell: Reuse curtains for focus animations 3 years ago
Daniel Stone bd9b0676dd shell: Make input capture optional for curtains 3 years ago
Daniel Stone 64ef87554b desktop-shell: Clean up fullscreen black view code 3 years ago
Daniel Stone de0cd53264 desktop-shell: Remove redundant geometry dirty call 3 years ago
Daniel Stone 791e8b1c5f desktop-shell: Fix opaque region co-ordinate confusion 3 years ago
Daniel Stone e81b8d7cc9 shell: Add alpha to weston_curtain_create 3 years ago
Daniel Stone d21563360a shell: Move weston_curtain_create params into the struct 3 years ago
Daniel Stone 3a298b0b05 shell: Rename weston_solid_color_surface to weston_curtain_params 3 years ago
Daniel Stone b77c2374ee shell: Rename solid_color_surface to weston_curtain 3 years ago
Daniel Stone 7059ec7807 desktop-shell: Explicitly destroy black views on shutdown 3 years ago
Daniel Stone c8a2fb7a40 pixel-formats: Add XYUV8888 format 3 years ago
Daniel Stone f48cf18a16 compositor: Fix harmless potential buffer overflow 3 years ago
nerdopolis cdfe94b105 launcher-logind: Don't check wl->vtnr before returning it. 3 years ago
Pekka Paalanen b3ba1becba libweston: remove fbdev backend 3 years ago
Pekka Paalanen 18df9108ea man: replace tablet shell with kiosk shell 3 years ago
Pekka Paalanen bfefe8e8d4 man: add gbm-format in output section 3 years ago
Pekka Paalanen ce059cffdb man: expand on gbm-format 3 years ago
Pekka Paalanen 1f5e19fab3 man: move pageflip-timeout from weston.ini(5) to weston-drm(7) 3 years ago
Pekka Paalanen 79f73d2247 man: move gbm-format from weston.ini(5) to weston-drm(7) 3 years ago
Marius Vlad d284ab0322 pipewire,remoting,tests: Replace asprintf w/ str_printf 3 years ago
Pekka Paalanen 3e94836a63 tests: add get_double in config-parser tests 3 years ago
Pekka Paalanen 0a38fc7e75 shared: fix WL_EXPORT style in config-parser.c 3 years ago
Pekka Paalanen f58a3a7e1d include: drop unused config-parser.h types 3 years ago
Robert Mader 2669853562 clients/simple-dmabuf-feedback: Add fallback print method for unknown formats 3 years ago
Robert Mader f81aacdf2f pixel-formats: Add support for 64bbp float RGB formats 3 years ago
Marius Vlad c19cf3d684 libweston: Enable logging for libseat launcher 3 years ago
Marius Vlad e9fe66a91c weston-log: Extract helper for generating a time stamp 3 years ago
Marius Vlad d40cedc8af desktop-shell: Remove wl_shell_surface::resize enum 3 years ago
Derek Foreman 7cae2a1fb0 backend-wayland: Stop supporting wl_shell 3 years ago
Derek Foreman e6b8f5a5e4 remove wl_shell 3 years ago
Pekka Paalanen 5ba7ae2937 tests: preserve ivi runner section 3 years ago
Michael Olbrich 78c94d0719 libweston: explicitly cancel start_drag if no matching input device is found 3 years ago
Pekka Paalanen 4fb095eca1 doc: running on different seat with libseat 3 years ago
Pekka Paalanen c26326bfb1 doc: expand on libseat via ssh 3 years ago
Pekka Paalanen 952a951662 build: enable libseat support by default 3 years ago
Derek Foreman 2c91c70250 launchers: Remove --tty option 3 years ago
Derek Foreman a96dfc7098 launchers: remove launchers 3 years ago
Vitaly Prosyak 87f2d09f18 color-lcms: Always use cmsContext for LCMS API which has THR suffix 3 years ago
Pekka Paalanen e2ee2b56f9 tests: make vertex-clip use WESTON_EXPORT_FOR_TESTS 3 years ago
Pekka Paalanen 32a790f774 shared: add WESTON_EXPORT_FOR_TESTS 3 years ago
nerdopolis 8e2c67c317 clients/desktop-shell: Add a displayname= option for launchers 3 years ago
James Le Cuirot 89587db3cb meson.build: Fix -Dbackend-default=auto following fbdev deprecation 3 years ago
Takuro Ashie 351e6a4b21 Don't send compositor's global key bindings to the input method 3 years ago
Pekka Paalanen 29d4472e13 tests: use color_float rgb[] alias more 3 years ago
Pekka Paalanen 4012062228 tests: add rgb[] alias in color_float 3 years ago
Daniel Stone 2ac6b6b084 tests: Add dependency on screenshooter client protocol 3 years ago
Robert Mader 3e6ef529f8 clients/simple-dmabuf-*: Increase buffer limit to four 3 years ago
Vitaly Prosyak fe35ca2d68 tests: color shaper-matrix test 3 years ago
Vitaly Prosyak 264a18f01a tests: shared color processing functions 3 years ago
Vitaly Prosyak 6099c0e24b color-lcms: LCMS transform for color mapping 3 years ago
Vitaly Prosyak c199aade3f color-lcms: linearization of an arbitrary color profile 3 years ago
Vitaly Prosyak 37e0d54cc9 color-lcms: add matches parameters based on category 3 years ago
Vitaly Prosyak 19913366e8 color-lcms: add new fields for transform search parameter 3 years ago
Vitaly Prosyak 19f318692e color-lcms: introduce sRGB stock profile 3 years ago
Vitaly Prosyak a92fa34d1d color-lcms: add wrapper API for refcounting cmlcms_color_profile 3 years ago
Vitaly Prosyak 494ff5b23b color-lcms: introduce cmlcms_category, EOTF and INV EOTF 3 years ago
Vitaly Prosyak 93c6180c71 gl-renderer: shaders implementation of color mapping function 3 years ago
Vitaly Prosyak cda130e4b0 gl-renderer: add declaration of color mapping function 3 years ago
Vitaly Prosyak 2e2ad02d5c libweston: add definition of color mapping function 3 years ago
Leandro Ribeiro 08dbd29e33 gitlab-ci: compile Linux image with support to VGEM 3 years ago
Daniel Stone ce8ead4bf7 debug: Show client PID in debug protocol stream 3 years ago
Marek Vasut b03195a8f7 screen-share: Name the seat "screen-share" 3 years ago
Marius Vlad 0b5c75f540 backend-drm/state-propose: Missing some newlines 3 years ago
Robert Mader c83f0a1539 tests: Add test for subsurfaces mapping hierachies 3 years ago
Robert Mader 8b04534c76 libweston/compositor: Do not map subsurfaces without buffer 3 years ago
Marius Vlad 73b17da7d5 meson.build: Bump libweston major version 3 years ago
Marius Vlad 2ab726b421 weston.ini.man: Clarify what startup-animation means 3 years ago
Simon Ser 7e70f9016a clients: drop weston-info 3 years ago
Veeresh Kadasani 773bcf9097 man: Document available debug bindings. 3 years ago
Derek Foreman 83927bb0e6 launcher-logind: Remove systemd-logind support 3 years ago
Derek Foreman 66374d48f1 compositor: Remove desktop zoom 3 years ago
Robert Mader dc3b349325 tests: Add test for synced subsurfaces and buffer damage 3 years ago
Robert Mader 933290e6ea libweston/compositor: Cache buffer damage for synced subsurfaces 3 years ago
Marius Vlad f3221832c5 kiosk-shell: Favor out views on same output 3 years ago
Marius Vlad f3ad593925 kiosk-shell: Don't occlude shsurf on other outputs 3 years ago
Marius Vlad 8a1849db8a kiosk-shell: Check if app_ids have been set after initial commit 3 years ago
Manuel Stoeckl b0ed4a2e3b gl-renderer: add support for (a|x)bgr16161616 shm formats 3 years ago
Daniel Stone 30de938624 backend-drm: Add more view-to-plane failure states 3 years ago
Daniel Stone a2c5709e71 backend-drm: Pass paint node through to plane_state find 3 years ago
Daniel Stone 0ace8b66af backend-drm: Unify overlay/primary view->plane code 3 years ago
Daniel Stone 81e74ff334 backend-drm: Don't take buffer-release reference for cursor views 3 years ago
Daniel Stone 873e32137e backend-drm: Remove unnecessary check for fb 3 years ago
Daniel Stone e1114228f5 backend-drm: Remove unused enum 3 years ago
Daniel Stone 2dd3af3c22 backend-drm: Move IN_FENCE_FD check to common code 3 years ago
Daniel Stone 1b34c5cd80 backend-drm: Remove unnecessary check in prepare_scanout_view 3 years ago
Daniel Stone 6b828c7b57 backend-drm: Don't try non-fullscreen views on the primary plane 3 years ago
Daniel Stone dc0de9ee2b backend-drm: Move overlay vs. primary plane check earlier 3 years ago
Daniel Stone 5e41b44b10 backend-drm: Change cursor checks to asserts 3 years ago
Daniel Stone b3d7df5c3e backend-drm: Move plane-type-specific checks to switch statement 3 years ago
Daniel Stone 0ecd6c3d33 backend-drm: Move renderer-only vs. scanout_plane test earlier 3 years ago
Daniel Stone ae60745b61 backend-drm: Move cursors_are_broken test earlier 3 years ago
Daniel Stone e5ad3c8865 backend-drm: Remove separate zpos_plane list 3 years ago
Daniel Stone 23257c073f backend-drm: Minor comment rewording 3 years ago
Daniel Stone 26c2f9a65f backend-drm: Don't try cursor buffers for client planes 3 years ago
Daniel Stone 9c6a069435 backend-drm: Early-out for non-SHM buffers in renderer-only mode 3 years ago
Daniel Stone 6aec64b2f7 backend-drm: Early-out for too-large SHM/cursor buffers 3 years ago
Daniel Stone ca4c2865e9 backend-drm: Early-out for cursor plane format testing 3 years ago
Daniel Stone 66244856e2 backend-drm: Don't try to import SHM buffers as drm_fb 3 years ago
Daniel Stone d5ec9a1a1d backend-drm: Don't try to steal other-output special planes 3 years ago
Daniel Stone 075c4ac286 backend-drm: Don't try to use planes without GBM 3 years ago
Daniel Stone af42fc1e33 backend-drm: Assign plane_idx by plane list order 3 years ago
Daniel Stone 6609840479 backend-drm: Pre-sort plane list by zpos 3 years ago
Daniel Stone 7ca7c14553 backend-drm: Rewrite zpos-sorting list insertion 3 years ago
Simon Ser 2833c28ff1 build: re-open main for regular development 3 years ago
  1. 9
      .gitlab-ci.yml
  2. 23
      .gitlab-ci/build-deps.sh
  3. 2
      .gitlab-ci/debian-install.sh
  4. 6
      .gitlab-ci/leak-sanitizer.supp
  5. 20
      .gitlab-ci/virtme-scripts/per-test-asan.sh
  6. 17
      .gitlab-ci/virtme-scripts/run-weston-tests.sh
  7. 4
      CONTRIBUTING.md
  8. 26
      README.md
  9. 92
      clients/desktop-shell.c
  10. 2
      clients/eventdemo.c
  11. 504
      clients/gears.c
  12. 28
      clients/meson.build
  13. 374
      clients/nested-client.c
  14. 1139
      clients/nested.c
  15. 2
      clients/presentation-shm.c
  16. 10
      clients/simple-dmabuf-egl.c
  17. 114
      clients/simple-dmabuf-feedback.c
  18. 426
      clients/simple-egl.c
  19. 6
      clients/simple-touch.c
  20. 25
      clients/terminal.c
  21. 1890
      clients/weston-info.c
  22. 409
      clients/window.c
  23. 20
      clients/window.h
  24. 634
      compositor/main.c
  25. 24
      compositor/meson.build
  26. 62
      compositor/screen-share.c
  27. 18
      compositor/text-backend.c
  28. 32
      compositor/weston-private.h
  29. 3
      compositor/weston-screenshooter.c
  30. 226
      compositor/xwayland.c
  31. 737
      desktop-shell/exposay.c
  32. 2
      desktop-shell/input-panel.c
  33. 4
      desktop-shell/meson.build
  34. 1201
      desktop-shell/shell.c
  35. 75
      desktop-shell/shell.h
  36. 299
      doc/sphinx/doxygen.ini.in
  37. 1
      doc/sphinx/index.rst
  38. 2
      doc/sphinx/meson.build
  39. BIN
      doc/sphinx/toc/images/ivi-shell.png
  40. 9
      doc/sphinx/toc/images/meson.build
  41. 111
      doc/sphinx/toc/ivi-shell.rst
  42. 2
      doc/sphinx/toc/libweston/images/create_output.msc
  43. 2
      doc/sphinx/toc/meson.build
  44. 31
      doc/sphinx/toc/running-weston.rst
  45. 17
      doc/sphinx/toc/test-suite.rst
  46. 94
      fullscreen-shell/fullscreen-shell.c
  47. 3
      fullscreen-shell/meson.build
  48. 27
      include/libweston-desktop/libweston-desktop.h
  49. 30
      include/libweston/backend-drm.h
  50. 70
      include/libweston/backend-fbdev.h
  51. 13
      include/libweston/backend-rdp.h
  52. 25
      include/libweston/config-parser.h
  53. 339
      include/libweston/libweston.h
  54. 13
      include/libweston/matrix.h
  55. 1
      include/libweston/meson.build
  56. 2
      include/libweston/weston-log.h
  57. 78
      ivi-shell/README
  58. 37
      ivi-shell/hmi-controller.c
  59. 4
      ivi-shell/ivi-layout-export.h
  60. 3
      ivi-shell/ivi-layout-shell.h
  61. 27
      ivi-shell/ivi-layout.c
  62. 14
      ivi-shell/ivi-shell.c
  63. 1
      ivi-shell/meson.build
  64. 72
      kiosk-shell/kiosk-shell.c
  65. 2
      kiosk-shell/kiosk-shell.h
  66. 5
      kiosk-shell/meson.build
  67. 36
      libweston-desktop/meson.build
  68. 497
      libweston-desktop/wl-shell.c
  69. 91
      libweston/backend-drm/drm-gbm.c
  70. 180
      libweston/backend-drm/drm-internal.h
  71. 64
      libweston/backend-drm/drm-virtual.c
  72. 703
      libweston/backend-drm/drm.c
  73. 118
      libweston/backend-drm/fb.c
  74. 178
      libweston/backend-drm/kms-color.c
  75. 311
      libweston/backend-drm/kms.c
  76. 27
      libweston/backend-drm/libbacklight.c
  77. 2
      libweston/backend-drm/meson.build
  78. 72
      libweston/backend-drm/modes.c
  79. 29
      libweston/backend-drm/state-helpers.c
  80. 682
      libweston/backend-drm/state-propose.c
  81. 998
      libweston/backend-fbdev/fbdev.c
  82. 33
      libweston/backend-fbdev/meson.build
  83. 77
      libweston/backend-headless/headless.c
  84. 22
      libweston/backend-rdp/meson.build
  85. 829
      libweston/backend-rdp/rdp.c
  86. 263
      libweston/backend-rdp/rdp.h
  87. 1754
      libweston/backend-rdp/rdpclip.c
  88. 262
      libweston/backend-rdp/rdputil.c
  89. 99
      libweston/backend-wayland/wayland.c
  90. 140
      libweston/backend-x11/x11.c
  91. 24
      libweston/backend.h
  92. 4
      libweston/bindings.c
  93. 2
      libweston/clipboard.c
  94. 283
      libweston/color-lcms/color-lcms.c
  95. 101
      libweston/color-lcms/color-lcms.h
  96. 287
      libweston/color-lcms/color-profile.c
  97. 242
      libweston/color-lcms/color-transform.c
  98. 1
      libweston/color-lcms/meson.build
  99. 68
      libweston/color-noop.c
  100. 52
      libweston/color.c
  101. Some files were not shown because too many files have changed in this diff Show More

@ -43,7 +43,7 @@
variables:
FDO_UPSTREAM_REPO: wayland/weston
FDO_REPO_SUFFIX: "$BUILD_OS/$BUILD_ARCH"
FDO_DISTRIBUTION_TAG: '2021-11-25.0-dmabuf-feedback'
FDO_DISTRIBUTION_TAG: '2022-07-13.00-wayland-protocols-1.26'
include:
@ -202,7 +202,7 @@ aarch64-debian-container_prep:
- $BUILDDIR/weston-virtme
- $PREFIX
reports:
junit: $BUILDDIR/meson-logs/testlog.junit.xml
junit: $BUILDDIR/meson-logs/testlog-per-test-asan.sh.junit.xml
# Same as above, but without running any tests.
.build-no-test:
@ -283,8 +283,8 @@ aarch64-debian-container_prep:
-Dwerror=true
-Dtest-skip-is-failure=true
-Dlauncher-libseat=true
-Ddeprecated-backend-fbdev=true
-Ddeprecated-weston-launch=true
-Ddeprecated-color-management-static=true
-Ddeprecated-color-management-colord=true
after_script:
- ninja -C "$BUILDDIR" coverage-html > "$BUILDDIR/meson-logs/ninja-coverage-html.txt"
- ninja -C "$BUILDDIR" coverage-xml
@ -340,7 +340,6 @@ docs-build:
-Dpipewire=false
-Dwerror=true
-Dlauncher-libseat=true
-Ddeprecated-weston-launch=true
x86_64-debian-no-gl-build:
extends:

@ -75,7 +75,8 @@ if [[ -n "$KERNEL_DEFCONFIG" ]]; then
--enable CONFIG_DRM \
--enable CONFIG_DRM_KMS_HELPER \
--enable CONFIG_DRM_KMS_FB_HELPER \
--enable CONFIG_DRM_VKMS
--enable CONFIG_DRM_VKMS \
--enable CONFIG_DRM_VGEM
make ARCH=${LINUX_ARCH} oldconfig
make ARCH=${LINUX_ARCH}
@ -94,7 +95,7 @@ fi
# Build and install Wayland; keep this version in sync with our dependency
# in meson.build.
git clone --branch 1.18.0 --depth=1 https://gitlab.freedesktop.org/wayland/wayland
git clone --branch 1.20.0 --depth=1 https://gitlab.freedesktop.org/wayland/wayland
cd wayland
git show -s HEAD
mkdir build
@ -106,7 +107,7 @@ rm -rf wayland
# Keep this version in sync with our dependency in meson.build. If you wish to
# raise a MR against custom protocol, please change this reference to clone
# your relevant tree, and make sure you bump $FDO_DISTRIBUTION_TAG.
git clone --branch 1.24 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols
git clone --branch 1.26 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols
cd wayland-protocols
git show -s HEAD
meson build
@ -129,6 +130,17 @@ ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf mesa
# Build and install our own version of libdrm. Debian 11 (bullseye) provides
# libdrm 2.4.104 which doesn't have the IN_FORMATS iterator api. We can stop
# building and installing libdrm as soon as we move to Debian 12.
git clone --branch libdrm-2.4.108 --depth=1 https://gitlab.freedesktop.org/mesa/drm.git
cd drm
meson build -Dauto_features=disabled \
-Dvc4=false -Dfreedreno=false -Detnaviv=false
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf drm
# PipeWire is used for remoting support. Unlike our other dependencies its
# behaviour will be stable, however as a pre-1.0 project its API is not yet
# stable, so again we lock it to a fixed version.
@ -142,9 +154,8 @@ ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf pipewire-src
# seatd lets us avoid the pain of handling VTs manually through weston-launch
# or open-coding TTY assignment within Weston. We use this for our tests using
# the DRM backend.
# seatd lets us avoid the pain of open-coding TTY assignment within Weston.
# We use this for our tests using the DRM backend.
git clone --depth=1 --branch 0.6.1 https://git.sr.ht/~kennylevinsen/seatd
cd seatd
meson build -Dauto_features=disabled \

@ -39,6 +39,7 @@ apt-get -y --no-install-recommends install \
clang-11 \
curl \
doxygen \
graphviz \
freerdp2-dev \
gcovr \
git \
@ -68,6 +69,7 @@ apt-get -y --no-install-recommends install \
libmtdev-dev \
libpam0g-dev \
libpango1.0-dev \
libpciaccess-dev \
libpixman-1-dev \
libpng-dev \
libpulse-dev \

@ -3,3 +3,9 @@
# Cairo internal leaks from weston-keyboard
leak:cairo_select_font_face
leak:cairo_text_extents
# Pango thread-global state (not destroyable?)
leak:pango_language_get_default
# This leaks in Debian's fontconfig/Xwayland setup?
leak:FcConfigSubstituteWithPat

@ -0,0 +1,20 @@
#!/bin/bash
# When running Debian's Xwayland and fontconfig, we hit memory leaks which
# aren't visible on other setups. We do have suppressions for these tests, but
# regrettably ASan can't see through omitted frame pointers in Expat, so for
# Xwayland specifically, we disable fast-unwind.
#
# Doing it globally makes the other tests far, far, too slow to run.
case "$1" in
*xwayland*)
export ASAN_OPTIONS="detect_leaks=0,fast_unwind_on_malloc=0"
;;
*)
export ASAN_OPTIONS="detect_leaks=0"
;;
esac
export ASAN_OPTIONS
exec "$@"

@ -7,8 +7,15 @@ chmod -R 0700 /tmp
# set environment variables to run Weston tests
export XDG_RUNTIME_DIR=/tmp/tests
export WESTON_TEST_SUITE_DRM_DEVICE=card0
export LIBSEAT_BACKEND=seatd
# In our test suite, we use VKMS to run DRM-backend tests. The order in which
# devices are loaded is not predictable, so the DRM node that VKMS takes can
# change across each boot. That's why we have this one-liner shell script to get
# the appropriate node for VKMS.
export WESTON_TEST_SUITE_DRM_DEVICE=$(basename /sys/devices/platform/vkms/drm/card*)
# To run tests in the CI that exercise the zwp_linux_dmabuf_v1 implementation in
# Weston, we use VGEM to allocate buffers.
export WESTON_TEST_SUITE_ALLOC_DEVICE=$(basename /sys/devices/platform/vgem/drm/card*)
# ninja test depends on meson, and meson itself looks for its modules on folder
# $HOME/.local/lib/pythonX.Y/site-packages (the Python version may differ).
@ -19,13 +26,17 @@ export HOME=/root
export PATH=$HOME/.local/bin:$PATH
export PATH=/usr/local/bin:$PATH
export ASAN_OPTIONS=detect_leaks=0,atexit=1
export SEATD_LOGLEVEL=debug
# Terrible hack, per comment in weston-test-runner.c's main(): find Mesa's
# llvmpipe driver module location
export WESTON_CI_LEAK_DL_HANDLE=$(find /usr/local -name swrast_dri.so -print 2>/dev/null || true)
# run the tests and save the exit status
# we give ourselves a very generous timeout multiplier due to ASan overhead
echo 0x1f > /sys/module/drm/parameters/debug
seatd-launch -- meson test --no-rebuild --timeout-multiplier 4
seatd-launch -- meson test --no-rebuild --timeout-multiplier 4 \
--wrapper $(pwd)/../.gitlab-ci/virtme-scripts/per-test-asan.sh
# note that we need to store the return value from the tests in order to
# determine if the test suite ran successfully or not.
TEST_RES=$?

@ -208,6 +208,10 @@ my_function(void)
parameter3, parameter4);
```
- do not write fallback paths for failed simple memory allocations, use the
`x*alloc()` wrappers from `shared/xalloc.h` instead or use
`abort_oom_if_null()`
Conduct
=======

@ -18,6 +18,12 @@ bugs and shortcomings, we avoid unknown or variable behaviour as much as
possible, including variable performance such as occasional spikes in frame
display time.
Weston and libweston are not suitable for memory constrained environments
where the compositor is expected to continue running even in the face of
trivial memory allocations failing. If standard functions like `malloc()`
fail for small allocations,
[you can expect libweston to abort](https://gitlab.freedesktop.org/wayland/weston/-/issues/631).
A small suite of example or demo clients are also provided: though they can be
useful in themselves, their main purpose is to be an example or test case for
others building compositors or clients.
@ -93,13 +99,6 @@ the available configuration options and display backends. It can also be
configured through a file on disk; more information on this can be found through
`man weston.ini`.
In some special cases, such as when running remotely or without logind's session
control, Weston may not be able to run directly from a text console. In these
situations, you can instead execute the `weston-launch` helper, which will gain
privileged access to input and output devices by running as root, then granting
access to the main Weston binary running as your user. Running Weston this way
is not recommended unless necessary.
Documentation
=============
@ -294,7 +293,7 @@ Details:
- Child process execution and management will be outside of libweston.
- The different backends (drm, fbdev, x11, etc) will be an internal
- The different backends (drm, x11, etc) will be an internal
detail of libweston. Libweston will not support third party
backends. However, hosting programs need to handle
backend-specific configuration due to differences in behaviour and
@ -308,19 +307,12 @@ Details:
- xwayland ???
- weston-launch is still with libweston even though it can only launch
Weston and nothing else. We would like to allow it to launch any compositor,
but since it gives by design root access to input devices and DRM, how can
we restrict it to intended programs?
There are still many more details to be decided.
For packagers
-------------
Always build Weston with --with-cairo=image.
The Weston project is (will be) intended to be split into several
binary packages, each with its own dependencies. The maximal split
would be roughly like this:
@ -337,15 +329,13 @@ would be roughly like this:
- xwayland (depends on X11/xcb libs)
- fbdev-backend (depends on libudev...)
- rdp-backend (depends on freerdp)
- weston (the executable, not parallel-installable):
+ desktop shell
+ ivi-shell
+ fullscreen shell
+ weston-info (deprecated), weston-terminal, etc. we install by default
+ weston-terminal, etc. we install by default
+ screen-share
- weston demos (not parallel-installable)

@ -41,22 +41,23 @@
#include <assert.h>
#include <wayland-client.h>
#include "window.h"
#include "shared/cairo-util.h"
#include <libweston/config-parser.h>
#include <libweston/zalloc.h>
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include <libweston/zalloc.h>
#include "shared/cairo-util.h"
#include "shared/file-util.h"
#include "shared/process-util.h"
#include "shared/timespec-util.h"
#include "window.h"
#include "weston-desktop-shell-client-protocol.h"
#define DEFAULT_CLOCK_FORMAT CLOCK_FORMAT_MINUTES
#define DEFAULT_SPACING 10
extern char **environ; /* defined by libc */
enum clock_format {
CLOCK_FORMAT_MINUTES,
CLOCK_FORMAT_SECONDS,
@ -142,9 +143,11 @@ struct panel_launcher {
cairo_surface_t *icon;
int focused, pressed;
char *path;
char *displayname;
struct wl_list link;
struct wl_array envp;
struct wl_array argv;
struct custom_env env;
char * const *argp;
char * const *envp;
};
struct panel_clock {
@ -211,7 +214,6 @@ check_desktop_ready(struct window *window)
static void
panel_launcher_activate(struct panel_launcher *widget)
{
char **argv;
pid_t pid;
pid = fork();
@ -223,13 +225,11 @@ panel_launcher_activate(struct panel_launcher *widget)
if (pid)
return;
argv = widget->argv.data;
if (setsid() == -1)
exit(EXIT_FAILURE);
if (execve(argv[0], argv, widget->envp.data) < 0) {
fprintf(stderr, "execl '%s' failed: %s\n", argv[0],
if (execve(widget->argp[0], widget->argp, widget->envp) < 0) {
fprintf(stderr, "execl '%s' failed: %s\n", widget->argp[0],
strerror(errno));
exit(1);
}
@ -277,7 +277,7 @@ panel_launcher_motion_handler(struct widget *widget, struct input *input,
{
struct panel_launcher *launcher = data;
widget_set_tooltip(widget, basename((char *)launcher->path), x, y);
widget_set_tooltip(widget, launcher->displayname, x, y);
return CURSOR_LEFT_PTR;
}
@ -575,10 +575,10 @@ panel_configure(void *data,
static void
panel_destroy_launcher(struct panel_launcher *launcher)
{
wl_array_release(&launcher->argv);
wl_array_release(&launcher->envp);
custom_env_fini(&launcher->env);
free(launcher->path);
free(launcher->displayname);
cairo_surface_destroy(launcher->icon);
@ -678,58 +678,19 @@ load_icon_or_fallback(const char *icon)
}
static void
panel_add_launcher(struct panel *panel, const char *icon, const char *path)
panel_add_launcher(struct panel *panel, const char *icon, const char *path, const char *displayname)
{
struct panel_launcher *launcher;
char *start, *p, *eq, **ps;
int i, j, k;
launcher = xzalloc(sizeof *launcher);
launcher->icon = load_icon_or_fallback(icon);
launcher->path = xstrdup(path);
launcher->displayname = xstrdup(displayname);
wl_array_init(&launcher->envp);
wl_array_init(&launcher->argv);
for (i = 0; environ[i]; i++) {
ps = wl_array_add(&launcher->envp, sizeof *ps);
*ps = environ[i];
}
j = 0;
start = launcher->path;
while (*start) {
for (p = start, eq = NULL; *p && !isspace(*p); p++)
if (*p == '=')
eq = p;
if (eq && j == 0) {
ps = launcher->envp.data;
for (k = 0; k < i; k++)
if (strncmp(ps[k], start, eq - start) == 0) {
ps[k] = start;
break;
}
if (k == i) {
ps = wl_array_add(&launcher->envp, sizeof *ps);
*ps = start;
i++;
}
} else {
ps = wl_array_add(&launcher->argv, sizeof *ps);
*ps = start;
j++;
}
while (*p && isspace(*p))
*p++ = '\0';
start = p;
}
ps = wl_array_add(&launcher->envp, sizeof *ps);
*ps = NULL;
ps = wl_array_add(&launcher->argv, sizeof *ps);
*ps = NULL;
custom_env_init_from_environ(&launcher->env);
custom_env_add_from_exec_string(&launcher->env, launcher->path);
launcher->envp = custom_env_get_envp(&launcher->env);
launcher->argp = custom_env_get_argp(&launcher->env);
launcher->panel = panel;
wl_list_insert(panel->launcher_list.prev, &launcher->link);
@ -1447,7 +1408,7 @@ static void
panel_add_launchers(struct panel *panel, struct desktop *desktop)
{
struct weston_config_section *s;
char *icon, *path;
char *icon, *path, *displayname;
const char *name;
int count;
@ -1459,9 +1420,12 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop)
weston_config_section_get_string(s, "icon", &icon, NULL);
weston_config_section_get_string(s, "path", &path, NULL);
weston_config_section_get_string(s, "displayname", &displayname, NULL);
if (displayname == NULL)
displayname = xstrdup(basename(path));
if (icon != NULL && path != NULL) {
panel_add_launcher(panel, icon, path);
panel_add_launcher(panel, icon, path, displayname);
count++;
} else {
fprintf(stderr, "invalid launcher section\n");
@ -1469,6 +1433,7 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop)
free(icon);
free(path);
free(displayname);
}
if (count == 0) {
@ -1477,7 +1442,8 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop)
/* add default launcher */
panel_add_launcher(panel,
name,
BINDIR "/weston-terminal");
BINDIR "/weston-terminal",
"Terminal");
free(name);
}
}

@ -351,8 +351,6 @@ axis_discrete_handler(struct widget *widget, struct input *input,
* \param widget widget
* \param input input device that caused the motion event
* \param time time the event happened
* \param x absolute x position
* \param y absolute y position
* \param x x position relative to the window
* \param y y position relative to the window
* \param data user data associated to the window

@ -1,504 +0,0 @@
/*
* Copyright © 2008 Kristian Høgsberg
*
* 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.
*/
#include "config.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <errno.h>
#include <GL/gl.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "window.h"
struct gears {
struct window *window;
struct widget *widget;
struct display *d;
EGLDisplay display;
EGLDisplay config;
EGLContext context;
GLfloat angle;
struct {
GLfloat rotx;
GLfloat roty;
} view;
int button_down;
int last_x, last_y;
GLint gear_list[3];
int fullscreen;
int frames;
uint32_t last_fps;
};
struct gear_template {
GLfloat material[4];
GLfloat inner_radius;
GLfloat outer_radius;
GLfloat width;
GLint teeth;
GLfloat tooth_depth;
};
static const struct gear_template gear_templates[] = {
{ { 0.8, 0.1, 0.0, 1.0 }, 1.0, 4.0, 1.0, 20, 0.7 },
{ { 0.0, 0.8, 0.2, 1.0 }, 0.5, 2.0, 2.0, 10, 0.7 },
{ { 0.2, 0.2, 1.0, 1.0 }, 1.3, 2.0, 0.5, 10, 0.7 },
};
static GLfloat light_pos[4] = {5.0, 5.0, 10.0, 0.0};
static void die(const char *msg)
{
fprintf(stderr, "%s", msg);
exit(EXIT_FAILURE);
}
static void
make_gear(const struct gear_template *t)
{
GLint i;
GLfloat r0, r1, r2;
GLfloat angle, da;
GLfloat u, v, len;
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->material);
r0 = t->inner_radius;
r1 = t->outer_radius - t->tooth_depth / 2.0;
r2 = t->outer_radius + t->tooth_depth / 2.0;
da = 2.0 * M_PI / t->teeth / 4.0;
glShadeModel(GL_FLAT);
glNormal3f(0.0, 0.0, 1.0);
/* draw front face */
glBegin(GL_QUAD_STRIP);
for (i = 0; i <= t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
if (i < t->teeth) {
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
}
}
glEnd();
/* draw front sides of teeth */
glBegin(GL_QUADS);
da = 2.0 * M_PI / t->teeth / 4.0;
for (i = 0; i < t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
}
glEnd();
glNormal3f(0.0, 0.0, -1.0);
/* draw back face */
glBegin(GL_QUAD_STRIP);
for (i = 0; i <= t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
if (i < t->teeth) {
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
}
}
glEnd();
/* draw back sides of teeth */
glBegin(GL_QUADS);
da = 2.0 * M_PI / t->teeth / 4.0;
for (i = 0; i < t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
}
glEnd();
/* draw outward faces of teeth */
glBegin(GL_QUAD_STRIP);
for (i = 0; i < t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
u = r2 * cos(angle + da) - r1 * cos(angle);
v = r2 * sin(angle + da) - r1 * sin(angle);
len = sqrt(u * u + v * v);
u /= len;
v /= len;
glNormal3f(v, -u, 0.0);
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
glNormal3f(cos(angle), sin(angle), 0.0);
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
glNormal3f(v, -u, 0.0);
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
glNormal3f(cos(angle), sin(angle), 0.0);
}
glVertex3f(r1 * cos(0), r1 * sin(0), t->width * 0.5);
glVertex3f(r1 * cos(0), r1 * sin(0), -t->width * 0.5);
glEnd();
glShadeModel(GL_SMOOTH);
/* draw inside radius cylinder */
glBegin(GL_QUAD_STRIP);
for (i = 0; i <= t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glNormal3f(-cos(angle), -sin(angle), 0.0);
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
}
glEnd();
}
static void
update_fps(struct gears *gears, uint32_t time)
{
long diff_ms;
static bool first_call = true;
if (first_call) {
gears->last_fps = time;
first_call = false;
} else
gears->frames++;
diff_ms = time - gears->last_fps;
if (diff_ms > 5000) {
float seconds = diff_ms / 1000.0;
float fps = gears->frames / seconds;
printf("%d frames in %6.3f seconds = %6.3f FPS\n", gears->frames, seconds, fps);
fflush(stdout);
gears->frames = 0;
gears->last_fps = time;
}
}
static void
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
struct gears *gears = data;
update_fps(gears, time);
gears->angle = (GLfloat) (time % 8192) * 360 / 8192.0;
window_schedule_redraw(gears->window);
if (callback)
wl_callback_destroy(callback);
}
static const struct wl_callback_listener listener = {
frame_callback
};
static int
motion_handler(struct widget *widget, struct input *input,
uint32_t time, float x, float y, void *data)
{
struct gears *gears = data;
int offset_x, offset_y;
float step = 0.5;
if (gears->button_down) {
offset_x = x - gears->last_x;
offset_y = y - gears->last_y;
gears->last_x = x;
gears->last_y = y;
gears->view.roty += offset_x * step;
gears->view.rotx += offset_y * step;
if (gears->view.roty >= 360)
gears->view.roty = gears->view.roty - 360;
if (gears->view.roty <= 0)
gears->view.roty = gears->view.roty + 360;
if (gears->view.rotx >= 360)
gears->view.rotx = gears->view.rotx - 360;
if (gears->view.rotx <= 0)
gears->view.rotx = gears->view.rotx + 360;
}
return CURSOR_LEFT_PTR;
}
static void
button_handler(struct widget *widget, struct input *input,
uint32_t time, uint32_t button,
enum wl_pointer_button_state state, void *data)
{
struct gears *gears = data;
if (button == BTN_LEFT) {
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
gears->button_down = 1;
input_get_position(input,
&gears->last_x, &gears->last_y);
} else {
gears->button_down = 0;
}
}
}
static void
redraw_handler(struct widget *widget, void *data)
{
struct rectangle window_allocation;
struct rectangle allocation;
struct wl_callback *callback;
struct gears *gears = data;
widget_get_allocation(gears->widget, &allocation);
window_get_allocation(gears->window, &window_allocation);
if (display_acquire_window_surface(gears->d,
gears->window,
gears->context) < 0) {
die("Unable to acquire window surface, "
"compiled without cairo-egl?\n");
}
glViewport(allocation.x,
window_allocation.height - allocation.height - allocation.y,
allocation.width, allocation.height);
glScissor(allocation.x,
window_allocation.height - allocation.height - allocation.y,
allocation.width, allocation.height);
glEnable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(0.0, 0.0, -50);
glRotatef(gears->view.rotx, 1.0, 0.0, 0.0);
glRotatef(gears->view.roty, 0.0, 1.0, 0.0);
glPushMatrix();
glTranslatef(-3.0, -2.0, 0.0);
glRotatef(gears->angle, 0.0, 0.0, 1.0);
glCallList(gears->gear_list[0]);
glPopMatrix();
glPushMatrix();
glTranslatef(3.1, -2.0, 0.0);
glRotatef(-2.0 * gears->angle - 9.0, 0.0, 0.0, 1.0);
glCallList(gears->gear_list[1]);
glPopMatrix();
glPushMatrix();
glTranslatef(-3.1, 4.2, 0.0);
glRotatef(-2.0 * gears->angle - 25.0, 0.0, 0.0, 1.0);
glCallList(gears->gear_list[2]);
glPopMatrix();
glPopMatrix();
glFlush();
display_release_window_surface(gears->d, gears->window);
callback = wl_surface_frame(window_get_wl_surface(gears->window));
wl_callback_add_listener(callback, &listener, gears);
}
static void
resize_handler(struct widget *widget,
int32_t width, int32_t height, void *data)
{
struct gears *gears = data;
int32_t size, big, small;
/* Constrain child size to be square and at least 300x300 */
if (width < height) {
small = width;
big = height;
} else {
small = height;
big = width;
}
if (gears->fullscreen)
size = small;
else
size = big;
widget_set_size(gears->widget, size, size);
}
static void
keyboard_focus_handler(struct window *window,
struct input *device, void *data)
{
window_schedule_redraw(window);
}
static void
fullscreen_handler(struct window *window, void *data)
{
struct gears *gears = data;
gears->fullscreen ^= 1;
window_set_fullscreen(window, gears->fullscreen);
}
static struct gears *
gears_create(struct display *display)
{
const int width = 450, height = 500;
struct gears *gears;
int i;
gears = zalloc(sizeof *gears);
gears->d = display;
gears->window = window_create(display);
gears->widget = window_frame_create(gears->window, gears);
window_set_title(gears->window, "Wayland Gears");
window_set_appid(gears->window, "org.freedesktop.weston.wayland-gears");
gears->display = display_get_egl_display(gears->d);
if (gears->display == NULL)
die("failed to create egl display\n");
eglBindAPI(EGL_OPENGL_API);
gears->config = display_get_argb_egl_config(gears->d);
gears->context = eglCreateContext(gears->display, gears->config,
EGL_NO_CONTEXT, NULL);
if (gears->context == NULL)
die("failed to create context\n");
if (!eglMakeCurrent(gears->display, NULL, NULL, gears->context))
die("failed to make context current\n");
for (i = 0; i < 3; i++) {
gears->gear_list[i] = glGenLists(1);
glNewList(gears->gear_list[i], GL_COMPILE);
make_gear(&gear_templates[i]);
glEndList();
}
gears->button_down = 0;
gears->last_x = 0;
gears->last_y = 0;
gears->view.rotx = 20.0;
gears->view.roty = 30.0;
printf("Warning: FPS count is limited by the wayland compositor or monitor refresh rate\n");
glEnable(GL_NORMALIZE);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 200.0);
glMatrixMode(GL_MODELVIEW);
glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glClearColor(0, 0, 0, 0.92);
window_set_user_data(gears->window, gears);
widget_set_resize_handler(gears->widget, resize_handler);
widget_set_redraw_handler(gears->widget, redraw_handler);
widget_set_button_handler(gears->widget, button_handler);
widget_set_motion_handler(gears->widget, motion_handler);
window_set_keyboard_focus_handler(gears->window,
keyboard_focus_handler);
window_set_fullscreen_handler(gears->window, fullscreen_handler);
window_schedule_resize(gears->window, width, height);
return gears;
}
static void
gears_destroy(struct gears *gears)
{
widget_destroy(gears->widget);
window_destroy(gears->window);
free(gears);
}
int main(int argc, char *argv[])
{
struct display *d;
struct gears *gears;
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
return -1;
}
gears = gears_create(d);
display_run(d);
gears_destroy(gears);
display_destroy(d);
return 0;
}

@ -35,6 +35,10 @@ dep_toytoolkit = declare_dependency(
link_with: lib_toytoolkit,
dependencies: deps_toytoolkit,
)
dep_gbm = dependency('gbm', required: false)
if dep_gbm.found() and dep_gbm.version().version_compare('>= 21.3')
config_h.set('HAVE_GBM_BO_CREATE_WITH_MODIFIERS2', '1')
endif
simple_clients_enabled = get_option('simple-clients')
simple_build_all = simple_clients_enabled.contains('all')
@ -60,6 +64,8 @@ simple_clients = [
'../libweston/pixel-formats.c',
linux_dmabuf_unstable_v1_client_protocol_h,
linux_dmabuf_unstable_v1_protocol_c,
presentation_time_client_protocol_h,
presentation_time_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
],
@ -125,7 +131,12 @@ simple_clients = [
ivi_application_client_protocol_h,
ivi_application_protocol_c,
],
'dep_objs': [ dep_wayland_client, dep_libshared, dep_libm ],
'dep_objs': [
dep_libm,
dep_libshared,
dep_matrix_c,
dep_wayland_client,
],
'deps': [ 'egl', 'wayland-egl', 'glesv2', 'wayland-cursor' ],
'options': [ 'renderer-gl' ]
},
@ -217,21 +228,6 @@ tools_list = [
],
'deps': [ dep_wayland_client ]
},
{
'name': 'info',
'sources': [
'weston-info.c',
presentation_time_client_protocol_h,
presentation_time_protocol_c,
linux_dmabuf_unstable_v1_client_protocol_h,
linux_dmabuf_unstable_v1_protocol_c,
tablet_unstable_v2_client_protocol_h,
tablet_unstable_v2_protocol_c,
xdg_output_unstable_v1_client_protocol_h,
xdg_output_unstable_v1_protocol_c,
],
'deps': [ dep_wayland_client, dep_libshared ]
},
{
'name': 'terminal',
'sources': [ 'terminal.c' ],

@ -1,374 +0,0 @@
/*
* Copyright © 2013 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (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.
*/
#include "config.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-egl.h>
#include <wayland-cursor.h>
#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include "shared/platform.h"
struct window;
struct seat;
struct nested_client {
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
EGLDisplay egl_display;
EGLContext egl_context;
EGLConfig egl_config;
EGLSurface egl_surface;
struct program *color_program;
GLuint vert, frag, program;
GLuint rotation;
GLuint pos;
GLuint col;
struct wl_surface *surface;
struct wl_egl_window *native;
int width, height;
};
#define POS 0
#define COL 1
static GLuint
create_shader(const char *source, GLenum shader_type)
{
GLuint shader;
GLint status;
shader = glCreateShader(shader_type);
if (shader == 0)
return 0;
glShaderSource(shader, 1, (const char **) &source, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (!status) {
char log[1000];
GLsizei len;
glGetShaderInfoLog(shader, 1000, &len, log);
fprintf(stderr, "Error: compiling %s: %.*s\n",
shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
len, log);
return 0;
}
return shader;
}
static void
create_program(struct nested_client *client,
const char *vert, const char *frag)
{
GLint status;
client->vert = create_shader(vert, GL_VERTEX_SHADER);
client->frag = create_shader(frag, GL_FRAGMENT_SHADER);
client->program = glCreateProgram();
glAttachShader(client->program, client->frag);
glAttachShader(client->program, client->vert);
glBindAttribLocation(client->program, POS, "pos");
glBindAttribLocation(client->program, COL, "color");
glLinkProgram(client->program);
glGetProgramiv(client->program, GL_LINK_STATUS, &status);
if (!status) {
char log[1000];
GLsizei len;
glGetProgramInfoLog(client->program, 1000, &len, log);
fprintf(stderr, "Error: linking:\n%.*s\n", len, log);
exit(1);
}
client->rotation =
glGetUniformLocation(client->program, "rotation");
}
static const char vertex_shader_text[] =
"uniform mat4 rotation;\n"
"attribute vec4 pos;\n"
"attribute vec4 color;\n"
"varying vec4 v_color;\n"
"void main() {\n"
" gl_Position = rotation * pos;\n"
" v_color = color;\n"
"}\n";
static const char color_fragment_shader_text[] =
"precision mediump float;\n"
"varying vec4 v_color;\n"
"void main() {\n"
" gl_FragColor = v_color;\n"
"}\n";
static void
render_triangle(struct nested_client *client, uint32_t time)
{
static const GLfloat verts[3][2] = {
{ -0.5, -0.5 },
{ 0.5, -0.5 },
{ 0, 0.5 }
};
static const GLfloat colors[3][3] = {
{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 }
};
GLfloat angle;
GLfloat rotation[4][4] = {
{ 1, 0, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 0, 1 }
};
static const int32_t speed_div = 5;
static uint32_t start_time = 0;
if (client->program == 0)
create_program(client, vertex_shader_text,
color_fragment_shader_text);
if (start_time == 0)
start_time = time;
angle = ((time - start_time) / speed_div) % 360 * M_PI / 180.0;
rotation[0][0] = cos(angle);
rotation[0][2] = sin(angle);
rotation[2][0] = -sin(angle);
rotation[2][2] = cos(angle);
glClearColor(0.4, 0.4, 0.4, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(client->program);
glViewport(0, 0, client->width, client->height);
glUniformMatrix4fv(client->rotation, 1, GL_FALSE,
(GLfloat *) rotation);
glVertexAttribPointer(POS, 2, GL_FLOAT, GL_FALSE, 0, verts);
glVertexAttribPointer(COL, 3, GL_FLOAT, GL_FALSE, 0, colors);
glEnableVertexAttribArray(POS);
glEnableVertexAttribArray(COL);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(POS);
glDisableVertexAttribArray(COL);
glFlush();
}
static void
frame_callback(void *data, struct wl_callback *callback, uint32_t time);
static const struct wl_callback_listener frame_listener = {
frame_callback
};
static void
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
struct nested_client *client = data;
if (callback)
wl_callback_destroy(callback);
callback = wl_surface_frame(client->surface);
wl_callback_add_listener(callback, &frame_listener, client);
render_triangle(client, time);
eglSwapBuffers(client->egl_display, client->egl_surface);
}
static void
registry_handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
struct nested_client *client = data;
if (strcmp(interface, "wl_compositor") == 0) {
client->compositor =
wl_registry_bind(registry, name,
&wl_compositor_interface, 1);
}
}
static void
registry_handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove
};
static struct nested_client *
nested_client_create(void)
{
static const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
static const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 1,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
EGLint major, minor, n;
EGLBoolean ret;
struct nested_client *client;
client = malloc(sizeof *client);
if (client == NULL)
return NULL;
client->width = 250;
client->height = 250;
client->display = wl_display_connect(NULL);
client->registry = wl_display_get_registry(client->display);
wl_registry_add_listener(client->registry,
&registry_listener, client);
/* get globals */
wl_display_roundtrip(client->display);
client->egl_display =
weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
client->display, NULL);
if (client->egl_display == NULL)
return NULL;
ret = eglInitialize(client->egl_display, &major, &minor);
if (!ret)
return NULL;
ret = eglBindAPI(EGL_OPENGL_ES_API);
if (!ret)
return NULL;
ret = eglChooseConfig(client->egl_display, config_attribs,
&client->egl_config, 1, &n);
if (!ret || n != 1)
return NULL;
client->egl_context = eglCreateContext(client->egl_display,
client->egl_config,
EGL_NO_CONTEXT,
context_attribs);
if (!client->egl_context)
return NULL;
client->surface = wl_compositor_create_surface(client->compositor);
client->native = wl_egl_window_create(client->surface,
client->width, client->height);
client->egl_surface = weston_platform_create_egl_surface(client->egl_display,
client->egl_config,
client->native, NULL);
eglMakeCurrent(client->egl_display, client->egl_surface,
client->egl_surface, client->egl_context);
wl_egl_window_resize(client->native,
client->width, client->height, 0, 0);
frame_callback(client, NULL, 0);
return client;
}
static void
nested_client_destroy(struct nested_client *client)
{
eglMakeCurrent(client->egl_display,
EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
weston_platform_destroy_egl_surface(client->egl_display,
client->egl_surface);
wl_egl_window_destroy(client->native);
wl_surface_destroy(client->surface);
if (client->compositor)
wl_compositor_destroy(client->compositor);
wl_registry_destroy(client->registry);
eglTerminate(client->egl_display);
eglReleaseThread();
wl_display_flush(client->display);
wl_display_disconnect(client->display);
}
int
main(int argc, char **argv)
{
struct nested_client *client;
int ret = 0;
if (getenv("WAYLAND_SOCKET") == NULL) {
fprintf(stderr,
"must be run by nested, don't run standalone\n");
return EXIT_FAILURE;
}
client = nested_client_create();
if (client == NULL)
return EXIT_FAILURE;
while (ret != -1)
ret = wl_display_dispatch(client->display);
nested_client_destroy(client);
return 0;
}

File diff suppressed because it is too large Load Diff

@ -765,7 +765,7 @@ registry_handle_global(void *data, struct wl_registry *registry,
} else if (strcmp(interface, "xdg_wm_base") == 0) {
d->wm_base =
wl_registry_bind(registry, name,
&xdg_wm_base_interface, version);
&xdg_wm_base_interface, 1);
} else if (strcmp(interface, "wl_shm") == 0) {
d->shm = wl_registry_bind(registry,
name, &wl_shm_interface, 1);

@ -343,12 +343,22 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer,
#ifdef HAVE_GBM_MODIFIERS
if (display->modifiers_count > 0) {
#ifdef HAVE_GBM_BO_CREATE_WITH_MODIFIERS2
buffer->bo = gbm_bo_create_with_modifiers2(display->gbm.device,
buffer->width,
buffer->height,
buffer->format,
display->modifiers,
display->modifiers_count,
GBM_BO_USE_RENDERING);
#else
buffer->bo = gbm_bo_create_with_modifiers(display->gbm.device,
buffer->width,
buffer->height,
buffer->format,
display->modifiers,
display->modifiers_count);
#endif
if (buffer->bo)
buffer->modifier = gbm_bo_get_modifier(buffer->bo);
}

@ -41,6 +41,7 @@
#include <libweston/pixel-formats.h>
#include "xdg-shell-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
#include <xf86drm.h>
#include <gbm.h>
@ -49,6 +50,11 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#define L_LINE "│ "
#define L_VAL "├───"
#define L_LAST "└───"
#define L_GAP " "
#define NUM_BUFFERS 4
/* We have to hack the DRM-backend to pretend that planes of the underlying
@ -135,6 +141,7 @@ struct display {
struct output output;
struct xdg_wm_base *wm_base;
struct zwp_linux_dmabuf_v1 *dmabuf;
struct wp_presentation *presentation;
struct gbm_device *gbm_device;
struct egl egl;
};
@ -166,12 +173,14 @@ struct window {
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct wl_callback *callback;
struct wp_presentation_feedback *presentation_feedback;
bool wait_for_configure;
uint32_t n_redraws;
bool presented_zero_copy;
struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback_obj;
struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback;
int card_fd;
struct drm_format format;
uint32_t bo_flags;
struct buffer buffers[NUM_BUFFERS];
};
@ -453,7 +462,7 @@ buffer_free(struct buffer *buf)
static void
create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width,
uint32_t height, uint32_t format, unsigned int count_modifiers,
uint64_t *modifiers);
uint64_t *modifiers, uint32_t bo_flags);
static void
buffer_recreate(struct buffer *buf)
@ -466,7 +475,7 @@ buffer_recreate(struct buffer *buf)
create_dmabuf_buffer(window, buf, width, height,
window->format.format,
window->format.modifiers.size / sizeof(uint64_t),
window->format.modifiers.data);
window->format.modifiers.data, window->bo_flags);
buf->recreate = false;
}
@ -516,7 +525,7 @@ static const struct zwp_linux_buffer_params_v1_listener params_listener = {
static void
create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width,
uint32_t height, uint32_t format, unsigned int count_modifiers,
uint64_t *modifiers)
uint64_t *modifiers, uint32_t bo_flags)
{
struct display *display = window->display;
static uint32_t flags = 0;
@ -531,10 +540,18 @@ create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width,
#ifdef HAVE_GBM_MODIFIERS
if (count_modifiers > 0) {
#ifdef HAVE_GBM_BO_CREATE_WITH_MODIFIERS2
buf->bo = gbm_bo_create_with_modifiers2(display->gbm_device,
buf->width, buf->height,
format, modifiers,
count_modifiers,
bo_flags);
#else
buf->bo = gbm_bo_create_with_modifiers(display->gbm_device,
buf->width, buf->height,
format, modifiers,
count_modifiers);
#endif
if (buf->bo)
buf->modifier = gbm_bo_get_modifier(buf->bo);
}
@ -543,7 +560,7 @@ create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width,
if (!buf->bo) {
buf->bo = gbm_bo_create(display->gbm_device, buf->width,
buf->height, buf->format,
GBM_BO_USE_RENDERING);
bo_flags);
buf->modifier = DRM_FORMAT_MOD_INVALID;
}
@ -639,6 +656,7 @@ render(struct buffer *buffer)
}
static const struct wl_callback_listener frame_listener;
static const struct wp_presentation_feedback_listener presentation_feedback_listener;
static void
redraw(void *data, struct wl_callback *callback, uint32_t time)
@ -660,6 +678,18 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
window->callback = wl_surface_frame(window->surface);
wl_callback_add_listener(window->callback, &frame_listener, window);
if (window->presentation_feedback)
wp_presentation_feedback_destroy(window->presentation_feedback);
if (window->display->presentation) {
window->presentation_feedback =
wp_presentation_feedback(window->display->presentation,
window->surface);
wp_presentation_feedback_add_listener(window->presentation_feedback,
&presentation_feedback_listener,
window);
}
wl_surface_commit(window->surface);
buf->status = IN_USE;
@ -674,6 +704,48 @@ static const struct wl_callback_listener frame_listener = {
redraw
};
static void presentation_feedback_handle_sync_output(void *data,
struct wp_presentation_feedback *feedback,
struct wl_output *output)
{
}
static void presentation_feedback_handle_presented(void *data,
struct wp_presentation_feedback *feedback,
uint32_t tv_sec_hi,
uint32_t tv_sec_lo,
uint32_t tv_nsec,
uint32_t refresh,
uint32_t seq_hi,
uint32_t seq_lo,
uint32_t flags)
{
struct window *window = data;
bool zero_copy = flags & WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
if (zero_copy && !window->presented_zero_copy) {
fprintf(stderr, "Presenting in zero-copy mode\n");
}
if (!zero_copy && window->presented_zero_copy) {
fprintf(stderr, "Stopped presenting in zero-copy mode\n");
}
window->presented_zero_copy = zero_copy;
wp_presentation_feedback_destroy(feedback);
}
static void presentation_feedback_handle_discarded(void *data,
struct wp_presentation_feedback *feedback)
{
wp_presentation_feedback_destroy(feedback);
}
static const struct wp_presentation_feedback_listener presentation_feedback_listener = {
.sync_output = presentation_feedback_handle_sync_output,
.presented = presentation_feedback_handle_presented,
.discarded = presentation_feedback_handle_discarded,
};
static void
xdg_surface_handle_configure(void *data, struct xdg_surface *surface,
uint32_t serial)
@ -810,6 +882,8 @@ destroy_window(struct window *window)
if (window->callback)
wl_callback_destroy(window->callback);
if (window->presentation_feedback)
wp_presentation_feedback_destroy(window->presentation_feedback);
for (i = 0; i < NUM_BUFFERS; i++)
if (window->buffers[i].buffer)
@ -873,7 +947,7 @@ create_window(struct display *display)
create_dmabuf_buffer(window, &window->buffers[i], width, height,
window->format.format,
window->format.modifiers.size / sizeof(uint64_t),
window->format.modifiers.data);
window->format.modifiers.data, window->bo_flags);
window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base,
@ -1000,8 +1074,7 @@ dmabuf_feedback_main_device(void *data,
drm_node = get_drm_node(feedback->main_device, false);
assert(drm_node && "error: failed to retrieve drm node");
fprintf(stderr, "compositor sent main_device event for dma-buf feedback - %s\n",
drm_node);
fprintf(stderr, "feedback: main device %s\n", drm_node);
if (!window->card_fd) {
window->card_fd = open(drm_node, O_RDWR | O_CLOEXEC);
@ -1110,12 +1183,12 @@ print_tranche_format_modifier(uint32_t format, uint64_t modifier)
char fourcc_str[5];
fourcc2str(format, fourcc_str, sizeof(fourcc_str));
len = asprintf(&format_str, "0x%08x (%s)", format, fourcc_str);
len = asprintf(&format_str, "%s (0x%08x)", fourcc_str, format);
}
assert(len > 0);
fprintf(stderr, "│ ├────────tranche format/modifier pair - format %s, modifier %s\n",
format_str, mod_name);
fprintf(stderr, L_LINE L_VAL " format %s, modifier %s\n",
format_str, mod_name);
free(format_str);
free(mod_name);
@ -1131,14 +1204,14 @@ print_dmabuf_feedback_tranche(struct dmabuf_feedback_tranche *tranche)
drm_node = get_drm_node(tranche->target_device, tranche->is_scanout_tranche);
assert(drm_node && "error: could not retrieve drm node");
fprintf(stderr, "├──────target_device for tranche - %s\n", drm_node);
fprintf(stderr, "│ └scanout tranche? %s\n", tranche->is_scanout_tranche ? "yes" : "no");
fprintf(stderr, L_VAL " tranche: target device %s, %s\n",
drm_node, tranche->is_scanout_tranche ? "scanout" : "no flags");
wl_array_for_each(fmt, &tranche->formats.arr)
wl_array_for_each(mod, &fmt->modifiers)
print_tranche_format_modifier(fmt->format, *mod);
fprintf(stderr, "│ └end of tranche\n");
fprintf(stderr, L_LINE L_LAST " end of tranche\n");
}
static void
@ -1173,6 +1246,8 @@ pick_initial_format_from_renderer_tranche(struct window *window,
window->format.format = fmt->format;
wl_array_copy(&window->format.modifiers, &fmt->modifiers);
window->bo_flags = GBM_BO_USE_RENDERING;
return true;
}
return false;
@ -1203,6 +1278,8 @@ pick_format_from_scanout_tranche(struct window *window,
window->format.format = fmt->format;
wl_array_copy(&window->format.modifiers, &fmt->modifiers);
window->bo_flags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
return true;
}
return false;
@ -1216,7 +1293,7 @@ dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_fee
bool got_scanout_tranche = false;
unsigned int i;
fprintf(stderr, "end of dma-buf feedback\n\n");
fprintf(stderr, L_LAST " end of dma-buf feedback\n\n");
/* The first time that we receive dma-buf feedback for a surface it
* contains only the renderer tranches. We pick the INITIAL_BUFFER_FORMAT
@ -1344,6 +1421,10 @@ registry_handle_global(void *data, struct wl_registry *registry,
d->dmabuf = wl_registry_bind(registry, id,
&zwp_linux_dmabuf_v1_interface,
MIN(version, 4));
} else if (strcmp(interface, "wp_presentation") == 0) {
d->presentation = wl_registry_bind(registry, id,
&wp_presentation_interface,
1);
}
}
@ -1368,6 +1449,9 @@ destroy_display(struct display *display)
if (display->egl.display != EGL_NO_DISPLAY)
eglTerminate(display->egl.display);
if (display->presentation)
wp_presentation_destroy(display->presentation);
zwp_linux_dmabuf_v1_destroy(display->dmabuf);
xdg_wm_base_destroy(display->wm_base);

@ -46,9 +46,11 @@
#include <sys/types.h>
#include <unistd.h>
#include <libweston/matrix.h>
#include "shared/helpers.h"
#include "shared/platform.h"
#include "shared/weston-egl-ext.h"
#include "shared/xalloc.h"
struct window;
struct seat;
@ -73,6 +75,8 @@ struct display {
} egl;
struct window *window;
struct wl_list output_list; /* struct output::link */
PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
};
@ -82,22 +86,45 @@ struct geometry {
struct window {
struct display *display;
struct geometry geometry, window_size;
struct geometry window_size;
struct geometry logical_size;
struct geometry buffer_size;
int32_t buffer_scale;
enum wl_output_transform buffer_transform;
bool needs_buffer_geometry_update;
struct {
GLuint rotation_uniform;
GLuint pos;
GLuint col;
} gl;
uint32_t benchmark_time, frames;
uint32_t frames;
uint32_t initial_frame_time;
uint32_t benchmark_time;
struct wl_egl_window *native;
struct wl_surface *surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
EGLSurface egl_surface;
struct wl_callback *callback;
int fullscreen, maximized, opaque, buffer_size, frame_sync, delay;
int fullscreen, maximized, opaque, buffer_bpp, frame_sync, delay;
bool wait_for_configure;
struct wl_list window_output_list; /* struct window_output::link */
};
struct output {
struct display *display;
struct wl_output *wl_output;
uint32_t name;
struct wl_list link; /* struct display::output_list */
enum wl_output_transform transform;
int32_t scale;
};
struct window_output {
struct output *output;
struct wl_list link; /* struct window::window_output_list */
};
static const char *vert_shader_text =
@ -155,7 +182,7 @@ init_egl(struct display *display, struct window *window)
EGLConfig *configs;
EGLBoolean ret;
if (window->opaque || window->buffer_size == 16)
if (window->opaque || window->buffer_bpp == 16)
config_attribs[9] = 0;
display->egl.dpy =
@ -179,13 +206,13 @@ init_egl(struct display *display, struct window *window)
assert(ret && n >= 1);
for (i = 0; i < n; i++) {
EGLint buffer_size, red_size;
EGLint buffer_bpp, red_size;
eglGetConfigAttrib(display->egl.dpy,
configs[i], EGL_BUFFER_SIZE, &buffer_size);
configs[i], EGL_BUFFER_SIZE, &buffer_bpp);
eglGetConfigAttrib(display->egl.dpy,
configs[i], EGL_RED_SIZE, &red_size);
if ((window->buffer_size == 0 ||
window->buffer_size == buffer_size) && red_size < 10) {
if ((window->buffer_bpp == 0 ||
window->buffer_bpp == buffer_bpp) && red_size < 10) {
display->egl.conf = configs[i];
break;
}
@ -193,7 +220,7 @@ init_egl(struct display *display, struct window *window)
free(configs);
if (display->egl.conf == NULL) {
fprintf(stderr, "did not find config with buffer size %d\n",
window->buffer_size);
window->buffer_bpp);
exit(EXIT_FAILURE);
}
@ -256,6 +283,92 @@ create_shader(struct window *window, const char *source, GLenum shader_type)
return shader;
}
static int32_t
compute_buffer_scale(struct window *window)
{
struct window_output *window_output;
int32_t scale = 1;
wl_list_for_each(window_output, &window->window_output_list, link) {
if (window_output->output->scale > scale)
scale = window_output->output->scale;
}
return scale;
}
static enum wl_output_transform
compute_buffer_transform(struct window *window)
{
struct window_output *window_output;
enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
wl_list_for_each(window_output, &window->window_output_list, link) {
/* If the surface spans over multiple outputs the optimal
* transform value can be ambiguous. Thus just return the value
* from the oldest entered output.
*/
transform = window_output->output->transform;
break;
}
return transform;
}
static void
update_buffer_geometry(struct window *window)
{
enum wl_output_transform new_buffer_transform;
int32_t new_buffer_scale;
struct geometry new_buffer_size;
new_buffer_transform = compute_buffer_transform(window);
if (window->buffer_transform != new_buffer_transform) {
window->buffer_transform = new_buffer_transform;
wl_surface_set_buffer_transform(window->surface,
window->buffer_transform);
}
new_buffer_scale = compute_buffer_scale(window);
if (window->buffer_scale != new_buffer_scale) {
window->buffer_scale = new_buffer_scale;
wl_surface_set_buffer_scale(window->surface,
window->buffer_scale);
}
switch (window->buffer_transform) {
case WL_OUTPUT_TRANSFORM_NORMAL:
case WL_OUTPUT_TRANSFORM_180:
case WL_OUTPUT_TRANSFORM_FLIPPED:
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
new_buffer_size.width = window->logical_size.width;
new_buffer_size.height = window->logical_size.height;
break;
case WL_OUTPUT_TRANSFORM_90:
case WL_OUTPUT_TRANSFORM_270:
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
new_buffer_size.width = window->logical_size.height;
new_buffer_size.height = window->logical_size.width;
break;
}
new_buffer_size.width *= window->buffer_scale;
new_buffer_size.height *= window->buffer_scale;
if (window->buffer_size.width != new_buffer_size.width ||
window->buffer_size.height != new_buffer_size.height) {
window->buffer_size = new_buffer_size;
if (window->native)
wl_egl_window_resize(window->native,
window->buffer_size.width,
window->buffer_size.height, 0, 0);
}
window->needs_buffer_geometry_update = false;
}
static void
init_gl(struct window *window)
{
@ -264,9 +377,12 @@ init_gl(struct window *window)
GLint status;
EGLBoolean ret;
if (window->needs_buffer_geometry_update)
update_buffer_geometry(window);
window->native = wl_egl_window_create(window->surface,
window->geometry.width,
window->geometry.height);
window->buffer_size.width,
window->buffer_size.height);
window->egl_surface =
weston_platform_create_egl_surface(window->display->egl.dpy,
window->display->egl.conf,
@ -276,6 +392,9 @@ init_gl(struct window *window)
window->egl_surface, window->display->egl.ctx);
assert(ret == EGL_TRUE);
if (!window->frame_sync)
eglSwapInterval(window->display->egl.dpy, 0);
frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER);
vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER);
@ -348,16 +467,13 @@ handle_toplevel_configure(void *data, struct xdg_toplevel *toplevel,
window->window_size.width = width;
window->window_size.height = height;
}
window->geometry.width = width;
window->geometry.height = height;
window->logical_size.width = width;
window->logical_size.height = height;
} else if (!window->fullscreen && !window->maximized) {
window->geometry = window->window_size;
window->logical_size = window->window_size;
}
if (window->native)
wl_egl_window_resize(window->native,
window->geometry.width,
window->geometry.height, 0, 0);
window->needs_buffer_geometry_update = true;
}
static void
@ -371,12 +487,80 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = {
handle_toplevel_close,
};
static void
add_window_output(struct window *window, struct wl_output *wl_output)
{
struct output *output;
struct output *output_found = NULL;
struct window_output *window_output;
wl_list_for_each(output, &window->display->output_list, link) {
if (output->wl_output == wl_output) {
output_found = output;
break;
}
}
if (!output_found)
return;
window_output = xmalloc(sizeof *window_output);
window_output->output = output_found;
wl_list_insert(window->window_output_list.prev, &window_output->link);
window->needs_buffer_geometry_update = true;
}
static void
destroy_window_output(struct window *window, struct wl_output *wl_output)
{
struct window_output *window_output;
struct window_output *window_output_found = NULL;
wl_list_for_each(window_output, &window->window_output_list, link) {
if (window_output->output->wl_output == wl_output) {
window_output_found = window_output;
break;
}
}
if (window_output_found) {
wl_list_remove(&window_output_found->link);
free(window_output_found);
window->needs_buffer_geometry_update = true;
}
}
static void
surface_enter(void *data,
struct wl_surface *wl_surface, struct wl_output *wl_output)
{
struct window *window = data;
add_window_output(window, wl_output);
}
static void
surface_leave(void *data,
struct wl_surface *wl_surface, struct wl_output *wl_output)
{
struct window *window = data;
destroy_window_output(window, wl_output);
}
static const struct wl_surface_listener surface_listener = {
surface_enter,
surface_leave
};
static void
create_surface(struct window *window)
{
struct display *display = window->display;
window->surface = wl_compositor_create_surface(display->compositor);
wl_surface_add_listener(window->surface, &surface_listener, window);
window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base,
window->surface);
@ -399,9 +583,6 @@ create_surface(struct window *window)
window->wait_for_configure = true;
wl_surface_commit(window->surface);
if (!window->frame_sync)
eglSwapInterval(display->egl.dpy, 0);
}
static void
@ -421,15 +602,12 @@ destroy_surface(struct window *window)
if (window->xdg_surface)
xdg_surface_destroy(window->xdg_surface);
wl_surface_destroy(window->surface);
if (window->callback)
wl_callback_destroy(window->callback);
}
static void
redraw(void *data, struct wl_callback *callback, uint32_t time)
redraw(struct window *window)
{
struct window *window = data;
struct display *display = window->display;
static const GLfloat verts[3][2] = {
{ -0.5, -0.5 },
@ -442,28 +620,22 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
{ 0, 0, 1 }
};
GLfloat angle;
GLfloat rotation[4][4] = {
{ 1, 0, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 0, 1 }
};
struct weston_matrix rotation;
static const uint32_t speed_div = 5, benchmark_interval = 5;
struct wl_region *region;
EGLint rect[4];
EGLint buffer_age = 0;
struct timeval tv;
assert(window->callback == callback);
window->callback = NULL;
if (callback)
wl_callback_destroy(callback);
if (window->needs_buffer_geometry_update)
update_buffer_geometry(window);
gettimeofday(&tv, NULL);
time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
if (window->frames == 0)
uint32_t time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
if (window->frames == 0) {
window->initial_frame_time = time;
window->benchmark_time = time;
}
if (time - window->benchmark_time > (benchmark_interval * 1000)) {
printf("%d frames in %d seconds: %f fps\n",
window->frames,
@ -473,20 +645,51 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
window->frames = 0;
}
angle = (time / speed_div) % 360 * M_PI / 180.0;
rotation[0][0] = cos(angle);
rotation[0][2] = sin(angle);
rotation[2][0] = -sin(angle);
rotation[2][2] = cos(angle);
weston_matrix_init(&rotation);
angle = ((time - window->initial_frame_time) / speed_div) % 360 * M_PI / 180.0;
rotation.d[0] = cos(angle);
rotation.d[2] = sin(angle);
rotation.d[8] = -sin(angle);
rotation.d[10] = cos(angle);
switch (window->buffer_transform) {
case WL_OUTPUT_TRANSFORM_FLIPPED:
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
weston_matrix_scale(&rotation, -1, 1, 1);
break;
default:
break;
}
switch (window->buffer_transform) {
default:
case WL_OUTPUT_TRANSFORM_NORMAL:
case WL_OUTPUT_TRANSFORM_FLIPPED:
break;
case WL_OUTPUT_TRANSFORM_90:
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
weston_matrix_rotate_xy(&rotation, 0, 1);
break;
case WL_OUTPUT_TRANSFORM_180:
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
weston_matrix_rotate_xy(&rotation, -1, 0);
break;
case WL_OUTPUT_TRANSFORM_270:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
weston_matrix_rotate_xy(&rotation, 0, -1);
break;
}
if (display->swap_buffers_with_damage)
eglQuerySurface(display->egl.dpy, window->egl_surface,
EGL_BUFFER_AGE_EXT, &buffer_age);
glViewport(0, 0, window->geometry.width, window->geometry.height);
glViewport(0, 0, window->buffer_size.width, window->buffer_size.height);
glUniformMatrix4fv(window->gl.rotation_uniform, 1, GL_FALSE,
(GLfloat *) rotation);
(GLfloat *) rotation.d);
if (window->opaque || window->fullscreen)
glClearColor(0.0, 0.0, 0.0, 1);
@ -508,9 +711,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
if (window->opaque || window->fullscreen) {
region = wl_compositor_create_region(window->display->compositor);
wl_region_add(region, 0, 0,
window->geometry.width,
window->geometry.height);
wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_set_opaque_region(window->surface, region);
wl_region_destroy(region);
} else {
@ -518,10 +719,10 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
}
if (display->swap_buffers_with_damage && buffer_age > 0) {
rect[0] = window->geometry.width / 4 - 1;
rect[1] = window->geometry.height / 4 - 1;
rect[2] = window->geometry.width / 2 + 2;
rect[3] = window->geometry.height / 2 + 2;
rect[0] = window->buffer_size.width / 4 - 1;
rect[1] = window->buffer_size.height / 4 - 1;
rect[2] = window->buffer_size.width / 2 + 2;
rect[3] = window->buffer_size.height / 2 + 2;
display->swap_buffers_with_damage(display->egl.dpy,
window->egl_surface,
rect, 1);
@ -745,6 +946,92 @@ static const struct xdg_wm_base_listener wm_base_listener = {
xdg_wm_base_ping,
};
static void
display_handle_geometry(void *data,
struct wl_output *wl_output,
int32_t x, int32_t y,
int32_t physical_width,
int32_t physical_height,
int32_t subpixel,
const char *make,
const char *model,
int32_t transform)
{
struct output *output = data;
output->transform = transform;
output->display->window->needs_buffer_geometry_update = true;
}
static void
display_handle_mode(void *data,
struct wl_output *wl_output,
uint32_t flags,
int32_t width,
int32_t height,
int32_t refresh)
{
}
static void
display_handle_done(void *data,
struct wl_output *wl_output)
{
}
static void
display_handle_scale(void *data,
struct wl_output *wl_output,
int32_t scale)
{
struct output *output = data;
output->scale = scale;
output->display->window->needs_buffer_geometry_update = true;
}
static const struct wl_output_listener output_listener = {
display_handle_geometry,
display_handle_mode,
display_handle_done,
display_handle_scale
};
static void
display_add_output(struct display *d, uint32_t name)
{
struct output *output;
output = xzalloc(sizeof *output);
output->display = d;
output->scale = 1;
output->wl_output =
wl_registry_bind(d->registry, name, &wl_output_interface, 2);
output->name = name;
wl_list_insert(d->output_list.prev, &output->link);
wl_output_add_listener(output->wl_output, &output_listener, output);
}
static void
display_destroy_output(struct display *d, struct output *output)
{
destroy_window_output(d->window, output->wl_output);
wl_output_destroy(output->wl_output);
wl_list_remove(&output->link);
free(output);
}
static void
display_destroy_outputs(struct display *d)
{
struct output *tmp;
struct output *output;
wl_list_for_each_safe(output, tmp, &d->output_list, link)
display_destroy_output(d, output);
}
static void
registry_handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
@ -778,6 +1065,8 @@ registry_handle_global(void *data, struct wl_registry *registry,
fprintf(stderr, "unable to load default left pointer\n");
// TODO: abort ?
}
} else if (strcmp(interface, "wl_output") == 0 && version >= 2) {
display_add_output(d, name);
}
}
@ -785,6 +1074,15 @@ static void
registry_handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name)
{
struct display *d = data;
struct output *output;
wl_list_for_each(output, &d->output_list, link) {
if (output->name == name) {
display_destroy_output(d, output);
break;
}
}
}
static const struct wl_registry_listener registry_listener = {
@ -823,13 +1121,19 @@ main(int argc, char **argv)
window.display = &display;
display.window = &window;
window.geometry.width = 250;
window.geometry.height = 250;
window.window_size = window.geometry;
window.buffer_size = 0;
window.buffer_size.width = 250;
window.buffer_size.height = 250;
window.window_size = window.buffer_size;
window.buffer_scale = 1;
window.buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL;
window.needs_buffer_geometry_update = false;
window.buffer_bpp = 0;
window.frame_sync = 1;
window.delay = 0;
wl_list_init(&display.output_list);
wl_list_init(&window.window_output_list);
for (i = 1; i < argc; i++) {
if (strcmp("-d", argv[i]) == 0 && i+1 < argc)
window.delay = atoi(argv[++i]);
@ -840,7 +1144,7 @@ main(int argc, char **argv)
else if (strcmp("-o", argv[i]) == 0)
window.opaque = 1;
else if (strcmp("-s", argv[i]) == 0)
window.buffer_size = 16;
window.buffer_bpp = 16;
else if (strcmp("-b", argv[i]) == 0)
window.frame_sync = 0;
else if (strcmp("-h", argv[i]) == 0)
@ -887,7 +1191,7 @@ main(int argc, char **argv)
while (running && ret != -1) {
ret = wl_display_dispatch_pending(display.display);
redraw(&window, NULL, 0);
redraw(&window);
}
fprintf(stderr, "simple-egl exiting\n");
@ -897,6 +1201,8 @@ main(int argc, char **argv)
wl_surface_destroy(display.cursor_surface);
out_no_xdg_shell:
display_destroy_outputs(&display);
if (display.cursor_theme)
wl_cursor_theme_destroy(display.cursor_theme);

@ -75,11 +75,9 @@ create_shm_buffer(struct touch *touch)
struct wl_shm_pool *pool;
int fd, size, stride;
void *data;
struct buffer *buffer = NULL;
struct buffer *buffer;
buffer = zalloc(sizeof(*buffer));
if (!buffer)
return NULL;
buffer = xzalloc(sizeof(*buffer));
stride = touch->width * 4;
size = stride * touch->height;

@ -3023,13 +3023,22 @@ static void
terminal_destroy(struct terminal *terminal)
{
display_unwatch_fd(terminal->display, terminal->master);
window_destroy(terminal->window);
close(terminal->master);
cairo_scaled_font_destroy(terminal->font_bold);
cairo_scaled_font_destroy(terminal->font_normal);
widget_destroy(terminal->widget);
window_destroy(terminal->window);
wl_list_remove(&terminal->link);
if (wl_list_empty(&terminal_list))
display_exit(terminal->display);
free(terminal->data);
free(terminal->data_attr);
free(terminal->tab_ruler);
free(terminal->title);
free(terminal);
}
@ -3048,10 +3057,12 @@ io_handler(struct task *task, uint32_t events)
}
len = read(terminal->master, buffer, sizeof buffer);
if (len < 0)
if (len < 0) {
terminal_destroy(terminal);
else
terminal_data(terminal, buffer, len);
return;
}
terminal_data(terminal, buffer, len);
}
static int
@ -3128,7 +3139,7 @@ static const struct weston_option terminal_options[] = {
int main(int argc, char *argv[])
{
struct display *d;
struct terminal *terminal;
struct terminal *terminal, *tmp;
const char *config_file;
struct sigaction sigpipe;
struct weston_config *config;
@ -3183,5 +3194,9 @@ int main(int argc, char *argv[])
display_run(d);
wl_list_for_each_safe(terminal, tmp, &terminal_list, link)
terminal_destroy(terminal);
display_destroy(d);
return 0;
}

File diff suppressed because it is too large Load Diff

@ -42,25 +42,6 @@
#include <sys/timerfd.h>
#include <stdbool.h>
#ifdef HAVE_CAIRO_EGL
#include <wayland-egl.h>
#ifdef USE_CAIRO_GLESV2
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#else
#include <GL/gl.h>
#endif
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <cairo-gl.h>
#elif !defined(ENABLE_EGL) /* platform.h defines these if EGL is enabled */
typedef void *EGLDisplay;
typedef void *EGLConfig;
typedef void *EGLContext;
#define EGL_NO_DISPLAY ((EGLDisplay)0)
#endif /* no HAVE_CAIRO_EGL */
#include <xkbcommon/xkbcommon.h>
#ifdef HAVE_XKBCOMMON_COMPOSE
@ -109,10 +90,6 @@ struct display {
struct xdg_wm_base *xdg_shell;
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
struct zwp_pointer_constraints_v1 *pointer_constraints;
EGLDisplay dpy;
EGLConfig argb_config;
EGLContext argb_ctx;
cairo_device_t *argb_device;
uint32_t serial;
int display_fd;
@ -178,18 +155,6 @@ struct toysurface {
enum wl_output_transform buffer_transform, int32_t buffer_scale,
struct rectangle *server_allocation);
/*
* Make the toysurface current with the given EGL context.
* Returns 0 on success, and negative on failure.
*/
int (*acquire)(struct toysurface *base, EGLContext ctx);
/*
* Release the toysurface from the EGL context, returning control
* to Cairo.
*/
void (*release)(struct toysurface *base);
/*
* Destroy the toysurface, including the Cairo surface, any
* backing storage, and the Wayland protocol objects.
@ -240,6 +205,7 @@ struct window {
int redraw_needed;
int redraw_task_scheduled;
struct task redraw_task;
struct task close_task;
int resize_needed;
int custom;
int focused;
@ -545,167 +511,6 @@ buffer_to_surface_size (enum wl_output_transform buffer_transform, int32_t buffe
*height /= buffer_scale;
}
#ifdef HAVE_CAIRO_EGL
struct egl_window_surface {
struct toysurface base;
cairo_surface_t *cairo_surface;
struct display *display;
struct wl_surface *surface;
struct wl_egl_window *egl_window;
EGLSurface egl_surface;
};
static struct egl_window_surface *
to_egl_window_surface(struct toysurface *base)
{
return container_of(base, struct egl_window_surface, base);
}
static cairo_surface_t *
egl_window_surface_prepare(struct toysurface *base, int dx, int dy,
int32_t width, int32_t height, uint32_t flags,
enum wl_output_transform buffer_transform, int32_t buffer_scale)
{
struct egl_window_surface *surface = to_egl_window_surface(base);
surface_to_buffer_size (buffer_transform, buffer_scale, &width, &height);
wl_egl_window_resize(surface->egl_window, width, height, dx, dy);
cairo_gl_surface_set_size(surface->cairo_surface, width, height);
return cairo_surface_reference(surface->cairo_surface);
}
static void
egl_window_surface_swap(struct toysurface *base,
enum wl_output_transform buffer_transform, int32_t buffer_scale,
struct rectangle *server_allocation)
{
struct egl_window_surface *surface = to_egl_window_surface(base);
cairo_gl_surface_swapbuffers(surface->cairo_surface);
wl_egl_window_get_attached_size(surface->egl_window,
&server_allocation->width,
&server_allocation->height);
buffer_to_surface_size (buffer_transform, buffer_scale,
&server_allocation->width,
&server_allocation->height);
}
static int
egl_window_surface_acquire(struct toysurface *base, EGLContext ctx)
{
struct egl_window_surface *surface = to_egl_window_surface(base);
cairo_device_t *device;
device = cairo_surface_get_device(surface->cairo_surface);
if (!device)
return -1;
if (!ctx) {
if (device == surface->display->argb_device)
ctx = surface->display->argb_ctx;
else
assert(0);
}
cairo_device_flush(device);
cairo_device_acquire(device);
if (!eglMakeCurrent(surface->display->dpy, surface->egl_surface,
surface->egl_surface, ctx))
fprintf(stderr, "failed to make surface current\n");
return 0;
}
static void
egl_window_surface_release(struct toysurface *base)
{
struct egl_window_surface *surface = to_egl_window_surface(base);
cairo_device_t *device;
device = cairo_surface_get_device(surface->cairo_surface);
if (!device)
return;
if (!eglMakeCurrent(surface->display->dpy,
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
fprintf(stderr, "failed to make context current\n");
cairo_device_release(device);
}
static void
egl_window_surface_destroy(struct toysurface *base)
{
struct egl_window_surface *surface = to_egl_window_surface(base);
struct display *d = surface->display;
cairo_surface_destroy(surface->cairo_surface);
weston_platform_destroy_egl_surface(d->dpy, surface->egl_surface);
wl_egl_window_destroy(surface->egl_window);
surface->surface = NULL;
free(surface);
}
static struct toysurface *
egl_window_surface_create(struct display *display,
struct wl_surface *wl_surface,
uint32_t flags,
struct rectangle *rectangle)
{
struct egl_window_surface *surface;
if (display->dpy == EGL_NO_DISPLAY)
return NULL;
surface = zalloc(sizeof *surface);
if (!surface)
return NULL;
surface->base.prepare = egl_window_surface_prepare;
surface->base.swap = egl_window_surface_swap;
surface->base.acquire = egl_window_surface_acquire;
surface->base.release = egl_window_surface_release;
surface->base.destroy = egl_window_surface_destroy;
surface->display = display;
surface->surface = wl_surface;
surface->egl_window = wl_egl_window_create(surface->surface,
rectangle->width,
rectangle->height);
surface->egl_surface =
weston_platform_create_egl_surface(display->dpy,
display->argb_config,
surface->egl_window, NULL);
surface->cairo_surface =
cairo_gl_surface_create_for_egl(display->argb_device,
surface->egl_surface,
rectangle->width,
rectangle->height);
return &surface->base;
}
#else
static struct toysurface *
egl_window_surface_create(struct display *display,
struct wl_surface *wl_surface,
uint32_t flags,
struct rectangle *rectangle)
{
return NULL;
}
#endif
struct shm_surface_data {
struct wl_buffer *buffer;
struct shm_pool *pool;
@ -1159,17 +964,6 @@ shm_surface_swap(struct toysurface *base,
surface->current = NULL;
}
static int
shm_surface_acquire(struct toysurface *base, EGLContext ctx)
{
return -1;
}
static void
shm_surface_release(struct toysurface *base)
{
}
static void
shm_surface_destroy(struct toysurface *base)
{
@ -1192,8 +986,6 @@ shm_surface_create(struct display *display, struct wl_surface *wl_surface,
surface = xzalloc(sizeof *surface);
surface->base.prepare = shm_surface_prepare;
surface->base.swap = shm_surface_swap;
surface->base.acquire = shm_surface_acquire;
surface->base.release = shm_surface_release;
surface->base.destroy = shm_surface_destroy;
surface->display = display;
@ -1432,13 +1224,23 @@ window_has_focus(struct window *window)
return window->focused;
}
static void
close_task_run(struct task *task, uint32_t events)
{
struct window *window = container_of(task, struct window, close_task);
window->close_handler(window->user_data);
}
static void
window_close(struct window *window)
{
if (window->close_handler)
window->close_handler(window->user_data);
else
if (window->close_handler && !window->close_task.run) {
window->close_task.run = close_task_run;
display_defer(window->display, &window->close_task);
} else {
display_exit(window->display);
}
}
struct display *
@ -1453,15 +1255,6 @@ surface_create_surface(struct surface *surface, uint32_t flags)
struct display *display = surface->window->display;
struct rectangle allocation = surface->allocation;
if (!surface->toysurface && display->dpy &&
surface->buffer_type == WINDOW_BUFFER_TYPE_EGL_WINDOW) {
surface->toysurface =
egl_window_surface_create(display,
surface->surface,
flags,
&allocation);
}
if (!surface->toysurface)
surface->toysurface = shm_surface_create(display,
surface->surface,
@ -4439,9 +4232,24 @@ xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_surface)
window_close(window);
}
static void
xdg_toplevel_handle_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height)
{
}
static void
xdg_toplevel_handle_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel,
struct wl_array *caps)
{
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
xdg_toplevel_handle_configure,
xdg_toplevel_handle_close,
xdg_toplevel_handle_configure_bounds,
xdg_toplevel_handle_wm_capabilities,
};
static void
@ -5272,11 +5080,6 @@ surface_create(struct window *window)
static enum window_buffer_type
get_preferred_buffer_type(struct display *display)
{
#ifdef HAVE_CAIRO_EGL
if (display->argb_device && !getenv("TOYTOOLKIT_NO_EGL"))
return WINDOW_BUFFER_TYPE_EGL_WINDOW;
#endif
return WINDOW_BUFFER_TYPE_SHM;
}
@ -5319,14 +5122,14 @@ window_create(struct display *display)
window->xdg_surface =
xdg_wm_base_get_xdg_surface(window->display->xdg_shell,
window->main_surface->surface);
fail_on_null(window->xdg_surface, 0, __FILE__, __LINE__);
abort_oom_if_null(window->xdg_surface);
xdg_surface_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
window->xdg_toplevel =
xdg_surface_get_toplevel(window->xdg_surface);
fail_on_null(window->xdg_toplevel, 0, __FILE__, __LINE__);
abort_oom_if_null(window->xdg_toplevel);
xdg_toplevel_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
@ -5531,7 +5334,7 @@ create_menu(struct display *display,
menu->widget = window_add_widget(menu->window, menu);
menu->frame = frame_create(window->display->theme, 0, 0,
FRAME_BUTTON_NONE, NULL, NULL);
fail_on_null(menu->frame, 0, __FILE__, __LINE__);
abort_oom_if_null(menu->frame);
menu->entries = entries;
menu->count = count;
menu->release_count = 0;
@ -5565,7 +5368,7 @@ create_simple_positioner(struct display *display,
struct xdg_positioner *positioner;
positioner = xdg_wm_base_create_positioner(display->xdg_shell);
fail_on_null(positioner, 0, __FILE__, __LINE__);
abort_oom_if_null(positioner);
xdg_positioner_set_anchor_rect(positioner, x, y, 1, 1);
xdg_positioner_set_size(positioner, w, h);
xdg_positioner_set_anchor(positioner,
@ -5610,7 +5413,7 @@ window_show_menu(struct display *display,
window->xdg_surface =
xdg_wm_base_get_xdg_surface(display->xdg_shell,
window->main_surface->surface);
fail_on_null(window->xdg_surface, 0, __FILE__, __LINE__);
abort_oom_if_null(window->xdg_surface);
xdg_surface_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
@ -5623,7 +5426,7 @@ window_show_menu(struct display *display,
window->xdg_popup = xdg_surface_get_popup(window->xdg_surface,
parent->xdg_surface,
positioner);
fail_on_null(window->xdg_popup, 0, __FILE__, __LINE__);
abort_oom_if_null(window->xdg_popup);
xdg_positioner_destroy(positioner);
xdg_popup_grab(window->xdg_popup, input->seat,
display_get_serial(window->display));
@ -6089,7 +5892,8 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
display_add_data_device(d, id, version);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
d->xdg_shell = wl_registry_bind(registry, id,
&xdg_wm_base_interface, 1);
&xdg_wm_base_interface,
MIN(version, 5));
xdg_wm_base_add_listener(d->xdg_shell, &wm_base_listener, d);
} else if (strcmp(interface, "text_cursor_position") == 0) {
d->text_cursor_position =
@ -6140,90 +5944,6 @@ static const struct wl_registry_listener registry_listener = {
registry_handle_global_remove
};
#ifdef HAVE_CAIRO_EGL
static int
init_egl(struct display *d)
{
EGLint major, minor;
EGLint n;
#ifdef USE_CAIRO_GLESV2
# define GL_BIT EGL_OPENGL_ES2_BIT
#else
# define GL_BIT EGL_OPENGL_BIT
#endif
static const EGLint argb_cfg_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 1,
EGL_DEPTH_SIZE, 1,
EGL_RENDERABLE_TYPE, GL_BIT,
EGL_NONE
};
#ifdef USE_CAIRO_GLESV2
static const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLint api = EGL_OPENGL_ES_API;
#else
EGLint *context_attribs = NULL;
EGLint api = EGL_OPENGL_API;
#endif
d->dpy =
weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
d->display, NULL);
if (!eglInitialize(d->dpy, &major, &minor)) {
fprintf(stderr, "failed to initialize EGL\n");
return -1;
}
if (!eglBindAPI(api)) {
fprintf(stderr, "failed to bind EGL client API\n");
return -1;
}
if (!eglChooseConfig(d->dpy, argb_cfg_attribs,
&d->argb_config, 1, &n) || n != 1) {
fprintf(stderr, "failed to choose argb EGL config\n");
return -1;
}
d->argb_ctx = eglCreateContext(d->dpy, d->argb_config,
EGL_NO_CONTEXT, context_attribs);
if (d->argb_ctx == NULL) {
fprintf(stderr, "failed to create EGL context\n");
return -1;
}
d->argb_device = cairo_egl_device_create(d->dpy, d->argb_ctx);
if (cairo_device_status(d->argb_device) != CAIRO_STATUS_SUCCESS) {
fprintf(stderr, "failed to get cairo EGL argb device\n");
return -1;
}
return 0;
}
static void
fini_egl(struct display *display)
{
cairo_device_destroy(display->argb_device);
eglMakeCurrent(display->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglTerminate(display->dpy);
eglReleaseThread();
}
#endif
static void
init_dummy_surface(struct display *display)
{
@ -6329,12 +6049,6 @@ display_create(int *argc, char *argv[])
return NULL;
}
#ifdef HAVE_CAIRO_EGL
if (init_egl(d) < 0)
fprintf(stderr, "EGL does not seem to work, "
"falling back to software rendering and wl_shm.\n");
#endif
create_cursors(d);
d->theme = theme_create();
@ -6393,10 +6107,7 @@ display_destroy(struct display *display)
theme_destroy(display->theme);
destroy_cursors(display);
#ifdef HAVE_CAIRO_EGL
if (display->argb_device)
fini_egl(display);
#endif
cleanup_after_cairo();
if (display->relative_pointer_manager)
zwp_relative_pointer_manager_v1_destroy(display->relative_pointer_manager);
@ -6462,12 +6173,6 @@ display_has_subcompositor(struct display *display)
return display->subcompositor != NULL;
}
cairo_device_t *
display_get_cairo_device(struct display *display)
{
return display->argb_device;
}
struct output *
display_get_output(struct display *display)
{
@ -6489,12 +6194,6 @@ display_get_serial(struct display *display)
return display->serial;
}
EGLDisplay
display_get_egl_display(struct display *d)
{
return d->dpy;
}
struct wl_data_source *
display_create_data_source(struct display *display)
{
@ -6504,38 +6203,6 @@ display_create_data_source(struct display *display)
return NULL;
}
EGLConfig
display_get_argb_egl_config(struct display *d)
{
return d->argb_config;
}
int
display_acquire_window_surface(struct display *display,
struct window *window,
EGLContext ctx)
{
struct surface *surface = window->main_surface;
if (surface->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
return -1;
widget_get_cairo_surface(window->main_surface->widget);
return surface->toysurface->acquire(surface->toysurface, ctx);
}
void
display_release_window_surface(struct display *display,
struct window *window)
{
struct surface *surface = window->main_surface;
if (surface->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
return;
surface->toysurface->release(surface->toysurface);
}
void
display_defer(struct display *display, struct task *task)
{

@ -71,9 +71,6 @@ display_get_display(struct display *display);
int
display_has_subcompositor(struct display *display);
cairo_device_t *
display_get_cairo_device(struct display *display);
struct wl_compositor *
display_get_compositor(struct display *display);
@ -114,22 +111,6 @@ display_set_output_configure_handler(struct display *display,
struct wl_data_source *
display_create_data_source(struct display *display);
#ifdef EGL_NO_DISPLAY
EGLDisplay
display_get_egl_display(struct display *d);
EGLConfig
display_get_argb_egl_config(struct display *d);
int
display_acquire_window_surface(struct display *display,
struct window *window,
EGLContext ctx);
void
display_release_window_surface(struct display *display,
struct window *window);
#endif
#define SURFACE_OPAQUE 0x01
#define SURFACE_SHM 0x02
@ -416,7 +397,6 @@ struct wl_subsurface *
widget_get_wl_subsurface(struct widget *widget);
enum window_buffer_type {
WINDOW_BUFFER_TYPE_EGL_WINDOW,
WINDOW_BUFFER_TYPE_SHM,
};

@ -1,7 +1,7 @@
/*
* Copyright © 2010-2011 Intel Corporation
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2012-2018 Collabora, Ltd.
* Copyright © 2012-2018,2022 Collabora, Ltd.
* Copyright © 2010-2011 Benjamin Franzke
* Copyright © 2013 Jason Ekstrand
* Copyright © 2017, 2018 General Electric Company
@ -36,6 +36,7 @@
#include <dlfcn.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <sys/wait.h>
@ -50,15 +51,16 @@
#include <libweston/libweston.h>
#include "shared/os-compatibility.h"
#include "shared/helpers.h"
#include "shared/process-util.h"
#include "shared/string-helpers.h"
#include "git-version.h"
#include <libweston/version.h>
#include "weston.h"
#include "weston-private.h"
#include <libweston/backend-drm.h>
#include <libweston/backend-headless.h>
#include <libweston/backend-rdp.h>
#include <libweston/backend-fbdev.h>
#include <libweston/backend-x11.h>
#include <libweston/backend-wayland.h>
#include <libweston/windowed-output-api.h>
@ -134,37 +136,6 @@ static struct weston_log_scope *log_scope;
static struct weston_log_scope *protocol_scope;
static int cached_tm_mday = -1;
static char *
weston_log_timestamp(char *buf, size_t len)
{
struct timeval tv;
struct tm *brokendown_time;
char datestr[128];
char timestr[128];
gettimeofday(&tv, NULL);
brokendown_time = localtime(&tv.tv_sec);
if (brokendown_time == NULL) {
snprintf(buf, len, "%s", "[(NULL)localtime] ");
return buf;
}
memset(datestr, 0, sizeof(datestr));
if (brokendown_time->tm_mday != cached_tm_mday) {
strftime(datestr, sizeof(datestr), "Date: %Y-%m-%d %Z\n",
brokendown_time);
cached_tm_mday = brokendown_time->tm_mday;
}
strftime(timestr, sizeof(timestr), "%H:%M:%S", brokendown_time);
/* if datestr is empty it prints only timestr*/
snprintf(buf, len, "%s[%s.%03li]", datestr,
timestr, (tv.tv_usec / 1000));
return buf;
}
static void
custom_handler(const char *fmt, va_list arg)
{
@ -172,7 +143,7 @@ custom_handler(const char *fmt, va_list arg)
weston_log_scope_printf(log_scope, "%s libwayland: ",
weston_log_timestamp(timestr,
sizeof(timestr)));
sizeof(timestr), &cached_tm_mday));
weston_log_scope_vprintf(log_scope, fmt, arg);
}
@ -218,7 +189,8 @@ vlog(const char *fmt, va_list ap)
if (weston_log_scope_is_enabled(log_scope)) {
int len_va;
char *log_timestamp = weston_log_timestamp(timestr,
sizeof(timestr));
sizeof(timestr),
&cached_tm_mday);
len_va = vasprintf(&str, fmt, ap);
if (len_va >= 0) {
len = weston_log_scope_printf(log_scope, "%s %s",
@ -270,6 +242,8 @@ protocol_log_fn(void *user_data,
size_t logsize;
char timestr[128];
struct wl_resource *res = message->resource;
struct wl_client *client = wl_resource_get_client(res);
pid_t pid = 0;
const char *signature = message->message->signature;
int i;
char type;
@ -281,10 +255,12 @@ protocol_log_fn(void *user_data,
if (!fp)
return;
wl_client_get_credentials(client, &pid, NULL, NULL);
weston_log_scope_timestamp(protocol_scope,
timestr, sizeof timestr);
fprintf(fp, "%s ", timestr);
fprintf(fp, "client %p %s ", wl_resource_get_client(res),
fprintf(fp, "client %p (PID %d) %s ", client, pid,
direction == WL_PROTOCOL_LOGGER_REQUEST ? "rq" : "ev");
fprintf(fp, "%s@%u.%s(",
wl_resource_get_class(res),
@ -382,6 +358,7 @@ sigchld_handler(int signal_number, void *data)
}
wl_list_remove(&p->link);
wl_list_init(&p->link);
p->cleanup(p, status);
}
@ -391,87 +368,111 @@ sigchld_handler(int signal_number, void *data)
return 1;
}
static void
child_client_exec(int sockfd, const char *path)
{
int clientfd;
char s[32];
sigset_t allsigs;
/* do not give our signal mask to the new process */
sigfillset(&allsigs);
sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
/* Launch clients as the user. Do not launch clients with wrong euid. */
if (seteuid(getuid()) == -1) {
weston_log("compositor: failed seteuid\n");
return;
}
/* SOCK_CLOEXEC closes both ends, so we dup the fd to get a
* non-CLOEXEC fd to pass through exec. */
clientfd = dup(sockfd);
if (clientfd == -1) {
weston_log("compositor: dup failed: %s\n", strerror(errno));
return;
}
snprintf(s, sizeof s, "%d", clientfd);
setenv("WAYLAND_SOCKET", s, 1);
if (execl(path, path, NULL) < 0)
weston_log("compositor: executing '%s' failed: %s\n",
path, strerror(errno));
}
WL_EXPORT struct wl_client *
weston_client_launch(struct weston_compositor *compositor,
struct weston_process *proc,
const char *path,
weston_process_cleanup_func_t cleanup)
{
int sv[2];
struct wl_client *client = NULL;
struct custom_env child_env;
struct fdstr wayland_socket;
const char *fail_cloexec = "Couldn't unset CLOEXEC on client socket";
const char *fail_seteuid = "Couldn't call seteuid";
char *fail_exec;
char * const *argp;
char * const *envp;
sigset_t allsigs;
pid_t pid;
struct wl_client *client;
bool ret;
size_t written __attribute__((unused));
weston_log("launching '%s'\n", path);
str_printf(&fail_exec, "Error: Couldn't launch client '%s'\n", path);
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
custom_env_init_from_environ(&child_env);
custom_env_add_arg(&child_env, path);
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0,
wayland_socket.fds) < 0) {
weston_log("weston_client_launch: "
"socketpair failed while launching '%s': %s\n",
path, strerror(errno));
custom_env_fini(&child_env);
return NULL;
}
fdstr_update_str1(&wayland_socket);
custom_env_set_env_var(&child_env, "WAYLAND_SOCKET",
wayland_socket.str1);
argp = custom_env_get_argp(&child_env);
envp = custom_env_get_envp(&child_env);
pid = fork();
if (pid == -1) {
close(sv[0]);
close(sv[1]);
weston_log("weston_client_launch: "
"fork failed while launching '%s': %s\n", path,
strerror(errno));
return NULL;
}
switch (pid) {
case 0:
/* Put the client in a new session so it won't catch signals
* intended for the parent. Sharing a session can be
* confusing when launching weston under gdb, as the ctrl-c
* intended for gdb will pass to the child, and weston
* will cleanly shut down when the child exits.
*/
setsid();
if (pid == 0) {
child_client_exec(sv[1], path);
_exit(-1);
}
/* do not give our signal mask to the new process */
sigfillset(&allsigs);
sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
close(sv[1]);
/* Launch clients as the user. Do not launch clients with wrong euid. */
if (seteuid(getuid()) == -1) {
written = write(STDERR_FILENO, fail_seteuid,
strlen(fail_seteuid));
_exit(EXIT_FAILURE);
}
ret = fdstr_clear_cloexec_fd1(&wayland_socket);
if (!ret) {
written = write(STDERR_FILENO, fail_cloexec,
strlen(fail_cloexec));
_exit(EXIT_FAILURE);
}
client = wl_client_create(compositor->wl_display, sv[0]);
if (!client) {
close(sv[0]);
execve(argp[0], argp, envp);
if (fail_exec)
written = write(STDERR_FILENO, fail_exec,
strlen(fail_exec));
_exit(EXIT_FAILURE);
default:
close(wayland_socket.fds[1]);
client = wl_client_create(compositor->wl_display,
wayland_socket.fds[0]);
if (!client) {
custom_env_fini(&child_env);
close(wayland_socket.fds[0]);
free(fail_exec);
weston_log("weston_client_launch: "
"wl_client_create failed while launching '%s'.\n",
path);
return NULL;
}
proc->pid = pid;
proc->cleanup = cleanup;
wet_watch_process(compositor, proc);
break;
case -1:
fdstr_close_all(&wayland_socket);
weston_log("weston_client_launch: "
"wl_client_create failed while launching '%s'.\n",
path);
return NULL;
"fork failed while launching '%s': %s\n", path,
strerror(errno));
break;
}
proc->pid = pid;
proc->cleanup = cleanup;
wet_watch_process(compositor, proc);
custom_env_fini(&child_env);
free(fail_exec);
return client;
}
@ -646,9 +647,6 @@ usage(int error_code)
#if defined(BUILD_DRM_COMPOSITOR)
"\t\t\t\tdrm-backend.so\n"
#endif
#if defined(BUILD_FBDEV_COMPOSITOR)
"\t\t\t\tfbdev-backend.so\n"
#endif
#if defined(BUILD_HEADLESS_COMPOSITOR)
"\t\t\t\theadless-backend.so\n"
#endif
@ -685,22 +683,12 @@ usage(int error_code)
fprintf(out,
"Options for drm-backend.so:\n\n"
" --seat=SEAT\t\tThe seat that weston should run on, instead of the seat defined in XDG_SEAT\n"
" --tty=TTY\t\tThe tty to use\n"
" --drm-device=CARD\tThe DRM device to use, e.g. \"card0\".\n"
" --use-pixman\t\tUse the pixman (CPU) renderer\n"
" --current-mode\tPrefer current KMS mode over EDID preferred mode\n"
" --continue-without-input\tAllow the compositor to start without input devices\n\n");
#endif
#if defined(BUILD_FBDEV_COMPOSITOR)
fprintf(out,
"Options for fbdev-backend.so:\n\n"
" --tty=TTY\t\tThe tty to use\n"
" --device=DEVICE\tThe framebuffer device to use\n"
" --seat=SEAT\t\tThe seat that weston should run on, instead of the seat defined in XDG_SEAT\n"
"\n");
#endif
#if defined(BUILD_HEADLESS_COMPOSITOR)
fprintf(out,
"Options for headless-backend.so:\n\n"
@ -721,6 +709,7 @@ usage(int error_code)
" --width=WIDTH\t\tWidth of desktop\n"
" --height=HEIGHT\tHeight of desktop\n"
" --env-socket\t\tUse socket defined in RDP_FD env variable as peer connection\n"
" --external-listener-fd=FD\tUse socket as listener connection\n"
" --address=ADDR\tThe address to bind\n"
" --port=PORT\t\tThe port to listen on\n"
" --no-clients-resize\tThe RDP peers will be forced to the size of the desktop\n"
@ -869,7 +858,7 @@ handle_primary_client_destroyed(struct wl_listener *listener, void *data)
static int
weston_create_listening_socket(struct wl_display *display, const char *socket_name)
{
char name_candidate[16];
char name_candidate[32];
if (socket_name) {
if (wl_display_add_socket(display, socket_name)) {
@ -1002,7 +991,7 @@ wet_get_bindir_path(const char *name)
static int
load_modules(struct weston_compositor *ec, const char *modules,
int *argc, char *argv[], bool *xwayland)
int *argc, char *argv[])
{
const char *p, *end;
char buffer[256];
@ -1016,11 +1005,11 @@ load_modules(struct weston_compositor *ec, const char *modules,
snprintf(buffer, sizeof buffer, "%.*s", (int) (end - p), p);
if (strstr(buffer, "xwayland.so")) {
weston_log("Old Xwayland module loading detected: "
weston_log("fatal: Old Xwayland module loading detected: "
"Please use --xwayland command line option "
"or set xwayland=true in the [core] section "
"in weston.ini\n");
*xwayland = true;
return -1;
} else {
if (wet_load_module(ec, buffer, argc, argv) < 0)
return -1;
@ -1348,6 +1337,223 @@ wet_output_set_color_profile(struct weston_output *output,
return ok ? 0 : -1;
}
static int
wet_output_set_eotf_mode(struct weston_output *output,
struct weston_config_section *section)
{
static const struct {
const char *name;
enum weston_eotf_mode eotf_mode;
} modes[] = {
{ "sdr", WESTON_EOTF_MODE_SDR },
{ "hdr-gamma", WESTON_EOTF_MODE_TRADITIONAL_HDR },
{ "st2084", WESTON_EOTF_MODE_ST2084 },
{ "hlg", WESTON_EOTF_MODE_HLG },
};
struct wet_compositor *compositor;
enum weston_eotf_mode eotf_mode = WESTON_EOTF_MODE_SDR;
char *str = NULL;
unsigned i;
compositor = to_wet_compositor(output->compositor);
if (section) {
weston_config_section_get_string(section, "eotf-mode",
&str, NULL);
}
if (!str) {
/* The default SDR mode is always supported. */
assert(weston_output_get_supported_eotf_modes(output) & eotf_mode);
weston_output_set_eotf_mode(output, eotf_mode);
return 0;
}
for (i = 0; i < ARRAY_LENGTH(modes); i++)
if (strcmp(str, modes[i].name) == 0)
break;
if (i == ARRAY_LENGTH(modes)) {
weston_log("Error in config for output '%s': '%s' is not a valid EOTF mode. Try one of:",
output->name, str);
for (i = 0; i < ARRAY_LENGTH(modes); i++)
weston_log_continue(" %s", modes[i].name);
weston_log_continue("\n");
return -1;
}
eotf_mode = modes[i].eotf_mode;
if ((weston_output_get_supported_eotf_modes(output) & eotf_mode) == 0) {
weston_log("Error: output '%s' does not support EOTF mode %s.\n",
output->name, str);
free(str);
return -1;
}
if (eotf_mode != WESTON_EOTF_MODE_SDR &&
!compositor->use_color_manager) {
weston_log("Error: EOTF mode %s on output '%s' requires color-management=true in weston.ini\n",
str, output->name);
free(str);
return -1;
}
weston_output_set_eotf_mode(output, eotf_mode);
free(str);
return 0;
}
struct wet_color_characteristics_keys {
const char *name;
enum weston_color_characteristics_groups group;
float minval;
float maxval;
};
#define COLOR_CHARAC_NAME "color_characteristics"
static int
parse_color_characteristics(struct weston_color_characteristics *cc_out,
struct weston_config_section *section)
{
static const struct wet_color_characteristics_keys keys[] = {
{ "red_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
{ "red_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
{ "green_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
{ "green_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
{ "blue_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
{ "blue_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
{ "white_x", WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE, 0.0f, 1.0f },
{ "white_y", WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE, 0.0f, 1.0f },
{ "max_L", WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL, 0.0f, 1e5f },
{ "min_L", WESTON_COLOR_CHARACTERISTICS_GROUP_MINL, 0.0f, 1e5f },
{ "maxFALL", WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL, 0.0f, 1e5f },
};
static const char *msgpfx = "Config error in weston.ini [" COLOR_CHARAC_NAME "]";
struct weston_color_characteristics cc = {};
float *const keyvalp[ARRAY_LENGTH(keys)] = {
/* These must be in the same order as keys[]. */
&cc.primary[0].x, &cc.primary[0].y,
&cc.primary[1].x, &cc.primary[1].y,
&cc.primary[2].x, &cc.primary[2].y,
&cc.white.x, &cc.white.y,
&cc.max_luminance,
&cc.min_luminance,
&cc.maxFALL,
};
bool found[ARRAY_LENGTH(keys)] = {};
uint32_t missing_group_mask = 0;
unsigned i;
char *section_name;
int ret = 0;
weston_config_section_get_string(section, "name",
&section_name, "<unnamed>");
if (strchr(section_name, ':') != NULL) {
ret = -1;
weston_log("%s name=%s: reserved name. Do not use ':' character in the name.\n",
msgpfx, section_name);
}
/* Parse keys if they exist */
for (i = 0; i < ARRAY_LENGTH(keys); i++) {
double value;
if (weston_config_section_get_double(section, keys[i].name,
&value, NAN) == 0) {
float f = value;
found[i] = true;
/* Range check, NaN shall not pass. */
if (f >= keys[i].minval && f <= keys[i].maxval) {
/* Key found, parsed, and good value. */
*keyvalp[i] = f;
continue;
}
ret = -1;
weston_log("%s name=%s: %s value %f is outside of the range %f - %f.\n",
msgpfx, section_name, keys[i].name, value,
keys[i].minval, keys[i].maxval);
continue;
}
if (errno == EINVAL) {
found[i] = true;
ret = -1;
weston_log("%s name=%s: failed to parse the value of key %s.\n",
msgpfx, section_name, keys[i].name);
}
}
/* Collect set and unset groups */
for (i = 0; i < ARRAY_LENGTH(keys); i++) {
uint32_t group = keys[i].group;
if (found[i])
cc.group_mask |= group;
else
missing_group_mask |= group;
}
/* Ensure groups are given fully or not at all. */
for (i = 0; i < ARRAY_LENGTH(keys); i++) {
uint32_t group = keys[i].group;
if ((cc.group_mask & group) && (missing_group_mask & group)) {
ret = -1;
weston_log("%s name=%s: group %d key %s is %s. "
"You must set either none or all keys of a group.\n",
msgpfx, section_name, ffs(group), keys[i].name,
found[i] ? "set" : "missing");
}
}
free(section_name);
if (ret == 0)
*cc_out = cc;
return ret;
}
WESTON_EXPORT_FOR_TESTS int
wet_output_set_color_characteristics(struct weston_output *output,
struct weston_config *wc,
struct weston_config_section *section)
{
char *cc_name = NULL;
struct weston_config_section *cc_section;
struct weston_color_characteristics cc;
weston_config_section_get_string(section, COLOR_CHARAC_NAME,
&cc_name, NULL);
if (!cc_name)
return 0;
cc_section = weston_config_get_section(wc, COLOR_CHARAC_NAME,
"name", cc_name);
if (!cc_section) {
weston_log("Config error in weston.ini, output %s: "
"no [" COLOR_CHARAC_NAME "] section with 'name=%s' found.\n",
output->name, cc_name);
goto out_error;
}
if (parse_color_characteristics(&cc, cc_section) < 0)
goto out_error;
weston_output_set_color_characteristics(output, &cc);
free(cc_name);
return 0;
out_error:
free(cc_name);
return -1;
}
static void
allow_content_protection(struct weston_output *output,
struct weston_config_section *section)
@ -1503,14 +1709,46 @@ wet_head_tracker_create(struct wet_compositor *compositor,
weston_head_add_destroy_listener(head, &track->head_destroy_listener);
}
/* Place output exactly to the right of the most recently enabled output.
*
* Historically, we haven't given much thought to output placement,
* simply adding outputs in a horizontal line as they're enabled. This
* function simply sets an output's x coordinate to the right of the
* most recently enabled output, and its y to zero.
*
* If you're adding new calls to this function, you're also not giving
* much thought to output placement, so please consider carefully if
* it's really doing what you want.
*
* You especially don't want to use this for any code that won't
* immediately enable the passed output.
*/
static void
weston_output_lazy_align(struct weston_output *output)
{
struct weston_compositor *c;
struct weston_output *peer;
int next_x = 0;
/* Put this output to the right of the most recently enabled output */
c = output->compositor;
if (!wl_list_empty(&c->output_list)) {
peer = container_of(c->output_list.prev,
struct weston_output, link);
next_x = peer->x + peer->width;
}
output->x = next_x;
output->y = 0;
}
static void
simple_head_enable(struct wet_compositor *wet, struct weston_head *head)
{
struct weston_output *output;
int ret = 0;
output = weston_compositor_create_output_with_head(wet->compositor,
head);
output = weston_compositor_create_output(wet->compositor, head,
head->name);
if (!output) {
weston_log("Could not create an output for head \"%s\".\n",
weston_head_get_name(head));
@ -1519,6 +1757,8 @@ simple_head_enable(struct wet_compositor *wet, struct weston_head *head)
return;
}
weston_output_lazy_align(output);
if (wet->simple_output_configure)
ret = wet->simple_output_configure(output);
if (ret < 0) {
@ -1813,6 +2053,8 @@ drm_backend_output_configure(struct weston_output *output,
enum weston_drm_backend_output_mode mode =
WESTON_DRM_BACKEND_OUTPUT_PREFERRED;
uint32_t transform = WL_OUTPUT_TRANSFORM_NORMAL;
uint32_t max_bpc = 0;
bool max_bpc_specified = false;
char *s;
char *modeline = NULL;
char *gbm_format = NULL;
@ -1825,12 +2067,18 @@ drm_backend_output_configure(struct weston_output *output,
}
weston_config_section_get_string(section, "mode", &s, "preferred");
if (weston_config_section_get_uint(section, "max-bpc", &max_bpc, 16) == 0)
max_bpc_specified = true;
if (strcmp(s, "off") == 0) {
assert(0 && "off was supposed to be pruned");
return -1;
} else if (wet->drm_use_current_mode || strcmp(s, "current") == 0) {
mode = WESTON_DRM_BACKEND_OUTPUT_CURRENT;
/* If mode=current and no max-bpc was specfied on the .ini file,
use current max_bpc so full modeset is not done. */
if (!max_bpc_specified)
max_bpc = 0;
} else if (strcmp(s, "preferred") != 0) {
modeline = s;
s = NULL;
@ -1844,6 +2092,8 @@ drm_backend_output_configure(struct weston_output *output,
}
free(modeline);
api->set_max_bpc(output, max_bpc);
if (count_remaining_heads(output, NULL) == 1) {
struct weston_head *head = weston_output_get_first_head(output);
transform = weston_head_get_transform(head);
@ -1871,6 +2121,13 @@ drm_backend_output_configure(struct weston_output *output,
allow_content_protection(output, section);
if (wet_output_set_eotf_mode(output, section) < 0)
return -1;
if (wet_output_set_color_characteristics(output,
wet->config, section) < 0)
return -1;
return 0;
}
@ -1958,7 +2215,9 @@ wet_output_handle_destroy(struct wl_listener *listener, void *data)
}
static struct wet_output *
wet_layoutput_create_output(struct wet_layoutput *lo, const char *name)
wet_layoutput_create_output_with_head(struct wet_layoutput *lo,
const char *name,
struct weston_head *head)
{
struct wet_output *output;
@ -1968,7 +2227,7 @@ wet_layoutput_create_output(struct wet_layoutput *lo, const char *name)
output->output =
weston_compositor_create_output(lo->compositor->compositor,
name);
head, name);
if (!output->output) {
free(output);
return NULL;
@ -2121,8 +2380,8 @@ drm_try_attach(struct weston_output *output,
{
unsigned i;
/* try to attach all heads, this probably succeeds */
for (i = 0; i < add->n; i++) {
/* try to attach remaining heads, this probably succeeds */
for (i = 1; i < add->n; i++) {
if (!add->heads[i])
continue;
@ -2142,6 +2401,8 @@ drm_try_enable(struct weston_output *output,
{
/* Try to enable, and detach heads one by one until it succeeds. */
while (!output->enabled) {
weston_output_lazy_align(output);
if (weston_output_enable(output) == 0)
return 0;
@ -2238,7 +2499,8 @@ drm_process_layoutput(struct wet_compositor *wet, struct wet_layoutput *lo)
if (ret < 0)
return -1;
}
output = wet_layoutput_create_output(lo, name);
output = wet_layoutput_create_output_with_head(lo, name,
lo->add.heads[0]);
free(name);
name = NULL;
@ -2625,17 +2887,26 @@ load_drm_backend(struct weston_compositor *c,
struct weston_config_section *section;
struct wet_compositor *wet = to_wet_compositor(c);
bool without_input = false;
bool use_pixman_default;
int ret = 0;
wet->drm_use_current_mode = false;
section = weston_config_get_section(wc, "core", NULL, NULL);
/* Use the pixman renderer by default when GBM/EGL support is
* not enabled */
#if defined(BUILD_DRM_GBM)
use_pixman_default = false;
#else
use_pixman_default = true;
#endif
weston_config_section_get_bool(section, "use-pixman", &config.use_pixman,
false);
use_pixman_default);
const struct weston_option options[] = {
{ WESTON_OPTION_STRING, "seat", 0, &config.seat_id },
{ WESTON_OPTION_INTEGER, "tty", 0, &config.tty },
{ WESTON_OPTION_STRING, "drm-device", 0, &config.specific_device },
{ WESTON_OPTION_BOOLEAN, "current-mode", 0, &wet->drm_use_current_mode },
{ WESTON_OPTION_BOOLEAN, "use-pixman", 0, &config.use_pixman },
@ -2688,6 +2959,15 @@ headless_backend_output_configure(struct weston_output *output)
.scale = 1,
.transform = WL_OUTPUT_TRANSFORM_NORMAL
};
struct weston_config *wc = wet_get_config(output->compositor);
struct weston_config_section *section;
section = weston_config_get_section(wc, "output", "name", output->name);
if (wet_output_set_eotf_mode(output, section) < 0)
return -1;
if (wet_output_set_color_characteristics(output, wc, section) < 0)
return -1;
return wet_configure_windowed_output_from_config(output, &defaults);
}
@ -2806,8 +3086,11 @@ weston_rdp_backend_config_init(struct weston_rdp_backend_config *config)
config->server_cert = NULL;
config->server_key = NULL;
config->env_socket = 0;
config->external_listener_fd = -1;
config->no_clients_resize = 0;
config->force_no_compression = 0;
config->remotefx_codec = true;
config->refresh_rate = RDP_DEFAULT_FREQ;
}
static int
@ -2815,7 +3098,9 @@ load_rdp_backend(struct weston_compositor *c,
int *argc, char *argv[], struct weston_config *wc)
{
struct weston_rdp_backend_config config = {{ 0, }};
struct weston_config_section *section;
int ret = 0;
bool no_remotefx_codec = false;
struct wet_output_config *parsed_options = wet_init_parsed_options(c);
if (!parsed_options)
@ -2825,6 +3110,7 @@ load_rdp_backend(struct weston_compositor *c,
const struct weston_option rdp_options[] = {
{ WESTON_OPTION_BOOLEAN, "env-socket", 0, &config.env_socket },
{ WESTON_OPTION_INTEGER, "external-listener-fd", 0, &config.external_listener_fd },
{ WESTON_OPTION_INTEGER, "width", 0, &parsed_options->width },
{ WESTON_OPTION_INTEGER, "height", 0, &parsed_options->height },
{ WESTON_OPTION_STRING, "address", 0, &config.bind_address },
@ -2834,11 +3120,17 @@ load_rdp_backend(struct weston_compositor *c,
{ WESTON_OPTION_STRING, "rdp-tls-cert", 0, &config.server_cert },
{ WESTON_OPTION_STRING, "rdp-tls-key", 0, &config.server_key },
{ WESTON_OPTION_BOOLEAN, "force-no-compression", 0, &config.force_no_compression },
{ WESTON_OPTION_BOOLEAN, "no-remotefx-codec", 0, &no_remotefx_codec },
};
parse_options(rdp_options, ARRAY_LENGTH(rdp_options), argc, argv);
config.remotefx_codec = !no_remotefx_codec;
wet_set_simple_head_configurator(c, rdp_backend_output_configure);
section = weston_config_get_section(wc, "rdp", NULL, NULL);
weston_config_section_get_int(section, "refresh-rate",
&config.refresh_rate,
RDP_DEFAULT_FREQ);
ret = weston_compositor_load_backend(c, WESTON_BACKEND_RDP,
&config.base);
@ -2851,54 +3143,6 @@ load_rdp_backend(struct weston_compositor *c,
return ret;
}
static int
fbdev_backend_output_configure(struct weston_output *output)
{
struct weston_config *wc = wet_get_config(output->compositor);
struct weston_config_section *section;
section = weston_config_get_section(wc, "output", "name", "fbdev");
if (wet_output_set_transform(output, section,
WL_OUTPUT_TRANSFORM_NORMAL,
UINT32_MAX) < 0) {
return -1;
}
weston_output_set_scale(output, 1);
return 0;
}
static int
load_fbdev_backend(struct weston_compositor *c,
int *argc, char **argv, struct weston_config *wc)
{
struct weston_fbdev_backend_config config = {{ 0, }};
int ret = 0;
const struct weston_option fbdev_options[] = {
{ WESTON_OPTION_INTEGER, "tty", 0, &config.tty },
{ WESTON_OPTION_STRING, "device", 0, &config.device },
{ WESTON_OPTION_STRING, "seat", 0, &config.seat_id },
};
parse_options(fbdev_options, ARRAY_LENGTH(fbdev_options), argc, argv);
config.base.struct_version = WESTON_FBDEV_BACKEND_CONFIG_VERSION;
config.base.struct_size = sizeof(struct weston_fbdev_backend_config);
config.configure_device = configure_input_device;
wet_set_simple_head_configurator(c, fbdev_backend_output_configure);
/* load the actual wayland backend and configure it */
ret = weston_compositor_load_backend(c, WESTON_BACKEND_FBDEV,
&config.base);
free(config.device);
return ret;
}
static int
x11_backend_output_configure(struct weston_output *output)
{
@ -3144,8 +3388,6 @@ load_backend(struct weston_compositor *compositor, const char *backend,
return load_headless_backend(compositor, argc, argv, config);
else if (strstr(backend, "rdp-backend.so"))
return load_rdp_backend(compositor, argc, argv, config);
else if (strstr(backend, "fbdev-backend.so"))
return load_fbdev_backend(compositor, argc, argv, config);
else if (strstr(backend, "drm-backend.so"))
return load_drm_backend(compositor, argc, argv, config);
else if (strstr(backend, "x11-backend.so"))
@ -3266,13 +3508,19 @@ weston_log_subscribe_to_scopes(struct weston_log_context *log_ctx,
weston_log_setup_scopes(log_ctx, flight_rec, flight_rec_scopes);
}
static void
sigint_helper(int sig)
{
raise(SIGUSR2);
}
WL_EXPORT int
wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
{
int ret = EXIT_FAILURE;
char *cmdline;
struct wl_display *display;
struct wl_event_source *signals[4];
struct wl_event_source *signals[3];
struct wl_event_loop *loop;
int i, fd;
char *backend = NULL;
@ -3302,6 +3550,7 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
struct weston_log_subscriber *logger = NULL;
struct weston_log_subscriber *flight_rec = NULL;
sigset_t mask;
struct sigaction action;
bool wait_for_debugger = false;
struct wl_protocol_logger *protologger = NULL;
@ -3392,16 +3641,28 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
loop = wl_display_get_event_loop(display);
signals[0] = wl_event_loop_add_signal(loop, SIGTERM, on_term_signal,
display);
signals[1] = wl_event_loop_add_signal(loop, SIGINT, on_term_signal,
display);
signals[2] = wl_event_loop_add_signal(loop, SIGQUIT, on_term_signal,
signals[1] = wl_event_loop_add_signal(loop, SIGUSR2, on_term_signal,
display);
wl_list_init(&wet.child_process_list);
signals[3] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler,
signals[2] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler,
&wet);
if (!signals[0] || !signals[1] || !signals[2] || !signals[3])
/* When debugging weston, if use wl_event_loop_add_signal() to catch
* SIGINT, the debugger can't catch it, and attempting to stop
* weston from within the debugger results in weston exiting
* cleanly.
*
* Instead, use the sigaction() function, which sets up the signal
* in a way that gdb can successfully catch, but have the handler
* for SIGINT send SIGUSR2 (xwayland uses SIGUSR1), which we catch
* via wl_event_loop_add_signal().
*/
action.sa_handler = sigint_helper;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(SIGINT, &action, NULL);
if (!signals[0] || !signals[1] || !signals[2])
goto out_signals;
/* Xwayland uses SIGUSR1 for communicating with weston. Since some
@ -3522,13 +3783,10 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
if (wet_load_shell(wet.compositor, shell, &argc, argv) < 0)
goto out;
weston_config_section_get_string(section, "modules", &modules, "");
if (load_modules(wet.compositor, modules, &argc, argv, &xwayland) < 0)
goto out;
if (load_modules(wet.compositor, option_modules, &argc, argv, &xwayland) < 0)
goto out;
/* Load xwayland before other modules - this way if we're using
* the systemd-notify module it will notify after we're ready
* to receive xwayland connections.
*/
if (!xwayland) {
weston_config_section_get_bool(section, "xwayland", &xwayland,
false);
@ -3538,6 +3796,13 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
goto out;
}
weston_config_section_get_string(section, "modules", &modules, "");
if (load_modules(wet.compositor, modules, &argc, argv) < 0)
goto out;
if (load_modules(wet.compositor, option_modules, &argc, argv) < 0)
goto out;
section = weston_config_get_section(config, "keyboard", NULL, NULL);
weston_config_section_get_bool(section, "numlock-on", &numlock_on, false);
if (numlock_on) {
@ -3573,8 +3838,6 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
ret = wet.compositor->exit_code;
out:
wet_compositor_destroy_layout(&wet);
/* free(NULL) is valid, and it won't be NULL if it's used */
free(wet.parsed_options);
@ -3582,6 +3845,7 @@ out:
wl_protocol_logger_destroy(protologger);
weston_compositor_destroy(wet.compositor);
wet_compositor_destroy_layout(&wet);
weston_log_scope_destroy(protocol_scope);
protocol_scope = NULL;

@ -78,6 +78,7 @@ if get_option('screenshare')
deps_screenshare = [
dep_libexec_weston,
dep_libshared,
dep_shell_utils,
dep_libweston_public,
dep_libweston_private_h, # XXX: https://gitlab.freedesktop.org/wayland/weston/issues/292
dep_wayland_client,
@ -95,19 +96,18 @@ if get_option('screenshare')
env_modmap += 'screen-share.so=@0@;'.format(plugin_screenshare.full_path())
endif
if get_option('color-management-lcms')
config_h.set('HAVE_LCMS', '1')
if get_option('deprecated-color-management-static')
srcs_lcms = [
'cms-static.c',
'cms-helper.c',
]
dep_lcms2 = dependency('lcms2', required: false)
if not dep_lcms2.found()
error('cms-static requires lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.')
error('cms-static requires lcms2 which was not found. Or, you can use \'-Ddeprecated-color-management-static=false\'.')
endif
config_h.set('HAVE_LCMS', '1')
plugin_lcms = shared_library(
'cms-static',
srcs_lcms,
@ -119,11 +119,13 @@ if get_option('color-management-lcms')
install_rpath: '$ORIGIN'
)
env_modmap += 'cms-static.so=@0@;'.format(plugin_lcms.full_path())
warning('deprecated-color-management-static is enabled. This will go away, see https://gitlab.freedesktop.org/wayland/weston/-/issues/634')
endif
if get_option('color-management-colord')
if not get_option('color-management-lcms')
error('LCMS must be enabled to support colord. Or, you can use \'-Dcolor-management-colord=false\'.')
if get_option('deprecated-color-management-colord')
if not get_option('deprecated-color-management-static')
error('deprecated-color-management-static must be enabled to support colord. Or, you can use \'-Ddeprecated-color-management-colord=false\'.')
endif
srcs_colord = [
@ -133,7 +135,7 @@ if get_option('color-management-colord')
dep_colord = dependency('colord', version: '>= 0.1.27', required: false)
if not dep_colord.found()
error('cms-colord requires colord >= 0.1.27 which was not found. Or, you can use \'-Dcolor-management-colord=false\'.')
error('cms-colord requires colord >= 0.1.27 which was not found. Or, you can use \'-Ddeprecated-color-management-colord=false\'.')
endif
plugin_colord_deps = [ dep_libweston_public, dep_colord, dep_lcms2 ]
@ -141,7 +143,7 @@ if get_option('color-management-colord')
foreach depname : [ 'glib-2.0', 'gobject-2.0' ]
dep = dependency(depname, required: false)
if not dep.found()
error('cms-colord requires \'@0@\' which was not found. If you rather not build this, set \'-Dcolor-management-colord=false\'.'.format(depname))
error('cms-colord requires \'@0@\' which was not found. If you rather not build this, set \'-Ddeprecated-color-management-colord=false\'.'.format(depname))
endif
plugin_colord_deps += dep
endforeach
@ -156,6 +158,8 @@ if get_option('color-management-colord')
install_dir: dir_module_weston
)
env_modmap += 'cms-colord.so=@0@;'.format(plugin_colord.full_path())
warning('deprecated-color-management-colord is enabled. This will go away, see https://gitlab.freedesktop.org/wayland/weston/-/issues/634')
endif
if get_option('systemd')

@ -43,10 +43,12 @@
#include <libweston/libweston.h>
#include "backend.h"
#include "libweston-internal.h"
#include "pixel-formats.h"
#include "weston.h"
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
#include "shared/timespec-util.h"
#include "shell-utils/shell-utils.h"
#include "fullscreen-shell-unstable-v1-client-protocol.h"
struct shared_output {
@ -59,7 +61,7 @@ struct shared_output {
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_shm *shm;
uint32_t shm_formats;
bool shm_formats_has_xrgb;
struct zwp_fullscreen_shell_v1 *fshell;
struct wl_output *output;
struct wl_surface *surface;
@ -114,9 +116,7 @@ struct ss_shm_buffer {
struct screen_share {
struct weston_compositor *compositor;
/* XXX: missing compositor destroy listener
* https://gitlab.freedesktop.org/wayland/weston/issues/298
*/
struct wl_listener compositor_destroy_listener;
char *command;
};
@ -368,7 +368,7 @@ ss_seat_create(struct shared_output *so, uint32_t id)
if (seat == NULL)
return NULL;
weston_seat_init(&seat->base, so->output->compositor, "default");
weston_seat_init(&seat->base, so->output->compositor, "screen-share");
seat->output = so;
seat->id = id;
seat->parent.seat = wl_registry_bind(so->parent.registry, id,
@ -702,7 +702,8 @@ shm_handle_format(void *data, struct wl_shm *wl_shm, uint32_t format)
{
struct shared_output *so = data;
so->parent.shm_formats |= (1 << format);
if (format == WL_SHM_FORMAT_XRGB8888)
so->parent.shm_formats_has_xrgb = true;
}
struct wl_shm_listener shm_listener = {
@ -828,6 +829,9 @@ shared_output_repainted(struct wl_listener *listener, void *data)
pixman_box32_t *r;
pixman_image_t *damaged_image;
pixman_transform_t transform;
const struct pixel_format_info *read_format =
so->output->compositor->read_format;
const pixman_format_code_t pixman_format = read_format->pixman_format;
width = so->output->current_mode->width;
height = so->output->current_mode->height;
@ -882,13 +886,13 @@ shared_output_repainted(struct wl_listener *listener, void *data)
y_orig = y;
so->output->compositor->renderer->read_pixels(
so->output, PIXMAN_a8r8g8b8, so->tmp_data,
x, y_orig, width, height);
so->output, read_format,
so->tmp_data, x, y_orig, width, height);
damaged_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
damaged_image = pixman_image_create_bits(pixman_format,
width, height,
so->tmp_data,
(PIXMAN_FORMAT_BPP(PIXMAN_a8r8g8b8) / 8) * width);
(PIXMAN_FORMAT_BPP(pixman_format) / 8) * width);
if (!damaged_image)
goto err_pixman_init;
@ -968,7 +972,7 @@ shared_output_create(struct weston_output *output, int parent_fd)
/* Get SHM formats */
wl_display_roundtrip(so->parent.display);
if (!(so->parent.shm_formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
if (!so->parent.shm_formats_has_xrgb) {
weston_log("Screen share failed: "
"WL_SHM_FORMAT_XRGB8888 not available\n");
goto err_display;
@ -1144,22 +1148,37 @@ share_output_binding(struct weston_keyboard *keyboard,
struct screen_share *ss = data;
pointer = weston_seat_get_pointer(keyboard->seat);
if (!pointer) {
weston_log("Cannot pick output: Seat does not have pointer\n");
return;
if (pointer) {
output = weston_output_find(pointer->seat->compositor,
wl_fixed_to_int(pointer->x),
wl_fixed_to_int(pointer->y));
} else {
output = get_focused_output(keyboard->seat->compositor);
if (!output)
output = get_default_output(keyboard->seat->compositor);
}
output = weston_output_find(pointer->seat->compositor,
wl_fixed_to_int(pointer->x),
wl_fixed_to_int(pointer->y));
if (!output) {
weston_log("Cannot pick output: Pointer not on any output\n");
weston_log("Cannot pick output: Pointer not on any output, "
"or no focused/default output found\n");
return;
}
weston_output_share(output, ss->command);
}
static void
compositor_destroy_listener(struct wl_listener *listener, void *data)
{
struct screen_share *ss =
wl_container_of(listener, ss, compositor_destroy_listener);
wl_list_remove(&ss->compositor_destroy_listener.link);
free(ss->command);
free(ss);
}
WL_EXPORT int
wet_module_init(struct weston_compositor *compositor,
int *argc, char *argv[])
@ -1175,11 +1194,16 @@ wet_module_init(struct weston_compositor *compositor,
return -1;
ss->compositor = compositor;
wl_list_init(&ss->compositor_destroy_listener.link);
ss->compositor_destroy_listener.notify = compositor_destroy_listener;
wl_signal_add(&compositor->destroy_signal, &ss->compositor_destroy_listener);
config = wet_get_config(compositor);
section = weston_config_get_section(config, "screen-share", NULL, NULL);
weston_config_section_get_string(section, "command", &ss->command, "");
weston_config_section_get_string(section, "command", &ss->command, NULL);
weston_compositor_add_key_binding(compositor, KEY_S,
MODIFIER_CTRL | MODIFIER_ALT,

@ -141,6 +141,12 @@ deactivate_input_method(struct input_method *input_method)
input_method->input = NULL;
input_method->context = NULL;
/* text_input_manager::destroy_listener by compositor shutdown */
if (!text_input->manager) {
zwp_text_input_v1_send_leave(text_input->resource);
return;
}
if (wl_list_empty(&text_input->input_methods) &&
text_input->input_panel_visible &&
text_input->manager->current_text_input == text_input) {
@ -456,6 +462,8 @@ text_input_manager_notifier_destroy(struct wl_listener *listener, void *data)
wl_list_remove(&text_input_manager->destroy_listener.link);
wl_global_destroy(text_input_manager->text_input_manager_global);
if (text_input_manager->current_text_input)
text_input_manager->current_text_input->manager = NULL;
free(text_input_manager);
}
@ -949,7 +957,7 @@ input_method_init_seat(struct weston_seat *seat)
seat->input_method->focus_listener_initialized = true;
}
static void launch_input_method(struct text_backend *text_backend);
static void launch_input_method(void *data);
static void
respawn_input_method_process(struct text_backend *text_backend)
@ -989,8 +997,10 @@ input_method_client_notifier(struct wl_listener *listener, void *data)
}
static void
launch_input_method(struct text_backend *text_backend)
launch_input_method(void *data)
{
struct text_backend *text_backend = data;
if (!text_backend->input_method.path)
return;
@ -1093,6 +1103,7 @@ text_backend_init(struct weston_compositor *ec)
{
struct text_backend *text_backend;
struct weston_seat *seat;
struct wl_event_loop *loop;
text_backend = zalloc(sizeof(*text_backend));
if (text_backend == NULL)
@ -1110,7 +1121,8 @@ text_backend_init(struct weston_compositor *ec)
text_input_manager_create(ec);
launch_input_method(text_backend);
loop = wl_display_get_event_loop(ec->wl_display);
wl_event_loop_add_idle(loop, launch_input_method, text_backend);
return text_backend;
}

@ -1,5 +1,5 @@
/*
* Copyright © 2008 Kristian Høgsberg
* Copyright 2022 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -23,28 +23,12 @@
* SOFTWARE.
*/
#include "config.h"
#pragma once
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <libweston/libweston.h>
#include <libweston/config-parser.h>
#include "xalloc.h"
void *
fail_on_null(void *p, size_t size, char *file, int32_t line)
{
if (p == NULL) {
fprintf(stderr, "[%s] ", program_invocation_short_name);
if (file)
fprintf(stderr, "%s:%d: ", file, line);
fprintf(stderr, "out of memory");
if (size)
fprintf(stderr, " (%zd)", size);
fprintf(stderr, "\n");
exit(EXIT_FAILURE);
}
return p;
}
int
wet_output_set_color_characteristics(struct weston_output *output,
struct weston_config *wc,
struct weston_config_section *section);

@ -68,8 +68,9 @@ screenshooter_take_shot(struct wl_client *client,
{
struct weston_output *output =
weston_head_from_resource(output_resource)->output;
struct weston_compositor *ec = output->compositor;
struct weston_buffer *buffer =
weston_buffer_from_resource(buffer_resource);
weston_buffer_from_resource(ec, buffer_resource);
if (buffer == NULL) {
wl_resource_post_no_memory(resource);

@ -30,34 +30,65 @@
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <libweston/libweston.h>
#include "compositor/weston.h"
#include <libweston/xwayland-api.h>
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
#include "shared/process-util.h"
#include "shared/string-helpers.h"
#ifdef HAVE_XWAYLAND_LISTENFD
# define LISTEN_STR "-listenfd"
#else
# define LISTEN_STR "-listen"
#endif
struct wet_xwayland {
struct weston_compositor *compositor;
struct wl_listener compositor_destroy_listener;
const struct weston_xwayland_api *api;
struct weston_xwayland *xwayland;
struct wl_event_source *sigusr1_source;
struct wl_event_source *display_fd_source;
struct wl_client *client;
int wm_fd;
struct weston_process process;
};
static int
handle_sigusr1(int signal_number, void *data)
handle_display_fd(int fd, uint32_t mask, void *data)
{
struct wet_xwayland *wxw = data;
char buf[64];
ssize_t n;
/* xwayland exited before being ready, don't finish initialization,
* the process watcher will cleanup */
if (!(mask & WL_EVENT_READABLE))
goto out;
/* Xwayland writes to the pipe twice, so if we close it too early
* it's possible the second write will fail and Xwayland shuts down.
* Make sure we read until end of line marker to avoid this. */
n = read(fd, buf, sizeof buf);
if (n < 0 && errno != EAGAIN) {
weston_log("read from Xwayland display_fd failed: %s\n",
strerror(errno));
goto out;
}
/* Returning 1 here means recheck and call us again if required. */
if (n <= 0 || (n > 0 && buf[n - 1] != '\n'))
return 1;
/* We'd be safer if we actually had the struct
* signalfd_siginfo from the signalfd data and could verify
* this came from Xwayland.*/
wxw->api->xserver_loaded(wxw->xwayland, wxw->client, wxw->wm_fd);
wl_event_source_remove(wxw->sigusr1_source);
return 1;
out:
wl_event_source_remove(wxw->display_fd_source);
close(fd);
return 0;
}
static pid_t
@ -65,93 +96,109 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
{
struct wet_xwayland *wxw = user_data;
pid_t pid;
char s[12], abstract_fd_str[12], unix_fd_str[12], wm_fd_str[12];
int sv[2], wm[2], fd;
struct fdstr wayland_socket;
struct fdstr x11_abstract_socket;
struct fdstr x11_unix_socket;
struct fdstr x11_wm_socket;
struct fdstr display_pipe;
char *xserver = NULL;
struct weston_config *config = wet_get_config(wxw->compositor);
struct weston_config_section *section;
struct wl_event_loop *loop;
char *exec_failure_msg;
struct custom_env child_env;
char *const *envp;
char *const *argp;
bool ret;
size_t written __attribute__ ((unused));
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, wayland_socket.fds) < 0) {
weston_log("wl connection socketpair failed\n");
return 1;
}
fdstr_update_str1(&wayland_socket);
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm) < 0) {
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, x11_wm_socket.fds) < 0) {
weston_log("X wm connection socketpair failed\n");
return 1;
}
fdstr_update_str1(&x11_wm_socket);
if (pipe2(display_pipe.fds, O_CLOEXEC) < 0) {
weston_log("pipe creation for displayfd failed\n");
return 1;
}
fdstr_update_str1(&display_pipe);
fdstr_set_fd1(&x11_abstract_socket, abstract_fd);
fdstr_set_fd1(&x11_unix_socket, unix_fd);
section = weston_config_get_section(config, "xwayland", NULL, NULL);
weston_config_section_get_string(section, "path",
&xserver, XSERVER_PATH);
str_printf(&exec_failure_msg,
"Error: executing Xwayland as '%s' failed.\n", xserver);
custom_env_init_from_environ(&child_env);
custom_env_set_env_var(&child_env, "WAYLAND_SOCKET", wayland_socket.str1);
custom_env_add_arg(&child_env, xserver);
custom_env_add_arg(&child_env, display);
custom_env_add_arg(&child_env, "-rootless");
custom_env_add_arg(&child_env, LISTEN_STR);
custom_env_add_arg(&child_env, x11_abstract_socket.str1);
custom_env_add_arg(&child_env, LISTEN_STR);
custom_env_add_arg(&child_env, x11_unix_socket.str1);
custom_env_add_arg(&child_env, "-displayfd");
custom_env_add_arg(&child_env, display_pipe.str1);
custom_env_add_arg(&child_env, "-wm");
custom_env_add_arg(&child_env, x11_wm_socket.str1);
custom_env_add_arg(&child_env, "-terminate");
envp = custom_env_get_envp(&child_env);
argp = custom_env_get_argp(&child_env);
pid = fork();
switch (pid) {
case 0:
setsid();
/* SOCK_CLOEXEC closes both ends, so we need to unset
* the flag on the client fd. */
fd = dup(sv[1]);
if (fd < 0)
goto fail;
snprintf(s, sizeof s, "%d", fd);
setenv("WAYLAND_SOCKET", s, 1);
fd = dup(abstract_fd);
if (fd < 0)
goto fail;
snprintf(abstract_fd_str, sizeof abstract_fd_str, "%d", fd);
fd = dup(unix_fd);
if (fd < 0)
goto fail;
snprintf(unix_fd_str, sizeof unix_fd_str, "%d", fd);
fd = dup(wm[1]);
if (fd < 0)
goto fail;
snprintf(wm_fd_str, sizeof wm_fd_str, "%d", fd);
section = weston_config_get_section(config,
"xwayland", NULL, NULL);
weston_config_section_get_string(section, "path",
&xserver, XSERVER_PATH);
/* Ignore SIGUSR1 in the child, which will make the X
* server send SIGUSR1 to the parent (weston) when
* it's done with initialization. During
* initialization the X server will round trip and
* block on the wayland compositor, so avoid making
* blocking requests (like xcb_connect_to_fd) until
* it's done with that. */
signal(SIGUSR1, SIG_IGN);
ret = fdstr_clear_cloexec_fd1(&wayland_socket);
ret &= fdstr_clear_cloexec_fd1(&x11_abstract_socket);
ret &= fdstr_clear_cloexec_fd1(&x11_unix_socket);
ret &= fdstr_clear_cloexec_fd1(&x11_wm_socket);
ret &= fdstr_clear_cloexec_fd1(&display_pipe);
if (!ret)
_exit(EXIT_FAILURE);
execve(xserver, argp, envp);
/* execve does not return on success, so it failed */
if (exec_failure_msg) {
written = write(STDERR_FILENO, exec_failure_msg,
strlen(exec_failure_msg));
}
if (execl(xserver,
xserver,
display,
"-rootless",
#ifdef HAVE_XWAYLAND_LISTENFD
"-listenfd", abstract_fd_str,
"-listenfd", unix_fd_str,
#else
"-listen", abstract_fd_str,
"-listen", unix_fd_str,
#endif
"-wm", wm_fd_str,
"-terminate",
NULL) < 0)
weston_log("exec of '%s %s -rootless "
#ifdef HAVE_XWAYLAND_LISTENFD
"-listenfd %s -listenfd %s "
#else
"-listen %s -listen %s "
#endif
"-wm %s -terminate' failed: %s\n",
xserver, display,
abstract_fd_str, unix_fd_str, wm_fd_str,
strerror(errno));
fail:
_exit(EXIT_FAILURE);
default:
close(sv[1]);
wxw->client = wl_client_create(wxw->compositor->wl_display, sv[0]);
close(wayland_socket.fds[1]);
wxw->client = wl_client_create(wxw->compositor->wl_display,
wayland_socket.fds[0]);
close(x11_wm_socket.fds[1]);
wxw->wm_fd = x11_wm_socket.fds[0];
close(wm[1]);
wxw->wm_fd = wm[0];
/* During initialization the X server will round trip
* and block on the wayland compositor, so avoid making
* blocking requests (like xcb_connect_to_fd) until
* it's done with that. */
close(display_pipe.fds[1]);
loop = wl_display_get_event_loop(wxw->compositor->wl_display);
wxw->display_fd_source =
wl_event_loop_add_fd(loop, display_pipe.fds[0],
WL_EVENT_READABLE,
handle_display_fd, wxw);
wxw->process.pid = pid;
wet_watch_process(wxw->compositor, &wxw->process);
@ -159,9 +206,16 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
case -1:
weston_log("Failed to fork to spawn xserver process\n");
fdstr_close_all(&wayland_socket);
fdstr_close_all(&x11_wm_socket);
fdstr_close_all(&display_pipe);
break;
}
custom_env_fini(&child_env);
free(exec_failure_msg);
free(xserver);
return pid;
}
@ -170,22 +224,34 @@ xserver_cleanup(struct weston_process *process, int status)
{
struct wet_xwayland *wxw =
container_of(process, struct wet_xwayland, process);
struct wl_event_loop *loop =
wl_display_get_event_loop(wxw->compositor->wl_display);
wxw->api->xserver_exited(wxw->xwayland, status);
wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
handle_sigusr1, wxw);
wxw->client = NULL;
}
static void
wxw_compositor_destroy(struct wl_listener *listener, void *data)
{
struct wet_xwayland *wxw =
wl_container_of(listener, wxw, compositor_destroy_listener);
wl_list_remove(&wxw->compositor_destroy_listener.link);
/* Don't call xserver_exited because Xwayland's own destroy handler
* already does this for us ... */
if (wxw->client)
kill(wxw->process.pid, SIGTERM);
wl_list_remove(&wxw->process.link);
free(wxw);
}
int
wet_load_xwayland(struct weston_compositor *comp)
{
const struct weston_xwayland_api *api;
struct weston_xwayland *xwayland;
struct wet_xwayland *wxw;
struct wl_event_loop *loop;
if (weston_compositor_load_xwayland(comp) < 0)
return -1;
@ -209,13 +275,13 @@ wet_load_xwayland(struct weston_compositor *comp)
wxw->compositor = comp;
wxw->api = api;
wxw->xwayland = xwayland;
wl_list_init(&wxw->process.link);
wxw->process.cleanup = xserver_cleanup;
wxw->compositor_destroy_listener.notify = wxw_compositor_destroy;
if (api->listen(xwayland, wxw, spawn_xserver) < 0)
return -1;
loop = wl_display_get_event_loop(comp->wl_display);
wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
handle_sigusr1, wxw);
wl_signal_add(&comp->destroy_signal, &wxw->compositor_destroy_listener);
return 0;
}

@ -1,737 +0,0 @@
/*
* Copyright © 2010-2012 Intel Corporation
* Copyright © 2011-2012 Collabora, Ltd.
* Copyright © 2013 Raspberry Pi Foundation
*
* 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.
*/
#include "config.h"
#include <stdint.h>
#include <linux/input.h>
#include "shell.h"
#include "shared/helpers.h"
struct exposay_surface {
struct desktop_shell *shell;
struct exposay_output *eoutput;
struct weston_surface *surface;
struct weston_view *view;
struct wl_listener view_destroy_listener;
struct wl_list link;
int x;
int y;
int width;
int height;
double scale;
int row;
int column;
/* The animations only apply a transformation for their own lifetime,
* and don't have an option to indefinitely maintain the
* transformation in a steady state - so, we apply our own once the
* animation has finished. */
struct weston_transform transform;
};
static void exposay_set_state(struct desktop_shell *shell,
enum exposay_target_state state,
struct weston_seat *seat);
static void exposay_check_state(struct desktop_shell *shell);
static void
exposay_surface_destroy(struct exposay_surface *esurface)
{
wl_list_remove(&esurface->link);
wl_list_remove(&esurface->view_destroy_listener.link);
if (esurface->shell->exposay.focus_current == esurface->view)
esurface->shell->exposay.focus_current = NULL;
if (esurface->shell->exposay.focus_prev == esurface->view)
esurface->shell->exposay.focus_prev = NULL;
free(esurface);
}
static void
exposay_in_flight_inc(struct desktop_shell *shell)
{
shell->exposay.in_flight++;
}
static void
exposay_in_flight_dec(struct desktop_shell *shell)
{
if (--shell->exposay.in_flight > 0)
return;
exposay_check_state(shell);
}
static void
exposay_animate_in_done(struct weston_view_animation *animation, void *data)
{
struct exposay_surface *esurface = data;
wl_list_insert(&esurface->view->geometry.transformation_list,
&esurface->transform.link);
weston_matrix_init(&esurface->transform.matrix);
weston_matrix_scale(&esurface->transform.matrix,
esurface->scale, esurface->scale, 1.0f);
weston_matrix_translate(&esurface->transform.matrix,
esurface->x - esurface->view->geometry.x,
esurface->y - esurface->view->geometry.y,
0);
weston_view_geometry_dirty(esurface->view);
weston_compositor_schedule_repaint(esurface->view->surface->compositor);
exposay_in_flight_dec(esurface->shell);
}
static void
exposay_animate_in(struct exposay_surface *esurface)
{
exposay_in_flight_inc(esurface->shell);
weston_move_scale_run(esurface->view,
esurface->x - esurface->view->geometry.x,
esurface->y - esurface->view->geometry.y,
1.0, esurface->scale, 0,
exposay_animate_in_done, esurface);
}
static void
exposay_animate_out_done(struct weston_view_animation *animation, void *data)
{
struct exposay_surface *esurface = data;
struct desktop_shell *shell = esurface->shell;
exposay_surface_destroy(esurface);
exposay_in_flight_dec(shell);
}
static void
exposay_animate_out(struct exposay_surface *esurface)
{
exposay_in_flight_inc(esurface->shell);
/* Remove the static transformation set up by
* exposay_transform_in_done(). */
wl_list_remove(&esurface->transform.link);
weston_view_geometry_dirty(esurface->view);
weston_move_scale_run(esurface->view,
esurface->x - esurface->view->geometry.x,
esurface->y - esurface->view->geometry.y,
1.0, esurface->scale, 1,
exposay_animate_out_done, esurface);
}
static void
exposay_highlight_surface(struct desktop_shell *shell,
struct exposay_surface *esurface)
{
struct weston_view *view = esurface->view;
if (shell->exposay.focus_current == view)
return;
shell->exposay.row_current = esurface->row;
shell->exposay.column_current = esurface->column;
shell->exposay.cur_output = esurface->eoutput;
activate(shell, view, shell->exposay.seat,
WESTON_ACTIVATE_FLAG_NONE);
shell->exposay.focus_current = view;
}
static int
exposay_is_animating(struct desktop_shell *shell)
{
if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE ||
shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW)
return 0;
return (shell->exposay.in_flight > 0);
}
static void
exposay_pick(struct desktop_shell *shell, int x, int y)
{
struct exposay_surface *esurface;
if (exposay_is_animating(shell))
return;
wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
if (x < esurface->x || x > esurface->x + esurface->width)
continue;
if (y < esurface->y || y > esurface->y + esurface->height)
continue;
exposay_highlight_surface(shell, esurface);
return;
}
}
static void
handle_view_destroy(struct wl_listener *listener, void *data)
{
struct exposay_surface *esurface = container_of(listener,
struct exposay_surface,
view_destroy_listener);
exposay_surface_destroy(esurface);
}
/* Compute each surface size and then inner pad (10% of surface size).
* After that, it's necessary to recompute surface size (90% of its
* original size). Also, each surface can't be bigger than half the
* exposay area width and height.
*/
static void
exposay_surface_and_inner_pad_size(pixman_rectangle32_t exposay_area, struct exposay_output *eoutput)
{
if (exposay_area.height < exposay_area.width)
eoutput->surface_size = exposay_area.height / eoutput->grid_size;
else
eoutput->surface_size = exposay_area.width / eoutput->grid_size;
eoutput->padding_inner = eoutput->surface_size / 10;
eoutput->surface_size -= eoutput->padding_inner;
if ((uint32_t)eoutput->surface_size > (exposay_area.width / 2))
eoutput->surface_size = exposay_area.width / 2;
if ((uint32_t)eoutput->surface_size > (exposay_area.height / 2))
eoutput->surface_size = exposay_area.height / 2;
}
/* Compute the exposay top/left margin in order to centralize it */
static void
exposay_margin_size(struct desktop_shell *shell, pixman_rectangle32_t exposay_area,
int row_size, int column_size, int *left_margin, int *top_margin)
{
(*left_margin) = exposay_area.x + (exposay_area.width - row_size) / 2;
(*top_margin) = exposay_area.y + (exposay_area.height - column_size) / 2;
}
/* Pretty lame layout for now; just tries to make a square. Should take
* aspect ratio into account really. Also needs to be notified of surface
* addition and removal and adjust layout/animate accordingly.
*
* Lay the grid out as square as possible, losing surfaces from the
* bottom row if required. Start with fixed padding of a 10% margin
* around the outside, and maximise the area made available to surfaces
* after this. Also, add an inner padding between surfaces that varies
* with the surface size (10% of its size).
*
* If we can't make a square grid, add one extra row at the bottom which
* will have a smaller number of columns.
*/
static enum exposay_layout_state
exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output)
{
struct workspace *workspace = shell->exposay.workspace;
struct weston_output *output = shell_output->output;
struct exposay_output *eoutput = &shell_output->eoutput;
struct weston_view *view;
struct exposay_surface *esurface, *highlight = NULL;
pixman_rectangle32_t exposay_area;
int pad, row_size, column_size, left_margin, top_margin;
int last_row_size, last_row_margin_increase;
int populated_rows;
int i;
eoutput->num_surfaces = 0;
wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
if (!get_shell_surface(view->surface))
continue;
if (view->output != output)
continue;
eoutput->num_surfaces++;
}
if (eoutput->num_surfaces == 0) {
eoutput->grid_size = 0;
eoutput->padding_inner = 0;
eoutput->surface_size = 0;
return EXPOSAY_LAYOUT_OVERVIEW;
}
/* Get exposay area and position, taking into account
* the shell panel position and size */
get_output_work_area(shell, output, &exposay_area);
/* Compute grid size */
eoutput->grid_size = floor(sqrtf(eoutput->num_surfaces));
if (pow(eoutput->grid_size, 2) != eoutput->num_surfaces)
eoutput->grid_size++;
/* Compute each surface size and the inner padding between them */
exposay_surface_and_inner_pad_size(exposay_area, eoutput);
/* Compute each row/column size */
pad = eoutput->surface_size + eoutput->padding_inner;
row_size = (pad * eoutput->grid_size) - eoutput->padding_inner;
/* We may have empty rows that should be desconsidered to compute
* column size */
populated_rows = ceil(eoutput->num_surfaces / (float) eoutput->grid_size);
column_size = (pad * populated_rows) - eoutput->padding_inner;
/* The last row size can be different, since it may have less surfaces
* than the grid size. Also, its margin may be increased to centralize
* its surfaces, in the case where we don't have a perfect grid. */
last_row_size = ((eoutput->num_surfaces % eoutput->grid_size) * pad) - eoutput->padding_inner;
if (eoutput->num_surfaces % eoutput->grid_size)
last_row_margin_increase = (row_size - last_row_size) / 2;
else
last_row_margin_increase = 0;
/* Compute a top/left margin to centralize the exposay */
exposay_margin_size(shell, exposay_area, row_size, column_size, &left_margin, &top_margin);
i = 0;
wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
if (!get_shell_surface(view->surface))
continue;
if (view->output != output)
continue;
esurface = malloc(sizeof(*esurface));
if (!esurface) {
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL,
shell->exposay.seat);
break;
}
wl_list_insert(&shell->exposay.surface_list, &esurface->link);
esurface->shell = shell;
esurface->eoutput = eoutput;
esurface->view = view;
esurface->row = i / eoutput->grid_size;
esurface->column = i % eoutput->grid_size;
esurface->x = left_margin + (pad * esurface->column);
esurface->y = top_margin + (pad * esurface->row);
/* If this is the last row, increase left margin (it sums 0 if
* we have a perfect square) to centralize the surfaces */
if (eoutput->num_surfaces / eoutput->grid_size == esurface->row)
esurface->x += last_row_margin_increase;
if (view->surface->width > view->surface->height)
esurface->scale = eoutput->surface_size / (float) view->surface->width;
else
esurface->scale = eoutput->surface_size / (float) view->surface->height;
esurface->width = view->surface->width * esurface->scale;
esurface->height = view->surface->height * esurface->scale;
/* Surfaces are usually rectangular, but their exposay surfaces
* are square. centralize them in their own square */
if (esurface->width > esurface->height)
esurface->y += (esurface->width - esurface->height) / 2;
else
esurface->x += (esurface->height - esurface->width) / 2;
if (shell->exposay.focus_current == esurface->view)
highlight = esurface;
exposay_animate_in(esurface);
/* We want our destroy handler to be after the animation
* destroy handler in the list, this way when the view is
* destroyed, the animation can safely call the animation
* completion callback before we free the esurface in our
* destroy handler.
*/
esurface->view_destroy_listener.notify = handle_view_destroy;
wl_signal_add(&view->destroy_signal, &esurface->view_destroy_listener);
i++;
}
if (highlight) {
shell->exposay.focus_current = NULL;
exposay_highlight_surface(shell, highlight);
}
weston_compositor_schedule_repaint(shell->compositor);
return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW;
}
static void
exposay_focus(struct weston_pointer_grab *grab)
{
}
static void
exposay_motion(struct weston_pointer_grab *grab,
const struct timespec *time,
struct weston_pointer_motion_event *event)
{
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_ptr);
weston_pointer_move(grab->pointer, event);
exposay_pick(shell,
wl_fixed_to_int(grab->pointer->x),
wl_fixed_to_int(grab->pointer->y));
}
static void
exposay_button(struct weston_pointer_grab *grab, const struct timespec *time,
uint32_t button, uint32_t state_w)
{
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_ptr);
struct weston_seat *seat = grab->pointer->seat;
enum wl_pointer_button_state state = state_w;
if (button != BTN_LEFT)
return;
/* Store the surface we clicked on, and don't do anything if we end up
* releasing on a different surface. */
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
shell->exposay.clicked = shell->exposay.focus_current;
return;
}
if (shell->exposay.focus_current == shell->exposay.clicked)
exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
else
shell->exposay.clicked = NULL;
}
static void
exposay_axis(struct weston_pointer_grab *grab,
const struct timespec *time,
struct weston_pointer_axis_event *event)
{
}
static void
exposay_axis_source(struct weston_pointer_grab *grab, uint32_t source)
{
}
static void
exposay_frame(struct weston_pointer_grab *grab)
{
}
static void
exposay_pointer_grab_cancel(struct weston_pointer_grab *grab)
{
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_ptr);
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
}
static const struct weston_pointer_grab_interface exposay_ptr_grab = {
exposay_focus,
exposay_motion,
exposay_button,
exposay_axis,
exposay_axis_source,
exposay_frame,
exposay_pointer_grab_cancel,
};
static int
exposay_maybe_move(struct desktop_shell *shell, int row, int column)
{
struct exposay_surface *esurface;
wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
if (esurface->eoutput != shell->exposay.cur_output ||
esurface->row != row || esurface->column != column)
continue;
exposay_highlight_surface(shell, esurface);
return 1;
}
return 0;
}
static void
exposay_key(struct weston_keyboard_grab *grab, const struct timespec *time,
uint32_t key, uint32_t state_w)
{
struct weston_seat *seat = grab->keyboard->seat;
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_kbd);
enum wl_keyboard_key_state state = state_w;
if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
return;
switch (key) {
case KEY_ESC:
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
break;
case KEY_ENTER:
exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
break;
case KEY_UP:
exposay_maybe_move(shell, shell->exposay.row_current - 1,
shell->exposay.column_current);
break;
case KEY_DOWN:
/* Special case for trying to move to the bottom row when it
* has fewer items than all the others. */
if (!exposay_maybe_move(shell, shell->exposay.row_current + 1,
shell->exposay.column_current) &&
shell->exposay.row_current < (shell->exposay.cur_output->grid_size - 1)) {
exposay_maybe_move(shell, shell->exposay.row_current + 1,
(shell->exposay.cur_output->num_surfaces %
shell->exposay.cur_output->grid_size) - 1);
}
break;
case KEY_LEFT:
exposay_maybe_move(shell, shell->exposay.row_current,
shell->exposay.column_current - 1);
break;
case KEY_RIGHT:
exposay_maybe_move(shell, shell->exposay.row_current,
shell->exposay.column_current + 1);
break;
case KEY_TAB:
/* Try to move right, then down (and to the leftmost column),
* then if all else fails, to the top left. */
if (!exposay_maybe_move(shell, shell->exposay.row_current,
shell->exposay.column_current + 1) &&
!exposay_maybe_move(shell, shell->exposay.row_current + 1, 0))
exposay_maybe_move(shell, 0, 0);
break;
default:
break;
}
}
static void
exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group)
{
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_kbd);
struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat;
/* We want to know when mod has been pressed and released.
* FIXME: There is a problem here: if mod is pressed, then a key
* is pressed and released, then mod is released, we will treat that
* as if only mod had been pressed and released. */
if (seat->modifier_state) {
if (seat->modifier_state == shell->binding_modifier) {
shell->exposay.mod_pressed = true;
} else {
shell->exposay.mod_invalid = true;
}
} else {
if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid)
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
shell->exposay.mod_invalid = false;
shell->exposay.mod_pressed = false;
}
return;
}
static void
exposay_cancel(struct weston_keyboard_grab *grab)
{
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_kbd);
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
}
static const struct weston_keyboard_grab_interface exposay_kbd_grab = {
exposay_key,
exposay_modifier,
exposay_cancel,
};
/**
* Called when the transition from overview -> inactive has completed.
*/
static enum exposay_layout_state
exposay_set_inactive(struct desktop_shell *shell)
{
struct weston_seat *seat = shell->exposay.seat;
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
if (pointer)
weston_pointer_end_grab(pointer);
if (keyboard) {
weston_keyboard_end_grab(keyboard);
if (keyboard->input_method_resource)
keyboard->grab = &keyboard->input_method_grab;
}
return EXPOSAY_LAYOUT_INACTIVE;
}
/**
* Begins the transition from overview to inactive. */
static enum exposay_layout_state
exposay_transition_inactive(struct desktop_shell *shell, int switch_focus)
{
struct exposay_surface *esurface;
/* Call activate() before we start the animations to avoid
* animating back the old state and then immediately transitioning
* to the new. */
if (switch_focus && shell->exposay.focus_current)
activate(shell, shell->exposay.focus_current,
shell->exposay.seat,
WESTON_ACTIVATE_FLAG_CONFIGURE);
else if (shell->exposay.focus_prev)
activate(shell, shell->exposay.focus_prev,
shell->exposay.seat,
WESTON_ACTIVATE_FLAG_CONFIGURE);
wl_list_for_each(esurface, &shell->exposay.surface_list, link)
exposay_animate_out(esurface);
weston_compositor_schedule_repaint(shell->compositor);
return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE;
}
static enum exposay_layout_state
exposay_transition_active(struct desktop_shell *shell)
{
struct weston_seat *seat = shell->exposay.seat;
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
struct shell_output *shell_output;
bool animate = false;
shell->exposay.workspace = get_current_workspace(shell);
shell->exposay.focus_prev = get_default_view(keyboard->focus);
shell->exposay.focus_current = get_default_view(keyboard->focus);
shell->exposay.clicked = NULL;
wl_list_init(&shell->exposay.surface_list);
lower_fullscreen_layer(shell, NULL);
shell->exposay.grab_kbd.interface = &exposay_kbd_grab;
weston_keyboard_start_grab(keyboard,
&shell->exposay.grab_kbd);
weston_keyboard_set_focus(keyboard, NULL);
shell->exposay.grab_ptr.interface = &exposay_ptr_grab;
if (pointer) {
weston_pointer_start_grab(pointer,
&shell->exposay.grab_ptr);
weston_pointer_clear_focus(pointer);
}
wl_list_for_each(shell_output, &shell->output_list, link) {
enum exposay_layout_state state;
state = exposay_layout(shell, shell_output);
if (state == EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW)
animate = true;
}
return animate ? EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW
: EXPOSAY_LAYOUT_OVERVIEW;
}
static void
exposay_check_state(struct desktop_shell *shell)
{
enum exposay_layout_state state_new = shell->exposay.state_cur;
int do_switch = 0;
/* Don't do anything whilst animations are running, just store up
* target state changes and only act on them when the animations have
* completed. */
if (exposay_is_animating(shell))
return;
switch (shell->exposay.state_target) {
case EXPOSAY_TARGET_OVERVIEW:
switch (shell->exposay.state_cur) {
case EXPOSAY_LAYOUT_OVERVIEW:
goto out;
case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW:
state_new = EXPOSAY_LAYOUT_OVERVIEW;
break;
default:
state_new = exposay_transition_active(shell);
break;
}
break;
case EXPOSAY_TARGET_SWITCH:
do_switch = 1; /* fallthrough */
case EXPOSAY_TARGET_CANCEL:
switch (shell->exposay.state_cur) {
case EXPOSAY_LAYOUT_INACTIVE:
goto out;
case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE:
state_new = exposay_set_inactive(shell);
break;
default:
state_new = exposay_transition_inactive(shell, do_switch);
break;
}
break;
}
out:
shell->exposay.state_cur = state_new;
}
static void
exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state,
struct weston_seat *seat)
{
shell->exposay.state_target = state;
shell->exposay.seat = seat;
exposay_check_state(shell);
}
void
exposay_binding(struct weston_keyboard *keyboard, enum weston_keyboard_modifier modifier,
void *data)
{
struct desktop_shell *shell = data;
exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, keyboard->seat);
}

@ -103,8 +103,8 @@ show_input_panel_surface(struct input_panel_surface *ipsurf)
&ipsurf->view->layer_link);
weston_view_geometry_dirty(ipsurf->view);
weston_view_update_transform(ipsurf->view);
ipsurf->surface->is_mapped = true;
ipsurf->view->is_mapped = true;
weston_surface_map(ipsurf->surface);
weston_surface_damage(ipsurf->surface);
if (ipsurf->anim)

@ -3,9 +3,7 @@ if get_option('shell-desktop')
srcs_shell_desktop = [
'shell.c',
'exposay.c',
'input-panel.c',
'../shared/shell-utils.c',
weston_desktop_shell_server_protocol_h,
weston_desktop_shell_protocol_c,
input_method_unstable_v1_server_protocol_h,
@ -15,8 +13,8 @@ if get_option('shell-desktop')
dep_libm,
dep_libexec_weston,
dep_libshared,
dep_lib_desktop,
dep_libweston_public,
dep_shell_utils,
]
plugin_shell_desktop = shared_library(
'desktop-shell',

File diff suppressed because it is too large Load Diff

@ -45,55 +45,8 @@ enum fade_type {
FADE_OUT
};
enum exposay_target_state {
EXPOSAY_TARGET_OVERVIEW, /* show all windows */
EXPOSAY_TARGET_CANCEL, /* return to normal, same focus */
EXPOSAY_TARGET_SWITCH, /* return to normal, switch focus */
};
enum exposay_layout_state {
EXPOSAY_LAYOUT_INACTIVE = 0, /* normal desktop */
EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE, /* in transition to normal */
EXPOSAY_LAYOUT_OVERVIEW, /* show all windows */
EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW, /* in transition to all windows */
};
struct exposay_output {
int num_surfaces;
int grid_size;
int surface_size;
int padding_inner;
};
struct exposay {
/* XXX: Make these exposay_surfaces. */
struct weston_view *focus_prev;
struct weston_view *focus_current;
struct weston_view *clicked;
struct workspace *workspace;
struct weston_seat *seat;
struct wl_list surface_list;
struct weston_keyboard_grab grab_kbd;
struct weston_pointer_grab grab_ptr;
enum exposay_target_state state_target;
enum exposay_layout_state state_cur;
int in_flight; /* number of animations still running */
int row_current;
int column_current;
struct exposay_output *cur_output;
bool mod_pressed;
bool mod_invalid;
};
struct focus_surface {
struct weston_surface *surface;
struct weston_view *view;
struct weston_transform workspace_transform;
struct weston_curtain *curtain;
};
struct workspace {
@ -110,7 +63,6 @@ struct workspace {
struct shell_output {
struct desktop_shell *shell;
struct weston_output *output;
struct exposay_output eoutput;
struct wl_listener destroy_listener;
struct wl_list link;
@ -121,7 +73,7 @@ struct shell_output {
struct wl_listener background_surface_listener;
struct {
struct weston_view *view;
struct weston_curtain *curtain;
struct weston_view_animation *animation;
enum fade_type type;
struct wl_event_source *startup_timer;
@ -175,32 +127,15 @@ struct desktop_shell {
struct weston_surface *lock_surface;
struct wl_listener lock_surface_listener;
struct {
struct wl_array array;
unsigned int current;
unsigned int num;
struct wl_list client_list;
struct weston_animation animation;
struct wl_list anim_sticky_list;
int anim_dir;
struct timespec anim_timestamp;
double anim_current;
struct workspace *anim_from;
struct workspace *anim_to;
} workspaces;
struct workspace workspace;
struct {
struct wl_resource *binding;
struct wl_list surfaces;
} input_panel;
struct exposay exposay;
bool allow_zap;
uint32_t binding_modifier;
uint32_t exposay_modifier;
enum animation_type win_animation_type;
enum animation_type win_close_animation_type;
enum animation_type startup_animation_type;
@ -246,10 +181,6 @@ void
activate(struct desktop_shell *shell, struct weston_view *view,
struct weston_seat *seat, uint32_t flags);
void
exposay_binding(struct weston_keyboard *keyboard,
enum weston_keyboard_modifier modifier,
void *data);
int
input_panel_setup(struct desktop_shell *shell);
void

@ -759,7 +759,7 @@ WARN_NO_PARAMDOC = NO
# a warning is encountered.
# The default value is: NO.
WARN_AS_ERROR = YES
WARN_AS_ERROR = @MESON_WERROR@
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
@ -1486,17 +1486,6 @@ EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
# Note that when changing this option you need to delete any form_*.png files in
# the HTML output directory before the changes have effect.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# http://www.mathjax.org) which uses client side Javascript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
@ -1632,241 +1621,6 @@ EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
# The default value is: YES.
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
# Note that when enabling USE_PDFLATEX this option is only used for generating
# bitmaps for formulas in the HTML output, but not in the Makefile that is
# written to the output directory.
# The default file is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used by the
# printer.
# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
# 14 inches) and executive (7.25 x 10.5 inches).
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
# by its name or with the correct syntax as to be used with the LaTeX
# \usepackage command. To get the times font for instance you can specify :
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
# To use the option intlimits with the amsmath package you can specify:
# EXTRA_PACKAGES=[intlimits]{amsmath}
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
# generated LaTeX document. The header should contain everything until the first
# chapter. If it is left blank doxygen will generate a standard header. See
# section "Doxygen usage" for information on how to let doxygen write the
# default header to a separate file.
#
# Note: Only use a user-defined header if you know what you are doing! The
# following commands have a special meaning inside the header: $title,
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
# string, for the replacement values of the other commands the user is referred
# to HTML_HEADER.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
# generated LaTeX document. The footer should contain everything after the last
# chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer.
#
# Note: Only use a user-defined footer if you know what you are doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# LaTeX style sheets that are included after the standard style sheets created
# by doxygen. Using this option one can overrule certain style aspects. Doxygen
# will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_STYLESHEET =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output
# directory. Note that the files will be copied as-is; there are no commands or
# markers available.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_FILES =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
# contain links (just like the HTML output) instead of page references. This
# makes the output suitable for online browsing using a PDF viewer.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# the PDF file directly from the LaTeX files. Set this option to YES, to get a
# higher quality PDF documentation.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help. This option is also used
# when generating formulas in HTML.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = NO
# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
# index chapters (such as File Index, Compound Index, etc.) in the output.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = NO
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
# code with syntax highlighting in the LaTeX output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
# RTF output is optimized for Word 97 and may not look too pretty with other RTF
# readers/editors.
# The default value is: NO.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: rtf.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
# contain hyperlink fields. The RTF file will contain links (just like the HTML
# output) instead of page references. This makes the output suitable for online
# browsing using Word or some other Word compatible readers that support those
# fields.
#
# Note: WordPad (write) and others do not support links.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's config
# file, i.e. a series of assignments. You only have to provide replacements,
# missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's config file. A template extensions file can be generated
# using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
# with syntax highlighting in the RTF output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
@ -1956,15 +1710,6 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
# program listings (including syntax highlighting and cross-referencing
# information) to the DOCBOOK output. Note that enabling this will significantly
# increase the size of the DOCBOOK output.
# The default value is: NO.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
@ -2143,15 +1888,6 @@ EXTERNAL_PAGES = YES
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
# NO turns the diagrams off. Note that this option also works with HAVE_DOT
# disabled, but it is recommended to install and use dot, since it yields more
# powerful graphs.
# The default value is: YES.
CLASS_DIAGRAMS = YES
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
@ -2172,7 +1908,7 @@ HIDE_UNDOC_RELATIONS = YES
# set to NO
# The default value is: YES.
HAVE_DOT = NO
HAVE_DOT = YES
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
@ -2184,23 +1920,6 @@ HAVE_DOT = NO
DOT_NUM_THREADS = 0
# When you want a differently looking font in the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
# setting DOT_FONTPATH to the directory containing the font.
# The default value is: Helvetica.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTNAME = Helvetica
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
# dot graphs.
# Minimum value: 4, maximum value: 24, default value: 10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTSIZE = 10
# By default doxygen will tell dot to use the default font as specified with
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
# the path where dot can find it using this tag.
@ -2401,7 +2120,7 @@ PLANTUML_INCLUDE_PATH =
# Minimum value: 0, maximum value: 10000, default value: 50.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 50
DOT_GRAPH_MAX_NODES = 250
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
# generated by dot. A depth value of 3 means that only nodes reachable from the
@ -2415,18 +2134,6 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, because dot on Windows does not seem
# to support this out of the box.
#
# Warning: Depending on the platform used, enabling this option may lead to
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
# read).
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_TRANSPARENT = YES
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support

@ -9,6 +9,7 @@ Welcome to Weston documentation!
toc/libweston.rst
toc/test-suite.rst
toc/kiosk-shell.rst
toc/ivi-shell.rst
Weston
------

@ -1,5 +1,6 @@
sphinx = find_program('sphinx-build', required: true)
doxygen = find_program('doxygen', required: true)
dot = find_program('dot', required: true)
breathe = find_program('breathe-apidoc', required: true)
sphinx_c = run_command(sphinx, '--version')
@ -38,6 +39,7 @@ sphinx_conf = configure_file(
doxy_conf_data = configuration_data()
doxy_conf_data.set('SRC_ROOT', meson.source_root())
doxy_conf_data.set('OUTPUT_DIR', doxygen_database)
doxy_conf_data.set('MESON_WERROR', get_option('werror') == true ? 'YES' : 'NO')
doxygen_conf_weston = configure_file(
input: 'doxygen.ini.in',
output: 'doxygen.ini',

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

@ -0,0 +1,9 @@
# Sphinx does not know look for these files in the source directory, so
# they must be copied to the build directory.
files = [
'ivi-shell.png',
]
foreach file : files
configure_file(input: file, output: file, copy: true)
endforeach

@ -0,0 +1,111 @@
Weston IVI-shell
================
Weston's IVI-shell is a highly customizable shell targeted at use cases which
need custom control over the shell's window layout with one or more applications
without interactive configuration of the layout by the user.
Example use cases for the IVI-shell are IVI applications or industrial human
machine interfaces. In general, whenever the user interface requires the exact
positioning of multiple application surfaces on one or more screens.
The IVI-shell also provides a means for applications to identify themselves to
the shell by application IDs via the ivi_application Wayland protocol.
IVI-shell client protocol
-------------------------
Wayland clients can implement the ``ivi_application`` Wayland protocol, which
allows them to specify an ``ivi_id`` to allow the IVI controller to identify the
application. This allows the controller to implement special behavior for
well-known applications.
The IVI-shell is able to also handle clients that use the ``xdg-shell``
protocol, but in these cases the IVI-shell needs other means to identify client
applications.
See ``ivi-application.xml`` for the protocol specification.
IVI-shell Weston modules
------------------------
The IVI-shell consists of two main components: The ``ivi-shell.so`` and custom
IVI controller (with the ``hmi-controller.so`` example implementation).
The ``ivi-shell.so`` is responsible for handling the application IDs and for
providing abstractions to configure the window layout via the
``ivi_layout_interface``. This interface is discussed in `IVI-shell compositor
implementation`.
The IVI controller uses the ``ivi_layout_interface`` to implement a window
manager and is responsible for configuring the window layout, i.e. the position
of the applications on the screens.
Due to this separation, both modules must be loaded in your ``weston.ini`` to
use the IVI-shell.
.. code-block:: ini
[core]
shell=ivi-shell.so
modules=hmi-controller.so
If you are using your custom controller, replace ``hmi-controller.so`` with the
name of your own controller module.
.. figure:: images/ivi-shell.png
:alt: IVI-shell architecture overview
Controlling the IVI-shell
-------------------------
The IVI-shell provides the ``ivi_layout_interface`` API that a controller must
use to control the window layout of the IVI-shell. See
``ivi-shell/ivi-layout-export.h`` for the definition of this API.
For the initial configuration, the controller has to create at least one
``ivi_layout_layer`` and add the ``ivi_layout_layer`` to a ``weston_output``.
The layers allow to group multiple applications surfaces and control them
together and are the main mechanism to group and organize surfaces. These are
always necessary to show something using the IVI-shell. The IVI-shell will
internally create an ``ivi_layout_screen``, but a controller always uses the
``weston_output`` directly.
To get control over the client surfaces, the controller must use notifiers that
trigger whenever there are changes to the client surfaces. The client surfaces
then show up as ``ivi_layout_surface``. These have an ID, which allows the
controller to identify the surface and reconfigure the window layout
accordingly.
The controller must add the ``ivi_layout_surface`` to an ``ivi_layout_layer``
and configure it's position and z-order wrt. the other surfaces in the layer.
Otherwise, the newly added surface will not show up on the screen.
The IVI-shell will internally create an ``ivi_layout_view`` for each layer that
the surface was added to. However, the views are not provided to the IVI
controller.
After configuring all expected changes, the controller must call the
``commit_changes`` to atomically update the display layout.
IVI-shell example implementation
--------------------------------
The IVI-shell comes with an example implementation of an IVI controller -- the
`hmi-controller`. The hmi-controller will usually replaced by a custom
implementation that implements the use-case-specific behavior.
The hmi-controller is split into two parts:
The ``hmi-controller.so`` is a Weston Plugin that uses the
``ivi_layout_interface`` to perform the window manager tasks. It allows some
reconfiguration of the window layout via the ``ivi_hmi_controller`` protocol.
Other implementations may keep all window management inside the module or may
expose even more window management via a custom protocol to an external process.
The ``weston-ivi-shell-user-interface`` is an example hmi-controller helper
client that serves as a user interface for controlling the hmi-controller.
The hmi-controller can be customized using the ``[ivi-shell]`` section in the
``weston.ini``. An example configuration will be generated in
``<build_dir>/ivi-shell/weston.ini``.

@ -11,7 +11,7 @@ msc {
--- [label = "Compositor creates an output for a head"];
c box c [label = "Have an existing head to process."];
c => w [label = "weston_compositor_create_output_with_head()"];
c => w [label = "weston_compositor_create_output()"];
w => b [label = "weston_backend::create_output()"];
w << b [label = "an empty output, no hw resources"];
w => b [label = "weston_output::attach_head()"];

@ -1,6 +1,7 @@
# you need to add here any files you add to the toc directory as well
files = [
'kiosk-shell.rst',
'ivi-shell.rst',
'running-weston.rst',
'libweston.rst',
'test-suite.rst',
@ -11,4 +12,5 @@ foreach file : files
configure_file(input: file, output: file, copy: true)
endforeach
subdir('images')
subdir('libweston')

@ -6,7 +6,7 @@ underlying environment where it runs on. Ultimately, the back-end is
responsible for handling the input and generate an output. Weston, as a
libweston user, can be run on different back-ends, including nested, by using
the wayland backend, but also on X11 or on a stand-alone back-end like
DRM/KMS and now deprecated fbdev.
DRM/KMS.
In most cases, people should allow Weston to choose the backend automatically
as it will produce the best results. That happens for instance when running
@ -28,7 +28,6 @@ Available back-ends:
* **x11** -- run as a x11 application, nested in a X11 display server instance
* **rdp** -- run as an RDP server without local input or output
* **headless** -- run without input or output, useful for test suite
* **fbdev** -- run stand-alone on fbdev/evdev (deprecated)
The job of gathering all the surfaces (windows) being displayed on an output and
stitching them together is performed by a *renderer*. By doing so, it is
@ -91,17 +90,20 @@ You can start Weston from a VT assuming that there's a seat manager supported by
backend to be used by ``libseat`` can optionally be selected with
``$LIBSEAT_BACKEND``. If ``libseat`` and ``seatd`` are both installed, but
``seatd`` is not already running, it can be started with ``sudo -- seatd -g
video``. If no seat manager supported by ``libseat`` is available, you can use
the ``weston-launch`` application that can handle VT switching.
video``.
Another way of launching Weston is via ssh or a serial terminal. The simplest
option here is to use the ``libseat`` launcher with ``seatd``. The process for
Launching Weston via ssh or a serial terminal is best with the ``libseat``
launcher and ``seatd``. Logind will refuse to give access to local seats from
remote connections directly. The process for
setting that up is identical to the one described above, where one just need to
ensure that ``seatd`` is running with the appropriate arguments, after which one
can just run ``weston``. Another option, is to rely on logind and start weston
as systemd user service: :ref:`weston-user-service`. Alternatively and as a last
resort, one can run Weston as root, specifying the tty to use on the command
line: If TTY 2 is active, one would run ``weston --tty 2`` as root.
can just run ``weston``. ``seatd`` will lend out the current VT, and if you want
to run on a different VT you need to ``chvt`` first. Make sure nothing will try
to take over the seat or VT via logind at the same time in case logind is
running.
If you want to rely on logind, you can start weston as a systemd user service:
:ref:`weston-user-service`.
Running Weston on a different seat on a stand-alone back-end
------------------------------------------------------------
@ -171,7 +173,14 @@ Then, weston can be run by selecting the DRM-backend and the seat ``seat-insecur
::
./weston -Bdrm-backend.so --seat=seat-insecure
SEATD_VTBOUND=0 ./weston -Bdrm-backend.so --seat=seat-insecure
This assumes you are using the libseat launcher of Weston with the "builtin"
backend of libseat. Libseat automatically falls back to the builtin backend if
``seatd`` is not running and a ``logind`` service is not running or refuses.
You can also force it with ``LIBSEAT_BACKEND=builtin`` if needed.
``SEATD_VTBOUND=0`` tells libseat that there is no VT associated with the
chosen seat.
If everything went well you should see weston be up-and-running on an output
connected to that DRM device.

@ -218,10 +218,19 @@ DRM-backend tests
DRM-backend tests require a DRM device, so they are a special case. To select a
device the test suite will simply look at the environment variable
``WESTON_TEST_SUITE_DRM_DEVICE``. So the first thing the user has to do in order
to run DRM-backend tests is to set this environment variable with the card that
should run the tests. For instance, in order to run DRM-backend tests with
``card0`` we need to run ``export WESTON_TEST_SUITE_DRM_DEVICE=card0``.
``WESTON_TEST_SUITE_DRM_DEVICE``. In Weston's CI, we set this variable to the
DRM node that VKMS takes (``cardX`` - X can change across each bot, as the order
in which devices are loaded is not predictable).
**IMPORTANT**: our DRM-backend tests are written specifically to run on top of
VKMS (KMS driver created to be used by headless machines in test suites, so it
aims to be more configurable and predictable than real hardware). We don't
guarantee that these tests will work on real hardware.
But if users want to run DRM-backend tests using real hardware anyway, the first
thing they need to do is to set this environment variable with the DRM node of
the card that should run the tests. For instance, in order to run DRM-backend
tests with ``card0`` we need to run ``export WESTON_TEST_SUITE_DRM_DEVICE=card0``.
Note that the card should not be in use by a desktop environment (or any other
program that requires master status), as there can only be one user at a time

@ -37,6 +37,7 @@
#include "compositor/weston.h"
#include "fullscreen-shell-unstable-v1-server-protocol.h"
#include "shared/helpers.h"
#include "shell-utils.h"
struct fullscreen_shell {
struct wl_client *client;
@ -80,7 +81,7 @@ struct fs_output {
struct weston_surface *surface;
struct wl_listener surface_destroyed;
struct weston_view *view;
struct weston_view *black_view;
struct weston_curtain *curtain;
struct weston_transform transform; /* matrix from x, y */
int presented_for_mode;
@ -226,37 +227,27 @@ black_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy)
{
}
static struct weston_view *
create_black_surface(struct weston_compositor *ec, struct fs_output *fsout,
float x, float y, int w, int h)
static struct weston_curtain *
create_curtain(struct weston_compositor *ec, struct fs_output *fsout,
float x, float y, int w, int h)
{
struct weston_surface *surface = NULL;
struct weston_view *view;
struct weston_curtain_params curtain_params = {
.r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0,
.x = x, .y = y, .width = w, .height = h,
.surface_committed = black_surface_committed,
.get_label = NULL,
.surface_private = fsout,
.capture_input = true,
};
struct weston_curtain *curtain;
surface = weston_surface_create(ec);
if (surface == NULL) {
weston_log("no memory\n");
return NULL;
}
view = weston_view_create(surface);
if (!view) {
weston_surface_destroy(surface);
curtain = weston_curtain_create(ec, &curtain_params);
if (!curtain) {
weston_log("no memory\n");
return NULL;
}
surface->committed = black_surface_committed;
surface->committed_private = fsout;
weston_surface_set_color(surface, 0.0f, 0.0f, 0.0f, 1.0f);
pixman_region32_fini(&surface->opaque);
pixman_region32_init_rect(&surface->opaque, 0, 0, w, h);
pixman_region32_fini(&surface->input);
pixman_region32_init_rect(&surface->input, 0, 0, w, h);
weston_surface_set_size(surface, w, h);
weston_view_set_position(view, x, y);
return view;
return curtain;
}
static void
@ -333,13 +324,12 @@ fs_output_create(struct fullscreen_shell *shell, struct weston_output *output)
fsout->surface_destroyed.notify = surface_destroyed;
fsout->pending.surface_destroyed.notify = pending_surface_destroyed;
fsout->black_view = create_black_surface(shell->compositor, fsout,
output->x, output->y,
output->width, output->height);
fsout->black_view->surface->is_mapped = true;
fsout->black_view->is_mapped = true;
fsout->curtain = create_curtain(shell->compositor, fsout,
output->x, output->y,
output->width, output->height);
fsout->curtain->view->is_mapped = true;
weston_layer_entry_insert(&shell->layer.view_list,
&fsout->black_view->layer_link);
&fsout->curtain->view->layer_link);
wl_list_init(&fsout->transform.link);
if (!wl_list_empty(&shell->default_surface_list)) {
@ -373,41 +363,6 @@ restore_output_mode(struct weston_output *output)
weston_output_mode_switch_to_native(output);
}
/*
* Returns the bounding box of a surface and all its sub-surfaces,
* in surface-local coordinates. */
static void
surface_subsurfaces_boundingbox(struct weston_surface *surface, int32_t *x,
int32_t *y, int32_t *w, int32_t *h) {
pixman_region32_t region;
pixman_box32_t *box;
struct weston_subsurface *subsurface;
pixman_region32_init_rect(&region, 0, 0,
surface->width,
surface->height);
wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
pixman_region32_union_rect(&region, &region,
subsurface->position.x,
subsurface->position.y,
subsurface->surface->width,
subsurface->surface->height);
}
box = pixman_region32_extents(&region);
if (x)
*x = box->x1;
if (y)
*y = box->y1;
if (w)
*w = box->x2 - box->x1;
if (h)
*h = box->y2 - box->y1;
pixman_region32_fini(&region);
}
static void
fs_output_center_view(struct fs_output *fsout)
{
@ -520,10 +475,10 @@ fs_output_configure_simple(struct fs_output *fsout,
break;
}
weston_view_set_position(fsout->black_view,
weston_view_set_position(fsout->curtain->view,
fsout->output->x - surf_x,
fsout->output->y - surf_y);
weston_surface_set_size(fsout->black_view->surface,
weston_surface_set_size(fsout->curtain->view->surface,
fsout->output->width,
fsout->output->height);
}
@ -670,6 +625,7 @@ fs_output_apply_pending(struct fs_output *fsout)
return;
}
fsout->view->is_mapped = true;
fsout->surface->is_mapped = true;
wl_signal_add(&fsout->surface->destroy_signal,
&fsout->surface_destroyed);

@ -4,9 +4,10 @@ if get_option('shell-fullscreen')
fullscreen_shell_unstable_v1_server_protocol_h,
fullscreen_shell_unstable_v1_protocol_c,
]
deps_shell_fullscreen=[
deps_shell_fullscreen = [
dep_libweston_public,
dep_libexec_weston,
dep_shell_utils,
]
shared_library(
'fullscreen-shell',

@ -44,6 +44,14 @@ enum weston_desktop_surface_edge {
WESTON_DESKTOP_SURFACE_EDGE_BOTTOM_RIGHT = 10,
};
enum weston_top_level_tiled_orientation {
WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE = 0 << 0,
WESTON_TOP_LEVEL_TILED_ORIENTATION_LEFT = 1 << 1,
WESTON_TOP_LEVEL_TILED_ORIENTATION_RIGHT = 1 << 2,
WESTON_TOP_LEVEL_TILED_ORIENTATION_TOP = 1 << 3,
WESTON_TOP_LEVEL_TILED_ORIENTATION_BOTTOM = 1 << 4,
};
struct weston_desktop;
struct weston_desktop_client;
struct weston_desktop_surface;
@ -113,6 +121,9 @@ struct weston_desktop_api {
*/
void (*set_xwayland_position)(struct weston_desktop_surface *surface,
int32_t x, int32_t y, void *user_data);
void (*get_position)(struct weston_desktop_surface *surface,
int32_t *x, int32_t *y,
void *user_data);
};
void
@ -163,6 +174,9 @@ void
weston_desktop_surface_set_size(struct weston_desktop_surface *surface,
int32_t width, int32_t height);
void
weston_desktop_surface_set_orientation(struct weston_desktop_surface *surface,
enum weston_top_level_tiled_orientation tile_orientation);
void
weston_desktop_surface_close(struct weston_desktop_surface *surface);
void
weston_desktop_surface_add_metadata_listener(struct weston_desktop_surface *surface,
@ -195,6 +209,19 @@ weston_desktop_surface_get_max_size(struct weston_desktop_surface *surface);
struct weston_size
weston_desktop_surface_get_min_size(struct weston_desktop_surface *surface);
bool
weston_desktop_window_menu_supported(struct weston_desktop *desktop);
bool
weston_desktop_move_supported(struct weston_desktop *desktop);
bool
weston_desktop_resize_supported(struct weston_desktop *desktop);
bool
weston_desktop_fullscreen_supported(struct weston_desktop *desktop);
bool
weston_desktop_minimize_supported(struct weston_desktop *desktop);
bool
weston_desktop_maximize_supported(struct weston_desktop *desktop);
#ifdef __cplusplus
}
#endif

@ -35,7 +35,7 @@
extern "C" {
#endif
#define WESTON_DRM_BACKEND_CONFIG_VERSION 4
#define WESTON_DRM_BACKEND_CONFIG_VERSION 5
struct libinput_device;
@ -78,6 +78,21 @@ struct weston_drm_output_api {
*/
void (*set_seat)(struct weston_output *output,
const char *seat);
/** Set the "max bpc" KMS connector property
*
* The property is used for working around faulty sink hardware like
* monitors or media converters that mishandle the kernel driver
* chosen bits-per-channel on the physical link. When having trouble,
* try a lower value like 8. A value of 0 means that the current max
* bpc will be reprogrammed.
*
* The value actually used in KMS is silently clamped to the range the
* KMS driver claims to support. The default value is 16.
*
* This can be set only while the output is disabled.
*/
void (*set_max_bpc)(struct weston_output *output, unsigned max_bpc);
};
static inline const struct weston_drm_output_api *
@ -90,7 +105,7 @@ weston_drm_output_get_api(struct weston_compositor *compositor)
return (const struct weston_drm_output_api *)api;
}
#define WESTON_DRM_VIRTUAL_OUTPUT_API_NAME "weston_drm_virtual_output_api_v1"
#define WESTON_DRM_VIRTUAL_OUTPUT_API_NAME "weston_drm_virtual_output_api_v2"
struct drm_fb;
typedef int (*submit_frame_cb)(struct weston_output *output, int fd,
@ -101,12 +116,16 @@ struct weston_drm_virtual_output_api {
* This is a low-level function, where the caller is expected to wrap
* the weston_output function pointers as necessary to make the virtual
* output useful. The caller must set up output make, model, serial,
* physical size, the mode list and current mode.
* physical size, the mode list and current mode. The destroy function
* pointer must not be overwritten, as it is used by the DRM backend to
* recognize its outputs. Instead, an auxiliary destroy callback has to
* be provided as a parameter.
*
* Returns output on success, NULL on failure.
*/
struct weston_output* (*create_output)(struct weston_compositor *c,
char *name);
char *name,
void (*destroy_func)(struct weston_output *base));
/** Set pixel format same as drm_output set_gbm_format().
*
@ -171,9 +190,6 @@ weston_drm_virtual_output_get_api(struct weston_compositor *compositor)
struct weston_drm_backend_config {
struct weston_backend_config base;
/** The tty to be used. Set to 0 to use the current tty. */
int tty;
/** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
bool use_pixman;

@ -1,70 +0,0 @@
/*
* Copyright © 2016 Benoit Gschwind
*
* 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 WESTON_COMPOSITOR_FBDEV_H
#define WESTON_COMPOSITOR_FBDEV_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <libweston/libweston.h>
#define WESTON_FBDEV_BACKEND_CONFIG_VERSION 2
struct libinput_device;
struct weston_fbdev_backend_config {
struct weston_backend_config base;
int tty;
char *device;
/** Callback used to configure input devices.
*
* This function will be called by the backend when a new input device
* needs to be configured.
* If NULL the device will use the default configuration.
*/
void (*configure_device)(struct weston_compositor *compositor,
struct libinput_device *device);
/** The seat to be used for input and output.
*
* If seat_id is NULL, the seat is taken from XDG_SEAT environment
* variable. If neither is set, "seat0" is used. The backend will
* take ownership of the seat_id pointer and will free it on
* backend destruction.
*/
char *seat_id;
};
#ifdef __cplusplus
}
#endif
#endif /* WESTON_COMPOSITOR_FBDEV_H */

@ -34,6 +34,7 @@ extern "C" {
#include <libweston/plugin-registry.h>
#define WESTON_RDP_OUTPUT_API_NAME "weston_rdp_output_api_v1"
#define RDP_DEFAULT_FREQ 60
struct weston_rdp_output_api {
/** Initialize a RDP output with specified width and height.
@ -56,6 +57,11 @@ weston_rdp_output_get_api(struct weston_compositor *compositor)
#define WESTON_RDP_BACKEND_CONFIG_VERSION 2
typedef void *(*rdp_audio_in_setup)(struct weston_compositor *c, void *vcm);
typedef void (*rdp_audio_in_teardown)(void *audio_private);
typedef void *(*rdp_audio_out_setup)(struct weston_compositor *c, void *vcm);
typedef void (*rdp_audio_out_teardown)(void *audio_private);
struct weston_rdp_backend_config {
struct weston_backend_config base;
char *bind_address;
@ -66,6 +72,13 @@ struct weston_rdp_backend_config {
int env_socket;
int no_clients_resize;
int force_no_compression;
bool remotefx_codec;
int external_listener_fd;
int refresh_rate;
rdp_audio_in_setup audio_in_setup;
rdp_audio_in_teardown audio_in_teardown;
rdp_audio_out_setup audio_out_setup;
rdp_audio_out_teardown audio_out_teardown;
};
#ifdef __cplusplus

@ -35,26 +35,6 @@ extern "C" {
#define WESTON_CONFIG_FILE_ENV_VAR "WESTON_CONFIG_FILE"
enum config_key_type {
CONFIG_KEY_INTEGER, /* typeof data = int */
CONFIG_KEY_UNSIGNED_INTEGER, /* typeof data = unsigned int */
CONFIG_KEY_STRING, /* typeof data = char* */
CONFIG_KEY_BOOLEAN /* typeof data = int */
};
struct config_key {
const char *name;
enum config_key_type type;
void *data;
};
struct config_section {
const char *name;
const struct config_key *keys;
int num_keys;
void (*done)(void *data);
};
enum weston_option_type {
WESTON_OPTION_INTEGER,
WESTON_OPTION_UNSIGNED_INTEGER,
@ -108,6 +88,9 @@ weston_config_section_get_bool(struct weston_config_section *section,
const char *
weston_config_get_name_from_env(void);
struct weston_config *
weston_config_parse_fp(FILE *file);
struct weston_config *
weston_config_parse(const char *name);
@ -121,10 +104,8 @@ int weston_config_next_section(struct weston_config *config,
struct weston_config_section **section,
const char **name);
#ifdef __cplusplus
}
#endif
#endif /* CONFIGPARSER_H */

@ -81,6 +81,7 @@ struct weston_pointer_constraint;
struct ro_anonymous_file;
struct weston_color_profile;
struct weston_color_transform;
struct pixel_format_info;
enum weston_keyboard_modifier {
MODIFIER_CTRL = (1 << 0),
@ -150,21 +151,6 @@ struct weston_spring {
uint32_t clip;
};
struct weston_output_zoom {
bool active;
float increment;
float level;
float max_level;
float trans_x, trans_y;
struct {
double x, y;
} current;
struct weston_seat *seat;
struct weston_animation animation_z;
struct weston_spring spring_z;
struct wl_listener motion_listener;
};
/* bit compatible with drm definitions. */
enum dpms_enum {
WESTON_DPMS_ON,
@ -222,6 +208,154 @@ struct weston_testsuite_data {
void *test_private_data;
};
/** EOTF mode for outputs and heads
*
* A list of EOTF modes for driving displays, defined by CTA-861-G for
* Dynamic Range and Mastering InfoFrame.
*
* On heads, a bitmask of one or more entries shows which modes are claimed
* supported.
*
* On outputs, the mode to be used for driving the video sink.
*
* For traditional non-HDR sRGB, use WESTON_EOTF_MODE_SDR.
*/
enum weston_eotf_mode {
/** Invalid EOTF mode, or none supported. */
WESTON_EOTF_MODE_NONE = 0,
/** Traditional gamma, SDR luminance range */
WESTON_EOTF_MODE_SDR = 0x01,
/** Traditional gamma, HDR luminance range */
WESTON_EOTF_MODE_TRADITIONAL_HDR = 0x02,
/** Preceptual quantizer, SMPTE ST 2084 */
WESTON_EOTF_MODE_ST2084 = 0x04,
/** Hybrid log-gamma, ITU-R BT.2100 */
WESTON_EOTF_MODE_HLG = 0x08,
};
/** Bitmask of all defined EOTF modes */
#define WESTON_EOTF_MODE_ALL_MASK \
((uint32_t)(WESTON_EOTF_MODE_SDR | WESTON_EOTF_MODE_TRADITIONAL_HDR | \
WESTON_EOTF_MODE_ST2084 | WESTON_EOTF_MODE_HLG))
/** CIE 1931 xy chromaticity coordinates */
struct weston_CIExy {
float x;
float y;
};
enum weston_hdr_metadata_type1_groups {
/** weston_hdr_metadata_type1::primary is set */
WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES = 0x01,
/** weston_hdr_metadata_type1::white is set */
WESTON_HDR_METADATA_TYPE1_GROUP_WHITE = 0x02,
/** weston_hdr_metadata_type1::maxDML is set */
WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML = 0x04,
/** weston_hdr_metadata_type1::minDML is set */
WESTON_HDR_METADATA_TYPE1_GROUP_MINDML = 0x08,
/** weston_hdr_metadata_type1::maxCLL is set */
WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL = 0x10,
/** weston_hdr_metadata_type1::maxFALL is set */
WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL = 0x20,
/** all valid bits */
WESTON_HDR_METADATA_TYPE1_GROUP_ALL_MASK = 0x3f
};
/** HDR static metadata type 1
*
* The fields are defined by CTA-861-G except here they use float encoding.
*
* In Weston used only with HDR display modes.
*/
struct weston_hdr_metadata_type1 {
/** Which fields are valid
*
* A bitmask of values from enum weston_hdr_metadata_type1_groups.
*/
uint32_t group_mask;
/* EOTF is tracked externally with enum weston_eotf_mode */
/** Chromaticities of the primaries, in any order */
struct weston_CIExy primary[3];
/** White point chromaticity */
struct weston_CIExy white;
/** Maximum display mastering luminance, 1 - 65535 cd/m² */
float maxDML;
/** Minimum display mastering luminance, 0.0001 - 6.5535 cd/m² */
float minDML;
/** Maximum content light level, 1 - 65535 cd/m² */
float maxCLL;
/** Maximum frame-average light level, 1 - 65535 cd/m² */
float maxFALL;
};
enum weston_color_characteristics_groups {
/** weston_color_characteristics::primary is set */
WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES = 0x01,
/** weston_color_characteristics::white is set */
WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE = 0x02,
/** weston_color_characteristics::max_luminance is set */
WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL = 0x04,
/** weston_color_characteristics::min_luminance is set */
WESTON_COLOR_CHARACTERISTICS_GROUP_MINL = 0x08,
/** weston_color_characteristics::maxFALL is set */
WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL = 0x10,
/** all valid bits */
WESTON_COLOR_CHARACTERISTICS_GROUP_ALL_MASK = 0x1f
};
/** Basic display color characteristics
*
* This is a simple description of a display or output (monitor) color
* characteristics. The parameters can be found in EDID, with caveats. They
* are particularly useful with HDR monitors.
*/
struct weston_color_characteristics {
/** Which fields are valid
*
* A bitmask of values from enum weston_color_characteristics_groups.
*/
uint32_t group_mask;
/* EOTF is tracked externally with enum weston_eotf_mode */
/** Chromaticities of the primaries */
struct weston_CIExy primary[3];
/** White point chromaticity */
struct weston_CIExy white;
/** Display's desired maximum content peak luminance, cd/m² */
float max_luminance;
/** Display's desired minimum content luminance, cd/m² */
float min_luminance;
/** Display's desired maximum frame-average light level, cd/m² */
float maxFALL;
};
/** Represents a head, usually a display connector
*
* \rst
@ -258,9 +392,36 @@ struct weston_head {
char *name; /**< head name, e.g. connector name */
bool connected; /**< is physically connected */
bool non_desktop; /**< non-desktop display, e.g. HMD */
uint32_t supported_eotf_mask; /**< supported weston_eotf_mode bits */
/** Current content protection status */
enum weston_hdcp_protection current_protection;
/** Opaque pointer used by backends to identify heads as theirs */
const void *backend_id;
};
/** Output properties derived from its color characteristics and profile
*
* These are constructed by a color manager.
*
* A weston_output_color_outcome owns (a reference to) everything it contains.
*
* \ingroup output
* \internal
*/
struct weston_output_color_outcome {
/** sRGB to output color space transformation */
struct weston_color_transform *from_sRGB_to_output;
/** sRGB to blending color space transformation */
struct weston_color_transform *from_sRGB_to_blend;
/** Blending to output color space transformation */
struct weston_color_transform *from_blend_to_output;
/** HDR Static Metadata Type 1 for WESTON_EOTF_MODE_ST2084 */
struct weston_hdr_metadata_type1 hdr_meta;
};
/** Content producer for heads
@ -324,7 +485,6 @@ struct weston_output {
/** For cancelling the idle_repaint callback on output destruction. */
struct wl_event_source *idle_repaint_source;
struct weston_output_zoom zoom;
int dirty;
struct wl_signal frame_signal;
struct wl_signal destroy_signal; /**< sent when disabled */
@ -352,11 +512,9 @@ struct weston_output {
bool allow_protection;
int (*start_repaint_loop)(struct weston_output *output);
int (*repaint)(struct weston_output *output,
pixman_region32_t *damage,
void *repaint_data);
int (*repaint)(struct weston_output *output, pixman_region32_t *damage);
void (*destroy)(struct weston_output *output);
void (*assign_planes)(struct weston_output *output, void *repaint_data);
void (*assign_planes)(struct weston_output *output);
int (*switch_mode)(struct weston_output *output, struct weston_mode *mode);
/* backlight values are on 0-255 range, where higher is brighter */
@ -375,10 +533,12 @@ struct weston_output {
int scale;
struct weston_color_profile *color_profile;
struct weston_color_transform *from_sRGB_to_output;
struct weston_color_transform *from_sRGB_to_blend;
struct weston_color_transform *from_blend_to_output;
bool from_blend_to_output_by_backend;
enum weston_eotf_mode eotf_mode;
struct weston_color_characteristics color_characteristics;
struct weston_output_color_outcome *color_outcome;
uint64_t color_outcome_serial;
int (*enable)(struct weston_output *output);
int (*disable)(struct weston_output *output);
@ -937,39 +1097,6 @@ struct weston_plane {
struct weston_drm_format_array;
struct weston_renderer {
int (*read_pixels)(struct weston_output *output,
pixman_format_code_t format, void *pixels,
uint32_t x, uint32_t y,
uint32_t width, uint32_t height);
void (*repaint_output)(struct weston_output *output,
pixman_region32_t *output_damage);
void (*flush_damage)(struct weston_surface *surface);
void (*attach)(struct weston_surface *es, struct weston_buffer *buffer);
void (*surface_set_color)(struct weston_surface *surface,
float red, float green,
float blue, float alpha);
void (*destroy)(struct weston_compositor *ec);
/** See weston_surface_get_content_size() */
void (*surface_get_content_size)(struct weston_surface *surface,
int *width, int *height);
/** See weston_surface_copy_content() */
int (*surface_copy_content)(struct weston_surface *surface,
void *target, size_t size,
int src_x, int src_y,
int width, int height);
/** See weston_compositor_import_dmabuf() */
bool (*import_dmabuf)(struct weston_compositor *ec,
struct linux_dmabuf_buffer *buffer);
const struct weston_drm_format_array *
(*get_supported_formats)(struct weston_compositor *ec);
};
enum weston_capability {
/* backend/renderer supports arbitrary rotation */
WESTON_CAP_ROTATION_ANY = 0x0001,
@ -1126,7 +1253,7 @@ struct weston_compositor {
struct weston_color_manager *color_manager;
struct weston_renderer *renderer;
pixman_format_code_t read_format;
const struct pixel_format_info *read_format;
struct weston_backend *backend;
struct weston_launcher *launcher;
@ -1137,6 +1264,7 @@ struct weston_compositor {
struct wl_list plugin_api_list; /* struct weston_plugin_api::link */
uint32_t output_id_pool;
bool output_flow_dirty;
struct xkb_rule_names xkb_names;
struct xkb_context *xkb_context;
@ -1182,8 +1310,17 @@ struct weston_compositor {
struct weston_log_context *weston_log_ctx;
struct weston_log_scope *debug_scene;
struct weston_log_scope *timeline;
struct weston_log_scope *libseat_debug;
struct content_protection *content_protection;
/* One-time warning about a view appearing in the layer list when it
* or its surface are not mapped. */
bool warned_about_unmapped_surface_or_view;
};
struct weston_solid_buffer_values {
float r, g, b, a;
};
struct weston_buffer {
@ -1191,19 +1328,45 @@ struct weston_buffer {
struct wl_signal destroy_signal;
struct wl_listener destroy_listener;
enum {
WESTON_BUFFER_SHM,
WESTON_BUFFER_DMABUF,
WESTON_BUFFER_RENDERER_OPAQUE,
WESTON_BUFFER_SOLID,
} type;
union {
struct wl_shm_buffer *shm_buffer;
void *dmabuf;
void *legacy_buffer;
struct weston_solid_buffer_values solid;
};
int32_t width, height;
uint32_t busy_count;
int y_inverted;
uint32_t passive_count;
enum {
ORIGIN_TOP_LEFT, /* buffer content starts at (0,0) */
ORIGIN_BOTTOM_LEFT, /* buffer content starts at (0, height) */
} buffer_origin;
bool direct_display;
void *renderer_private;
void *backend_private;
const struct pixel_format_info *pixel_format;
uint64_t format_modifier;
};
enum weston_buffer_reference_type {
BUFFER_REF_NONE,
BUFFER_MAY_BE_ACCESSED,
BUFFER_WILL_NOT_BE_ACCESSED,
};
struct weston_buffer_reference {
struct weston_buffer *buffer;
struct wl_listener destroy_listener;
enum weston_buffer_reference_type type;
};
struct weston_buffer_viewport {
@ -1377,6 +1540,7 @@ struct weston_surface_state {
int newly_attached;
struct weston_buffer *buffer;
struct wl_listener buffer_destroy_listener;
int32_t sx;
int32_t sy;
@ -1762,6 +1926,21 @@ weston_surface_create(struct weston_compositor *compositor);
struct weston_view *
weston_view_create(struct weston_surface *surface);
struct weston_buffer_reference *
weston_buffer_create_solid_rgba(struct weston_compositor *compositor,
float r, float g, float b, float a);
void
weston_surface_attach_solid(struct weston_surface *surface,
struct weston_buffer_reference *buffer_ref,
int w, int h);
void
weston_buffer_destroy_solid(struct weston_buffer_reference *buffer_ref);
bool
weston_surface_has_content(struct weston_surface *surface);
void
weston_view_destroy(struct weston_view *view);
@ -1802,6 +1981,9 @@ weston_view_damage_below(struct weston_view *view);
void
weston_view_unmap(struct weston_view *view);
void
weston_surface_map(struct weston_surface *surface);
void
weston_surface_unmap(struct weston_surface *surface);
@ -1835,7 +2017,8 @@ weston_surface_copy_content(struct weston_surface *surface,
int width, int height);
struct weston_buffer *
weston_buffer_from_resource(struct wl_resource *resource);
weston_buffer_from_resource(struct weston_compositor *ec,
struct wl_resource *resource);
void
weston_compositor_get_time(struct timespec *time);
@ -1858,7 +2041,6 @@ weston_compositor_add_destroy_listener_once(struct weston_compositor *compositor
enum weston_compositor_backend {
WESTON_BACKEND_DRM,
WESTON_BACKEND_FBDEV,
WESTON_BACKEND_HEADLESS,
WESTON_BACKEND_RDP,
WESTON_BACKEND_WAYLAND,
@ -1877,11 +2059,6 @@ void
weston_compositor_exit_with_code(struct weston_compositor *compositor,
int exit_code);
void
weston_output_update_zoom(struct weston_output *output);
void
weston_output_activate_zoom(struct weston_output *output,
struct weston_seat *seat);
void
weston_output_add_destroy_listener(struct weston_output *output,
struct wl_listener *listener);
struct wl_listener *
@ -1960,12 +2137,11 @@ struct weston_view_animation *
weston_slide_run(struct weston_view *view, float start, float stop,
weston_view_animation_done_func_t done, void *data);
void
weston_surface_set_color(struct weston_surface *surface,
float red, float green, float blue, float alpha);
struct weston_surface *
weston_surface_ref(struct weston_surface *surface);
void
weston_surface_destroy(struct weston_surface *surface);
weston_surface_unref(struct weston_surface *surface);
int
weston_output_mode_switch_to_temporary(struct weston_output *output,
@ -2068,11 +2244,7 @@ weston_compositor_find_output_by_name(struct weston_compositor *compositor,
struct weston_output *
weston_compositor_create_output(struct weston_compositor *compositor,
const char *name);
struct weston_output *
weston_compositor_create_output_with_head(struct weston_compositor *compositor,
struct weston_head *head);
struct weston_head *head, const char *name);
void
weston_output_destroy(struct weston_output *output);
@ -2097,6 +2269,20 @@ bool
weston_output_set_color_profile(struct weston_output *output,
struct weston_color_profile *cprof);
void
weston_output_set_eotf_mode(struct weston_output *output,
enum weston_eotf_mode eotf_mode);
enum weston_eotf_mode
weston_output_get_eotf_mode(const struct weston_output *output);
void
weston_output_set_color_characteristics(struct weston_output *output,
const struct weston_color_characteristics *cc);
const struct weston_color_characteristics *
weston_output_get_color_characteristics(struct weston_output *output);
void
weston_output_init(struct weston_output *output,
struct weston_compositor *compositor,
@ -2111,6 +2297,9 @@ weston_output_enable(struct weston_output *output);
void
weston_output_disable(struct weston_output *output);
uint32_t
weston_output_get_supported_eotf_modes(struct weston_output *output);
void
weston_compositor_flush_heads_changed(struct weston_compositor *compositor);

@ -65,19 +65,6 @@ int
weston_matrix_invert(struct weston_matrix *inverse,
const struct weston_matrix *matrix);
#ifdef UNIT_TEST
# define MATRIX_TEST_EXPORT WL_EXPORT
int
matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix);
void
inverse_transform(const double *LU, const unsigned *p, float *v);
#else
# define MATRIX_TEST_EXPORT static
#endif
#ifdef __cplusplus
}
#endif

@ -12,7 +12,6 @@ install_headers(
)
backend_drm_h = files('backend-drm.h')
backend_fbdev_h = files('backend-fbdev.h')
backend_headless_h = files('backend-headless.h')
backend_rdp_h = files('backend-rdp.h')
backend_wayland_h = files('backend-wayland.h')

@ -109,6 +109,8 @@ weston_log_subscription_complete(struct weston_log_subscription *sub);
char *
weston_log_scope_timestamp(struct weston_log_scope *scope,
char *buf, size_t len);
char *
weston_log_timestamp(char *buf, size_t len, int *cached_tm_mday);
void
weston_log_subscriber_destroy(struct weston_log_subscriber *subscriber);

@ -1,78 +0,0 @@
In-vehicle infotainment (information and entertainment)
graphical environment support modules for Weston
IVI-shell is an alternative shell for Weston, a Wayland display server.
Window management and application interaction with the display server
are very different to that of a normal desktop, which is why this is
a separate shell and not an extension to the desktop-shell suite with
xdg_shell. As such, applications need to be specifically written to use
IVI-shell.
IVI-shell contains two main features:
- Common layout library for surface, which allow ivi-shell developer
to develop own shell, linking Common layout library.
For the time being, the library refers Genivi ilm interface.
https://at.projects.genivi.org/wiki/display/WIE/Wayland+IVI+Extension+Home
- Extension protocol; ivi-application to tie wl_surface and a given ID.
With this ID, shell can identify which wl_surface is drawn by which
application. In in-vehicle infortainment system, a shell has to update
a property of a wl_surface. E.g. there may be a use case when vehicle
starts to move, the wl_surface drawn by Car navigation is expected to
move top of surfaces.
The actual software components delivered with Weston are:
- ivi-application.xml:
Wayland protocol extension for IVI-applications; the public
shell protocol (the same concept as xdg_shell).
Implemented by ivi-shell.so.
- ivi-shell.so:
A Weston shell module that implements ivi-application.xml interfaces.
Loads ivi-layout.so.
- ivi-layout.so:
Implements the IVI window management concepts: Screen, Layer,
Surface, groups of Layers, groups of Surfaces, see:
https://at.projects.genivi.org/wiki/display/WIE/Summary+of+Layer+manager+APIs
Offers a stable API for writing IVI-controller modules like
hmi-controller.so against the IVI concepts. In other words,
it offers an API to write IVI window manager modules.
- hmi-controller.so:
A sample implementation of an IVI-controller module, usually
replaced by IVI system vendors.
Uses ivi-layout.so to perform essentially window manager tasks.
This implementation keeps all window management inside the module,
while IVI-systems may use another module that exposes all window
management via Wayland or other protocol for an external process
to control.
- ivi-hmi-controller.xml:
Wayland protocol extension for IVI display control; the private
shell protocol for weston-ivi-shell-user-interface client
(the same concept as desktop-shell.xml).
Implemented by hmi-controller.so, and usually replaced by IVI
system vendors.
- weston-ivi-shell-user-interface:
A sample implementation of an IVI shell helper client, usually
replaced by IVI system vendors.
A helper client for basic display content, similar to
weston-desktop-shell.
How to compile:
same as weston. To disable, use option: --disable-ivi-shell for configure.
How to configure weston.ini:
reference ini file will be generated in <build_dir>/ivi-shell.
How to run:
same as weston. exec weston.
How to use UI:
http://lists.freedesktop.org/archives/wayland-devel/attachments/20140625/abbfc064/attachment-0001.png

@ -153,13 +153,6 @@ struct launcher_info {
/*****************************************************************************
* local functions
****************************************************************************/
static void *
mem_alloc(size_t size, char *file, int32_t line)
{
return fail_on_null(calloc(1, size), size, file, line);
}
#define MEM_ALLOC(s) mem_alloc((s),__FILE__,__LINE__)
static int32_t
is_surf_in_ui_widget(struct hmi_controller *hmi_ctrl,
@ -222,8 +215,8 @@ mode_divided_into_tiling(struct hmi_controller *hmi_ctrl,
int32_t surf_num = 0;
int32_t idx = 0;
surfaces = MEM_ALLOC(sizeof(*surfaces) * surface_length);
new_order = MEM_ALLOC(sizeof(*surfaces) * surface_length);
surfaces = xcalloc(surface_length, sizeof(*surfaces));
new_order = xcalloc(surface_length, sizeof(*surfaces));
for (i = 0; i < surface_length; i++) {
ivisurf = pp_surface[i];
@ -297,8 +290,8 @@ mode_divided_into_sidebyside(struct hmi_controller *hmi_ctrl,
int32_t surf_num = 0;
int32_t idx = 0;
surfaces = MEM_ALLOC(sizeof(*surfaces) * surface_length);
new_order = MEM_ALLOC(sizeof(*surfaces) * surface_length);
surfaces = xcalloc(surface_length, sizeof(*surfaces));
new_order = xcalloc(surface_length, sizeof(*surfaces));
for (i = 0; i < surface_length; i++) {
ivisurf = pp_surface[i];
@ -362,7 +355,7 @@ mode_fullscreen_someone(struct hmi_controller *hmi_ctrl,
int32_t surf_num = 0;
struct ivi_layout_surface **surfaces;
surfaces = MEM_ALLOC(sizeof(*surfaces) * surface_length);
surfaces = xcalloc(surface_length, sizeof(*surfaces));
for (i = 0; i < surface_length; i++) {
ivisurf = pp_surface[i];
@ -412,7 +405,7 @@ mode_random_replace(struct hmi_controller *hmi_ctrl,
int32_t i = 0;
int32_t layer_idx = 0;
layers = MEM_ALLOC(sizeof(*layers) * hmi_ctrl->screen_num);
layers = xcalloc(hmi_ctrl->screen_num, sizeof(*layers));
wl_list_for_each(application_layer, layer_list, link) {
layers[layer_idx] = application_layer;
@ -689,7 +682,7 @@ set_notification_configure_desktop_surface(struct wl_listener *listener, void *d
static struct hmi_server_setting *
hmi_server_setting_create(struct weston_compositor *ec)
{
struct hmi_server_setting *setting = MEM_ALLOC(sizeof(*setting));
struct hmi_server_setting *setting = xzalloc(sizeof(*setting));
struct weston_config *config = wet_get_config(ec);
struct weston_config_section *shell_section = NULL;
char *ivi_ui_config;
@ -745,6 +738,8 @@ hmi_controller_destroy(struct wl_listener *listener, void *data)
struct hmi_controller *hmi_ctrl =
container_of(listener, struct hmi_controller, destroy_listener);
wl_list_remove(&hmi_ctrl->destroy_listener.link);
wl_list_for_each_safe(link, next,
&hmi_ctrl->workspace_fade.layer_list, link) {
wl_list_remove(&link->link);
@ -804,7 +799,7 @@ hmi_controller_create(struct weston_compositor *ec)
return NULL;
}
hmi_ctrl = MEM_ALLOC(sizeof(*hmi_ctrl));
hmi_ctrl = xzalloc(sizeof(*hmi_ctrl));
i = 0;
wl_array_init(&hmi_ctrl->ui_widgets);
@ -817,7 +812,7 @@ hmi_controller_create(struct weston_compositor *ec)
/* init base ivi_layer*/
wl_list_init(&hmi_ctrl->base_layer_list);
wl_list_for_each(output, &ec->output_list, link) {
base_layer = MEM_ALLOC(1 * sizeof(struct hmi_controller_layer));
base_layer = xzalloc(sizeof(struct hmi_controller_layer));
base_layer->x = 0;
base_layer->y = 0;
base_layer->width = output->current_mode->width;
@ -837,7 +832,7 @@ hmi_controller_create(struct weston_compositor *ec)
/* init application ivi_layer */
wl_list_init(&hmi_ctrl->application_layer_list);
wl_list_for_each(output, &ec->output_list, link) {
application_layer = MEM_ALLOC(1 * sizeof(struct hmi_controller_layer));
application_layer = xzalloc(sizeof(struct hmi_controller_layer));
application_layer->x = 0;
application_layer->y = 0;
application_layer->width = output->current_mode->width;
@ -872,7 +867,7 @@ hmi_controller_create(struct weston_compositor *ec)
wl_list_init(&hmi_ctrl->workspace_fade.layer_list);
tmp_link_layer = MEM_ALLOC(sizeof(*tmp_link_layer));
tmp_link_layer = xzalloc(sizeof(*tmp_link_layer));
tmp_link_layer->layout_layer =
hmi_ctrl->workspace_background_layer.ivilayer;
wl_list_insert(&hmi_ctrl->workspace_fade.layer_list,
@ -1267,7 +1262,7 @@ ivi_hmi_controller_add_launchers(struct hmi_controller *hmi_ctrl,
hmi_ctrl->interface->layer_set_visibility(hmi_ctrl->workspace_layer.ivilayer,
false);
tmp_link_layer = MEM_ALLOC(sizeof(*tmp_link_layer));
tmp_link_layer = xzalloc(sizeof(*tmp_link_layer));
tmp_link_layer->layout_layer = hmi_ctrl->workspace_layer.ivilayer;
wl_list_insert(&hmi_ctrl->workspace_fade.layer_list,
&tmp_link_layer->link);
@ -1756,7 +1751,7 @@ create_workspace_pointer_move(struct weston_pointer *pointer,
struct wl_resource* resource)
{
struct pointer_move_grab *pnt_move_grab =
MEM_ALLOC(sizeof(*pnt_move_grab));
xzalloc(sizeof(*pnt_move_grab));
pnt_move_grab->base.resource = resource;
move_grab_init_workspace(&pnt_move_grab->move, pointer->grab_x,
@ -1770,7 +1765,7 @@ create_workspace_touch_move(struct weston_touch *touch,
struct wl_resource* resource)
{
struct touch_move_grab *tch_move_grab =
MEM_ALLOC(sizeof(*tch_move_grab));
xzalloc(sizeof(*tch_move_grab));
tch_move_grab->base.resource = resource;
tch_move_grab->is_active = 1;

@ -43,9 +43,6 @@
* way in In-Vehicle Infotainment system, which integrate several domains
* in one system. A layer is allocated to a domain in order to control
* application surfaces grouped to the layer all together.
*
* This API and ABI follow following specifications.
* https://at.projects.genivi.org/wiki/display/PROJ/Wayland+IVI+Extension+Design
*/
#ifndef _IVI_LAYOUT_EXPORT_H_
@ -67,7 +64,6 @@ extern "C" {
#define IVI_INVALID_ID UINT_MAX
struct ivi_layout_layer;
struct ivi_layout_screen;
struct ivi_layout_surface;
struct ivi_layout_surface_properties

@ -44,7 +44,8 @@ ivi_layout_desktop_surface_configure(struct ivi_layout_surface *ivisurf,
int32_t width, int32_t height);
struct ivi_layout_surface*
ivi_layout_desktop_surface_create(struct weston_surface *wl_surface);
ivi_layout_desktop_surface_create(struct weston_surface *wl_surface,
struct weston_desktop_surface *surface);
void
ivi_layout_surface_configure(struct ivi_layout_surface *ivisurf,

@ -835,7 +835,7 @@ build_view_list(struct ivi_layout *layout)
weston_layer_entry_insert(&layout->layout_layer.view_list,
&ivi_view->view->layer_link);
ivi_view->ivisurf->surface->is_mapped = true;
weston_surface_map(ivi_view->ivisurf->surface);
ivi_view->view->is_mapped = true;
}
}
@ -1192,15 +1192,14 @@ ivi_layout_get_layers_under_surface(struct ivi_layout_surface *ivisurf,
else
length--;
}
if (length == 0) {
free(*ppArray);
*ppArray = NULL;
}
}
*pLength = length;
if (!length) {
free(*ppArray);
*ppArray = NULL;
}
return IVI_SUCCEEDED;
}
@ -1882,7 +1881,6 @@ ivi_layout_surface_set_id(struct ivi_layout_surface *ivisurf,
ivisurf->id_surface = id_surface;
wl_signal_emit(&layout->surface_notification.created, ivisurf);
wl_signal_emit(&layout->surface_notification.configure_changed,
ivisurf);
@ -1979,9 +1977,20 @@ ivi_layout_desktop_surface_configure(struct ivi_layout_surface *ivisurf,
}
struct ivi_layout_surface*
ivi_layout_desktop_surface_create(struct weston_surface *wl_surface)
ivi_layout_desktop_surface_create(struct weston_surface *wl_surface,
struct weston_desktop_surface *surface)
{
return surface_create(wl_surface, IVI_INVALID_ID);
struct ivi_layout *layout = get_instance();
struct ivi_layout_surface *ivisurf;
ivisurf = surface_create(wl_surface, IVI_INVALID_ID);
if (ivisurf) {
ivisurf->weston_desktop_surface = surface;
wl_signal_emit(&layout->surface_notification.created, ivisurf);
}
return ivisurf;
}
void

@ -346,7 +346,6 @@ shell_destroy(struct wl_listener *listener, void *data)
wl_list_remove(&shell->destroy_listener.link);
wl_list_remove(&shell->wake_listener.link);
weston_desktop_destroy(shell->desktop);
wl_list_for_each_safe(ivisurf, next, &shell->ivi_surface_list, link) {
if (ivisurf->layout_surface != NULL)
@ -357,6 +356,7 @@ shell_destroy(struct wl_listener *listener, void *data)
ivi_layout_fini();
weston_desktop_destroy(shell->desktop);
free(shell);
}
@ -489,13 +489,11 @@ desktop_surface_added(struct weston_desktop_surface *surface,
struct weston_surface *weston_surf =
weston_desktop_surface_get_surface(surface);
layout_surface = ivi_layout_desktop_surface_create(weston_surf);
layout_surface = ivi_layout_desktop_surface_create(weston_surf, surface);
if (!layout_surface) {
return;
}
layout_surface->weston_desktop_surface = surface;
ivisurf = zalloc(sizeof *ivisurf);
if (!ivisurf) {
return;
@ -509,6 +507,8 @@ desktop_surface_added(struct weston_desktop_surface *surface,
ivisurf->layout_surface = layout_surface;
ivisurf->surface = weston_surf;
wl_list_insert(&shell->ivi_surface_list, &ivisurf->link);
weston_desktop_surface_set_user_data(surface, ivisurf);
}
@ -521,8 +521,14 @@ desktop_surface_removed(struct weston_desktop_surface *surface,
assert(ivisurf != NULL);
weston_desktop_surface_set_user_data(surface, NULL);
if (ivisurf->layout_surface)
layout_surface_cleanup(ivisurf);
wl_list_remove(&ivisurf->link);
free(ivisurf);
}
static void

@ -15,7 +15,6 @@ if get_option('shell-ivi')
dependencies: [
dep_libm,
dep_libexec_weston,
dep_lib_desktop,
dep_libweston_public
],
name_prefix: '',

@ -33,7 +33,7 @@
#include "kiosk-shell-grab.h"
#include "compositor/weston.h"
#include "shared/helpers.h"
#include "shared/shell-utils.h"
#include "shell-utils.h"
#include <libweston/xwayland-api.h>
@ -413,6 +413,7 @@ kiosk_shell_surface_activate(struct kiosk_shell_surface *shsurf,
weston_layer_entry_insert(&shsurf->shell->normal_layer.view_list,
&shsurf->view->layer_link);
weston_view_geometry_dirty(shsurf->view);
weston_view_update_transform(shsurf->view);
weston_surface_damage(shsurf->view->surface);
}
@ -480,13 +481,14 @@ static void
kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput)
{
struct kiosk_shell *shell = shoutput->shell;
struct weston_compositor *ec = shell->compositor;
struct weston_output *output = shoutput->output;
struct weston_config_section *shell_section = NULL;
uint32_t bg_color = 0x0;
struct weston_solid_color_surface solid_surface = {};
struct weston_curtain_params curtain_params = {};
if (shoutput->background_view)
weston_surface_destroy(shoutput->background_view->surface);
if (shoutput->curtain)
weston_curtain_destroy(shoutput->curtain);
if (!output)
return;
@ -497,31 +499,33 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput)
weston_config_section_get_color(shell_section, "background-color",
&bg_color, 0x00000000);
solid_surface.r = ((bg_color >> 16) & 0xff) / 255.0;
solid_surface.g = ((bg_color >> 8) & 0xff) / 255.0;
solid_surface.b = ((bg_color >> 0) & 0xff) / 255.0;
curtain_params.r = ((bg_color >> 16) & 0xff) / 255.0;
curtain_params.g = ((bg_color >> 8) & 0xff) / 255.0;
curtain_params.b = ((bg_color >> 0) & 0xff) / 255.0;
curtain_params.a = 1.0;
curtain_params.x = output->x;
curtain_params.y = output->y;
curtain_params.width = output->width;
curtain_params.height = output->height;
solid_surface.get_label = kiosk_shell_background_surface_get_label;
solid_surface.surface_committed = NULL;
solid_surface.surface_private = NULL;
curtain_params.capture_input = true;
shoutput->background_view =
create_solid_color_surface(shoutput->shell->compositor,
&solid_surface,
output->x, output->y,
output->width,
output->height);
curtain_params.get_label = kiosk_shell_background_surface_get_label;
curtain_params.surface_committed = NULL;
curtain_params.surface_private = NULL;
weston_surface_set_role(shoutput->background_view->surface,
shoutput->curtain = weston_curtain_create(ec, &curtain_params);
weston_surface_set_role(shoutput->curtain->view->surface,
"kiosk-shell-background", NULL, 0);
weston_layer_entry_insert(&shell->background_layer.view_list,
&shoutput->background_view->layer_link);
&shoutput->curtain->view->layer_link);
shoutput->background_view->is_mapped = true;
shoutput->background_view->surface->is_mapped = true;
shoutput->background_view->surface->output = output;
weston_view_set_output(shoutput->background_view, output);
shoutput->curtain->view->is_mapped = true;
shoutput->curtain->view->surface->output = output;
weston_view_set_output(shoutput->curtain->view, output);
}
static void
@ -530,8 +534,8 @@ kiosk_shell_output_destroy(struct kiosk_shell_output *shoutput)
shoutput->output = NULL;
shoutput->output_destroy_listener.notify = NULL;
if (shoutput->background_view)
weston_surface_destroy(shoutput->background_view->surface);
if (shoutput->curtain)
weston_curtain_destroy(shoutput->curtain);
wl_list_remove(&shoutput->output_destroy_listener.link);
wl_list_remove(&shoutput->link);
@ -795,7 +799,7 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface,
struct kiosk_shell_seat *kiosk_seat;
shsurf->view->is_mapped = true;
surface->is_mapped = true;
weston_surface_map(surface);
kiosk_seat = get_kiosk_shell_seat(seat);
if (seat && kiosk_seat)
@ -948,6 +952,17 @@ desktop_surface_set_xwayland_position(struct weston_desktop_surface *desktop_sur
shsurf->xwayland.is_set = true;
}
static void
desktop_surface_get_position(struct weston_desktop_surface *desktop_surface,
int32_t *x, int32_t *y, void *shell)
{
struct kiosk_shell_surface *shsurf =
weston_desktop_surface_get_user_data(desktop_surface);
*x = shsurf->view->geometry.x;
*y = shsurf->view->geometry.y;
}
static const struct weston_desktop_api kiosk_shell_desktop_api = {
.struct_size = sizeof(struct weston_desktop_api),
.surface_added = desktop_surface_added,
@ -962,6 +977,7 @@ static const struct weston_desktop_api kiosk_shell_desktop_api = {
.ping_timeout = desktop_surface_ping_timeout,
.pong = desktop_surface_pong,
.set_xwayland_position = desktop_surface_set_xwayland_position,
.get_position = desktop_surface_get_position,
};
/*
@ -1049,6 +1065,10 @@ kiosk_shell_touch_to_activate_binding(struct weston_touch *touch,
static void
kiosk_shell_add_bindings(struct kiosk_shell *shell)
{
uint32_t mod = 0;
mod = weston_shell_get_binding_modifier(shell->config, MODIFIER_SUPER);
weston_compositor_add_button_binding(shell->compositor, BTN_LEFT, 0,
kiosk_shell_click_to_activate_binding,
shell);
@ -1058,6 +1078,8 @@ kiosk_shell_add_bindings(struct kiosk_shell *shell)
weston_compositor_add_touch_binding(shell->compositor, 0,
kiosk_shell_touch_to_activate_binding,
shell);
weston_install_debug_key_binding(shell->compositor, mod);
}
static void

@ -88,7 +88,7 @@ struct kiosk_shell_seat {
struct kiosk_shell_output {
struct weston_output *output;
struct wl_listener output_destroy_listener;
struct weston_view *background_view;
struct weston_curtain *curtain;
struct kiosk_shell *shell;
struct wl_list link;

@ -2,9 +2,6 @@ if get_option('shell-kiosk')
srcs_shell_kiosk = [
'kiosk-shell.c',
'kiosk-shell-grab.c',
'../shared/shell-utils.c',
weston_desktop_shell_server_protocol_h,
weston_desktop_shell_protocol_c,
input_method_unstable_v1_server_protocol_h,
input_method_unstable_v1_protocol_c,
]
@ -12,8 +9,8 @@ if get_option('shell-kiosk')
dep_libm,
dep_libexec_weston,
dep_libshared,
dep_lib_desktop,
dep_libweston_public,
dep_shell_utils,
]
plugin_shell_kiosk = shared_library(
'kiosk-shell',

@ -1,36 +0,0 @@
srcs_libdesktop = [
'libweston-desktop.c',
'client.c',
'seat.c',
'surface.c',
'xwayland.c',
'wl-shell.c',
'xdg-shell.c',
'xdg-shell-v6.c',
xdg_shell_unstable_v6_server_protocol_h,
xdg_shell_unstable_v6_protocol_c,
xdg_shell_server_protocol_h,
xdg_shell_protocol_c,
]
lib_desktop = shared_library(
'weston-desktop-@0@'.format(libweston_major),
srcs_libdesktop,
include_directories: common_inc,
install: true,
version: '0.0.@0@'.format(libweston_revision),
dependencies: dep_libweston_public
)
dep_lib_desktop = declare_dependency(
link_with: lib_desktop,
dependencies: dep_libweston_public
)
pkgconfig.generate(
lib_desktop,
filebase: 'libweston-desktop-@0@'.format(libweston_major),
name: 'libweston-desktop',
version: version_weston,
description: 'Desktop shells abstraction library for libweston compositors',
requires_private: [ lib_weston, dep_wayland_server ],
subdirs: dir_include_libweston
)

@ -1,497 +0,0 @@
/*
* Copyright © 2010-2012 Intel Corporation
* Copyright © 2011-2012 Collabora, Ltd.
* Copyright © 2013 Raspberry Pi Foundation
* Copyright © 2016 Quentin "Sardem FF7" Glidic
*
* 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.
*/
#include "config.h"
#include <assert.h>
#include <wayland-server.h>
#include <libweston/libweston.h>
#include <libweston/zalloc.h>
#include <libweston-desktop/libweston-desktop.h>
#include "internal.h"
#define WD_WL_SHELL_PROTOCOL_VERSION 1
enum weston_desktop_wl_shell_surface_state {
NONE,
TOPLEVEL,
MAXIMIZED,
FULLSCREEN,
TRANSIENT,
POPUP,
};
struct weston_desktop_wl_shell_surface {
struct wl_resource *resource;
struct weston_desktop *desktop;
struct wl_display *display;
struct weston_desktop_surface *surface;
struct weston_desktop_surface *parent;
bool added;
struct weston_desktop_seat *popup_seat;
enum weston_desktop_wl_shell_surface_state state;
struct wl_listener wl_surface_resource_destroy_listener;
};
static void
weston_desktop_wl_shell_surface_set_size(struct weston_desktop_surface *dsurface,
void *user_data,
int32_t width, int32_t height)
{
struct weston_desktop_wl_shell_surface *surface = user_data;
struct weston_surface *wsurface =
weston_desktop_surface_get_surface(surface->surface);
if ((wsurface->width == width && wsurface->height == height) ||
(width == 0 && height == 0))
return;
wl_shell_surface_send_configure(surface->resource,
WL_SHELL_SURFACE_RESIZE_NONE,
width, height);
}
static void
weston_desktop_wl_shell_surface_maybe_ungrab(struct weston_desktop_wl_shell_surface *surface)
{
if (surface->state != POPUP ||
!weston_desktop_surface_get_grab(surface->surface))
return;
weston_desktop_surface_popup_ungrab(surface->surface,
surface->popup_seat);
surface->popup_seat = NULL;
}
static void
weston_desktop_wl_shell_surface_committed(struct weston_desktop_surface *dsurface,
void *user_data,
int32_t sx, int32_t sy)
{
struct weston_desktop_wl_shell_surface *surface = user_data;
struct weston_surface *wsurface =
weston_desktop_surface_get_surface(dsurface);
if (wsurface->buffer_ref.buffer == NULL)
weston_desktop_wl_shell_surface_maybe_ungrab(surface);
if (surface->added)
weston_desktop_api_committed(surface->desktop, surface->surface,
sx, sy);
}
static void
weston_desktop_wl_shell_surface_ping(struct weston_desktop_surface *dsurface,
uint32_t serial, void *user_data)
{
struct weston_desktop_wl_shell_surface *surface = user_data;
wl_shell_surface_send_ping(surface->resource, serial);
}
static void
weston_desktop_wl_shell_surface_close(struct weston_desktop_surface *dsurface,
void *user_data)
{
struct weston_desktop_wl_shell_surface *surface = user_data;
if (surface->state == POPUP)
wl_shell_surface_send_popup_done(surface->resource);
}
static bool
weston_desktop_wl_shell_surface_get_maximized(struct weston_desktop_surface *dsurface,
void *user_data)
{
struct weston_desktop_wl_shell_surface *surface = user_data;
return surface->state == MAXIMIZED;
}
static bool
weston_desktop_wl_shell_surface_get_fullscreen(struct weston_desktop_surface *dsurface,
void *user_data)
{
struct weston_desktop_wl_shell_surface *surface = user_data;
return surface->state == FULLSCREEN;
}
static void
weston_desktop_wl_shell_change_state(struct weston_desktop_wl_shell_surface *surface,
enum weston_desktop_wl_shell_surface_state state,
struct weston_desktop_surface *parent,
int32_t x, int32_t y)
{
bool to_add = (parent == NULL);
assert(state != NONE);
if (to_add && surface->added) {
surface->state = state;
return;
}
if (surface->state != state) {
if (surface->state == POPUP)
weston_desktop_wl_shell_surface_maybe_ungrab(surface);
if (to_add) {
weston_desktop_surface_unset_relative_to(surface->surface);
weston_desktop_api_surface_added(surface->desktop,
surface->surface);
} else if (surface->added) {
weston_desktop_api_surface_removed(surface->desktop,
surface->surface);
}
surface->state = state;
surface->added = to_add;
}
if (parent != NULL)
weston_desktop_surface_set_relative_to(surface->surface, parent,
x, y, false);
}
static void
weston_desktop_wl_shell_surface_destroy(struct weston_desktop_surface *dsurface,
void *user_data)
{
struct weston_desktop_wl_shell_surface *surface = user_data;
wl_list_remove(&surface->wl_surface_resource_destroy_listener.link);
weston_desktop_wl_shell_surface_maybe_ungrab(surface);
weston_desktop_surface_unset_relative_to(surface->surface);
if (surface->added)
weston_desktop_api_surface_removed(surface->desktop,
surface->surface);
free(surface);
}
static void
weston_desktop_wl_shell_surface_protocol_pong(struct wl_client *wl_client,
struct wl_resource *resource,
uint32_t serial)
{
struct weston_desktop_surface *surface = wl_resource_get_user_data(resource);
weston_desktop_client_pong(weston_desktop_surface_get_client(surface), serial);
}
static void
weston_desktop_wl_shell_surface_protocol_move(struct wl_client *wl_client,
struct wl_resource *resource,
struct wl_resource *seat_resource,
uint32_t serial)
{
struct weston_desktop_surface *dsurface =
wl_resource_get_user_data(resource);
struct weston_seat *seat =
wl_resource_get_user_data(seat_resource);
struct weston_desktop_wl_shell_surface *surface =
weston_desktop_surface_get_implementation_data(dsurface);
if (seat == NULL)
return;
weston_desktop_api_move(surface->desktop, dsurface, seat, serial);
}
static void
weston_desktop_wl_shell_surface_protocol_resize(struct wl_client *wl_client,
struct wl_resource *resource,
struct wl_resource *seat_resource,
uint32_t serial,
enum wl_shell_surface_resize edges)
{
struct weston_desktop_surface *dsurface =
wl_resource_get_user_data(resource);
struct weston_seat *seat = wl_resource_get_user_data(seat_resource);
struct weston_desktop_wl_shell_surface *surface =
weston_desktop_surface_get_implementation_data(dsurface);
enum weston_desktop_surface_edge surf_edges =
(enum weston_desktop_surface_edge) edges;
if (seat == NULL)
return;
weston_desktop_api_resize(surface->desktop, dsurface, seat, serial, surf_edges);
}
static void
weston_desktop_wl_shell_surface_protocol_set_toplevel(struct wl_client *wl_client,
struct wl_resource *resource)
{
struct weston_desktop_surface *dsurface =
wl_resource_get_user_data(resource);
struct weston_desktop_wl_shell_surface *surface =
weston_desktop_surface_get_implementation_data(dsurface);
weston_desktop_wl_shell_change_state(surface, TOPLEVEL, NULL, 0, 0);
if (surface->parent == NULL)
return;
surface->parent = NULL;
weston_desktop_api_set_parent(surface->desktop, surface->surface, NULL);
}
static void
weston_desktop_wl_shell_surface_protocol_set_transient(struct wl_client *wl_client,
struct wl_resource *resource,
struct wl_resource *parent_resource,
int32_t x, int32_t y,
enum wl_shell_surface_transient flags)
{
struct weston_desktop_surface *dsurface =
wl_resource_get_user_data(resource);
struct weston_surface *wparent =
wl_resource_get_user_data(parent_resource);
struct weston_desktop_surface *parent;
struct weston_desktop_wl_shell_surface *surface =
weston_desktop_surface_get_implementation_data(dsurface);
if (!weston_surface_is_desktop_surface(wparent))
return;
parent = weston_surface_get_desktop_surface(wparent);
if (flags & WL_SHELL_SURFACE_TRANSIENT_INACTIVE) {
weston_desktop_wl_shell_change_state(surface, TRANSIENT, parent,
x, y);
} else {
weston_desktop_wl_shell_change_state(surface, TOPLEVEL, NULL,
0, 0);
surface->parent = parent;
weston_desktop_api_set_parent(surface->desktop,
surface->surface, parent);
}
}
static void
weston_desktop_wl_shell_surface_protocol_set_fullscreen(struct wl_client *wl_client,
struct wl_resource *resource,
enum wl_shell_surface_fullscreen_method method,
uint32_t framerate,
struct wl_resource *output_resource)
{
struct weston_desktop_surface *dsurface =
wl_resource_get_user_data(resource);
struct weston_desktop_wl_shell_surface *surface =
weston_desktop_surface_get_implementation_data(dsurface);
struct weston_output *output = NULL;
if (output_resource != NULL)
output = weston_head_from_resource(output_resource)->output;
weston_desktop_wl_shell_change_state(surface, FULLSCREEN, NULL, 0, 0);
weston_desktop_api_fullscreen_requested(surface->desktop, dsurface,
true, output);
}
static void
weston_desktop_wl_shell_surface_protocol_set_popup(struct wl_client *wl_client,
struct wl_resource *resource,
struct wl_resource *seat_resource,
uint32_t serial,
struct wl_resource *parent_resource,
int32_t x, int32_t y,
enum wl_shell_surface_transient flags)
{
struct weston_desktop_surface *dsurface =
wl_resource_get_user_data(resource);
struct weston_seat *wseat = wl_resource_get_user_data(seat_resource);
struct weston_desktop_seat *seat = weston_desktop_seat_from_seat(wseat);
struct weston_surface *parent =
wl_resource_get_user_data(parent_resource);
struct weston_desktop_surface *parent_surface;
struct weston_desktop_wl_shell_surface *surface =
weston_desktop_surface_get_implementation_data(dsurface);
/* Check that if we have a valid wseat we also got a valid desktop seat */
if (wseat != NULL && seat == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
if (!weston_surface_is_desktop_surface(parent))
return;
parent_surface = weston_surface_get_desktop_surface(parent);
weston_desktop_wl_shell_change_state(surface, POPUP,
parent_surface, x, y);
weston_desktop_surface_popup_grab(surface->surface, seat, serial);
surface->popup_seat = seat;
}
static void
weston_desktop_wl_shell_surface_protocol_set_maximized(struct wl_client *wl_client,
struct wl_resource *resource,
struct wl_resource *output_resource)
{
struct weston_desktop_surface *dsurface =
wl_resource_get_user_data(resource);
struct weston_desktop_wl_shell_surface *surface =
weston_desktop_surface_get_implementation_data(dsurface);
weston_desktop_wl_shell_change_state(surface, MAXIMIZED, NULL, 0, 0);
weston_desktop_api_maximized_requested(surface->desktop, dsurface, true);
}
static void
weston_desktop_wl_shell_surface_protocol_set_title(struct wl_client *wl_client,
struct wl_resource *resource,
const char *title)
{
struct weston_desktop_surface *surface =
wl_resource_get_user_data(resource);
weston_desktop_surface_set_title(surface, title);
}
static void
weston_desktop_wl_shell_surface_protocol_set_class(struct wl_client *wl_client,
struct wl_resource *resource,
const char *class_)
{
struct weston_desktop_surface *surface =
wl_resource_get_user_data(resource);
weston_desktop_surface_set_app_id(surface, class_);
}
static const struct wl_shell_surface_interface weston_desktop_wl_shell_surface_implementation = {
.pong = weston_desktop_wl_shell_surface_protocol_pong,
.move = weston_desktop_wl_shell_surface_protocol_move,
.resize = weston_desktop_wl_shell_surface_protocol_resize,
.set_toplevel = weston_desktop_wl_shell_surface_protocol_set_toplevel,
.set_transient = weston_desktop_wl_shell_surface_protocol_set_transient,
.set_fullscreen = weston_desktop_wl_shell_surface_protocol_set_fullscreen,
.set_popup = weston_desktop_wl_shell_surface_protocol_set_popup,
.set_maximized = weston_desktop_wl_shell_surface_protocol_set_maximized,
.set_title = weston_desktop_wl_shell_surface_protocol_set_title,
.set_class = weston_desktop_wl_shell_surface_protocol_set_class,
};
static const struct weston_desktop_surface_implementation weston_desktop_wl_shell_surface_internal_implementation = {
.set_size = weston_desktop_wl_shell_surface_set_size,
.committed = weston_desktop_wl_shell_surface_committed,
.ping = weston_desktop_wl_shell_surface_ping,
.close = weston_desktop_wl_shell_surface_close,
.get_maximized = weston_desktop_wl_shell_surface_get_maximized,
.get_fullscreen = weston_desktop_wl_shell_surface_get_fullscreen,
.destroy = weston_desktop_wl_shell_surface_destroy,
};
static void
wl_surface_resource_destroyed(struct wl_listener *listener,
void *data)
{
struct weston_desktop_wl_shell_surface *surface =
wl_container_of(listener, surface,
wl_surface_resource_destroy_listener);
/* the wl_shell_surface spec says that wl_shell_surfaces are to be
* destroyed automatically when the wl_surface is destroyed. */
weston_desktop_surface_destroy(surface->surface);
}
static void
weston_desktop_wl_shell_protocol_get_shell_surface(struct wl_client *wl_client,
struct wl_resource *resource,
uint32_t id,
struct wl_resource *surface_resource)
{
struct weston_desktop_client *client = wl_resource_get_user_data(resource);
struct weston_surface *wsurface = wl_resource_get_user_data(surface_resource);
struct weston_desktop_wl_shell_surface *surface;
if (weston_surface_set_role(wsurface, "wl_shell_surface", resource, WL_SHELL_ERROR_ROLE) < 0)
return;
surface = zalloc(sizeof(struct weston_desktop_wl_shell_surface));
if (surface == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
surface->desktop = weston_desktop_client_get_desktop(client);
surface->display = weston_desktop_get_display(surface->desktop);
surface->surface =
weston_desktop_surface_create(surface->desktop, client, wsurface,
&weston_desktop_wl_shell_surface_internal_implementation,
surface);
if (surface->surface == NULL) {
free(surface);
return;
}
surface->wl_surface_resource_destroy_listener.notify =
wl_surface_resource_destroyed;
wl_resource_add_destroy_listener(wsurface->resource,
&surface->wl_surface_resource_destroy_listener);
surface->resource =
weston_desktop_surface_add_resource(surface->surface,
&wl_shell_surface_interface,
&weston_desktop_wl_shell_surface_implementation,
id, NULL);
}
static const struct wl_shell_interface weston_desktop_wl_shell_implementation = {
.get_shell_surface = weston_desktop_wl_shell_protocol_get_shell_surface,
};
static void
weston_desktop_wl_shell_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id)
{
struct weston_desktop *desktop = data;
weston_desktop_client_create(desktop, client, NULL, &wl_shell_interface,
&weston_desktop_wl_shell_implementation,
version, id);
}
struct wl_global *
weston_desktop_wl_shell_create(struct weston_desktop *desktop,
struct wl_display *display)
{
return wl_global_create(display,
&wl_shell_interface,
WD_WL_SHELL_PROTOCOL_VERSION, desktop,
weston_desktop_wl_shell_bind);
}

@ -118,8 +118,9 @@ drm_backend_create_gl_renderer(struct drm_backend *b)
int
init_egl(struct drm_backend *b)
{
b->gbm = create_gbm_device(b->drm.fd);
struct drm_device *device = b->drm;
b->gbm = create_gbm_device(device->drm.fd);
if (!b->gbm)
return -1;
@ -145,6 +146,7 @@ static void drm_output_fini_cursor_egl(struct drm_output *output)
static int
drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
{
struct drm_device *device = output->device;
unsigned int i;
/* No point creating cursors if we don't have a plane for them. */
@ -154,14 +156,14 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) {
struct gbm_bo *bo;
bo = gbm_bo_create(b->gbm, b->cursor_width, b->cursor_height,
bo = gbm_bo_create(b->gbm, device->cursor_width, device->cursor_height,
GBM_FORMAT_ARGB8888,
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
if (!bo)
goto err;
output->gbm_cursor_fb[i] =
drm_fb_get_from_bo(bo, b, false, BUFFER_CURSOR);
drm_fb_get_from_bo(bo, device, false, BUFFER_CURSOR);
if (!output->gbm_cursor_fb[i]) {
gbm_bo_destroy(bo);
goto err;
@ -173,7 +175,7 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
err:
weston_log("cursor buffers unavailable, using gl cursors\n");
b->cursors_are_broken = true;
device->cursors_are_broken = true;
drm_output_fini_cursor_egl(output);
return -1;
}
@ -183,6 +185,7 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output)
{
struct weston_mode *mode = output->base.current_mode;
struct drm_plane *plane = output->scanout_plane;
const struct pixel_format_info *pixel_format;
struct weston_drm_format *fmt;
const uint64_t *modifiers;
unsigned int num_modifiers;
@ -190,8 +193,15 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output)
fmt = weston_drm_format_array_find_format(&plane->formats,
output->gbm_format);
if (!fmt) {
weston_log("format 0x%x not supported by output %s\n",
output->gbm_format, output->base.name);
pixel_format = pixel_format_get_info(output->gbm_format);
if (pixel_format)
weston_log("format %s not supported by output %s\n",
pixel_format->drm_format_name,
output->base.name);
else
weston_log("format 0x%x not supported by output %s\n",
output->gbm_format,
output->base.name);
return;
}
@ -280,7 +290,7 @@ struct drm_fb *
drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
{
struct drm_output *output = state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
struct gbm_bo *bo;
struct drm_fb *ret;
@ -295,7 +305,7 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
}
/* The renderer always produces an opaque image. */
ret = drm_fb_get_from_bo(bo, b, true, BUFFER_GBM_SURFACE);
ret = drm_fb_get_from_bo(bo, device, true, BUFFER_GBM_SURFACE);
if (!ret) {
weston_log("failed to get drm_fb for bo\n");
gbm_surface_release_buffer(output->gbm_surface, bo);
@ -305,68 +315,3 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
return ret;
}
static void
switch_to_gl_renderer(struct drm_backend *b)
{
struct drm_output *output;
bool dmabuf_support_inited;
bool linux_explicit_sync_inited;
if (!b->use_pixman)
return;
dmabuf_support_inited = !!b->compositor->renderer->import_dmabuf;
linux_explicit_sync_inited =
b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC;
weston_log("Switching to GL renderer\n");
b->gbm = create_gbm_device(b->drm.fd);
if (!b->gbm) {
weston_log("Failed to create gbm device. "
"Aborting renderer switch\n");
return;
}
wl_list_for_each(output, &b->compositor->output_list, base.link)
pixman_renderer_output_destroy(&output->base);
b->compositor->renderer->destroy(b->compositor);
if (drm_backend_create_gl_renderer(b) < 0) {
gbm_device_destroy(b->gbm);
weston_log("Failed to create GL renderer. Quitting.\n");
/* FIXME: we need a function to shutdown cleanly */
assert(0);
}
wl_list_for_each(output, &b->compositor->output_list, base.link)
drm_output_init_egl(output, b);
b->use_pixman = 0;
if (!dmabuf_support_inited && b->compositor->renderer->import_dmabuf) {
if (linux_dmabuf_setup(b->compositor) < 0)
weston_log("Error: initializing dmabuf "
"support failed.\n");
}
if (!linux_explicit_sync_inited &&
(b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC)) {
if (linux_explicit_synchronization_setup(b->compositor) < 0)
weston_log("Error: initializing explicit "
" synchronization support failed.\n");
}
}
void
renderer_switch_binding(struct weston_keyboard *keyboard,
const struct timespec *time, uint32_t key, void *data)
{
struct drm_backend *b =
to_drm_backend(keyboard->seat->compositor);
switch_to_gl_renderer(b);
}

@ -186,6 +186,8 @@ enum wdrm_connector_property {
WDRM_CONNECTOR_CONTENT_PROTECTION,
WDRM_CONNECTOR_HDCP_CONTENT_TYPE,
WDRM_CONNECTOR_PANEL_ORIENTATION,
WDRM_CONNECTOR_HDR_OUTPUT_METADATA,
WDRM_CONNECTOR_MAX_BPC,
WDRM_CONNECTOR__COUNT
};
@ -232,11 +234,20 @@ enum wdrm_crtc_property {
*/
enum try_view_on_plane_failure_reasons {
FAILURE_REASONS_NONE = 0,
FAILURE_REASONS_FORCE_RENDERER = (1 << 0),
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE = (1 << 1),
FAILURE_REASONS_DMABUF_MODIFIER_INVALID = (1 << 2),
FAILURE_REASONS_ADD_FB_FAILED = (1 << 3),
FAILURE_REASONS_GBM_BO_IMPORT_FAILED = (1 << 4)
FAILURE_REASONS_FORCE_RENDERER = 1 << 0,
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE = 1 << 1,
FAILURE_REASONS_DMABUF_MODIFIER_INVALID = 1 << 2,
FAILURE_REASONS_ADD_FB_FAILED = 1 << 3,
FAILURE_REASONS_NO_PLANES_AVAILABLE = 1 << 4,
FAILURE_REASONS_PLANES_REJECTED = 1 << 5,
FAILURE_REASONS_INADEQUATE_CONTENT_PROTECTION = 1 << 6,
FAILURE_REASONS_INCOMPATIBLE_TRANSFORM = 1 << 7,
FAILURE_REASONS_NO_BUFFER = 1 << 8,
FAILURE_REASONS_BUFFER_TYPE = 1 << 9,
FAILURE_REASONS_GLOBAL_ALPHA = 1 << 10,
FAILURE_REASONS_NO_GBM = 1 << 11,
FAILURE_REASONS_GBM_BO_IMPORT_FAILED = 1 << 12,
FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED = 1 << 13,
};
/**
@ -249,15 +260,8 @@ enum actions_needed_dmabuf_feedback {
ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE = (1 << 1),
};
struct drm_backend {
struct weston_backend base;
struct weston_compositor *compositor;
struct udev *udev;
struct wl_event_source *drm_source;
struct udev_monitor *udev_monitor;
struct wl_event_source *udev_drm_source;
struct drm_device {
struct drm_backend *backend;
struct {
int id;
@ -265,51 +269,63 @@ struct drm_backend {
char *filename;
dev_t devnum;
} drm;
struct gbm_device *gbm;
struct wl_listener session_listener;
uint32_t gbm_format;
/* we need these parameters in order to not fail drmModeAddFB2()
* due to out of bounds dimensions, and then mistakenly set
* sprites_are_broken:
*/
int min_width, max_width;
int min_height, max_height;
/* drm_crtc::link */
struct wl_list crtc_list;
struct wl_list plane_list;
uint32_t next_plane_idx;
void *repaint_data;
/* drm_writeback::link */
struct wl_list writeback_connector_list;
bool state_invalid;
/* drm_crtc::link */
struct wl_list crtc_list;
bool atomic_modeset;
/* drm_writeback::link */
struct wl_list writeback_connector_list;
bool aspect_ratio_supported;
int32_t cursor_width;
int32_t cursor_height;
bool sprites_are_broken;
bool cursors_are_broken;
bool sprites_are_broken;
bool atomic_modeset;
void *repaint_data;
bool fb_modifiers;
/* we need these parameters in order to not fail drmModeAddFB2()
* due to out of bounds dimensions, and then mistakenly set
* sprites_are_broken:
*/
int min_width, max_width;
int min_height, max_height;
};
struct drm_backend {
struct weston_backend base;
struct weston_compositor *compositor;
struct udev *udev;
struct wl_event_source *drm_source;
struct udev_monitor *udev_monitor;
struct wl_event_source *udev_drm_source;
struct drm_device *drm;
struct gbm_device *gbm;
struct wl_listener session_listener;
uint32_t gbm_format;
bool use_pixman;
bool use_pixman_shadow;
struct udev_input input;
int32_t cursor_width;
int32_t cursor_height;
uint32_t pageflip_timeout;
bool shutting_down;
bool aspect_ratio_supported;
bool fb_modifiers;
struct weston_log_scope *debug;
};
@ -373,7 +389,7 @@ struct drm_edid {
* output state will complete and be retired separately.
*/
struct drm_pending_state {
struct drm_backend *backend;
struct drm_device *device;
struct wl_list output_list;
};
@ -396,16 +412,6 @@ struct drm_output_state {
struct wl_list plane_list;
};
/**
* An instance of this class is created each time we believe we have a plane
* suitable to be used by a view as a direct scan-out. The list is initialized
* and populated locally.
*/
struct drm_plane_zpos {
struct drm_plane *plane;
struct wl_list link; /**< :candidate_plane_zpos_list */
};
/**
* Plane state holds the dynamic state for a plane: where it is positioned,
* and which buffer it is currently displaying.
@ -461,7 +467,7 @@ struct drm_plane_state {
struct drm_plane {
struct weston_plane base;
struct drm_backend *backend;
struct drm_device *device;
enum wdrm_plane_type type;
@ -483,7 +489,7 @@ struct drm_plane {
};
struct drm_connector {
struct drm_backend *backend;
struct drm_device *device;
drmModeConnector *conn;
uint32_t connector_id;
@ -495,16 +501,15 @@ struct drm_connector {
};
struct drm_writeback {
/* drm_backend::writeback_connector_list */
/* drm_device::writeback_connector_list */
struct wl_list link;
struct drm_backend *backend;
struct drm_device *device;
struct drm_connector connector;
};
struct drm_head {
struct weston_head base;
struct drm_backend *backend;
struct drm_connector connector;
struct drm_edid edid;
@ -512,13 +517,17 @@ struct drm_head {
struct backlight *backlight;
drmModeModeInfo inherited_mode; /**< Original mode on the connector */
uint32_t inherited_max_bpc; /**< Original max_bpc on the connector */
uint32_t inherited_crtc_id; /**< Original CRTC assignment */
/* drm_output::disable_head */
struct wl_list disable_head_link;
};
struct drm_crtc {
/* drm_backend::crtc_list */
/* drm_device::crtc_list */
struct wl_list link;
struct drm_backend *backend;
struct drm_device *device;
/* The output driven by the CRTC */
struct drm_output *output;
@ -532,14 +541,18 @@ struct drm_crtc {
struct drm_output {
struct weston_output base;
struct drm_backend *backend;
struct drm_device *device;
struct drm_crtc *crtc;
/* drm_head::disable_head_link */
struct wl_list disable_head;
bool page_flip_pending;
bool atomic_complete_pending;
bool destroy_pending;
bool disable_pending;
bool dpms_off_pending;
bool mode_switch_pending;
uint32_t gbm_cursor_handle[2];
struct drm_fb *gbm_cursor_fb[2];
@ -552,6 +565,11 @@ struct drm_output {
uint32_t gbm_format;
uint32_t gbm_bo_flags;
uint32_t hdr_output_metadata_blob_id;
uint64_t ackd_color_outcome_serial;
unsigned max_bpc;
/* Plane being displayed directly on the CRTC */
struct drm_plane *scanout_plane;
@ -572,19 +590,36 @@ struct drm_output {
struct wl_event_source *pageflip_timer;
bool virtual;
void (*virtual_destroy)(struct weston_output *base);
submit_frame_cb virtual_submit_frame;
};
void
drm_head_destroy(struct weston_head *head_base);
static inline struct drm_head *
to_drm_head(struct weston_head *base)
{
if (base->backend_id != drm_head_destroy)
return NULL;
return container_of(base, struct drm_head, base);
}
void
drm_output_destroy(struct weston_output *output_base);
void
drm_virtual_output_destroy(struct weston_output *output_base);
static inline struct drm_output *
to_drm_output(struct weston_output *base)
{
if (
#ifdef BUILD_DRM_VIRTUAL
base->destroy != drm_virtual_output_destroy &&
#endif
base->destroy != drm_output_destroy)
return NULL;
return container_of(base, struct drm_output, base);
}
@ -617,7 +652,7 @@ drm_output_get_plane_type_name(struct drm_plane *p)
}
struct drm_crtc *
drm_crtc_find(struct drm_backend *b, uint32_t crtc_id);
drm_crtc_find(struct drm_device *device, uint32_t crtc_id);
struct drm_head *
drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id);
@ -642,7 +677,7 @@ drm_view_transform_supported(struct weston_view *ev, struct weston_output *outpu
}
int
drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode);
drm_mode_ensure_blob(struct drm_device *device, struct drm_mode *mode);
struct drm_mode *
drm_output_choose_mode(struct drm_output *output,
@ -651,7 +686,7 @@ void
update_head_from_connector(struct drm_head *head);
void
drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list);
drm_mode_list_destroy(struct drm_device *device, struct wl_list *mode_list);
void
drm_output_print_modes(struct drm_output *output);
@ -662,7 +697,7 @@ drm_output_set_mode(struct weston_output *base,
const char *modeline);
void
drm_property_info_populate(struct drm_backend *b,
drm_property_info_populate(struct drm_device *device,
const struct drm_property_info *src,
struct drm_property_info *info,
unsigned int num_infos,
@ -690,7 +725,7 @@ extern const struct drm_property_info connector_props[];
extern const struct drm_property_info crtc_props[];
int
init_kms_caps(struct drm_backend *b);
init_kms_caps(struct drm_device *device);
int
drm_pending_state_test(struct drm_pending_state *pending_state);
@ -717,15 +752,18 @@ void
drm_fb_unref(struct drm_fb *fb);
struct drm_fb *
drm_fb_create_dumb(struct drm_backend *b, int width, int height,
drm_fb_create_dumb(struct drm_device *device, int width, int height,
uint32_t format);
struct drm_fb *
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_device *device,
bool is_opaque, enum drm_fb_type type);
void
drm_output_set_cursor_view(struct drm_output *output, struct weston_view *ev);
int
drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output);
#ifdef BUILD_DRM_GBM
extern struct drm_fb *
drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
@ -749,7 +787,7 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec,
#endif
struct drm_pending_state *
drm_pending_state_alloc(struct drm_backend *backend);
drm_pending_state_alloc(struct drm_device *device);
void
drm_pending_state_free(struct drm_pending_state *pending_state);
struct drm_output_state *
@ -800,7 +838,7 @@ void
drm_plane_reset_state(struct drm_plane *plane);
void
drm_assign_planes(struct weston_output *output_base, void *repaint_data);
drm_assign_planes(struct weston_output *output_base);
bool
drm_plane_is_available(struct drm_plane *plane, struct drm_output *output);
@ -837,9 +875,6 @@ drm_output_fini_egl(struct drm_output *output);
struct drm_fb *
drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage);
void
renderer_switch_binding(struct weston_keyboard *keyboard,
const struct timespec *time, uint32_t key, void *data);
#else
inline static int
init_egl(struct drm_backend *b)
@ -864,11 +899,4 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
{
return NULL;
}
inline static void
renderer_switch_binding(struct weston_keyboard *keyboard,
const struct timespec *time, uint32_t key, void *data)
{
weston_log("Compiled without GBM/EGL support\n");
}
#endif

@ -47,7 +47,7 @@
* CRTC's. Also, as this is a fake CRTC, it will not try to populate props.
*/
static struct drm_crtc *
drm_virtual_crtc_create(struct drm_backend *b, struct drm_output *output)
drm_virtual_crtc_create(struct drm_device *device, struct drm_output *output)
{
struct drm_crtc *crtc;
@ -55,7 +55,7 @@ drm_virtual_crtc_create(struct drm_backend *b, struct drm_output *output)
if (!crtc)
return NULL;
crtc->backend = b;
crtc->device = device;
crtc->output = output;
crtc->crtc_id = 0;
@ -81,17 +81,32 @@ drm_virtual_crtc_destroy(struct drm_crtc *crtc)
free(crtc);
}
static uint32_t
get_drm_plane_index_maximum(struct drm_device *device)
{
uint32_t max = 0;
struct drm_plane *p;
wl_list_for_each(p, &device->plane_list, link) {
if (p->plane_idx > max)
max = p->plane_idx;
}
return max;
}
/**
* Create a drm_plane for virtual output
*
* Call drm_virtual_plane_destroy to clean up the plane.
*
* @param b DRM compositor backend
* @param device DRM device
* @param output Output to create internal plane for
*/
static struct drm_plane *
drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
drm_virtual_plane_create(struct drm_device *device, struct drm_output *output)
{
struct drm_backend *b = device->backend;
struct drm_plane *plane;
struct weston_drm_format *fmt;
uint64_t mod;
@ -103,7 +118,7 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
}
plane->type = WDRM_PLANE_TYPE_PRIMARY;
plane->backend = b;
plane->device = device;
plane->state_cur = drm_plane_state_alloc(NULL, plane);
plane->state_cur->complete = true;
@ -115,7 +130,7 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
/* If output supports linear modifier, we add it to the plane.
* Otherwise we add DRM_FORMAT_MOD_INVALID, as explicit modifiers
* are not supported. */
if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && b->fb_modifiers)
if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && device->fb_modifiers)
mod = DRM_FORMAT_MOD_LINEAR;
else
mod = DRM_FORMAT_MOD_INVALID;
@ -124,7 +139,8 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
goto err;
weston_plane_init(&plane->base, b->compositor, 0, 0);
wl_list_insert(&b->plane_list, &plane->link);
plane->plane_idx = get_drm_plane_index_maximum(device) + 1;
wl_list_insert(&device->plane_list, &plane->link);
return plane;
@ -163,11 +179,10 @@ static int
drm_virtual_output_submit_frame(struct drm_output *output,
struct drm_fb *fb)
{
struct drm_backend *b = to_drm_backend(output->base.compositor);
int fd, ret;
assert(fb->num_planes == 1);
ret = drmPrimeHandleToFD(b->drm.fd, fb->handles[0], DRM_CLOEXEC, &fd);
ret = drmPrimeHandleToFD(fb->fd, fb->handles[0], DRM_CLOEXEC, &fd);
if (ret) {
weston_log("drmPrimeHandleFD failed, errno=%d\n", errno);
return -1;
@ -185,17 +200,20 @@ drm_virtual_output_submit_frame(struct drm_output *output,
static int
drm_virtual_output_repaint(struct weston_output *output_base,
pixman_region32_t *damage,
void *repaint_data)
pixman_region32_t *damage)
{
struct drm_pending_state *pending_state = repaint_data;
struct drm_output_state *state = NULL;
struct drm_output *output = to_drm_output(output_base);
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_plane_state *scanout_state;
struct drm_pending_state *pending_state;
struct drm_device *device;
assert(output->virtual);
device = output->device;
pending_state = device->repaint_data;
if (output->disable_pending || output->destroy_pending)
goto err;
@ -242,7 +260,7 @@ drm_virtual_output_deinit(struct weston_output *base)
drm_virtual_crtc_destroy(output->crtc);
}
static void
void
drm_virtual_output_destroy(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
@ -256,6 +274,9 @@ drm_virtual_output_destroy(struct weston_output *base)
drm_output_state_free(output->state_cur);
if (output->virtual_destroy)
output->virtual_destroy(base);
free(output);
}
@ -263,7 +284,8 @@ static int
drm_virtual_output_enable(struct weston_output *output_base)
{
struct drm_output *output = to_drm_output(output_base);
struct drm_backend *b = to_drm_backend(output_base->compositor);
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
assert(output->virtual);
@ -277,7 +299,7 @@ drm_virtual_output_enable(struct weston_output *output_base)
goto err;
}
output->scanout_plane = drm_virtual_plane_create(b, output);
output->scanout_plane = drm_virtual_plane_create(device, output);
if (!output->scanout_plane) {
weston_log("Failed to find primary plane for output %s\n",
output->base.name);
@ -320,22 +342,27 @@ drm_virtual_output_disable(struct weston_output *base)
}
static struct weston_output *
drm_virtual_output_create(struct weston_compositor *c, char *name)
drm_virtual_output_create(struct weston_compositor *c, char *name,
void (*destroy_func)(struct weston_output *))
{
struct drm_output *output;
struct drm_backend *b = to_drm_backend(c);
/* Always use the main device for virtual outputs */
struct drm_device *device = b->drm;
output = zalloc(sizeof *output);
if (!output)
return NULL;
output->crtc = drm_virtual_crtc_create(b, output);
output->device = device;
output->crtc = drm_virtual_crtc_create(device, output);
if (!output->crtc) {
free(output);
return NULL;
}
output->virtual = true;
output->virtual_destroy = destroy_func;
output->gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING;
weston_output_init(&output->base, c, name);
@ -357,7 +384,8 @@ drm_virtual_output_set_gbm_format(struct weston_output *base,
const char *gbm_format)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1)
output->gbm_format = b->gbm_format;

File diff suppressed because it is too large Load Diff

@ -69,7 +69,7 @@ drm_fb_destroy_dumb(struct drm_fb *fb)
}
static int
drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
drm_fb_addfb(struct drm_device *device, struct drm_fb *fb)
{
int ret = -EINVAL;
uint64_t mods[4] = { };
@ -77,7 +77,7 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
/* If we have a modifier set, we must only use the WithModifiers
* entrypoint; we cannot import it through legacy ioctls. */
if (b->fb_modifiers && fb->modifier != DRM_FORMAT_MOD_INVALID) {
if (device->fb_modifiers && fb->modifier != DRM_FORMAT_MOD_INVALID) {
/* KMS demands that if a modifier is set, it must be the same
* for all planes. */
for (i = 0; i < ARRAY_LENGTH(mods) && fb->handles[i]; i++)
@ -98,7 +98,7 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
/* Legacy AddFB can't always infer the format from depth/bpp alone, so
* check if our format is one of the lucky ones. */
if (!fb->format->depth || !fb->format->bpp)
if (!fb->format->addfb_legacy_depth || !fb->format->bpp)
return ret;
/* Cannot fall back to AddFB for multi-planar formats either. */
@ -106,13 +106,13 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
return ret;
ret = drmModeAddFB(fb->fd, fb->width, fb->height,
fb->format->depth, fb->format->bpp,
fb->format->addfb_legacy_depth, fb->format->bpp,
fb->strides[0], fb->handles[0], &fb->fb_id);
return ret;
}
struct drm_fb *
drm_fb_create_dumb(struct drm_backend *b, int width, int height,
drm_fb_create_dumb(struct drm_device *device, int width, int height,
uint32_t format)
{
struct drm_fb *fb;
@ -134,7 +134,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
goto err_fb;
}
if (!fb->format->depth || !fb->format->bpp) {
if (!fb->format->addfb_legacy_depth || !fb->format->bpp) {
weston_log("format 0x%lx is not compatible with dumb buffers\n",
(unsigned long) format);
goto err_fb;
@ -145,7 +145,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
create_arg.width = width;
create_arg.height = height;
ret = drmIoctl(b->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
ret = drmIoctl(device->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
if (ret)
goto err_fb;
@ -157,9 +157,9 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
fb->size = create_arg.size;
fb->width = width;
fb->height = height;
fb->fd = b->drm.fd;
fb->fd = device->drm.fd;
if (drm_fb_addfb(b, fb) != 0) {
if (drm_fb_addfb(device, fb) != 0) {
weston_log("failed to create kms fb: %s\n", strerror(errno));
goto err_bo;
}
@ -171,18 +171,18 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
goto err_add_fb;
fb->map = mmap(NULL, fb->size, PROT_WRITE,
MAP_SHARED, b->drm.fd, map_arg.offset);
MAP_SHARED, device->drm.fd, map_arg.offset);
if (fb->map == MAP_FAILED)
goto err_add_fb;
return fb;
err_add_fb:
drmModeRmFB(b->drm.fd, fb->fb_id);
drmModeRmFB(device->drm.fd, fb->fb_id);
err_bo:
memset(&destroy_arg, 0, sizeof(destroy_arg));
destroy_arg.handle = create_arg.handle;
drmIoctl(b->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
drmIoctl(device->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
err_fb:
free(fb);
return NULL;
@ -218,7 +218,7 @@ drm_fb_destroy_dmabuf(struct drm_fb *fb)
static struct drm_fb *
drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
struct drm_backend *backend, bool is_opaque,
struct drm_device *device, bool is_opaque,
uint32_t *try_view_on_plane_failure_reasons)
{
#ifndef HAVE_GBM_FD_IMPORT
@ -227,6 +227,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
* of GBM_BO_IMPORT_FD_MODIFIER. */
return NULL;
#else
struct drm_backend *backend = device->backend;
struct drm_fb *fb;
int i;
struct gbm_import_fd_modifier_data import_mod = {
@ -287,7 +288,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
fb->height = dmabuf->attributes.height;
fb->modifier = dmabuf->attributes.modifier[0];
fb->size = 0;
fb->fd = backend->drm.fd;
fb->fd = device->drm.fd;
ARRAY_COPY(fb->strides, dmabuf->attributes.stride);
ARRAY_COPY(fb->offsets, dmabuf->attributes.offset);
@ -302,10 +303,10 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
if (is_opaque)
fb->format = pixel_format_get_opaque_substitute(fb->format);
if (backend->min_width > fb->width ||
fb->width > backend->max_width ||
backend->min_height > fb->height ||
fb->height > backend->max_height) {
if (device->min_width > fb->width ||
fb->width > device->max_width ||
device->min_height > fb->height ||
fb->height > device->max_height) {
weston_log("bo geometry out of bounds\n");
goto err_free;
}
@ -315,12 +316,15 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
union gbm_bo_handle handle;
handle = gbm_bo_get_handle_for_plane(fb->bo, i);
if (handle.s32 == -1)
if (handle.s32 == -1) {
*try_view_on_plane_failure_reasons |=
FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED;
goto err_free;
}
fb->handles[i] = handle.u32;
}
if (drm_fb_addfb(backend, fb) != 0) {
if (drm_fb_addfb(device, fb) != 0) {
if (try_view_on_plane_failure_reasons)
*try_view_on_plane_failure_reasons |=
FAILURE_REASONS_ADD_FB_FAILED;
@ -336,7 +340,7 @@ err_free:
}
struct drm_fb *
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_device *device,
bool is_opaque, enum drm_fb_type type)
{
struct drm_fb *fb = gbm_bo_get_user_data(bo);
@ -356,7 +360,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
fb->type = type;
fb->refcnt = 1;
fb->bo = bo;
fb->fd = backend->drm.fd;
fb->fd = device->drm.fd;
fb->width = gbm_bo_get_width(bo);
fb->height = gbm_bo_get_height(bo);
@ -389,15 +393,15 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
if (is_opaque)
fb->format = pixel_format_get_opaque_substitute(fb->format);
if (backend->min_width > fb->width ||
fb->width > backend->max_width ||
backend->min_height > fb->height ||
fb->height > backend->max_height) {
if (device->min_width > fb->width ||
fb->width > device->max_width ||
device->min_height > fb->height ||
fb->height > device->max_height) {
weston_log("bo geometry out of bounds\n");
goto err_free;
}
if (drm_fb_addfb(backend, fb) != 0) {
if (drm_fb_addfb(device, fb) != 0) {
if (type == BUFFER_GBM_SURFACE)
weston_log("failed to create kms fb: %s\n",
strerror(errno));
@ -453,22 +457,25 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec,
{
struct drm_fb *fb;
struct drm_backend *b = to_drm_backend(ec);
struct drm_device *device = b->drm;
bool ret = false;
uint32_t try_reason = 0x0;
fb = drm_fb_get_from_dmabuf(dmabuf, b, true, NULL);
fb = drm_fb_get_from_dmabuf(dmabuf, device, true, &try_reason);
if (fb)
ret = true;
drm_fb_unref(fb);
drm_debug(b, "[dmabuf] dmabuf %p, import test %s\n", dmabuf,
ret ? "succeeded" : "failed");
drm_debug(b, "[dmabuf] dmabuf %p, import test %s, with reason 0x%x\n", dmabuf,
ret ? "succeeded" : "failed", try_reason);
return ret;
}
static bool
drm_fb_compatible_with_plane(struct drm_fb *fb, struct drm_plane *plane)
{
struct drm_backend *b = plane->backend;
struct drm_device *device = plane->device;
struct drm_backend *b = device->backend;
struct weston_drm_format *fmt;
/* Check whether the format is supported */
@ -508,6 +515,8 @@ drm_fb_handle_buffer_destroy(struct wl_listener *listener, void *data)
struct drm_buffer_fb *buf_fb =
container_of(listener, struct drm_buffer_fb, buffer_destroy_listener);
wl_list_remove(&buf_fb->buffer_destroy_listener.link);
if (buf_fb->fb) {
assert(buf_fb->fb->type == BUFFER_CLIENT ||
buf_fb->fb->type == BUFFER_DMABUF);
@ -523,25 +532,36 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
{
struct drm_output *output = state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
struct drm_buffer_fb *buf_fb;
bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox);
struct linux_dmabuf_buffer *dmabuf;
struct drm_fb *fb;
struct drm_plane *plane;
if (ev->alpha != 1.0f)
if (ev->alpha != 1.0f) {
*try_view_on_plane_failure_reasons |=
FAILURE_REASONS_GLOBAL_ALPHA;
return NULL;
}
if (!drm_view_transform_supported(ev, &output->base))
if (!drm_view_transform_supported(ev, &output->base)) {
*try_view_on_plane_failure_reasons |=
FAILURE_REASONS_INCOMPATIBLE_TRANSFORM;
return NULL;
}
if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED &&
ev->surface->desired_protection > output->base.current_protection)
ev->surface->desired_protection > output->base.current_protection) {
*try_view_on_plane_failure_reasons |=
FAILURE_REASONS_INADEQUATE_CONTENT_PROTECTION;
return NULL;
}
if (!buffer)
if (!buffer) {
*try_view_on_plane_failure_reasons |= FAILURE_REASONS_NO_BUFFER;
return NULL;
}
if (buffer->backend_private) {
buf_fb = buffer->backend_private;
@ -554,20 +574,18 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
buf_fb->buffer_destroy_listener.notify = drm_fb_handle_buffer_destroy;
wl_signal_add(&buffer->destroy_signal, &buf_fb->buffer_destroy_listener);
if (wl_shm_buffer_get(buffer->resource))
goto unsuitable;
/* GBM is used for dmabuf import as well as from client wl_buffer. */
if (!b->gbm)
if (!b->gbm) {
*try_view_on_plane_failure_reasons |= FAILURE_REASONS_NO_GBM;
goto unsuitable;
}
dmabuf = linux_dmabuf_buffer_get(buffer->resource);
if (dmabuf) {
fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque,
if (buffer->type == WESTON_BUFFER_DMABUF) {
fb = drm_fb_get_from_dmabuf(buffer->dmabuf, device, is_opaque,
&buf_fb->failure_reasons);
if (!fb)
goto unsuitable;
} else {
} else if (buffer->type == WESTON_BUFFER_RENDERER_OPAQUE) {
struct gbm_bo *bo;
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
@ -575,16 +593,24 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
if (!bo)
goto unsuitable;
fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT);
fb = drm_fb_get_from_bo(bo, device, is_opaque, BUFFER_CLIENT);
if (!fb) {
*try_view_on_plane_failure_reasons |=
(1 << FAILURE_REASONS_ADD_FB_FAILED);
gbm_bo_destroy(bo);
goto unsuitable;
}
} else {
*try_view_on_plane_failure_reasons |= FAILURE_REASONS_BUFFER_TYPE;
goto unsuitable;
}
/* Check if this buffer can ever go on any planes. If it can't, we have
* no reason to ever have a drm_fb, so we fail it here. */
wl_list_for_each(plane, &b->plane_list, link) {
wl_list_for_each(plane, &device->plane_list, link) {
/* only SHM buffers can go into cursor planes */
if (plane->type == WDRM_PLANE_TYPE_CURSOR)
continue;
if (drm_fb_compatible_with_plane(fb, plane))
fb->plane_mask |= (1 << plane->plane_idx);
}

@ -0,0 +1,178 @@
/*
* Copyright 2021-2022 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.
*/
#include "config.h"
#include <stdint.h>
#include <libweston/libweston.h>
#include <assert.h>
#include <string.h>
#include <math.h>
#include "drm-internal.h"
static inline uint16_t
color_xy_to_u16(float v)
{
assert(v >= 0.0f);
assert(v <= 1.0f);
/*
* CTA-861-G
* 6.9.1 Static Metadata Type 1
* chromaticity coordinate encoding
*/
return (uint16_t)round(v * 50000.0);
}
static inline uint16_t
nits_to_u16(float nits)
{
assert(nits >= 1.0f);
assert(nits <= 65535.0f);
/*
* CTA-861-G
* 6.9.1 Static Metadata Type 1
* max display mastering luminance, max content light level,
* max frame-average light level
*/
return (uint16_t)round(nits);
}
static inline uint16_t
nits_to_u16_dark(float nits)
{
assert(nits >= 0.0001f);
assert(nits <= 6.5535f);
/*
* CTA-861-G
* 6.9.1 Static Metadata Type 1
* min display mastering luminance
*/
return (uint16_t)round(nits * 10000.0);
}
static void
weston_hdr_metadata_type1_to_kms(struct hdr_metadata_infoframe *dst,
const struct weston_hdr_metadata_type1 *src)
{
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES) {
unsigned i;
for (i = 0; i < 3; i++) {
dst->display_primaries[i].x = color_xy_to_u16(src->primary[i].x);
dst->display_primaries[i].y = color_xy_to_u16(src->primary[i].y);
}
}
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_WHITE) {
dst->white_point.x = color_xy_to_u16(src->white.x);
dst->white_point.y = color_xy_to_u16(src->white.y);
}
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML)
dst->max_display_mastering_luminance = nits_to_u16(src->maxDML);
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MINDML)
dst->min_display_mastering_luminance = nits_to_u16_dark(src->minDML);
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL)
dst->max_cll = nits_to_u16(src->maxCLL);
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL)
dst->max_fall = nits_to_u16(src->maxFALL);
}
int
drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output)
{
struct drm_device *device = output->device;
const struct weston_hdr_metadata_type1 *src;
struct hdr_output_metadata meta;
uint32_t blob_id = 0;
int ret;
if (output->hdr_output_metadata_blob_id &&
output->ackd_color_outcome_serial == output->base.color_outcome_serial)
return 0;
src = weston_output_get_hdr_metadata_type1(&output->base);
/*
* Set up the data for Dynamic Range and Mastering InfoFrame,
* CTA-861-G, a.k.a the static HDR metadata.
*/
memset(&meta, 0, sizeof meta);
meta.metadata_type = 0; /* Static Metadata Type 1 */
/* Duplicated field in UABI struct */
meta.hdmi_metadata_type1.metadata_type = meta.metadata_type;
switch (output->base.eotf_mode) {
case WESTON_EOTF_MODE_NONE:
assert(0 && "bad eotf_mode: none");
return -1;
case WESTON_EOTF_MODE_SDR:
/*
* Do not send any static HDR metadata. Video sinks should
* respond by switching to traditional SDR mode. If they
* do not, the kernel should fix that up.
*/
assert(output->hdr_output_metadata_blob_id == 0);
return 0;
case WESTON_EOTF_MODE_TRADITIONAL_HDR:
meta.hdmi_metadata_type1.eotf = 1; /* from CTA-861-G */
break;
case WESTON_EOTF_MODE_ST2084:
meta.hdmi_metadata_type1.eotf = 2; /* from CTA-861-G */
weston_hdr_metadata_type1_to_kms(&meta.hdmi_metadata_type1, src);
break;
case WESTON_EOTF_MODE_HLG:
meta.hdmi_metadata_type1.eotf = 3; /* from CTA-861-G */
break;
}
if (meta.hdmi_metadata_type1.eotf == 0) {
assert(0 && "bad eotf_mode");
return -1;
}
ret = drmModeCreatePropertyBlob(device->drm.fd,
&meta, sizeof meta, &blob_id);
if (ret != 0) {
weston_log("Error: failed to create KMS blob for HDR metadata on output '%s': %s\n",
output->base.name, strerror(-ret));
return -1;
}
drmModeDestroyPropertyBlob(device->drm.fd,
output->hdr_output_metadata_blob_id);
output->hdr_output_metadata_blob_id = blob_id;
output->ackd_color_outcome_serial = output->base.color_outcome_serial;
return 0;
}

@ -143,6 +143,10 @@ const struct drm_property_info connector_props[] = {
.enum_values = panel_orientation_enums,
.num_enum_values = WDRM_PANEL_ORIENTATION__COUNT,
},
[WDRM_CONNECTOR_HDR_OUTPUT_METADATA] = {
.name = "HDR_OUTPUT_METADATA",
},
[WDRM_CONNECTOR_MAX_BPC] = { .name = "max bpc", },
};
const struct drm_property_info crtc_props[] = {
@ -272,14 +276,14 @@ drm_property_get_range_values(struct drm_property_info *info,
* The values given in enum_names are searched for, and stored in the
* same-indexed field of the map array.
*
* @param b DRM backend object
* @param device DRM device object
* @param src DRM property info array to source from
* @param info DRM property info array to copy into
* @param num_infos Number of entries in the source array
* @param props DRM object properties for the object
*/
void
drm_property_info_populate(struct drm_backend *b,
drm_property_info_populate(struct drm_device *device,
const struct drm_property_info *src,
struct drm_property_info *info,
unsigned int num_infos,
@ -311,7 +315,7 @@ drm_property_info_populate(struct drm_backend *b,
for (i = 0; i < props->count_props; i++) {
unsigned int k;
prop = drmModeGetProperty(b->drm.fd, props->props[i]);
prop = drmModeGetProperty(device->drm.fd, props->props[i]);
if (!prop)
continue;
@ -411,19 +415,6 @@ drm_property_info_free(struct drm_property_info *info, int num_props)
memset(info, 0, sizeof(*info) * num_props);
}
static inline uint32_t *
formats_ptr(struct drm_format_modifier_blob *blob)
{
return (uint32_t *)(((char *)blob) + blob->formats_offset);
}
static inline struct drm_format_modifier *
modifiers_ptr(struct drm_format_modifier_blob *blob)
{
return (struct drm_format_modifier *)
(((char *)blob) + blob->modifiers_offset);
}
/**
* Populates the plane's formats array, using either the IN_FORMATS blob
* property (if available), or the plane's format list if not.
@ -433,13 +424,11 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane,
const drmModeObjectProperties *props,
const bool use_modifiers)
{
unsigned i, j;
struct drm_device *device = plane->device;
uint32_t i, blob_id, fmt_prev = DRM_FORMAT_INVALID;
drmModeFormatModifierIterator drm_iter = {0};
struct weston_drm_format *fmt = NULL;
drmModePropertyBlobRes *blob = NULL;
struct drm_format_modifier_blob *fmt_mod_blob;
struct drm_format_modifier *blob_modifiers;
uint32_t *blob_formats;
uint32_t blob_id;
struct weston_drm_format *fmt;
int ret = 0;
if (!use_modifiers)
@ -451,39 +440,26 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane,
if (blob_id == 0)
goto fallback;
blob = drmModeGetPropertyBlob(plane->backend->drm.fd, blob_id);
blob = drmModeGetPropertyBlob(device->drm.fd, blob_id);
if (!blob)
goto fallback;
fmt_mod_blob = blob->data;
blob_formats = formats_ptr(fmt_mod_blob);
blob_modifiers = modifiers_ptr(fmt_mod_blob);
assert(kplane->count_formats == fmt_mod_blob->count_formats);
while (drmModeFormatModifierBlobIterNext(blob, &drm_iter)) {
if (fmt_prev != drm_iter.fmt) {
fmt = weston_drm_format_array_add_format(&plane->formats,
drm_iter.fmt);
if (!fmt) {
ret = -1;
goto out;
}
for (i = 0; i < fmt_mod_blob->count_formats; i++) {
fmt = weston_drm_format_array_add_format(&plane->formats,
blob_formats[i]);
if (!fmt) {
ret = -1;
goto out;
fmt_prev = drm_iter.fmt;
}
for (j = 0; j < fmt_mod_blob->count_modifiers; j++) {
struct drm_format_modifier *mod = &blob_modifiers[j];
if ((i < mod->offset) || (i > mod->offset + 63))
continue;
if (!(mod->formats & (1 << (i - mod->offset))))
continue;
ret = weston_drm_format_add_modifier(fmt, mod->modifier);
if (ret < 0)
goto out;
}
ret = weston_drm_format_add_modifier(fmt, drm_iter.mod);
if (ret < 0)
goto out;
if (fmt->modifiers.size == 0)
weston_drm_format_array_remove_latest_format(&plane->formats);
}
out:
@ -510,14 +486,15 @@ drm_output_set_gamma(struct weston_output *output_base,
{
int rc;
struct drm_output *output = to_drm_output(output_base);
struct drm_backend *backend =
to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
assert(output);
/* check */
if (output_base->gamma_size != size)
return;
rc = drmModeCrtcSetGamma(backend->drm.fd,
rc = drmModeCrtcSetGamma(device->drm.fd,
output->crtc->crtc_id,
size, r, g, b);
if (rc)
@ -535,7 +512,8 @@ drm_output_assign_state(struct drm_output_state *state,
enum drm_state_apply_mode mode)
{
struct drm_output *output = state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
struct drm_plane_state *plane_state;
struct drm_head *head;
@ -552,13 +530,13 @@ drm_output_assign_state(struct drm_output_state *state,
output->state_cur = state;
if (b->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) {
if (device->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) {
drm_debug(b, "\t[CRTC:%u] setting pending flip\n",
output->crtc->crtc_id);
output->atomic_complete_pending = true;
}
if (b->atomic_modeset &&
if (device->atomic_modeset &&
state->protection == WESTON_HDCP_DISABLE)
wl_list_for_each(head, &output->base.head_list, base.output_link)
weston_head_set_content_protection_status(&head->base,
@ -580,7 +558,7 @@ drm_output_assign_state(struct drm_output_state *state,
continue;
}
if (b->atomic_modeset)
if (device->atomic_modeset)
continue;
assert(plane->type != WDRM_PLANE_TYPE_OVERLAY);
@ -593,7 +571,7 @@ static void
drm_output_set_cursor(struct drm_output_state *output_state)
{
struct drm_output *output = output_state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
struct drm_crtc *crtc = output->crtc;
struct drm_plane *plane = output->cursor_plane;
struct drm_plane_state *state;
@ -609,7 +587,7 @@ drm_output_set_cursor(struct drm_output_state *output_state)
if (!state->fb) {
pixman_region32_fini(&plane->base.damage);
pixman_region32_init(&plane->base.damage);
drmModeSetCursor(b->drm.fd, crtc->crtc_id, 0, 0, 0);
drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0);
return;
}
@ -618,8 +596,8 @@ drm_output_set_cursor(struct drm_output_state *output_state)
handle = output->gbm_cursor_handle[output->current_cursor];
if (plane->state_cur->fb != state->fb) {
if (drmModeSetCursor(b->drm.fd, crtc->crtc_id, handle,
b->cursor_width, b->cursor_height)) {
if (drmModeSetCursor(device->drm.fd, crtc->crtc_id, handle,
device->cursor_width, device->cursor_height)) {
weston_log("failed to set cursor: %s\n",
strerror(errno));
goto err;
@ -629,7 +607,7 @@ drm_output_set_cursor(struct drm_output_state *output_state)
pixman_region32_fini(&plane->base.damage);
pixman_region32_init(&plane->base.damage);
if (drmModeMoveCursor(b->drm.fd, crtc->crtc_id,
if (drmModeMoveCursor(device->drm.fd, crtc->crtc_id,
state->dest_x, state->dest_y)) {
weston_log("failed to move cursor: %s\n", strerror(errno));
goto err;
@ -638,15 +616,16 @@ drm_output_set_cursor(struct drm_output_state *output_state)
return;
err:
b->cursors_are_broken = true;
drmModeSetCursor(b->drm.fd, crtc->crtc_id, 0, 0, 0);
device->cursors_are_broken = true;
drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0);
}
static int
drm_output_apply_state_legacy(struct drm_output_state *state)
{
struct drm_output *output = state->output;
struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
struct drm_backend *backend = device->backend;
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_crtc *crtc = output->crtc;
struct drm_property_info *dpms_prop;
@ -678,14 +657,14 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
if (state->dpms != WESTON_DPMS_ON) {
if (output->cursor_plane) {
ret = drmModeSetCursor(backend->drm.fd, crtc->crtc_id,
ret = drmModeSetCursor(device->drm.fd, crtc->crtc_id,
0, 0, 0);
if (ret)
weston_log("drmModeSetCursor failed disable: %s\n",
strerror(errno));
}
ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id, 0, 0, 0,
ret = drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0,
NULL, 0, NULL);
if (ret)
weston_log("drmModeSetCrtc failed disabling: %s\n",
@ -719,12 +698,12 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
assert(scanout_state->in_fence_fd == -1);
mode = to_drm_mode(output->base.current_mode);
if (backend->state_invalid ||
if (device->state_invalid ||
!scanout_plane->state_cur->fb ||
scanout_plane->state_cur->fb->strides[0] !=
scanout_state->fb->strides[0]) {
ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id,
ret = drmModeSetCrtc(device->drm.fd, crtc->crtc_id,
scanout_state->fb->fb_id,
0, 0,
connectors, n_conn,
@ -740,7 +719,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
crtc->crtc_id, scanout_state->plane->plane_id,
pinfo ? pinfo->drm_format_name : "UNKNOWN");
if (drmModePageFlip(backend->drm.fd, crtc->crtc_id,
if (drmModePageFlip(device->drm.fd, crtc->crtc_id,
scanout_state->fb->fb_id,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
weston_log("queueing pageflip failed: %s\n", strerror(errno));
@ -761,7 +740,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
if (dpms_prop->prop_id == 0)
continue;
ret = drmModeConnectorSetProperty(backend->drm.fd,
ret = drmModeConnectorSetProperty(device->drm.fd,
head->connector.connector_id,
dpms_prop->prop_id,
state->dpms);
@ -786,6 +765,8 @@ static int
crtc_add_prop(drmModeAtomicReq *req, struct drm_crtc *crtc,
enum wdrm_crtc_property prop, uint64_t val)
{
struct drm_device *device = crtc->device;
struct drm_backend *b = device->backend;
struct drm_property_info *info = &crtc->props_crtc[prop];
int ret;
@ -794,7 +775,7 @@ crtc_add_prop(drmModeAtomicReq *req, struct drm_crtc *crtc,
ret = drmModeAtomicAddProperty(req, crtc->crtc_id, info->prop_id,
val);
drm_debug(crtc->backend, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n",
drm_debug(b, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n",
(unsigned long) crtc->crtc_id,
(unsigned long) info->prop_id, info->name,
(unsigned long long) val, (unsigned long long) val);
@ -805,6 +786,8 @@ static int
connector_add_prop(drmModeAtomicReq *req, struct drm_connector *connector,
enum wdrm_connector_property prop, uint64_t val)
{
struct drm_device *device = connector->device;
struct drm_backend *b = device->backend;
struct drm_property_info *info = &connector->props[prop];
uint32_t connector_id = connector->connector_id;
int ret;
@ -813,7 +796,7 @@ connector_add_prop(drmModeAtomicReq *req, struct drm_connector *connector,
return -1;
ret = drmModeAtomicAddProperty(req, connector_id, info->prop_id, val);
drm_debug(connector->backend, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n",
drm_debug(b, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n",
(unsigned long) connector_id,
(unsigned long) info->prop_id, info->name,
(unsigned long long) val, (unsigned long long) val);
@ -824,6 +807,8 @@ static int
plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
enum wdrm_plane_property prop, uint64_t val)
{
struct drm_device *device = plane->device;
struct drm_backend *b = device->backend;
struct drm_property_info *info = &plane->props[prop];
int ret;
@ -832,7 +817,7 @@ plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
ret = drmModeAtomicAddProperty(req, plane->plane_id, info->prop_id,
val);
drm_debug(plane->backend, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n",
drm_debug(b, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n",
(unsigned long) plane->plane_id,
(unsigned long) info->prop_id, info->name,
(unsigned long long) val, (unsigned long long) val);
@ -921,17 +906,52 @@ drm_connector_set_hdcp_property(struct drm_connector *connector,
assert(ret == 0);
}
static int
drm_connector_set_max_bpc(struct drm_connector *connector,
struct drm_output *output,
drmModeAtomicReq *req)
{
const struct drm_property_info *info;
struct drm_head *head;
struct drm_backend *backend = output->device->backend;
uint64_t max_bpc;
uint64_t a, b;
if (!drm_connector_has_prop(connector, WDRM_CONNECTOR_MAX_BPC))
return 0;
if (output->max_bpc == 0) {
/* A value of 0 means that the current max_bpc must be programmed. */
head = drm_head_find_by_connector(backend, connector->connector_id);
max_bpc = head->inherited_max_bpc;
} else {
info = &connector->props[WDRM_CONNECTOR_MAX_BPC];
assert(info->flags & DRM_MODE_PROP_RANGE);
assert(info->num_range_values == 2);
a = info->range_values[0];
b = info->range_values[1];
assert(a <= b);
max_bpc = MAX(a, MIN(output->max_bpc, b));
}
return connector_add_prop(req, connector,
WDRM_CONNECTOR_MAX_BPC, max_bpc);
}
static int
drm_output_apply_state_atomic(struct drm_output_state *state,
drmModeAtomicReq *req,
uint32_t *flags)
{
struct drm_output *output = state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
struct drm_crtc *crtc = output->crtc;
struct drm_plane_state *plane_state;
struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
struct drm_head *head;
struct drm_head *tmp;
int ret = 0;
drm_debug(b, "\t\t[atomic] %s output %lu (%s) state\n",
@ -944,7 +964,7 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
}
if (state->dpms == WESTON_DPMS_ON) {
ret = drm_mode_ensure_blob(b, current_mode);
ret = drm_mode_ensure_blob(device, current_mode);
if (ret != 0)
return ret;
@ -968,12 +988,30 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
wl_list_for_each(head, &output->base.head_list, base.output_link)
ret |= connector_add_prop(req, &head->connector,
WDRM_CONNECTOR_CRTC_ID, 0);
wl_list_for_each_safe(head, tmp, &output->disable_head,
disable_head_link) {
ret |= connector_add_prop(req, &head->connector,
WDRM_CONNECTOR_CRTC_ID, 0);
wl_list_remove(&head->disable_head_link);
wl_list_init(&head->disable_head_link);
}
}
wl_list_for_each(head, &output->base.head_list, base.output_link)
wl_list_for_each(head, &output->base.head_list, base.output_link) {
drm_connector_set_hdcp_property(&head->connector,
state->protection, req);
if (drm_connector_has_prop(&head->connector,
WDRM_CONNECTOR_HDR_OUTPUT_METADATA)) {
ret |= connector_add_prop(req, &head->connector,
WDRM_CONNECTOR_HDR_OUTPUT_METADATA,
output->hdr_output_metadata_blob_id);
}
ret |= drm_connector_set_max_bpc(&head->connector, output, req);
}
if (ret != 0) {
weston_log("couldn't set atomic CRTC/connector state\n");
return ret;
@ -1010,9 +1048,9 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
if (plane_state->fb && plane_state->fb->format)
pinfo = plane_state->fb->format;
drm_debug(plane->backend, "\t\t\t[PLANE:%lu] FORMAT: %s\n",
(unsigned long) plane->plane_id,
pinfo ? pinfo->drm_format_name : "UNKNOWN");
drm_debug(b, "\t\t\t[PLANE:%lu] FORMAT: %s\n",
(unsigned long) plane->plane_id,
pinfo ? pinfo->drm_format_name : "UNKNOWN");
if (plane_state->in_fence_fd >= 0) {
ret |= plane_add_prop(req, plane,
@ -1044,7 +1082,8 @@ static int
drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
enum drm_state_apply_mode mode)
{
struct drm_backend *b = pending_state->backend;
struct drm_device *device = pending_state->device;
struct drm_backend *b = device->backend;
struct drm_output_state *output_state, *tmp;
struct drm_plane *plane;
drmModeAtomicReq *req = drmModeAtomicAlloc();
@ -1066,7 +1105,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
break;
}
if (b->state_invalid) {
if (device->state_invalid) {
struct weston_head *head_base;
struct drm_head *head;
struct drm_crtc *crtc;
@ -1082,12 +1121,16 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
wl_list_for_each(head_base,
&b->compositor->head_list, compositor_link) {
struct drm_property_info *info;
head = to_drm_head(head_base);
if (!head)
continue;
if (weston_head_is_enabled(head_base))
continue;
head = to_drm_head(head_base);
connector_id = head->connector.connector_id;
if (head->connector.device != device)
continue;
drm_debug(b, "\t\t[atomic] disabling inactive head %s\n",
head_base->name);
@ -1103,7 +1146,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
ret = -1;
}
wl_list_for_each(crtc, &b->crtc_list, link) {
wl_list_for_each(crtc, &device->crtc_list, link) {
struct drm_property_info *info;
drmModeObjectProperties *props;
uint64_t active;
@ -1116,7 +1159,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
* off, as the kernel will refuse to generate an event
* for an off->off state and fail the commit.
*/
props = drmModeObjectGetProperties(b->drm.fd,
props = drmModeObjectGetProperties(device->drm.fd,
crtc->crtc_id,
DRM_MODE_OBJECT_CRTC);
if (!props) {
@ -1139,7 +1182,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
/* Disable all the planes; planes which are being used will
* override this state in the output-state application. */
wl_list_for_each(plane, &b->plane_list, link) {
wl_list_for_each(plane, &device->plane_list, link) {
drm_debug(b, "\t\t[atomic] starting with plane %lu disabled\n",
(unsigned long) plane->plane_id);
plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID, 0);
@ -1162,7 +1205,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
goto out;
}
ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
ret = drmModeAtomicCommit(device->drm.fd, req, flags, device);
drm_debug(b, "[atomic] drmModeAtomicCommit\n");
/* Test commits do not take ownership of the state; return
@ -1182,7 +1225,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
link)
drm_output_assign_state(output_state, mode);
b->state_invalid = false;
device->state_invalid = false;
assert(wl_list_empty(&pending_state->output_list));
@ -1213,9 +1256,9 @@ out:
int
drm_pending_state_test(struct drm_pending_state *pending_state)
{
struct drm_backend *b = pending_state->backend;
struct drm_device *device = pending_state->device;
if (b->atomic_modeset)
if (device->atomic_modeset)
return drm_pending_state_apply_atomic(pending_state,
DRM_STATE_TEST_ONLY);
@ -1234,24 +1277,25 @@ drm_pending_state_test(struct drm_pending_state *pending_state)
int
drm_pending_state_apply(struct drm_pending_state *pending_state)
{
struct drm_backend *b = pending_state->backend;
struct drm_device *device = pending_state->device;
struct drm_backend *b = device->backend;
struct drm_output_state *output_state, *tmp;
struct drm_crtc *crtc;
if (b->atomic_modeset)
if (device->atomic_modeset)
return drm_pending_state_apply_atomic(pending_state,
DRM_STATE_APPLY_ASYNC);
if (b->state_invalid) {
if (device->state_invalid) {
/* If we need to reset all our state (e.g. because we've
* just started, or just been VT-switched in), explicitly
* disable all the CRTCs we aren't using. This also disables
* all connectors on these CRTCs, so we don't need to do that
* separately with the pre-atomic API. */
wl_list_for_each(crtc, &b->crtc_list, link) {
wl_list_for_each(crtc, &device->crtc_list, link) {
if (crtc->output)
continue;
drmModeSetCrtc(b->drm.fd, crtc->crtc_id, 0, 0, 0,
drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0,
NULL, 0, NULL);
}
}
@ -1274,7 +1318,7 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
weston_output_repaint_failed(&output->base);
drm_output_state_free(output->state_cur);
output->state_cur = drm_output_state_alloc(output, NULL);
b->state_invalid = true;
device->state_invalid = true;
if (!b->use_pixman) {
drm_output_fini_egl(output);
drm_output_init_egl(output, b);
@ -1282,7 +1326,7 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
}
}
b->state_invalid = false;
device->state_invalid = false;
assert(wl_list_empty(&pending_state->output_list));
@ -1301,24 +1345,24 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
int
drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
{
struct drm_backend *b = pending_state->backend;
struct drm_device *device = pending_state->device;
struct drm_output_state *output_state, *tmp;
struct drm_crtc *crtc;
if (b->atomic_modeset)
if (device->atomic_modeset)
return drm_pending_state_apply_atomic(pending_state,
DRM_STATE_APPLY_SYNC);
if (b->state_invalid) {
if (device->state_invalid) {
/* If we need to reset all our state (e.g. because we've
* just started, or just been VT-switched in), explicitly
* disable all the CRTCs we aren't using. This also disables
* all connectors on these CRTCs, so we don't need to do that
* separately with the pre-atomic API. */
wl_list_for_each(crtc, &b->crtc_list, link) {
wl_list_for_each(crtc, &device->crtc_list, link) {
if (crtc->output)
continue;
drmModeSetCrtc(b->drm.fd, crtc->crtc_id, 0, 0, 0,
drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0,
NULL, 0, NULL);
}
}
@ -1335,7 +1379,7 @@ drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
}
}
b->state_invalid = false;
device->state_invalid = false;
assert(wl_list_empty(&pending_state->output_list));
@ -1360,14 +1404,14 @@ page_flip_handler(int fd, unsigned int frame,
unsigned int sec, unsigned int usec, void *data)
{
struct drm_output *output = data;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
drm_output_update_msc(output, frame);
assert(!b->atomic_modeset);
assert(!device->atomic_modeset);
assert(output->page_flip_pending);
output->page_flip_pending = false;
@ -1378,14 +1422,15 @@ static void
atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
unsigned int usec, unsigned int crtc_id, void *data)
{
struct drm_backend *b = data;
struct drm_device *device = data;
struct drm_backend *b = device->backend;
struct drm_crtc *crtc;
struct drm_output *output;
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
crtc = drm_crtc_find(b, crtc_id);
crtc = drm_crtc_find(device, crtc_id);
assert(crtc);
output = crtc->output;
@ -1399,7 +1444,7 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
drm_output_update_msc(output, frame);
drm_debug(b, "[atomic][CRTC:%u] flip processing started\n", crtc_id);
assert(b->atomic_modeset);
assert(device->atomic_modeset);
assert(output->atomic_complete_pending);
output->atomic_complete_pending = false;
@ -1410,12 +1455,12 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
int
on_drm_input(int fd, uint32_t mask, void *data)
{
struct drm_backend *b = data;
struct drm_device *device = data;
drmEventContext evctx;
memset(&evctx, 0, sizeof evctx);
evctx.version = 3;
if (b->atomic_modeset)
if (device->atomic_modeset)
evctx.page_flip_handler2 = atomic_flip_handler;
else
evctx.page_flip_handler = page_flip_handler;
@ -1425,61 +1470,63 @@ on_drm_input(int fd, uint32_t mask, void *data)
}
int
init_kms_caps(struct drm_backend *b)
init_kms_caps(struct drm_device *device)
{
struct drm_backend *b = device->backend;
struct weston_compositor *compositor = b->compositor;
uint64_t cap;
int ret;
weston_log("using %s\n", b->drm.filename);
weston_log("using %s\n", device->drm.filename);
ret = drmGetCap(b->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
ret = drmGetCap(device->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
if (ret != 0 || cap != 1) {
weston_log("Error: kernel DRM KMS does not support DRM_CAP_TIMESTAMP_MONOTONIC.\n");
return -1;
}
if (weston_compositor_set_presentation_clock(b->compositor, CLOCK_MONOTONIC) < 0) {
if (weston_compositor_set_presentation_clock(compositor, CLOCK_MONOTONIC) < 0) {
weston_log("Error: failed to set presentation clock to CLOCK_MONOTONIC.\n");
return -1;
}
ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap);
ret = drmGetCap(device->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap);
if (ret == 0)
b->cursor_width = cap;
device->cursor_width = cap;
else
b->cursor_width = 64;
device->cursor_width = 64;
ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap);
ret = drmGetCap(device->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap);
if (ret == 0)
b->cursor_height = cap;
device->cursor_height = cap;
else
b->cursor_height = 64;
device->cursor_height = 64;
ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
if (ret) {
weston_log("Error: drm card doesn't support universal planes!\n");
return -1;
}
if (!getenv("WESTON_DISABLE_ATOMIC")) {
ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
ret = drmGetCap(device->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
if (ret != 0)
cap = 0;
ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
b->atomic_modeset = ((ret == 0) && (cap == 1));
ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
device->atomic_modeset = ((ret == 0) && (cap == 1));
}
weston_log("DRM: %s atomic modesetting\n",
b->atomic_modeset ? "supports" : "does not support");
device->atomic_modeset ? "supports" : "does not support");
if (!getenv("WESTON_DISABLE_GBM_MODIFIERS")) {
ret = drmGetCap(b->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap);
ret = drmGetCap(device->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap);
if (ret == 0)
b->fb_modifiers = cap;
device->fb_modifiers = cap;
}
weston_log("DRM: %s GBM modifiers\n",
b->fb_modifiers ? "supports" : "does not support");
device->fb_modifiers ? "supports" : "does not support");
drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
/*
* KMS support for hardware planes cannot properly synchronize
@ -1489,13 +1536,13 @@ init_kms_caps(struct drm_backend *b)
* to a fraction. For cursors, it's not so bad, so they are
* enabled.
*/
if (!b->atomic_modeset || getenv("WESTON_FORCE_RENDERER"))
b->sprites_are_broken = true;
if (!device->atomic_modeset || getenv("WESTON_FORCE_RENDERER"))
device->sprites_are_broken = true;
ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1);
b->aspect_ratio_supported = (ret == 0);
ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1);
device->aspect_ratio_supported = (ret == 0);
weston_log("DRM: %s picture aspect ratio\n",
b->aspect_ratio_supported ? "supports" : "does not support");
device->aspect_ratio_supported ? "supports" : "does not support");
return 0;
}

@ -53,8 +53,10 @@ static long backlight_get(struct backlight *backlight, char *node)
int fd, value;
long ret;
if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
str_printf(&path, "%s/%s", backlight->path, node);
if (!path)
return -ENOMEM;
fd = open(path, O_RDONLY);
if (fd < 0) {
ret = -1;
@ -67,6 +69,9 @@ static long backlight_get(struct backlight *backlight, char *node)
goto out;
}
if (buffer[ret - 1] == '\n')
buffer[ret - 1] = '\0';
if (!safe_strtoint(buffer, &value)) {
ret = -1;
goto out;
@ -103,7 +108,8 @@ long backlight_set_brightness(struct backlight *backlight, long brightness)
int fd;
long ret;
if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
str_printf(&path, "%s/%s", backlight->path, "brightness");
if (!path)
return -ENOMEM;
fd = open(path, O_RDWR);
@ -118,7 +124,8 @@ long backlight_set_brightness(struct backlight *backlight, long brightness)
goto out;
}
if (asprintf(&buffer, "%ld", brightness) < 0) {
str_printf(&buffer, "%ld", brightness);
if (!buffer) {
ret = -1;
goto out;
}
@ -171,7 +178,8 @@ struct backlight *backlight_init(struct udev_device *drm_device,
if (!syspath)
return NULL;
if (asprintf(&path, "%s/%s", syspath, "device") < 0)
str_printf(&path, "%s/%s", syspath, "device");
if (!path)
return NULL;
ret = readlink(path, buffer, sizeof(buffer) - 1);
@ -214,11 +222,13 @@ struct backlight *backlight_init(struct udev_device *drm_device,
if (entry->d_name[0] == '.')
continue;
if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
entry->d_name) < 0)
str_printf(&backlight_path, "%s/%s", "/sys/class/backlight",
entry->d_name);
if (!backlight_path)
goto err;
if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) {
str_printf(&path, "%s/%s", backlight_path, "type");
if (!path) {
free(backlight_path);
goto err;
}
@ -255,7 +265,8 @@ struct backlight *backlight_init(struct udev_device *drm_device,
free (path);
if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
str_printf(&path, "%s/%s", backlight_path, "device");
if (!path)
goto err;
ret = readlink(path, buffer, sizeof(buffer) - 1);

@ -24,6 +24,7 @@ srcs_drm = [
'fb.c',
'modes.c',
'kms.c',
'kms-color.c',
'state-helpers.c',
'state-propose.c',
linux_dmabuf_unstable_v1_protocol_c,
@ -32,6 +33,7 @@ srcs_drm = [
]
deps_drm = [
dep_libm,
dep_libdl,
dep_libweston_private,
dep_session_helper,

@ -98,14 +98,15 @@ drm_subpixel_to_wayland(int drm_value)
}
int
drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode)
drm_mode_ensure_blob(struct drm_device *device, struct drm_mode *mode)
{
struct drm_backend *backend = device->backend;
int ret;
if (mode->blob_id)
return 0;
ret = drmModeCreatePropertyBlob(backend->drm.fd,
ret = drmModeCreatePropertyBlob(device->drm.fd,
&mode->mode_info,
sizeof(mode->mode_info),
&mode->blob_id);
@ -304,6 +305,8 @@ edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length)
* \param[out] make The monitor make (PNP ID).
* \param[out] model The monitor model (name).
* \param[out] serial_number The monitor serial number.
* \param[out] eotf_mask The monitor supported EOTF modes, combination of
* enum weston_eotf_mode bits.
*
* Each of \c *make, \c *model and \c *serial_number are set only if the
* information is found in the EDID. The pointers they are set to must not
@ -315,8 +318,10 @@ find_and_parse_output_edid(struct drm_head *head,
drmModeObjectPropertiesPtr props,
const char **make,
const char **model,
const char **serial_number)
const char **serial_number,
uint32_t *eotf_mask)
{
struct drm_device *device = head->connector.device;
drmModePropertyBlobPtr edid_blob = NULL;
uint32_t blob_id;
int rc;
@ -328,7 +333,7 @@ find_and_parse_output_edid(struct drm_head *head,
if (!blob_id)
return;
edid_blob = drmModeGetPropertyBlob(head->backend->drm.fd, blob_id);
edid_blob = drmModeGetPropertyBlob(device->drm.fd, blob_id);
if (!edid_blob)
return;
@ -344,6 +349,21 @@ find_and_parse_output_edid(struct drm_head *head,
*serial_number = head->edid.serial_number;
}
drmModeFreePropertyBlob(edid_blob);
/* TODO: parse this from EDID */
*eotf_mask = WESTON_EOTF_MODE_ALL_MASK;
}
static void
prune_eotf_modes_by_kms_support(struct drm_head *head, uint32_t *eotf_mask)
{
const struct drm_property_info *info;
/* Without the KMS property, cannot do anything but SDR. */
info = &head->connector.props[WDRM_CONNECTOR_HDR_OUTPUT_METADATA];
if (!head->connector.device->atomic_modeset || info->prop_id == 0)
*eotf_mask = WESTON_EOTF_MODE_SDR;
}
static uint32_t
@ -406,26 +426,26 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
* Destroys a mode, and removes it from the list.
*/
static void
drm_output_destroy_mode(struct drm_backend *backend, struct drm_mode *mode)
drm_output_destroy_mode(struct drm_device *device, struct drm_mode *mode)
{
if (mode->blob_id)
drmModeDestroyPropertyBlob(backend->drm.fd, mode->blob_id);
drmModeDestroyPropertyBlob(device->drm.fd, mode->blob_id);
wl_list_remove(&mode->base.link);
free(mode);
}
/** Destroy a list of drm_modes
*
* @param backend The backend for releasing mode property blobs.
* @param device The device for releasing mode property blobs.
* @param mode_list The list linked by drm_mode::base.link.
*/
void
drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list)
drm_mode_list_destroy(struct drm_device *device, struct wl_list *mode_list)
{
struct drm_mode *mode, *next;
wl_list_for_each_safe(mode, next, mode_list, base.link)
drm_output_destroy_mode(backend, mode);
drm_output_destroy_mode(device, mode);
}
void
@ -469,16 +489,16 @@ drm_output_choose_mode(struct drm_output *output,
struct drm_mode *tmp_mode = NULL, *mode_fall_back = NULL, *mode;
enum weston_mode_aspect_ratio src_aspect = WESTON_MODE_PIC_AR_NONE;
enum weston_mode_aspect_ratio target_aspect = WESTON_MODE_PIC_AR_NONE;
struct drm_backend *b;
struct drm_device *device;
b = to_drm_backend(output->base.compositor);
device = output->device;
target_aspect = target_mode->aspect_ratio;
src_aspect = output->base.current_mode->aspect_ratio;
if (output->base.current_mode->width == target_mode->width &&
output->base.current_mode->height == target_mode->height &&
(output->base.current_mode->refresh == target_mode->refresh ||
target_mode->refresh == 0)) {
if (!b->aspect_ratio_supported || src_aspect == target_aspect)
if (!device->aspect_ratio_supported || src_aspect == target_aspect)
return to_drm_mode(output->base.current_mode);
}
@ -489,7 +509,7 @@ drm_output_choose_mode(struct drm_output *output,
mode->mode_info.vdisplay == target_mode->height) {
if (mode->base.refresh == target_mode->refresh ||
target_mode->refresh == 0) {
if (!b->aspect_ratio_supported ||
if (!device->aspect_ratio_supported ||
src_aspect == target_aspect)
return mode;
else if (!mode_fall_back)
@ -515,9 +535,12 @@ update_head_from_connector(struct drm_head *head)
const char *make = "unknown";
const char *model = "unknown";
const char *serial_number = "unknown";
uint32_t eotf_mask = WESTON_EOTF_MODE_SDR;
find_and_parse_output_edid(head, props, &make, &model, &serial_number);
find_and_parse_output_edid(head, props, &make, &model, &serial_number, &eotf_mask);
weston_head_set_monitor_strings(&head->base, make, model, serial_number);
prune_eotf_modes_by_kms_support(head, &eotf_mask);
weston_head_set_supported_eotf_mask(&head->base, eotf_mask);
weston_head_set_non_desktop(&head->base,
check_non_desktop(connector, props));
weston_head_set_subpixel(&head->base,
@ -539,7 +562,7 @@ update_head_from_connector(struct drm_head *head)
* Find the most suitable mode to use for initial setup (or reconfiguration on
* hotplug etc) for a DRM output.
*
* @param backend the DRM backend
* @param device the DRM device
* @param output DRM output to choose mode for
* @param mode Strategy and preference to use when choosing mode
* @param modeline Manually-entered mode (may be NULL)
@ -547,7 +570,7 @@ update_head_from_connector(struct drm_head *head)
* @returns A mode from the output's mode list, or NULL if none available
*/
static struct drm_mode *
drm_output_choose_initial_mode(struct drm_backend *backend,
drm_output_choose_initial_mode(struct drm_device *device,
struct drm_output *output,
enum weston_drm_backend_output_mode mode,
const char *modeline,
@ -571,7 +594,7 @@ drm_output_choose_initial_mode(struct drm_backend *backend,
if (mode == WESTON_DRM_BACKEND_OUTPUT_PREFERRED && modeline) {
n = sscanf(modeline, "%dx%d@%d %u:%u", &width, &height,
&refresh, &aspect_width, &aspect_height);
if (backend->aspect_ratio_supported && n == 5) {
if (device->aspect_ratio_supported && n == 5) {
if (aspect_width == 4 && aspect_height == 3)
aspect_ratio = WESTON_MODE_PIC_AR_4_3;
else if (aspect_width == 16 && aspect_height == 9)
@ -602,7 +625,7 @@ drm_output_choose_initial_mode(struct drm_backend *backend,
if (width == drm_mode->base.width &&
height == drm_mode->base.height &&
(refresh == 0 || refresh == drm_mode->mode_info.vrefresh)) {
if (!backend->aspect_ratio_supported ||
if (!device->aspect_ratio_supported ||
aspect_ratio == drm_mode->base.aspect_ratio)
configured = drm_mode;
else
@ -714,7 +737,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
{
struct weston_mode *base;
struct drm_mode *mode = NULL;
struct drm_backend *backend;
struct drm_device *device = output->device;
const drmModeModeInfo *chosen = NULL;
assert(info);
@ -728,8 +751,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
if (chosen == info) {
assert(mode);
backend = to_drm_backend(output->base.compositor);
drm_output_destroy_mode(backend, mode);
drm_output_destroy_mode(device, mode);
chosen = NULL;
}
@ -756,7 +778,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
static int
drm_output_update_modelist_from_heads(struct drm_output *output)
{
struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
struct weston_head *head_base;
struct drm_head *head;
drmModeConnector *conn;
@ -765,7 +787,7 @@ drm_output_update_modelist_from_heads(struct drm_output *output)
assert(!output->base.enabled);
drm_mode_list_destroy(backend, &output->base.mode_list);
drm_mode_list_destroy(device, &output->base.mode_list);
wl_list_for_each(head_base, &output->base.head_list, output_link) {
head = to_drm_head(head_base);
@ -786,7 +808,7 @@ drm_output_set_mode(struct weston_output *base,
const char *modeline)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
struct drm_device *device = output->device;
struct drm_head *head = to_drm_head(weston_output_get_first_head(base));
struct drm_mode *current;
@ -797,7 +819,7 @@ drm_output_set_mode(struct weston_output *base,
if (drm_output_update_modelist_from_heads(output) < 0)
return -1;
current = drm_output_choose_initial_mode(b, output, mode, modeline,
current = drm_output_choose_initial_mode(device, output, mode, modeline,
&head->inherited_mode);
if (!current)
return -1;

@ -73,6 +73,8 @@ drm_plane_state_alloc(struct drm_output_state *state_output,
void
drm_plane_state_free(struct drm_plane_state *state, bool force)
{
struct drm_device *device;
if (!state)
return;
@ -86,14 +88,17 @@ drm_plane_state_free(struct drm_plane_state *state, bool force)
* by the kernel, which means we can safely discard it.
*/
if (state->damage_blob_id != 0) {
drmModeDestroyPropertyBlob(state->plane->backend->drm.fd,
device = state->plane->device;
drmModeDestroyPropertyBlob(device->drm.fd,
state->damage_blob_id);
state->damage_blob_id = 0;
}
if (force || state != state->plane->state_cur) {
drm_fb_unref(state->fb);
weston_buffer_reference(&state->fb_ref.buffer, NULL);
weston_buffer_reference(&state->fb_ref.buffer, NULL,
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&state->fb_ref.release, NULL);
free(state);
}
@ -135,10 +140,20 @@ drm_plane_state_duplicate(struct drm_output_state *state_output,
* buffer, then we must also transfer the reference on the client
* buffer. */
if (src->fb) {
struct weston_buffer *buffer;
dst->fb = drm_fb_ref(src->fb);
memset(&dst->fb_ref, 0, sizeof(dst->fb_ref));
weston_buffer_reference(&dst->fb_ref.buffer,
src->fb_ref.buffer.buffer);
if (src->fb->type == BUFFER_CLIENT ||
src->fb->type == BUFFER_DMABUF) {
buffer = src->fb_ref.buffer.buffer;
} else {
buffer = NULL;
}
weston_buffer_reference(&dst->fb_ref.buffer, buffer,
buffer ? BUFFER_MAY_BE_ACCESSED :
BUFFER_WILL_NOT_BE_ACCESSED);
weston_buffer_release_reference(&dst->fb_ref.release,
src->fb_ref.release.buffer_release);
} else {
@ -421,11 +436,11 @@ drm_output_state_free(struct drm_output_state *state)
* Allocate a new, empty, 'pending state' structure to be used across a
* repaint cycle or similar.
*
* @param backend DRM backend
* @param device DRM device
* @returns Newly-allocated pending state structure
*/
struct drm_pending_state *
drm_pending_state_alloc(struct drm_backend *backend)
drm_pending_state_alloc(struct drm_device *device)
{
struct drm_pending_state *ret;
@ -433,7 +448,7 @@ drm_pending_state_alloc(struct drm_backend *backend)
if (!ret)
return NULL;
ret->backend = backend;
ret->device = device;
wl_list_init(&ret->output_list);
return ret;

@ -41,6 +41,7 @@
#include "color.h"
#include "linux-dmabuf.h"
#include "presentation-time-server-protocol.h"
#include "linux-dmabuf-unstable-v1-server-protocol.h"
enum drm_output_propose_state_mode {
DRM_OUTPUT_PROPOSE_STATE_MIXED, /**< mix renderer & planes */
@ -63,55 +64,6 @@ drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode)
return drm_output_propose_state_mode_as_string[mode];
}
static void
drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes)
{
struct drm_backend *b = plane->backend;
struct drm_plane_zpos *h_plane;
struct drm_plane_zpos *plane_zpos;
plane_zpos = zalloc(sizeof(*plane_zpos));
if (!plane_zpos)
return;
plane_zpos->plane = plane;
drm_debug(b, "\t\t\t\t[plane] plane %d added to candidate list\n",
plane->plane_id);
if (wl_list_empty(planes)) {
wl_list_insert(planes, &plane_zpos->link);
return;
}
h_plane = wl_container_of(planes->next, h_plane, link);
if (h_plane->plane->zpos_max >= plane->zpos_max) {
wl_list_insert(planes->prev, &plane_zpos->link);
} else {
struct drm_plane_zpos *p_zpos = NULL;
if (wl_list_length(planes) == 1) {
wl_list_insert(planes->prev, &plane_zpos->link);
return;
}
wl_list_for_each(p_zpos, planes, link) {
if (p_zpos->plane->zpos_max >
plane_zpos->plane->zpos_max)
break;
}
wl_list_insert(p_zpos->link.prev, &plane_zpos->link);
}
}
static void
drm_output_destroy_zpos_plane(struct drm_plane_zpos *plane_zpos)
{
wl_list_remove(&plane_zpos->link);
free(plane_zpos);
}
static bool
drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
struct drm_output_state *output_state)
@ -125,87 +77,79 @@ drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
}
static struct drm_plane_state *
drm_output_prepare_overlay_view(struct drm_plane *plane,
struct drm_output_state *output_state,
struct weston_view *ev,
enum drm_output_propose_state_mode mode,
struct drm_fb *fb, uint64_t zpos)
drm_output_try_view_on_plane(struct drm_plane *plane,
struct drm_output_state *output_state,
struct weston_view *ev,
enum drm_output_propose_state_mode mode,
struct drm_fb *fb, uint64_t zpos)
{
struct drm_output *output = output_state->output;
struct weston_compositor *ec = output->base.compositor;
struct drm_backend *b = to_drm_backend(ec);
struct weston_surface *surface = ev->surface;
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
struct drm_plane_state *state = NULL;
int ret;
assert(!b->sprites_are_broken);
assert(b->atomic_modeset);
if (!fb) {
drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
" couldn't get fb\n", ev);
return NULL;
}
assert(!device->sprites_are_broken);
assert(device->atomic_modeset);
assert(fb);
assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY ||
(mode == DRM_OUTPUT_PROPOSE_STATE_MIXED &&
plane->type == WDRM_PLANE_TYPE_OVERLAY));
state = drm_output_state_get_plane(output_state, plane);
/* we can't have a 'pending' framebuffer as never set one before reaching here */
assert(!state->fb);
state->ev = ev;
state->output = output;
if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
drm_debug(b, "\t\t\t\t[view] not placing view %p on plane: "
"unsuitable transform\n", ev);
drm_plane_state_put_back(state);
state = NULL;
goto out;
}
/* If the surface buffer has an in-fence fd, but the plane
* doesn't support fences, we can't place the buffer on this
* plane. */
if (ev->surface->acquire_fence_fd >= 0 &&
plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
"no in-fence support\n", ev);
drm_plane_state_put_back(state);
state = NULL;
goto out;
/* Should've been ensured by weston_view_matches_entire_output. */
if (plane->type == WDRM_PLANE_TYPE_PRIMARY) {
assert(state->dest_x == 0 && state->dest_y == 0 &&
state->dest_w == (unsigned) output->base.current_mode->width &&
state->dest_h == (unsigned) output->base.current_mode->height);
}
/* We hold one reference for the lifetime of this function; from
* calling drm_fb_get_from_view() in drm_output_prepare_plane_view(),
* so, we take another reference here to live within the state. */
state->ev = ev;
state->fb = drm_fb_ref(fb);
state->in_fence_fd = ev->surface->acquire_fence_fd;
/* In planes-only mode, we don't have an incremental state to
* test against, so we just hope it'll work. */
if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
drm_debug(b, "\t\t\t[overlay] provisionally placing "
"view %p on overlay %lu in planes-only mode\n",
if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY &&
drm_pending_state_test(output_state->pending_state) != 0) {
drm_debug(b, "\t\t\t[view] not placing view %p on plane %lu: "
"atomic test failed\n",
ev, (unsigned long) plane->plane_id);
goto out;
}
ret = drm_pending_state_test(output_state->pending_state);
if (ret == 0) {
drm_debug(b, "\t\t\t[overlay] provisionally placing "
"view %p on overlay %d in mixed mode\n",
ev, plane->plane_id);
goto out;
}
drm_debug(b, "\t\t\t[overlay] not placing view %p on overlay %lu "
"in mixed mode: kernel test failed\n",
drm_debug(b, "\t\t\t[view] provisionally placing view %p on plane %lu\n",
ev, (unsigned long) plane->plane_id);
drm_plane_state_put_back(state);
state = NULL;
/* Take a reference on the buffer so that we don't release it
* back to the client until we're done with it; cursor buffers
* don't require a reference since we copy them. */
assert(state->fb_ref.buffer.buffer == NULL);
assert(state->fb_ref.release.buffer_release == NULL);
weston_buffer_reference(&state->fb_ref.buffer,
surface->buffer_ref.buffer,
BUFFER_MAY_BE_ACCESSED);
weston_buffer_release_reference(&state->fb_ref.release,
surface->buffer_release_ref.buffer_release);
out:
return state;
out:
drm_plane_state_put_back(state);
return NULL;
}
#ifdef BUILD_DRM_GBM
@ -218,18 +162,18 @@ out:
static void
cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev)
{
struct drm_backend *b = plane_state->plane->backend;
struct drm_output *output = plane_state->output;
struct drm_device *device = output->device;
struct gbm_bo *bo = plane_state->fb->bo;
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
uint32_t buf[b->cursor_width * b->cursor_height];
uint32_t buf[device->cursor_width * device->cursor_height];
int32_t stride;
uint8_t *s;
int i;
assert(buffer && buffer->shm_buffer);
assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
assert(buffer->width <= b->cursor_width);
assert(buffer->height <= b->cursor_height);
assert(buffer->width <= device->cursor_width);
assert(buffer->height <= device->cursor_height);
memset(buf, 0, sizeof buf);
stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
@ -237,7 +181,7 @@ cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev)
wl_shm_buffer_begin_access(buffer->shm_buffer);
for (i = 0; i < buffer->height; i++)
memcpy(buf + i * b->cursor_width,
memcpy(buf + i * device->cursor_width,
s + i * stride,
buffer->width * 4);
wl_shm_buffer_end_access(buffer->shm_buffer);
@ -251,32 +195,23 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
struct weston_view *ev, uint64_t zpos)
{
struct drm_output *output = output_state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
struct drm_plane *plane = output->cursor_plane;
struct drm_plane_state *plane_state;
bool needs_update = false;
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
const char *p_name = drm_output_get_plane_type_name(plane);
assert(!b->cursors_are_broken);
if (!plane)
return NULL;
if (!plane->state_cur->complete)
return NULL;
if (plane->state_cur->output && plane->state_cur->output != output)
return NULL;
assert(!device->cursors_are_broken);
assert(plane);
assert(plane->state_cur->complete);
assert(!plane->state_cur->output || plane->state_cur->output == output);
/* We use GBM to import SHM buffers. */
if (b->gbm == NULL)
return NULL;
assert(b->gbm);
plane_state = drm_output_state_get_plane(output_state, plane);
if (plane_state && plane_state->fb)
return NULL;
assert(!plane_state->fb);
/* We can't scale with the legacy API, and we don't try to account for
* simple cropping/translation in cursor_bo_update. */
@ -287,19 +222,9 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
goto err;
}
if (buffer->width > b->cursor_width ||
buffer->height > b->cursor_height) {
drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
"(surface buffer (%dx%d) larger than permitted"
" (%dx%d))\n", p_name, ev, p_name,
buffer->width, buffer->height,
b->cursor_width, b->cursor_height);
goto err;
}
if (plane_state->src_x != 0 || plane_state->src_y != 0 ||
plane_state->src_w > (unsigned) b->cursor_width << 16 ||
plane_state->src_h > (unsigned) b->cursor_height << 16 ||
plane_state->src_w > (unsigned) device->cursor_width << 16 ||
plane_state->src_h > (unsigned) device->cursor_height << 16 ||
plane_state->src_w != plane_state->dest_w << 16 ||
plane_state->src_h != plane_state->dest_h << 16) {
drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
@ -338,10 +263,10 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
* a buffer which is always cursor_width x cursor_height, even if the
* surface we want to promote is actually smaller than this. Manually
* mangle the plane state to deal with this. */
plane_state->src_w = b->cursor_width << 16;
plane_state->src_h = b->cursor_height << 16;
plane_state->dest_w = b->cursor_width;
plane_state->dest_h = b->cursor_height;
plane_state->src_w = device->cursor_width << 16;
plane_state->src_h = device->cursor_height << 16;
plane_state->dest_w = device->cursor_width;
plane_state->dest_h = device->cursor_height;
drm_debug(b, "\t\t\t\t[%s] provisionally assigned view %p to cursor\n",
p_name, ev);
@ -361,190 +286,6 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
}
#endif
static struct drm_plane_state *
drm_output_prepare_scanout_view(struct drm_output_state *output_state,
struct weston_view *ev,
enum drm_output_propose_state_mode mode,
struct drm_fb *fb, uint64_t zpos)
{
struct drm_output *output = output_state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_plane_state *state;
const char *p_name = drm_output_get_plane_type_name(scanout_plane);
assert(!b->sprites_are_broken);
assert(b->atomic_modeset);
assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
/* Check the view spans exactly the output size, calculated in the
* logical co-ordinate space. */
if (!weston_view_matches_output_entirely(ev, &output->base)) {
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
" view does not match output entirely\n",
p_name, ev, p_name);
return NULL;
}
/* If the surface buffer has an in-fence fd, but the plane doesn't
* support fences, we can't place the buffer on this plane. */
if (ev->surface->acquire_fence_fd >= 0 &&
scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
"no in-fence support\n", p_name, ev, p_name);
return NULL;
}
if (!fb) {
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
" couldn't get fb\n", p_name, ev, p_name);
return NULL;
}
state = drm_output_state_get_plane(output_state, scanout_plane);
/* The only way we can already have a buffer in the scanout plane is
* if we are in mixed mode, or if a client buffer has already been
* placed into scanout. The former case will never call into here,
* and in the latter case, the view must have been marked as occluded,
* meaning we should never have ended up here. */
assert(!state->fb);
/* take another reference here to live within the state */
state->fb = drm_fb_ref(fb);
state->ev = ev;
state->output = output;
if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
"unsuitable transform\n", p_name, ev, p_name);
goto err;
}
if (state->dest_x != 0 || state->dest_y != 0 ||
state->dest_w != (unsigned) output->base.current_mode->width ||
state->dest_h != (unsigned) output->base.current_mode->height) {
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
" invalid plane state\n", p_name, ev, p_name);
goto err;
}
state->in_fence_fd = ev->surface->acquire_fence_fd;
/* In plane-only mode, we don't need to test the state now, as we
* will only test it once at the end. */
return state;
err:
drm_plane_state_put_back(state);
return NULL;
}
static struct drm_plane_state *
drm_output_try_view_on_plane(struct drm_plane *plane,
struct drm_output_state *state,
struct weston_view *ev,
enum drm_output_propose_state_mode mode,
struct drm_fb *fb, uint64_t zpos)
{
struct drm_backend *b = state->pending_state->backend;
struct weston_output *wet_output = &state->output->base;
bool view_matches_entire_output, scanout_has_view_assigned;
struct drm_plane *scanout_plane = state->output->scanout_plane;
struct drm_plane_state *ps = NULL;
const char *p_name = drm_output_get_plane_type_name(plane);
struct weston_surface *surface = ev->surface;
enum {
NO_PLANES, /* generic err-handle */
NO_PLANES_ACCEPTED,
PLACED_ON_PLANE,
} availability = NO_PLANES;
/* sanity checks in case we over/underflow zpos or pass incorrect
* values */
assert(zpos <= plane->zpos_max ||
zpos != DRM_PLANE_ZPOS_INVALID_PLANE);
switch (plane->type) {
case WDRM_PLANE_TYPE_CURSOR:
if (b->cursors_are_broken) {
availability = NO_PLANES_ACCEPTED;
goto out;
}
ps = drm_output_prepare_cursor_view(state, ev, zpos);
if (ps)
availability = PLACED_ON_PLANE;
break;
case WDRM_PLANE_TYPE_OVERLAY:
/* do not attempt to place it in the overlay if we don't have
* anything in the scanout/primary and the view doesn't cover
* the entire output */
view_matches_entire_output =
weston_view_matches_output_entirely(ev, wet_output);
scanout_has_view_assigned =
drm_output_check_plane_has_view_assigned(scanout_plane,
state);
if (view_matches_entire_output && !scanout_has_view_assigned) {
availability = NO_PLANES_ACCEPTED;
goto out;
}
ps = drm_output_prepare_overlay_view(plane, state, ev, mode,
fb, zpos);
if (ps)
availability = PLACED_ON_PLANE;
break;
case WDRM_PLANE_TYPE_PRIMARY:
if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
availability = NO_PLANES_ACCEPTED;
goto out;
}
ps = drm_output_prepare_scanout_view(state, ev, mode,
fb, zpos);
if (ps)
availability = PLACED_ON_PLANE;
break;
default:
assert(0);
break;
}
out:
switch (availability) {
case NO_PLANES:
/* set initial to this catch-all case, such that
* prepare_cursor/overlay/scanout() should have/contain the
* reason for failling */
break;
case NO_PLANES_ACCEPTED:
drm_debug(b, "\t\t\t\t[plane] plane %d refusing to "
"place view %p in %s\n",
plane->plane_id, ev, p_name);
break;
case PLACED_ON_PLANE:
/* Take a reference on the buffer so that we don't release it
* back to the client until we're done with it; cursor buffers
* don't require a reference since we copy them. */
assert(ps->fb_ref.buffer.buffer == NULL);
assert(ps->fb_ref.release.buffer_release == NULL);
if (ps->plane->type == WDRM_PLANE_TYPE_CURSOR) {
assert(ps->fb->type == BUFFER_CURSOR);
} else if (fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF) {
assert(ps->fb == fb);
weston_buffer_reference(&ps->fb_ref.buffer,
surface->buffer_ref.buffer);
weston_buffer_release_reference(&ps->fb_ref.release,
surface->buffer_release_ref.buffer_release);
}
break;
}
return ps;
}
static void
drm_output_check_zpos_plane_states(struct drm_output_state *state)
{
@ -585,12 +326,13 @@ drm_output_check_zpos_plane_states(struct drm_output_state *state)
}
static bool
dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
dmabuf_feedback_maybe_update(struct drm_device *device, struct weston_view *ev,
uint32_t try_view_on_plane_failure_reasons)
{
struct weston_dmabuf_feedback *dmabuf_feedback = ev->surface->dmabuf_feedback;
struct weston_dmabuf_feedback_tranche *scanout_tranche;
dev_t scanout_dev = b->drm.devnum;
struct drm_backend *b = device->backend;
dev_t scanout_dev = device->drm.devnum;
uint32_t scanout_flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT;
uint32_t action_needed = ACTION_NEEDED_NONE;
struct timespec current_time, delta_time;
@ -603,7 +345,8 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
(FAILURE_REASONS_ADD_FB_FAILED |
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE |
FAILURE_REASONS_DMABUF_MODIFIER_INVALID |
FAILURE_REASONS_GBM_BO_IMPORT_FAILED))
FAILURE_REASONS_GBM_BO_IMPORT_FAILED |
FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED))
action_needed |= ACTION_NEEDED_ADD_SCANOUT_TRANCHE;
assert(action_needed != (ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE |
@ -681,58 +424,161 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
}
static struct drm_plane_state *
drm_output_prepare_plane_view(struct drm_output_state *state,
struct weston_view *ev,
enum drm_output_propose_state_mode mode,
struct drm_plane_state *scanout_state,
uint64_t current_lowest_zpos,
uint32_t *try_view_on_plane_failure_reasons)
drm_output_find_plane_for_view(struct drm_output_state *state,
struct weston_paint_node *pnode,
enum drm_output_propose_state_mode mode,
struct drm_plane_state *scanout_state,
uint64_t current_lowest_zpos)
{
struct drm_output *output = state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
struct drm_plane_state *ps = NULL;
struct drm_plane *plane;
struct drm_plane_zpos *p_zpos, *p_zpos_next;
struct wl_list zpos_candidate_list;
struct weston_view *ev = pnode->view;
struct weston_buffer *buffer;
struct wl_shm_buffer *shmbuf;
struct drm_fb *fb;
struct drm_fb *fb = NULL;
bool view_matches_entire_output, scanout_has_view_assigned;
uint32_t possible_plane_mask = 0;
wl_list_init(&zpos_candidate_list);
pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE;
/* check view for valid buffer, doesn't make sense to even try */
if (!weston_view_has_valid_buffer(ev))
return ps;
if (!weston_view_has_valid_buffer(ev)) {
pnode->try_view_on_plane_failure_reasons |=
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
return NULL;
}
buffer = ev->surface->buffer_ref.buffer;
shmbuf = wl_shm_buffer_get(buffer->resource);
fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons);
if (!shmbuf && !fb)
if (buffer->type == WESTON_BUFFER_SOLID) {
pnode->try_view_on_plane_failure_reasons |=
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
return NULL;
} else if (buffer->type == WESTON_BUFFER_SHM) {
if (!output->cursor_plane || device->cursors_are_broken) {
pnode->try_view_on_plane_failure_reasons |=
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
return NULL;
}
/* Even though this is a SHM buffer, pixel_format stores the
* format code as DRM FourCC */
if (buffer->pixel_format->format != DRM_FORMAT_ARGB8888) {
drm_debug(b, "\t\t\t\t[view] not placing view %p on "
"plane; SHM buffers must be ARGB8888 for "
"cursor view\n", ev);
pnode->try_view_on_plane_failure_reasons |=
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
return NULL;
}
if (buffer->width > device->cursor_width ||
buffer->height > device->cursor_height) {
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
"(buffer (%dx%d) too large for cursor plane)\n",
ev, buffer->width, buffer->height);
pnode->try_view_on_plane_failure_reasons |=
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
return NULL;
}
possible_plane_mask = (1 << output->cursor_plane->plane_idx);
} else {
if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY) {
drm_debug(b, "\t\t\t\t[view] not assigning view %p "
"to plane: renderer-only mode\n", ev);
return NULL;
}
fb = drm_fb_get_from_view(state, ev,
&pnode->try_view_on_plane_failure_reasons);
if (!fb) {
drm_debug(b, "\t\t\t[view] couldn't get FB for view: 0x%lx\n",
(unsigned long) pnode->try_view_on_plane_failure_reasons);
return NULL;
}
possible_plane_mask = fb->plane_mask;
}
view_matches_entire_output =
weston_view_matches_output_entirely(ev, &output->base);
scanout_has_view_assigned =
drm_output_check_plane_has_view_assigned(output->scanout_plane,
state);
/* assemble a list with possible candidates */
wl_list_for_each(plane, &b->plane_list, link) {
wl_list_for_each(plane, &device->plane_list, link) {
const char *p_name = drm_output_get_plane_type_name(plane);
uint64_t zpos;
if (possible_plane_mask == 0)
break;
if (!(possible_plane_mask & (1 << plane->plane_idx)))
continue;
possible_plane_mask &= ~(1 << plane->plane_idx);
switch (plane->type) {
case WDRM_PLANE_TYPE_CURSOR:
assert(buffer->shm_buffer);
assert(plane == output->cursor_plane);
break;
case WDRM_PLANE_TYPE_PRIMARY:
assert(fb);
if (plane != output->scanout_plane)
continue;
if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)
continue;
if (!view_matches_entire_output)
continue;
break;
case WDRM_PLANE_TYPE_OVERLAY:
assert(fb);
assert(mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY);
/* if the view covers the whole output, put it in the
* scanout plane, not overlay */
if (view_matches_entire_output &&
!scanout_has_view_assigned)
continue;
break;
default:
assert(false && "unknown plane type");
}
if (!drm_plane_is_available(plane, output))
continue;
if (drm_output_check_plane_has_view_assigned(plane, state)) {
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to"
" candidate list: view already assigned "
"to a plane\n", plane->plane_id);
drm_debug(b, "\t\t\t\t[plane] not trying plane %d: "
"another view already assigned\n",
plane->plane_id);
continue;
}
if (plane->zpos_min >= current_lowest_zpos) {
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
"candidate list: minimum zpos (%"PRIu64") "
"plane's above current lowest zpos "
"(%"PRIu64")\n", plane->plane_id,
plane->zpos_min, current_lowest_zpos);
drm_debug(b, "\t\t\t\t[plane] not trying plane %d: "
"plane's minimum zpos (%"PRIu64") above "
"current lowest zpos (%"PRIu64")\n",
plane->plane_id, plane->zpos_min,
current_lowest_zpos);
continue;
}
/* If the surface buffer has an in-fence fd, but the plane doesn't
* support fences, we can't place the buffer on this plane. */
if (ev->surface->acquire_fence_fd >= 0 &&
plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
"no in-fence support\n", p_name, ev, p_name);
return NULL;
}
if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
assert(scanout_state != NULL);
if (scanout_state->zpos >= plane->zpos_max) {
@ -746,49 +592,6 @@ drm_output_prepare_plane_view(struct drm_output_state *state,
}
}
if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY &&
(plane->type == WDRM_PLANE_TYPE_OVERLAY ||
plane->type == WDRM_PLANE_TYPE_PRIMARY)) {
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
"candidate list: renderer-only mode\n",
plane->plane_id);
continue;
}
if (plane->type == WDRM_PLANE_TYPE_CURSOR &&
(!shmbuf || wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888)) {
drm_debug(b, "\t\t\t\t[plane] not adding plane %d, type cursor to "
"candidate list: cursor planes only support ARGB8888"
"wl_shm buffers and the view buffer is of another type\n",
plane->plane_id);
continue;
}
if (plane->type != WDRM_PLANE_TYPE_CURSOR &&
(!fb || !(fb->plane_mask & (1 << plane->plane_idx)))) {
*try_view_on_plane_failure_reasons |=
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
"candidate list: invalid pixel format\n",
plane->plane_id);
continue;
}
drm_output_add_zpos_plane(plane, &zpos_candidate_list);
}
/* go over the potential candidate list and try to find a possible
* plane suitable for \c ev; start with the highest zpos value of a
* plane to maximize our chances, but do note we pass the zpos value
* based on current tracked value by \c current_lowest_zpos_in_use */
while (!wl_list_empty(&zpos_candidate_list)) {
struct drm_plane_zpos *head_p_zpos =
wl_container_of(zpos_candidate_list.next,
head_p_zpos, link);
struct drm_plane *plane = head_p_zpos->plane;
const char *p_name = drm_output_get_plane_type_name(plane);
uint64_t zpos;
if (current_lowest_zpos == DRM_PLANE_ZPOS_INVALID_PLANE)
zpos = plane->zpos_max;
else
@ -798,20 +601,32 @@ drm_output_prepare_plane_view(struct drm_output_state *state,
"from candidate list, type: %s\n",
plane->plane_id, p_name);
ps = drm_output_try_view_on_plane(plane, state, ev,
mode, fb, zpos);
drm_output_destroy_zpos_plane(head_p_zpos);
if (plane->type == WDRM_PLANE_TYPE_CURSOR) {
ps = drm_output_prepare_cursor_view(state, ev, zpos);
} else {
ps = drm_output_try_view_on_plane(plane, state, ev,
mode, fb, zpos);
}
if (ps) {
drm_debug(b, "\t\t\t\t[view] view %p has been placed to "
"%s plane with computed zpos %"PRIu64"\n",
ev, p_name, zpos);
break;
}
pnode->try_view_on_plane_failure_reasons |=
FAILURE_REASONS_PLANES_REJECTED;
}
wl_list_for_each_safe(p_zpos, p_zpos_next, &zpos_candidate_list, link)
drm_output_destroy_zpos_plane(p_zpos);
if (!ps &&
pnode->try_view_on_plane_failure_reasons == FAILURE_REASONS_NONE) {
pnode->try_view_on_plane_failure_reasons |=
FAILURE_REASONS_NO_PLANES_AVAILABLE;
}
/* if we have a plane state, it has its own ref to the fb; if not then
* we drop ours here */
drm_fb_unref(fb);
return ps;
}
@ -822,7 +637,8 @@ drm_output_propose_state(struct weston_output *output_base,
enum drm_output_propose_state_mode mode)
{
struct drm_output *output = to_drm_output(output_base);
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
struct weston_paint_node *pnode;
struct drm_output_state *state;
struct drm_plane_state *scanout_state = NULL;
@ -957,12 +773,29 @@ drm_output_propose_state(struct weston_output *output_base,
force_renderer = true;
}
if (!b->gbm) {
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
"(GBM not available)\n", ev);
force_renderer = true;
}
if (!weston_view_has_valid_buffer(ev)) {
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
"(no buffer available)\n", ev);
force_renderer = true;
}
/* We can support this with the 'CRTC background colour' property,
* if it is fullscreen (i.e. we disable the primary plane), and
* opaque (as it is only shown in the absence of any covering
* plane, not as a replacement for the primary plane per se). */
if (ev->surface->buffer_ref.buffer &&
ev->surface->buffer_ref.buffer->type == WESTON_BUFFER_SOLID) {
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
"(solid-colour surface)\n", ev);
force_renderer = true;
}
if (pnode->surf_xform.transform != NULL ||
!pnode->surf_xform.identity_pipeline) {
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
@ -996,15 +829,9 @@ drm_output_propose_state(struct weston_output *output_base,
if (!force_renderer) {
drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n",
current_lowest_zpos);
ps = drm_output_prepare_plane_view(state, ev, mode,
scanout_state,
current_lowest_zpos,
&pnode->try_view_on_plane_failure_reasons);
/* If we were able to place the view in a plane, set
* failure reasons to none. */
if (ps)
pnode->try_view_on_plane_failure_reasons =
FAILURE_REASONS_NONE;
ps = drm_output_find_plane_for_view(state, pnode, mode,
scanout_state,
current_lowest_zpos);
} else {
/* We are forced to place the view in the renderer, set
* the failure reason accordingly. */
@ -1088,21 +915,24 @@ err:
}
void
drm_assign_planes(struct weston_output *output_base, void *repaint_data)
drm_assign_planes(struct weston_output *output_base)
{
struct drm_backend *b = to_drm_backend(output_base->compositor);
struct drm_pending_state *pending_state = repaint_data;
struct drm_output *output = to_drm_output(output_base);
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
struct drm_pending_state *pending_state = device->repaint_data;
struct drm_output_state *state = NULL;
struct drm_plane_state *plane_state;
struct weston_paint_node *pnode;
struct weston_plane *primary = &output_base->compositor->primary_plane;
enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY;
assert(output);
drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n",
output_base->name, (unsigned long) output_base->id);
if (!b->sprites_are_broken && !output->virtual) {
if (!device->sprites_are_broken && !output->virtual && b->gbm) {
drm_debug(b, "\t[repaint] trying planes-only build state\n");
state = drm_output_propose_state(output_base, pending_state, mode);
if (!state) {
@ -1144,26 +974,24 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
/* Update dmabuf-feedback if needed */
if (ev->surface->dmabuf_feedback)
dmabuf_feedback_maybe_update(b, ev,
dmabuf_feedback_maybe_update(device, ev,
pnode->try_view_on_plane_failure_reasons);
pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE;
/* Test whether this buffer can ever go into a plane:
* non-shm, or small enough to be a cursor.
*
* Also, keep a reference when using the pixman renderer.
* That makes it possible to do a seamless switch to the GL
* renderer and since the pixman renderer keeps a reference
* to the buffer anyway, there is no side effects.
*/
if (b->use_pixman ||
(weston_view_has_valid_buffer(ev) &&
(!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) ||
(ev->surface->width <= b->cursor_width &&
ev->surface->height <= b->cursor_height))))
ev->surface->keep_buffer = true;
else
ev->surface->keep_buffer = false;
* non-shm, or small enough to be a cursor. */
ev->surface->keep_buffer = false;
if (weston_view_has_valid_buffer(ev)) {
struct weston_buffer *buffer =
ev->surface->buffer_ref.buffer;
if (buffer->type == WESTON_BUFFER_DMABUF ||
buffer->type == WESTON_BUFFER_RENDERER_OPAQUE)
ev->surface->keep_buffer = true;
else if (buffer->type == WESTON_BUFFER_SHM &&
(ev->surface->width <= device->cursor_width &&
ev->surface->height <= device->cursor_height))
ev->surface->keep_buffer = true;
}
/* This is a bit unpleasant, but lacking a temporary place to
* hang a plane off the view, we have to do a nested walk.

@ -1,998 +0,0 @@
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 Intel Corporation
* Copyright © 2012 Raspberry Pi Foundation
* Copyright © 2013 Philip Withnall
*
* 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.
*/
#include "config.h"
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fb.h>
#include <linux/input.h>
#include <assert.h>
#include <libudev.h>
#include "shared/helpers.h"
#include <libweston/libweston.h>
#include <libweston/backend-fbdev.h>
#include "launcher-util.h"
#include "pixman-renderer.h"
#include "libinput-seat.h"
#include "presentation-time-server-protocol.h"
struct fbdev_backend {
struct weston_backend base;
struct weston_compositor *compositor;
uint32_t prev_state;
struct udev *udev;
struct udev_input input;
uint32_t output_transform;
struct wl_listener session_listener;
};
struct fbdev_screeninfo {
unsigned int x_resolution; /* pixels, visible area */
unsigned int y_resolution; /* pixels, visible area */
unsigned int width_mm; /* visible screen width in mm */
unsigned int height_mm; /* visible screen height in mm */
unsigned int bits_per_pixel;
size_t buffer_length; /* length of frame buffer memory in bytes */
size_t line_length; /* length of a line in bytes */
char id[16]; /* screen identifier */
pixman_format_code_t pixel_format; /* frame buffer pixel format */
unsigned int refresh_rate; /* Hertz */
};
struct fbdev_head {
struct weston_head base;
/* Frame buffer details. */
char *device;
struct fbdev_screeninfo fb_info;
};
struct fbdev_output {
struct fbdev_backend *backend;
struct weston_output base;
struct weston_mode mode;
struct wl_event_source *finish_frame_timer;
/* framebuffer mmap details */
size_t buffer_length;
void *fb;
/* pixman details. */
pixman_image_t *hw_surface;
};
static const char default_seat[] = "seat0";
static inline struct fbdev_head *
to_fbdev_head(struct weston_head *base)
{
return container_of(base, struct fbdev_head, base);
}
static inline struct fbdev_output *
to_fbdev_output(struct weston_output *base)
{
return container_of(base, struct fbdev_output, base);
}
static inline struct fbdev_backend *
to_fbdev_backend(struct weston_compositor *base)
{
return container_of(base->backend, struct fbdev_backend, base);
}
static struct fbdev_head *
fbdev_output_get_head(struct fbdev_output *output)
{
if (wl_list_length(&output->base.head_list) != 1)
return NULL;
return container_of(output->base.head_list.next,
struct fbdev_head, base.output_link);
}
static int
fbdev_output_start_repaint_loop(struct weston_output *output)
{
struct timespec ts;
weston_compositor_read_presentation_clock(output->compositor, &ts);
weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
return 0;
}
static int
fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage,
void *repaint_data)
{
struct fbdev_output *output = to_fbdev_output(base);
struct weston_compositor *ec = output->base.compositor;
/* Repaint the damaged region onto the back buffer. */
pixman_renderer_output_set_buffer(base, output->hw_surface);
ec->renderer->repaint_output(base, damage);
/* Update the damage region. */
pixman_region32_subtract(&ec->primary_plane.damage,
&ec->primary_plane.damage, damage);
/* Schedule the end of the frame. We do not sync this to the frame
* buffer clock because users who want that should be using the DRM
* compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires
* panning, which is broken in most kernel drivers.
*
* Finish the frame synchronised to the specified refresh rate. The
* refresh rate is given in mHz and the interval in ms. */
wl_event_source_timer_update(output->finish_frame_timer,
1000000 / output->mode.refresh);
return 0;
}
static int
finish_frame_handler(void *data)
{
struct fbdev_output *output = data;
struct timespec ts;
weston_compositor_read_presentation_clock(output->base.compositor, &ts);
weston_output_finish_frame(&output->base, &ts, 0);
return 1;
}
static pixman_format_code_t
calculate_pixman_format(struct fb_var_screeninfo *vinfo,
struct fb_fix_screeninfo *finfo)
{
/* Calculate the pixman format supported by the frame buffer from the
* buffer's metadata. Return 0 if no known pixman format is supported
* (since this has depth 0 it's guaranteed to not conflict with any
* actual pixman format).
*
* Documentation on the vinfo and finfo structures:
* http://www.mjmwired.net/kernel/Documentation/fb/api.txt
*
* TODO: Try a bit harder to support other formats, including setting
* the preferred format in the hardware. */
int type;
weston_log("Calculating pixman format from:\n"
STAMP_SPACE " - type: %i (aux: %i)\n"
STAMP_SPACE " - visual: %i\n"
STAMP_SPACE " - bpp: %i (grayscale: %i)\n"
STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n"
STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n"
STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n"
STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n",
finfo->type, finfo->type_aux, finfo->visual,
vinfo->bits_per_pixel, vinfo->grayscale,
vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right,
vinfo->green.offset, vinfo->green.length,
vinfo->green.msb_right,
vinfo->blue.offset, vinfo->blue.length,
vinfo->blue.msb_right,
vinfo->transp.offset, vinfo->transp.length,
vinfo->transp.msb_right);
/* We only handle packed formats at the moment. */
if (finfo->type != FB_TYPE_PACKED_PIXELS)
return 0;
/* We only handle true-colour frame buffers at the moment. */
switch(finfo->visual) {
case FB_VISUAL_TRUECOLOR:
case FB_VISUAL_DIRECTCOLOR:
if (vinfo->grayscale != 0)
return 0;
break;
default:
return 0;
}
/* We only support formats with MSBs on the left. */
if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 ||
vinfo->blue.msb_right != 0)
return 0;
/* Work out the format type from the offsets. We only support RGBA, ARGB
* and ABGR at the moment. */
type = PIXMAN_TYPE_OTHER;
if ((vinfo->transp.offset >= vinfo->red.offset ||
vinfo->transp.length == 0) &&
vinfo->red.offset >= vinfo->green.offset &&
vinfo->green.offset >= vinfo->blue.offset)
type = PIXMAN_TYPE_ARGB;
else if (vinfo->red.offset >= vinfo->green.offset &&
vinfo->green.offset >= vinfo->blue.offset &&
vinfo->blue.offset >= vinfo->transp.offset)
type = PIXMAN_TYPE_RGBA;
else if (vinfo->transp.offset >= vinfo->blue.offset &&
vinfo->blue.offset >= vinfo->green.offset &&
vinfo->green.offset >= vinfo->red.offset)
type = PIXMAN_TYPE_ABGR;
if (type == PIXMAN_TYPE_OTHER)
return 0;
/* Build the format. */
return PIXMAN_FORMAT(vinfo->bits_per_pixel, type,
vinfo->transp.length,
vinfo->red.length,
vinfo->green.length,
vinfo->blue.length);
}
static int
calculate_refresh_rate(struct fb_var_screeninfo *vinfo)
{
uint64_t quot;
/* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */
quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
quot *= vinfo->pixclock;
if (quot > 0) {
uint64_t refresh_rate;
refresh_rate = 1000000000000000LLU / quot;
if (refresh_rate > 200000)
refresh_rate = 200000; /* cap at 200 Hz */
if (refresh_rate >= 1000) /* at least 1 Hz */
return refresh_rate;
}
return 60 * 1000; /* default to 60 Hz */
}
static int
fbdev_query_screen_info(int fd, struct fbdev_screeninfo *info)
{
struct fb_var_screeninfo varinfo;
struct fb_fix_screeninfo fixinfo;
/* Probe the device for screen information. */
if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 ||
ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
return -1;
}
/* Store the pertinent data. */
info->x_resolution = varinfo.xres;
info->y_resolution = varinfo.yres;
info->width_mm = varinfo.width;
info->height_mm = varinfo.height;
info->bits_per_pixel = varinfo.bits_per_pixel;
info->buffer_length = fixinfo.smem_len;
info->line_length = fixinfo.line_length;
strncpy(info->id, fixinfo.id, sizeof(info->id));
info->id[sizeof(info->id)-1] = '\0';
info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo);
info->refresh_rate = calculate_refresh_rate(&varinfo);
if (info->pixel_format == 0) {
weston_log("Frame buffer uses an unsupported format.\n");
return -1;
}
return 1;
}
static int
fbdev_set_screen_info(int fd, struct fbdev_screeninfo *info)
{
struct fb_var_screeninfo varinfo;
/* Grab the current screen information. */
if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
return -1;
}
/* Update the information. */
varinfo.xres = info->x_resolution;
varinfo.yres = info->y_resolution;
varinfo.width = info->width_mm;
varinfo.height = info->height_mm;
varinfo.bits_per_pixel = info->bits_per_pixel;
/* Try to set up an ARGB (x8r8g8b8) pixel format. */
varinfo.grayscale = 0;
varinfo.transp.offset = 24;
varinfo.transp.length = 0;
varinfo.transp.msb_right = 0;
varinfo.red.offset = 16;
varinfo.red.length = 8;
varinfo.red.msb_right = 0;
varinfo.green.offset = 8;
varinfo.green.length = 8;
varinfo.green.msb_right = 0;
varinfo.blue.offset = 0;
varinfo.blue.length = 8;
varinfo.blue.msb_right = 0;
/* Set the device's screen information. */
if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
return -1;
}
return 1;
}
static int
fbdev_wakeup_screen(int fd, struct fbdev_screeninfo *info)
{
struct fb_var_screeninfo varinfo;
/* Grab the current screen information. */
if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
return -1;
}
/* force the framebuffer to wake up */
varinfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
/* Set the device's screen information. */
if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
return -1;
}
return 1;
}
/* Returns an FD for the frame buffer device. */
static int
fbdev_frame_buffer_open(const char *fb_dev,
struct fbdev_screeninfo *screen_info)
{
int fd = -1;
weston_log("Opening fbdev frame buffer.\n");
/* Open the frame buffer device. */
fd = open(fb_dev, O_RDWR | O_CLOEXEC);
if (fd < 0) {
weston_log("Failed to open frame buffer device ‘%s’: %s\n",
fb_dev, strerror(errno));
return -1;
}
/* Grab the screen info. */
if (fbdev_query_screen_info(fd, screen_info) < 0) {
weston_log("Failed to get frame buffer info: %s\n",
strerror(errno));
close(fd);
return -1;
}
/* Attempt to wake up the framebuffer device, needed for secondary
* framebuffer devices */
if (fbdev_wakeup_screen(fd, screen_info) < 0) {
weston_log("Failed to activate framebuffer display. "
"Attempting to open output anyway.\n");
}
return fd;
}
/* Closes the FD on success or failure. */
static int
fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
{
struct fbdev_head *head;
int retval = -1;
head = fbdev_output_get_head(output);
weston_log("Mapping fbdev frame buffer.\n");
/* Map the frame buffer. Write-only mode, since we don't want to read
* anything back (because it's slow). */
output->buffer_length = head->fb_info.buffer_length;
output->fb = mmap(NULL, output->buffer_length,
PROT_WRITE, MAP_SHARED, fd, 0);
if (output->fb == MAP_FAILED) {
weston_log("Failed to mmap frame buffer: %s\n",
strerror(errno));
output->fb = NULL;
goto out_close;
}
/* Create a pixman image to wrap the memory mapped frame buffer. */
output->hw_surface =
pixman_image_create_bits(head->fb_info.pixel_format,
head->fb_info.x_resolution,
head->fb_info.y_resolution,
output->fb,
head->fb_info.line_length);
if (output->hw_surface == NULL) {
weston_log("Failed to create surface for frame buffer.\n");
goto out_unmap;
}
/* Success! */
retval = 0;
out_unmap:
if (retval != 0 && output->fb != NULL) {
munmap(output->fb, output->buffer_length);
output->fb = NULL;
}
out_close:
if (fd >= 0)
close(fd);
return retval;
}
static void
fbdev_frame_buffer_unmap(struct fbdev_output *output)
{
if (!output->fb) {
assert(!output->hw_surface);
return;
}
weston_log("Unmapping fbdev frame buffer.\n");
if (output->hw_surface)
pixman_image_unref(output->hw_surface);
output->hw_surface = NULL;
if (munmap(output->fb, output->buffer_length) < 0)
weston_log("Failed to munmap frame buffer: %s\n",
strerror(errno));
output->fb = NULL;
}
static int
fbdev_output_attach_head(struct weston_output *output_base,
struct weston_head *head_base)
{
struct fbdev_output *output = to_fbdev_output(output_base);
struct fbdev_head *head = to_fbdev_head(head_base);
/* Clones not supported. */
if (!wl_list_empty(&output->base.head_list))
return -1;
/* only one static mode in list */
output->mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
output->mode.width = head->fb_info.x_resolution;
output->mode.height = head->fb_info.y_resolution;
output->mode.refresh = head->fb_info.refresh_rate;
wl_list_init(&output->base.mode_list);
wl_list_insert(&output->base.mode_list, &output->mode.link);
output->base.current_mode = &output->mode;
return 0;
}
static void fbdev_output_destroy(struct weston_output *base);
static int
fbdev_output_enable(struct weston_output *base)
{
struct fbdev_output *output = to_fbdev_output(base);
struct fbdev_backend *backend = to_fbdev_backend(base->compositor);
struct fbdev_head *head;
int fb_fd;
struct wl_event_loop *loop;
const struct pixman_renderer_output_options options = {
.use_shadow = true,
};
head = fbdev_output_get_head(output);
/* Create the frame buffer. */
fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
if (fb_fd < 0) {
weston_log("Creating frame buffer failed.\n");
return -1;
}
if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
weston_log("Mapping frame buffer failed.\n");
return -1;
}
output->base.start_repaint_loop = fbdev_output_start_repaint_loop;
output->base.repaint = fbdev_output_repaint;
if (pixman_renderer_output_create(&output->base, &options) < 0)
goto out_hw_surface;
loop = wl_display_get_event_loop(backend->compositor->wl_display);
output->finish_frame_timer =
wl_event_loop_add_timer(loop, finish_frame_handler, output);
weston_log("fbdev output %d×%d px\n",
output->mode.width, output->mode.height);
weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n",
output->mode.refresh / 1000);
return 0;
out_hw_surface:
fbdev_frame_buffer_unmap(output);
return -1;
}
static int
fbdev_output_disable(struct weston_output *base)
{
struct fbdev_output *output = to_fbdev_output(base);
if (!base->enabled)
return 0;
wl_event_source_remove(output->finish_frame_timer);
output->finish_frame_timer = NULL;
pixman_renderer_output_destroy(&output->base);
fbdev_frame_buffer_unmap(output);
return 0;
}
static struct fbdev_head *
fbdev_head_create(struct fbdev_backend *backend, const char *device)
{
struct fbdev_head *head;
int fb_fd;
head = zalloc(sizeof *head);
if (!head)
return NULL;
head->device = strdup(device);
/* Create the frame buffer. */
fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
if (fb_fd < 0) {
weston_log("Creating frame buffer head failed.\n");
goto out_free;
}
close(fb_fd);
weston_head_init(&head->base, "fbdev");
weston_head_set_connection_status(&head->base, true);
weston_head_set_monitor_strings(&head->base, "unknown",
head->fb_info.id, NULL);
weston_head_set_subpixel(&head->base, WL_OUTPUT_SUBPIXEL_UNKNOWN);
weston_head_set_physical_size(&head->base, head->fb_info.width_mm,
head->fb_info.height_mm);
weston_compositor_add_head(backend->compositor, &head->base);
weston_log("Created head '%s' for device %s (%s)\n",
head->base.name, head->device, head->base.model);
return head;
out_free:
free(head->device);
free(head);
return NULL;
}
static void
fbdev_head_destroy(struct fbdev_head *head)
{
weston_head_release(&head->base);
free(head->device);
free(head);
}
static struct weston_output *
fbdev_output_create(struct weston_compositor *compositor,
const char *name)
{
struct fbdev_output *output;
weston_log("Creating fbdev output.\n");
output = zalloc(sizeof *output);
if (output == NULL)
return NULL;
output->backend = to_fbdev_backend(compositor);
weston_output_init(&output->base, compositor, name);
output->base.destroy = fbdev_output_destroy;
output->base.disable = fbdev_output_disable;
output->base.enable = fbdev_output_enable;
output->base.attach_head = fbdev_output_attach_head;
weston_compositor_add_pending_output(&output->base, compositor);
return &output->base;
}
static void
fbdev_output_destroy(struct weston_output *base)
{
struct fbdev_output *output = to_fbdev_output(base);
weston_log("Destroying fbdev output.\n");
fbdev_output_disable(base);
/* Remove the output. */
weston_output_release(&output->base);
free(output);
}
/* strcmp()-style return values. */
static int
compare_screen_info (const struct fbdev_screeninfo *a,
const struct fbdev_screeninfo *b)
{
if (a->x_resolution == b->x_resolution &&
a->y_resolution == b->y_resolution &&
a->width_mm == b->width_mm &&
a->height_mm == b->height_mm &&
a->bits_per_pixel == b->bits_per_pixel &&
a->pixel_format == b->pixel_format &&
a->refresh_rate == b->refresh_rate)
return 0;
return 1;
}
static int
fbdev_output_reenable(struct fbdev_backend *backend,
struct weston_output *base)
{
struct fbdev_output *output = to_fbdev_output(base);
struct fbdev_head *head;
struct fbdev_screeninfo new_screen_info;
int fb_fd;
head = fbdev_output_get_head(output);
weston_log("Re-enabling fbdev output.\n");
assert(output->base.enabled);
/* Create the frame buffer. */
fb_fd = fbdev_frame_buffer_open(head->device, &new_screen_info);
if (fb_fd < 0) {
weston_log("Creating frame buffer failed.\n");
return -1;
}
/* Check whether the frame buffer details have changed since we were
* disabled. */
if (compare_screen_info(&head->fb_info, &new_screen_info) != 0) {
/* Perform a mode-set to restore the old mode. */
if (fbdev_set_screen_info(fb_fd, &head->fb_info) < 0) {
weston_log("Failed to restore mode settings. "
"Attempting to re-open output anyway.\n");
}
close(fb_fd);
/* Disable and enable the output so that resources depending on
* the frame buffer X/Y resolution (such as the shadow buffer)
* are re-initialised. */
fbdev_output_disable(&output->base);
return fbdev_output_enable(&output->base);
}
/* Map the device if it has the same details as before. */
if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
weston_log("Mapping frame buffer failed.\n");
return -1;
}
return 0;
}
static void
fbdev_backend_destroy(struct weston_compositor *base)
{
struct fbdev_backend *backend = to_fbdev_backend(base);
struct weston_head *head, *next;
udev_input_destroy(&backend->input);
/* Destroy the output. */
weston_compositor_shutdown(base);
wl_list_for_each_safe(head, next, &base->head_list, compositor_link)
fbdev_head_destroy(to_fbdev_head(head));
/* Chain up. */
weston_launcher_destroy(base->launcher);
udev_unref(backend->udev);
free(backend);
}
static void
session_notify(struct wl_listener *listener, void *data)
{
struct weston_compositor *compositor = data;
struct fbdev_backend *backend = to_fbdev_backend(compositor);
struct weston_output *output;
if (compositor->session_active) {
weston_log("entering VT\n");
compositor->state = backend->prev_state;
wl_list_for_each(output, &compositor->output_list, link) {
fbdev_output_reenable(backend, output);
}
weston_compositor_damage_all(compositor);
udev_input_enable(&backend->input);
} else {
weston_log("leaving VT\n");
udev_input_disable(&backend->input);
wl_list_for_each(output, &compositor->output_list, link) {
fbdev_frame_buffer_unmap(to_fbdev_output(output));
}
backend->prev_state = compositor->state;
weston_compositor_offscreen(compositor);
/* If we have a repaint scheduled (from the idle handler), make
* sure we cancel that so we don't try to pageflip when we're
* vt switched away. The OFFSCREEN state will prevent
* further attempts at repainting. When we switch
* back, we schedule a repaint, which will process
* pending frame callbacks. */
wl_list_for_each(output,
&compositor->output_list, link) {
output->repaint_needed = false;
}
}
}
static char *
find_framebuffer_device(struct fbdev_backend *b, const char *seat)
{
struct udev_enumerate *e;
struct udev_list_entry *entry;
const char *path, *device_seat, *id;
char *fb_device_path = NULL;
struct udev_device *device, *fb_device, *pci;
e = udev_enumerate_new(b->udev);
udev_enumerate_add_match_subsystem(e, "graphics");
udev_enumerate_add_match_sysname(e, "fb[0-9]*");
udev_enumerate_scan_devices(e);
fb_device = NULL;
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
bool is_boot_vga = false;
path = udev_list_entry_get_name(entry);
device = udev_device_new_from_syspath(b->udev, path);
if (!device)
continue;
device_seat = udev_device_get_property_value(device, "ID_SEAT");
if (!device_seat)
device_seat = default_seat;
if (strcmp(device_seat, seat)) {
udev_device_unref(device);
continue;
}
pci = udev_device_get_parent_with_subsystem_devtype(device,
"pci", NULL);
if (pci) {
id = udev_device_get_sysattr_value(pci, "boot_vga");
if (id && !strcmp(id, "1"))
is_boot_vga = true;
}
/* If a framebuffer device was found, and this device isn't
* the boot-VGA device, don't use it. */
if (!is_boot_vga && fb_device) {
udev_device_unref(device);
continue;
}
/* There can only be one boot_vga device. Try to use it
* at all costs. */
if (is_boot_vga) {
if (fb_device)
udev_device_unref(fb_device);
fb_device = device;
break;
}
/* Per the (!is_boot_vga && fb_device) test above, only
* trump existing saved devices with boot-VGA devices, so if
* the test ends up here, this must be the first device seen. */
assert(!fb_device);
fb_device = device;
}
udev_enumerate_unref(e);
if (fb_device) {
fb_device_path = strdup(udev_device_get_devnode(fb_device));
udev_device_unref(fb_device);
}
return fb_device_path;
}
static struct fbdev_backend *
fbdev_backend_create(struct weston_compositor *compositor,
struct weston_fbdev_backend_config *param)
{
struct fbdev_backend *backend;
const char *seat_id = default_seat;
const char *session_seat;
session_seat = getenv("XDG_SEAT");
if (session_seat)
seat_id = session_seat;
if (param->seat_id)
seat_id = param->seat_id;
weston_log("initializing fbdev backend\n");
weston_log("warning: the fbdev backend is deprecated, please migrate "
"to the DRM backend\n");
backend = zalloc(sizeof *backend);
if (backend == NULL)
return NULL;
backend->compositor = compositor;
compositor->backend = &backend->base;
if (weston_compositor_set_presentation_clock_software(
compositor) < 0)
goto out_compositor;
backend->udev = udev_new();
if (backend->udev == NULL) {
weston_log("Failed to initialize udev context.\n");
goto out_compositor;
}
if (!param->device)
param->device = find_framebuffer_device(backend, seat_id);
if (!param->device) {
weston_log("fatal: no framebuffer devices detected.\n");
goto out_udev;
}
/* Set up the TTY. */
backend->session_listener.notify = session_notify;
wl_signal_add(&compositor->session_signal,
&backend->session_listener);
compositor->launcher =
weston_launcher_connect(compositor, param->tty, seat_id, false);
if (!compositor->launcher) {
weston_log("fatal: fbdev backend should be run using "
"weston-launch binary, or your system should "
"provide the logind D-Bus API.\n");
goto out_udev;
}
backend->base.destroy = fbdev_backend_destroy;
backend->base.create_output = fbdev_output_create;
backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
weston_setup_vt_switch_bindings(compositor);
if (pixman_renderer_init(compositor) < 0)
goto out_launcher;
if (!fbdev_head_create(backend, param->device))
goto out_launcher;
free(param->device);
udev_input_init(&backend->input, compositor, backend->udev,
seat_id, param->configure_device);
return backend;
out_launcher:
free(param->device);
weston_launcher_destroy(compositor->launcher);
out_udev:
udev_unref(backend->udev);
out_compositor:
weston_compositor_shutdown(compositor);
free(backend);
return NULL;
}
static void
config_init_to_defaults(struct weston_fbdev_backend_config *config)
{
config->tty = 0; /* default to current tty */
config->device = NULL;
config->seat_id = NULL;
}
WL_EXPORT int
weston_backend_init(struct weston_compositor *compositor,
struct weston_backend_config *config_base)
{
struct fbdev_backend *b;
struct weston_fbdev_backend_config config = {{ 0, }};
if (config_base == NULL ||
config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION ||
config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) {
weston_log("fbdev backend config structure is invalid\n");
return -1;
}
config_init_to_defaults(&config);
memcpy(&config, config_base, config_base->struct_size);
b = fbdev_backend_create(compositor, &config);
if (b == NULL)
return -1;
return 0;
}

@ -1,33 +0,0 @@
if not get_option('deprecated-backend-fbdev')
subdir_done()
endif
warning('Support for the deprecated fbdev backend is enabled.')
warning('This feature will be removed in a future version.')
config_h.set('BUILD_FBDEV_COMPOSITOR', '1')
srcs_fbdev = [
'fbdev.c',
presentation_time_server_protocol_h,
]
deps_fbdev = [
dep_libweston_private,
dep_session_helper,
dep_libinput_backend,
dependency('libudev', version: '>= 136'),
]
plugin_fbdev = shared_library(
'fbdev-backend',
srcs_fbdev,
include_directories: common_inc,
dependencies: deps_fbdev,
name_prefix: '',
install: true,
install_dir: dir_module_libweston
)
env_modmap += 'fbdev-backend.so=@0@;'.format(plugin_fbdev.full_path())
install_headers(backend_fbdev_h, subdir: dir_include_libweston_install)

@ -37,6 +37,7 @@
#include <libweston/backend-headless.h>
#include "shared/helpers.h"
#include "linux-explicit-synchronization.h"
#include "pixel-formats.h"
#include "pixman-renderer.h"
#include "renderer-gl/gl-renderer.h"
#include "shared/weston-drm-fourcc.h"
@ -70,24 +71,33 @@ struct headless_output {
struct weston_mode mode;
struct wl_event_source *finish_frame_timer;
uint32_t *image_buf;
pixman_image_t *image;
};
static const uint32_t headless_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XRGB8888, /* default for pixman-renderer */
DRM_FORMAT_ARGB8888,
};
static void
headless_head_destroy(struct weston_head *base);
static inline struct headless_head *
to_headless_head(struct weston_head *base)
{
if (base->backend_id != headless_head_destroy)
return NULL;
return container_of(base, struct headless_head, base);
}
static void
headless_output_destroy(struct weston_output *base);
static inline struct headless_output *
to_headless_output(struct weston_output *base)
{
if (base->destroy != headless_output_destroy)
return NULL;
return container_of(base, struct headless_output, base);
}
@ -122,11 +132,14 @@ finish_frame_handler(void *data)
static int
headless_output_repaint(struct weston_output *output_base,
pixman_region32_t *damage,
void *repaint_data)
pixman_region32_t *damage)
{
struct headless_output *output = to_headless_output(output_base);
struct weston_compositor *ec = output->base.compositor;
struct weston_compositor *ec;
assert(output);
ec = output->base.compositor;
ec->renderer->repaint_output(&output->base, damage);
@ -152,18 +165,21 @@ headless_output_disable_pixman(struct headless_output *output)
{
pixman_renderer_output_destroy(&output->base);
pixman_image_unref(output->image);
free(output->image_buf);
}
static int
headless_output_disable(struct weston_output *base)
{
struct headless_output *output = to_headless_output(base);
struct headless_backend *b = to_headless_backend(base->compositor);
struct headless_backend *b;
assert(output);
if (!output->base.enabled)
return 0;
b = to_headless_backend(base->compositor);
wl_event_source_remove(output->finish_frame_timer);
switch (b->renderer_type) {
@ -185,6 +201,8 @@ headless_output_destroy(struct weston_output *base)
{
struct headless_output *output = to_headless_output(base);
assert(output);
headless_output_disable(&output->base);
weston_output_release(&output->base);
@ -214,20 +232,20 @@ headless_output_enable_gl(struct headless_output *output)
static int
headless_output_enable_pixman(struct headless_output *output)
{
const struct pixel_format_info *pfmt;
const struct pixman_renderer_output_options options = {
.use_shadow = true,
};
output->image_buf = malloc(output->base.current_mode->width *
output->base.current_mode->height * 4);
if (!output->image_buf)
return -1;
pfmt = pixel_format_get_info(headless_formats[0]);
output->image = pixman_image_create_bits(PIXMAN_x8r8g8b8,
output->base.current_mode->width,
output->base.current_mode->height,
output->image_buf,
output->base.current_mode->width * 4);
output->image =
pixman_image_create_bits_no_clear(pfmt->pixman_format,
output->base.current_mode->width,
output->base.current_mode->height,
NULL, 0);
if (!output->image)
return -1;
if (pixman_renderer_output_create(&output->base, &options) < 0)
goto err_renderer;
@ -238,7 +256,6 @@ headless_output_enable_pixman(struct headless_output *output)
err_renderer:
pixman_image_unref(output->image);
free(output->image_buf);
return -1;
}
@ -247,10 +264,14 @@ static int
headless_output_enable(struct weston_output *base)
{
struct headless_output *output = to_headless_output(base);
struct headless_backend *b = to_headless_backend(base->compositor);
struct headless_backend *b;
struct wl_event_loop *loop;
int ret = 0;
assert(output);
b = to_headless_backend(base->compositor);
loop = wl_display_get_event_loop(b->compositor->wl_display);
output->finish_frame_timer =
wl_event_loop_add_timer(loop, finish_frame_handler, output);
@ -287,6 +308,9 @@ headless_output_set_size(struct weston_output *base,
struct weston_head *head;
int output_width, output_height;
if (!output)
return -1;
/* We can only be called once. */
assert(!output->base.current_mode);
@ -361,7 +385,12 @@ headless_head_create(struct weston_compositor *compositor,
return -1;
weston_head_init(&head->base, name);
head->base.backend_id = headless_head_destroy;
weston_head_set_connection_status(&head->base, true);
weston_head_set_supported_eotf_mask(&head->base,
WESTON_EOTF_MODE_ALL_MASK);
/* Ideally all attributes of the head would be set here, so that the
* user has all the information when deciding to create outputs.
@ -374,8 +403,12 @@ headless_head_create(struct weston_compositor *compositor,
}
static void
headless_head_destroy(struct headless_head *head)
headless_head_destroy(struct weston_head *base)
{
struct headless_head *head = to_headless_head(base);
assert(head);
weston_head_release(&head->base);
free(head);
}
@ -388,8 +421,10 @@ headless_destroy(struct weston_compositor *ec)
weston_compositor_shutdown(ec);
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
headless_head_destroy(to_headless_head(base));
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) {
if (to_headless_head(base))
headless_head_destroy(base);
}
free(b);
}

@ -4,24 +4,36 @@ endif
config_h.set('BUILD_RDP_COMPOSITOR', '1')
dep_frdp = dependency('freerdp2', version: '>= 2.2.0', required: false)
dep_frdp = dependency('freerdp2', version: '>= 2.3.0', required: false)
if not dep_frdp.found()
error('RDP-backend requires freerdp >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
error('RDP-backend requires freerdp >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
endif
dep_wpr = dependency('winpr2', version: '>= 2.2.0', required: false)
dep_frdp_server = dependency('freerdp-server2', version: '>= 2.3.0', required: false)
if not dep_frdp_server.found()
error('RDP-backend requires freerdp-server2 >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
endif
dep_wpr = dependency('winpr2', version: '>= 2.3.0', required: false)
if not dep_wpr.found()
error('RDP-backend requires winpr >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
error('RDP-backend requires winpr >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
endif
deps_rdp = [
dep_libweston_private,
dep_frdp,
dep_frdp_server,
dep_wpr,
]
srcs_rdp = [
'rdp.c',
'rdpclip.c',
'rdputil.c',
]
plugin_rdp = shared_library(
'rdp-backend',
'rdp.c',
srcs_rdp,
include_directories: common_inc,
dependencies: deps_rdp,
name_prefix: '',

File diff suppressed because it is too large Load Diff

@ -0,0 +1,263 @@
/*
* Copyright © 2013 Hardening <rdp.effort@gmail.com>
* Copyright © 2020 Microsoft
*
* 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 RDP_H
#define RDP_H
#include <freerdp/version.h>
#include <freerdp/freerdp.h>
#include <freerdp/listener.h>
#include <freerdp/update.h>
#include <freerdp/input.h>
#include <freerdp/codec/color.h>
#include <freerdp/codec/rfx.h>
#include <freerdp/codec/nsc.h>
#include <freerdp/locale/keyboard.h>
#include <freerdp/channels/wtsvc.h>
#include <freerdp/server/cliprdr.h>
#include <libweston/libweston.h>
#include <libweston/backend-rdp.h>
#include <libweston/weston-log.h>
#include "backend.h"
#include "shared/helpers.h"
#include "shared/string-helpers.h"
#define MAX_FREERDP_FDS 32
#define DEFAULT_AXIS_STEP_DISTANCE 10
#define DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_BGRA32
/* https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardtype
* defines a keyboard type that isn't currently defined in FreeRDP, but is
* available for RDP connections */
#ifndef KBD_TYPE_KOREAN
#define KBD_TYPE_KOREAN 8
#endif
/* WinPR's GetVirtualKeyCodeFromVirtualScanCode() can't handle hangul/hanja keys */
/* 0x1f1 and 0x1f2 keys are only exists on Korean 103 keyboard (Type 8:SubType 6) */
/* From Linux's keyboard driver at drivers/input/keyboard/atkbd.c */
#define ATKBD_RET_HANJA 0xf1
#define ATKBD_RET_HANGEUL 0xf2
struct rdp_output;
struct rdp_backend {
struct weston_backend base;
struct weston_compositor *compositor;
freerdp_listener *listener;
struct wl_event_source *listener_events[MAX_FREERDP_FDS];
struct rdp_output *output;
struct weston_log_scope *debug;
struct weston_log_scope *verbose;
struct weston_log_scope *clipboard_debug;
struct weston_log_scope *clipboard_verbose;
struct wl_list peers;
char *server_cert;
char *server_key;
char *rdp_key;
int tls_enabled;
int no_clients_resize;
int force_no_compression;
bool remotefx_codec;
int external_listener_fd;
int rdp_monitor_refresh_rate;
pid_t compositor_tid;
rdp_audio_in_setup audio_in_setup;
rdp_audio_in_teardown audio_in_teardown;
rdp_audio_out_setup audio_out_setup;
rdp_audio_out_teardown audio_out_teardown;
};
enum peer_item_flags {
RDP_PEER_ACTIVATED = (1 << 0),
RDP_PEER_OUTPUT_ENABLED = (1 << 1),
};
struct rdp_peers_item {
int flags;
freerdp_peer *peer;
struct weston_seat *seat;
struct wl_list link;
};
struct rdp_head {
struct weston_head base;
};
struct rdp_output {
struct weston_output base;
struct wl_event_source *finish_frame_timer;
pixman_image_t *shadow_surface;
};
struct rdp_peer_context {
rdpContext _p;
struct rdp_backend *rdpBackend;
struct wl_event_source *events[MAX_FREERDP_FDS + 1]; /* +1 for WTSVirtualChannelManagerGetFileDescriptor */
RFX_CONTEXT *rfx_context;
wStream *encode_stream;
RFX_RECT *rfx_rects;
NSC_CONTEXT *nsc_context;
struct rdp_peers_item item;
bool button_state[5];
int verticalAccumWheelRotationPrecise;
int verticalAccumWheelRotationDiscrete;
int horizontalAccumWheelRotationPrecise;
int horizontalAccumWheelRotationDiscrete;
HANDLE vcm;
/* list of outstanding event_source sent from FreeRDP thread to display loop.*/
int loop_task_event_source_fd;
struct wl_event_source *loop_task_event_source;
pthread_mutex_t loop_task_list_mutex;
struct wl_list loop_task_list; /* struct rdp_loop_task::link */
/* Clipboard support */
CliprdrServerContext *clipboard_server_context;
void *audio_in_private;
void *audio_out_private;
struct rdp_clipboard_data_source *clipboard_client_data_source;
struct rdp_clipboard_data_source *clipboard_inflight_client_data_source;
struct wl_listener clipboard_selection_listener;
};
typedef struct rdp_peer_context RdpPeerContext;
typedef void (*rdp_loop_task_func_t)(bool freeOnly, void *data);
struct rdp_loop_task {
struct wl_list link;
RdpPeerContext *peerCtx;
rdp_loop_task_func_t func;
};
#define rdp_debug_verbose(b, ...) \
rdp_debug_print(b->verbose, false, __VA_ARGS__)
#define rdp_debug_verbose_continue(b, ...) \
rdp_debug_print(b->verbose, true, __VA_ARGS__)
#define rdp_debug(b, ...) \
rdp_debug_print(b->debug, false, __VA_ARGS__)
#define rdp_debug_continue(b, ...) \
rdp_debug_print(b->debug, true, __VA_ARGS__)
#define rdp_debug_clipboard_verbose(b, ...) \
rdp_debug_print(b->clipboard_verbose, false, __VA_ARGS__)
#define rdp_debug_clipboard_verbose_continue(b, ...) \
rdp_debug_print(b->clipboard_verbose, true, __VA_ARGS__)
#define rdp_debug_clipboard(b, ...) \
rdp_debug_print(b->clipboard_debug, false, __VA_ARGS__)
#define rdp_debug_clipboard_continue(b, ...) \
rdp_debug_print(b->clipboard_debug, true, __VA_ARGS__)
/* rdputil.c */
void
rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...);
int
rdp_wl_array_read_fd(struct wl_array *array, int fd);
void
convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, UINT32 KeyboardSubType, UINT32 KeyboardLayout, struct xkb_rule_names *xkbRuleNames);
void
assert_compositor_thread(struct rdp_backend *b);
void
assert_not_compositor_thread(struct rdp_backend *b);
bool
rdp_event_loop_add_fd(struct wl_event_loop *loop,
int fd, uint32_t mask,
wl_event_loop_fd_func_t func,
void *data,
struct wl_event_source **event_source);
void
rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx,
rdp_loop_task_func_t func,
struct rdp_loop_task *task);
bool
rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx);
void
rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx);
/* rdpclip.c */
int
rdp_clipboard_init(freerdp_peer *client);
void
rdp_clipboard_destroy(RdpPeerContext *peerCtx);
void
rdp_head_destroy(struct weston_head *base);
static inline struct rdp_head *
to_rdp_head(struct weston_head *base)
{
if (base->backend_id != rdp_head_destroy)
return NULL;
return container_of(base, struct rdp_head, base);
}
void
rdp_output_destroy(struct weston_output *base);
static inline struct rdp_output *
to_rdp_output(struct weston_output *base)
{
if (base->destroy != rdp_output_destroy)
return NULL;
return container_of(base, struct rdp_output, base);
}
static inline struct rdp_backend *
to_rdp_backend(struct weston_compositor *base)
{
return container_of(base->backend, struct rdp_backend, base);
}
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,262 @@
/*
* Copyright © 2020 Microsoft
*
* 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.
*/
#include "config.h"
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/eventfd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "rdp.h"
static int cached_tm_mday = -1;
void rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...)
{
char timestr[128];
int len_va;
char *str;
if (!log_scope || !weston_log_scope_is_enabled(log_scope))
return;
va_list ap;
va_start(ap, fmt);
if (cont) {
weston_log_scope_vprintf(log_scope, fmt, ap);
goto end;
}
weston_log_timestamp(timestr, sizeof(timestr), &cached_tm_mday);
len_va = vasprintf(&str, fmt, ap);
if (len_va >= 0) {
weston_log_scope_printf(log_scope, "%s %s",
timestr, str);
free(str);
} else {
const char *oom = "Out of memory";
weston_log_scope_printf(log_scope, "%s %s",
timestr, oom);
}
end:
va_end(ap);
}
void
assert_compositor_thread(struct rdp_backend *b)
{
assert(b->compositor_tid == gettid());
}
void
assert_not_compositor_thread(struct rdp_backend *b)
{
assert(b->compositor_tid != gettid());
}
bool
rdp_event_loop_add_fd(struct wl_event_loop *loop,
int fd, uint32_t mask,
wl_event_loop_fd_func_t func,
void *data, struct wl_event_source **event_source)
{
*event_source = wl_event_loop_add_fd(loop, fd, 0, func, data);
if (!*event_source) {
weston_log("%s: wl_event_loop_add_fd failed.\n", __func__);
return false;
}
wl_event_source_fd_update(*event_source, mask);
return true;
}
void
rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx,
rdp_loop_task_func_t func,
struct rdp_loop_task *task)
{
/* this function is ONLY used to queue the task from FreeRDP thread,
* and the task to be processed at wayland display loop thread. */
assert_not_compositor_thread(peerCtx->rdpBackend);
task->peerCtx = peerCtx;
task->func = func;
pthread_mutex_lock(&peerCtx->loop_task_list_mutex);
/* this inserts at head */
wl_list_insert(&peerCtx->loop_task_list, &task->link);
pthread_mutex_unlock(&peerCtx->loop_task_list_mutex);
eventfd_write(peerCtx->loop_task_event_source_fd, 1);
}
static int
rdp_dispatch_task(int fd, uint32_t mask, void *arg)
{
RdpPeerContext *peerCtx = (RdpPeerContext *)arg;
struct rdp_loop_task *task, *tmp;
eventfd_t dummy;
/* this must be called back at wayland display loop thread */
assert_compositor_thread(peerCtx->rdpBackend);
eventfd_read(peerCtx->loop_task_event_source_fd, &dummy);
pthread_mutex_lock(&peerCtx->loop_task_list_mutex);
/* dequeue the first task which is at last, so use reverse. */
assert(!wl_list_empty(&peerCtx->loop_task_list));
wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) {
wl_list_remove(&task->link);
break;
}
pthread_mutex_unlock(&peerCtx->loop_task_list_mutex);
/* Dispatch and task will be freed by caller. */
task->func(false, task);
return 0;
}
bool
rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx)
{
struct rdp_backend *b = peerCtx->rdpBackend;
struct wl_event_loop *loop;
bool ret;
if (pthread_mutex_init(&peerCtx->loop_task_list_mutex, NULL) == -1) {
weston_log("%s: pthread_mutex_init failed. %s\n", __func__, strerror(errno));
goto error_mutex;
}
assert(peerCtx->loop_task_event_source_fd == -1);
peerCtx->loop_task_event_source_fd = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC);
if (peerCtx->loop_task_event_source_fd == -1) {
weston_log("%s: eventfd(EFD_SEMAPHORE) failed. %s\n", __func__, strerror(errno));
goto error_event_source_fd;
}
assert(wl_list_empty(&peerCtx->loop_task_list));
loop = wl_display_get_event_loop(b->compositor->wl_display);
assert(peerCtx->loop_task_event_source == NULL);
ret = rdp_event_loop_add_fd(loop,
peerCtx->loop_task_event_source_fd,
WL_EVENT_READABLE, rdp_dispatch_task,
peerCtx,
&peerCtx->loop_task_event_source);
if (!ret)
goto error_event_loop_add_fd;
return true;
error_event_loop_add_fd:
close(peerCtx->loop_task_event_source_fd);
peerCtx->loop_task_event_source_fd = -1;
error_event_source_fd:
pthread_mutex_destroy(&peerCtx->loop_task_list_mutex);
error_mutex:
return false;
}
void
rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx)
{
struct rdp_loop_task *task, *tmp;
/* This function must be called all virtual channel thread at FreeRDP is terminated,
* that ensures no more incoming tasks. */
if (peerCtx->loop_task_event_source) {
wl_event_source_remove(peerCtx->loop_task_event_source);
peerCtx->loop_task_event_source = NULL;
}
wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) {
wl_list_remove(&task->link);
/* inform caller task is not really scheduled prior to context destruction,
* inform them to clean them up. */
task->func(true /* freeOnly */, task);
}
assert(wl_list_empty(&peerCtx->loop_task_list));
if (peerCtx->loop_task_event_source_fd != -1) {
close(peerCtx->loop_task_event_source_fd);
peerCtx->loop_task_event_source_fd = -1;
}
pthread_mutex_destroy(&peerCtx->loop_task_list_mutex);
}
/* This is a little tricky - it makes sure there's always at least
* one spare byte in the array in case the caller needs to add a
* null terminator to it. We can't just null terminate the array
* here, because some callers won't want that - and some won't
* like having an odd number of bytes.
*/
int
rdp_wl_array_read_fd(struct wl_array *array, int fd)
{
int len, size;
char *data;
/* Make sure we have at least 1024 bytes of space left */
if (array->alloc - array->size < 1024) {
if (!wl_array_add(array, 1024)) {
errno = ENOMEM;
return -1;
}
array->size -= 1024;
}
data = (char *)array->data + array->size;
/* Leave one char in case the caller needs space for a
* null terminator */
size = array->alloc - array->size - 1;
do {
len = read(fd, data, size);
} while (len == -1 && errno == EINTR);
if (len == -1)
return -1;
array->size += len;
return len;
}

@ -233,15 +233,25 @@ struct wayland_input {
struct gl_renderer_interface *gl_renderer;
static void
wayland_head_destroy(struct weston_head *base);
static inline struct wayland_head *
to_wayland_head(struct weston_head *base)
{
if (base->backend_id != wayland_head_destroy)
return NULL;
return container_of(base, struct wayland_head, base);
}
static void
wayland_output_destroy(struct weston_output *base);
static inline struct wayland_output *
to_wayland_output(struct weston_output *base)
{
if (base->destroy != wayland_output_destroy)
return NULL;
return container_of(base, struct wayland_output, base);
}
@ -501,8 +511,11 @@ static int
wayland_output_start_repaint_loop(struct weston_output *output_base)
{
struct wayland_output *output = to_wayland_output(output_base);
struct wayland_backend *wb =
to_wayland_backend(output->base.compositor);
struct wayland_backend *wb;
assert(output);
wb = to_wayland_backend(output->base.compositor);
/* If this is the initial frame, we need to attach a buffer so that
* the compositor can map the surface and include it in its render
@ -526,11 +539,14 @@ wayland_output_start_repaint_loop(struct weston_output *output_base)
#ifdef ENABLE_EGL
static int
wayland_output_repaint_gl(struct weston_output *output_base,
pixman_region32_t *damage,
void *repaint_data)
pixman_region32_t *damage)
{
struct wayland_output *output = to_wayland_output(output_base);
struct weston_compositor *ec = output->base.compositor;
struct weston_compositor *ec;
assert(output);
ec = output->base.compositor;
output->frame_cb = wl_surface_frame(output->parent.surface);
wl_callback_add_listener(output->frame_cb, &frame_listener, output);
@ -638,14 +654,16 @@ wayland_shm_buffer_attach(struct wayland_shm_buffer *sb)
static int
wayland_output_repaint_pixman(struct weston_output *output_base,
pixman_region32_t *damage,
void *repaint_data)
pixman_region32_t *damage)
{
struct wayland_output *output = to_wayland_output(output_base);
struct wayland_backend *b =
to_wayland_backend(output->base.compositor);
struct wayland_backend *b;
struct wayland_shm_buffer *sb;
assert(output);
b = to_wayland_backend(output->base.compositor);
if (output->frame) {
if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
wl_list_for_each(sb, &output->shm.buffers, link)
@ -718,7 +736,11 @@ static int
wayland_output_disable(struct weston_output *base)
{
struct wayland_output *output = to_wayland_output(base);
struct wayland_backend *b = to_wayland_backend(base->compositor);
struct wayland_backend *b;
assert(output);
b = to_wayland_backend(base->compositor);
if (!output->base.enabled)
return 0;
@ -752,6 +774,8 @@ wayland_output_destroy(struct weston_output *base)
{
struct wayland_output *output = to_wayland_output(base);
assert(output);
wayland_output_disable(&output->base);
weston_output_release(&output->base);
@ -1062,7 +1086,7 @@ static int
wayland_output_switch_mode(struct weston_output *output_base,
struct weston_mode *mode)
{
struct wayland_output *output = to_wayland_output(output_base);
struct wayland_output *output;
struct wayland_backend *b;
struct wl_surface *old_surface;
struct weston_mode *old_mode;
@ -1073,6 +1097,9 @@ wayland_output_switch_mode(struct weston_output *output_base,
return -1;
}
output = to_wayland_output(output_base);
assert(output);
if (mode == NULL) {
weston_log("mode is NULL.\n");
return -1;
@ -1243,10 +1270,14 @@ static int
wayland_output_enable(struct weston_output *base)
{
struct wayland_output *output = to_wayland_output(base);
struct wayland_backend *b = to_wayland_backend(base->compositor);
struct wayland_backend *b;
enum mode_status mode_status;
int ret = 0;
assert(output);
b = to_wayland_backend(base->compositor);
wl_list_init(&output->shm.buffers);
wl_list_init(&output->shm.free_buffers);
@ -1326,9 +1357,16 @@ static int
wayland_output_attach_head(struct weston_output *output_base,
struct weston_head *head_base)
{
struct wayland_backend *b = to_wayland_backend(output_base->compositor);
struct wayland_output *output = to_wayland_output(output_base);
struct wayland_head *head = to_wayland_head(head_base);
struct wayland_backend *b;
assert(output);
if (!head)
return -1;
b = to_wayland_backend(output_base->compositor);
if (!wl_list_empty(&output->base.head_list))
return -1;
@ -1353,6 +1391,8 @@ wayland_output_detach_head(struct weston_output *output_base,
{
struct wayland_output *output = to_wayland_output(output_base);
assert(output);
/* Rely on the disable hook if the output was enabled. We do not
* support cloned heads, so detaching is guaranteed to disable the
* output.
@ -1411,6 +1451,9 @@ wayland_head_create(struct weston_compositor *compositor, const char *name)
return NULL;
weston_head_init(&head->base, name);
head->base.backend_id = wayland_head_destroy;
weston_head_set_connection_status(&head->base, true);
weston_compositor_add_head(compositor, &head->base);
@ -1458,8 +1501,12 @@ wayland_head_create_for_parent_output(struct weston_compositor *compositor,
}
static void
wayland_head_destroy(struct wayland_head *head)
wayland_head_destroy(struct weston_head *base)
{
struct wayland_head *head = to_wayland_head(base);
assert(head);
if (head->parent_output)
head->parent_output->head = NULL;
@ -1474,6 +1521,9 @@ wayland_output_set_size(struct weston_output *base, int width, int height)
struct weston_head *head;
int output_width, output_height;
if (!output)
return -1;
/* We can only be called once. */
assert(!output->base.current_mode);
@ -2187,10 +2237,6 @@ input_handle_touch_up(void *data, struct wl_touch *wl_touch,
timespec_from_msec(&ts, time);
input->touch_points--;
if (input->touch_points == 0) {
input->touch_focus = NULL;
input->touch_active = false;
}
if (!output)
return;
@ -2253,6 +2299,11 @@ input_handle_touch_frame(void *data, struct wl_touch *wl_touch)
return;
notify_touch_frame(input->touch_device);
if (input->touch_points == 0) {
input->touch_focus = NULL;
input->touch_active = false;
}
}
static void
@ -2629,7 +2680,7 @@ wayland_parent_output_destroy(struct wayland_parent_output *output)
wl_callback_destroy(output->sync_cb);
if (output->head)
wayland_head_destroy(output->head);
wayland_head_destroy(&output->head->base);
wl_output_destroy(output->global);
free(output->physical.make);
@ -2742,14 +2793,20 @@ wayland_destroy(struct weston_compositor *ec)
{
struct wayland_backend *b = to_wayland_backend(ec);
struct weston_head *base, *next;
struct wayland_parent_output *output, *next_output;
struct wayland_input *input, *next_input;
wl_event_source_remove(b->parent.wl_source);
weston_compositor_shutdown(ec);
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
wayland_head_destroy(to_wayland_head(base));
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) {
if (to_wayland_head(base))
wayland_head_destroy(base);
}
wl_list_for_each_safe(output, next_output, &b->parent.output_list, link)
wayland_parent_output_destroy(output);
wl_list_for_each_safe(input, next_input, &b->input_list, link)
wayland_input_destroy(input);

@ -119,6 +119,7 @@ struct x11_backend {
xcb_atom_t cardinal;
xcb_atom_t xkb_names;
} atom;
xcb_generic_event_t *prev_event;
};
struct x11_head {
@ -151,15 +152,25 @@ struct window_delete_data {
struct gl_renderer_interface *gl_renderer;
static void
x11_head_destroy(struct weston_head *base);
static inline struct x11_head *
to_x11_head(struct weston_head *base)
{
if (base->backend_id != x11_head_destroy)
return NULL;
return container_of(base, struct x11_head, base);
}
static void
x11_output_destroy(struct weston_output *base);
static inline struct x11_output *
to_x11_output(struct weston_output *base)
{
if (base->destroy != x11_output_destroy)
return NULL;
return container_of(base, struct x11_output, base);
}
@ -417,11 +428,14 @@ x11_output_start_repaint_loop(struct weston_output *output)
static int
x11_output_repaint_gl(struct weston_output *output_base,
pixman_region32_t *damage,
void *repaint_data)
pixman_region32_t *damage)
{
struct x11_output *output = to_x11_output(output_base);
struct weston_compositor *ec = output->base.compositor;
struct weston_compositor *ec;
assert(output);
ec = output->base.compositor;
ec->renderer->repaint_output(output_base, damage);
@ -436,8 +450,8 @@ static void
set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region)
{
struct x11_output *output = to_x11_output(output_base);
struct weston_compositor *ec = output->base.compositor;
struct x11_backend *b = to_x11_backend(ec);
struct weston_compositor *ec;
struct x11_backend *b;
pixman_region32_t transformed_region;
pixman_box32_t *rects;
xcb_rectangle_t *output_rects;
@ -445,6 +459,12 @@ set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region
int nrects, i;
xcb_generic_error_t *err;
if (!output)
return;
ec = output->base.compositor;
b = to_x11_backend(ec);
pixman_region32_init(&transformed_region);
pixman_region32_copy(&transformed_region, region);
pixman_region32_translate(&transformed_region,
@ -486,15 +506,19 @@ set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region
static int
x11_output_repaint_shm(struct weston_output *output_base,
pixman_region32_t *damage,
void *repaint_data)
pixman_region32_t *damage)
{
struct x11_output *output = to_x11_output(output_base);
struct weston_compositor *ec = output->base.compositor;
struct x11_backend *b = to_x11_backend(ec);
struct weston_compositor *ec;
struct x11_backend *b;
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
assert(output);
ec = output->base.compositor;
b = to_x11_backend(ec);
pixman_renderer_output_set_buffer(output_base, output->hw_surface);
ec->renderer->repaint_output(output_base, damage);
@ -567,13 +591,13 @@ x11_output_set_wm_protocols(struct x11_backend *b,
}
struct wm_normal_hints {
uint32_t flags;
uint32_t flags;
uint32_t pad[4];
int32_t min_width, min_height;
int32_t max_width, max_height;
int32_t width_inc, height_inc;
int32_t min_aspect_x, min_aspect_y;
int32_t max_aspect_x, max_aspect_y;
int32_t width_inc, height_inc;
int32_t min_aspect_x, min_aspect_y;
int32_t max_aspect_x, max_aspect_y;
int32_t base_width, base_height;
int32_t win_gravity;
};
@ -803,12 +827,13 @@ static int
x11_output_switch_mode(struct weston_output *base, struct weston_mode *mode)
{
struct x11_backend *b;
struct x11_output *output;
struct x11_output *output = to_x11_output(base);
static uint32_t values[2];
int ret;
assert(output);
b = to_x11_backend(base->compositor);
output = to_x11_output(base);
if (mode->width == output->mode.width &&
mode->height == output->mode.height)
@ -880,7 +905,11 @@ static int
x11_output_disable(struct weston_output *base)
{
struct x11_output *output = to_x11_output(base);
struct x11_backend *backend = to_x11_backend(base->compositor);
struct x11_backend *backend;
assert(output);
backend = to_x11_backend(base->compositor);
if (!output->base.enabled)
return 0;
@ -905,6 +934,8 @@ x11_output_destroy(struct weston_output *base)
{
struct x11_output *output = to_x11_output(base);
assert(output);
x11_output_disable(&output->base);
weston_output_release(&output->base);
@ -915,7 +946,12 @@ static int
x11_output_enable(struct weston_output *base)
{
struct x11_output *output = to_x11_output(base);
struct x11_backend *b = to_x11_backend(base->compositor);
const struct weston_mode *mode = output->base.current_mode;
struct x11_backend *b;
assert(output);
b = to_x11_backend(base->compositor);
static const char name[] = "Weston Compositor";
static const char class[] = "weston-1\0Weston Compositor";
@ -954,8 +990,7 @@ x11_output_enable(struct weston_output *base)
output->window,
screen->root,
0, 0,
output->base.current_mode->width,
output->base.current_mode->height,
mode->width, mode->height,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
@ -1020,8 +1055,7 @@ x11_output_enable(struct weston_output *base)
.use_shadow = true,
};
if (x11_output_init_shm(b, output,
output->base.current_mode->width,
output->base.current_mode->height) < 0) {
mode->width, mode->height) < 0) {
weston_log("Failed to initialize SHM for the X11 output\n");
goto err;
}
@ -1062,9 +1096,7 @@ x11_output_enable(struct weston_output *base)
wl_event_loop_add_timer(loop, finish_frame_handler, output);
weston_log("x11 output %dx%d, window id %d\n",
output->base.current_mode->width,
output->base.current_mode->height,
output->window);
mode->width, mode->height, output->window);
return 0;
@ -1079,11 +1111,17 @@ static int
x11_output_set_size(struct weston_output *base, int width, int height)
{
struct x11_output *output = to_x11_output(base);
struct x11_backend *b = to_x11_backend(base->compositor);
struct x11_backend *b;
struct weston_head *head;
xcb_screen_t *scrn = b->screen;
xcb_screen_t *scrn;
int output_width, output_height;
if (!output)
return -1;
b = to_x11_backend(base->compositor);
scrn = b->screen;
/* We can only be called once. */
assert(!output->base.current_mode);
@ -1165,6 +1203,9 @@ x11_head_create(struct weston_compositor *compositor, const char *name)
return -1;
weston_head_init(&head->base, name);
head->base.backend_id = x11_head_destroy;
weston_head_set_connection_status(&head->base, true);
weston_compositor_add_head(compositor, &head->base);
@ -1172,8 +1213,12 @@ x11_head_create(struct weston_compositor *compositor, const char *name)
}
static void
x11_head_destroy(struct x11_head *head)
x11_head_destroy(struct weston_head *base)
{
struct x11_head *head = to_x11_head(base);
assert(head);
weston_head_release(&head->base);
free(head);
}
@ -1450,7 +1495,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
{
struct x11_backend *b = data;
struct x11_output *output;
xcb_generic_event_t *event, *prev;
xcb_generic_event_t *event;
xcb_client_message_event_t *client_message;
xcb_enter_notify_event_t *enter_notify;
xcb_key_press_event_t *key_press, *key_release;
@ -1466,16 +1511,15 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
int count;
struct timespec time;
prev = NULL;
count = 0;
while (x11_backend_next_event(b, &event, mask)) {
response_type = event->response_type & ~0x80;
switch (prev ? prev->response_type & ~0x80 : 0x80) {
switch (b->prev_event ? b->prev_event->response_type & ~0x80 : 0x80) {
case XCB_KEY_RELEASE:
/* Suppress key repeat events; this is only used if we
* don't have XCB XKB support. */
key_release = (xcb_key_press_event_t *) prev;
key_release = (xcb_key_press_event_t *) b->prev_event;
key_press = (xcb_key_press_event_t *) event;
if (response_type == XCB_KEY_PRESS &&
key_release->time == key_press->time &&
@ -1483,8 +1527,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
/* Don't deliver the held key release
* event or the new key press event. */
free(event);
free(prev);
prev = NULL;
free(b->prev_event);
b->prev_event = NULL;
continue;
} else {
/* Deliver the held key release now
@ -1497,8 +1541,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
key_release->detail - 8,
WL_KEYBOARD_KEY_STATE_RELEASED,
STATE_UPDATE_AUTOMATIC);
free(prev);
prev = NULL;
free(b->prev_event);
b->prev_event = NULL;
break;
}
@ -1522,8 +1566,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
notify_keyboard_focus_in(&b->core_seat, &b->keys,
STATE_UPDATE_AUTOMATIC);
free(prev);
prev = NULL;
free(b->prev_event);
b->prev_event = NULL;
break;
default:
@ -1548,7 +1592,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
/* If we don't have XKB, we need to use the lame
* autorepeat detection above. */
if (!b->has_xkb) {
prev = event;
b->prev_event = event;
break;
}
key_release = (xcb_key_press_event_t *) event;
@ -1643,7 +1687,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED)
break;
prev = event;
b->prev_event = event;
break;
case XCB_FOCUS_OUT:
@ -1677,13 +1721,13 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
#endif
count++;
if (prev != event)
free (event);
if (b->prev_event != event)
free(event);
}
switch (prev ? prev->response_type & ~0x80 : 0x80) {
switch (b->prev_event ? b->prev_event->response_type & ~0x80 : 0x80) {
case XCB_KEY_RELEASE:
key_release = (xcb_key_press_event_t *) prev;
key_release = (xcb_key_press_event_t *) b->prev_event;
update_xkb_state_from_core(b, key_release->state);
weston_compositor_get_time(&time);
notify_key(&b->core_seat,
@ -1691,8 +1735,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
key_release->detail - 8,
WL_KEYBOARD_KEY_STATE_RELEASED,
STATE_UPDATE_AUTOMATIC);
free(prev);
prev = NULL;
free(b->prev_event);
b->prev_event = NULL;
break;
default:
break;
@ -1792,8 +1836,10 @@ x11_destroy(struct weston_compositor *ec)
weston_compositor_shutdown(ec); /* destroys outputs, too */
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
x11_head_destroy(to_x11_head(base));
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) {
if (to_x11_head(base))
x11_head_destroy(base);
}
XCloseDisplay(backend->dpy);
free(backend);

@ -32,6 +32,8 @@
#ifndef LIBWESTON_BACKEND_INTERNAL_H
#define LIBWESTON_BACKEND_INTERNAL_H
struct weston_hdr_metadata_type1;
struct weston_backend {
void (*destroy)(struct weston_compositor *compositor);
@ -46,27 +48,21 @@ struct weston_backend {
* Returns an opaque pointer, which the backend may use as private
* data referring to the repaint cycle.
*/
void * (*repaint_begin)(struct weston_compositor *compositor);
void (*repaint_begin)(struct weston_compositor *compositor);
/** Cancel a repaint sequence
*
* Cancels a repaint sequence, when an error has occurred during
* one output's repaint; see repaint_begin.
*
* @param repaint_data Data returned by repaint_begin
*/
void (*repaint_cancel)(struct weston_compositor *compositor,
void *repaint_data);
void (*repaint_cancel)(struct weston_compositor *compositor);
/** Conclude a repaint sequence
*
* Called on successful completion of a repaint sequence; see
* repaint_begin.
*
* @param repaint_data Data returned by repaint_begin
*/
int (*repaint_flush)(struct weston_compositor *compositor,
void *repaint_data);
int (*repaint_flush)(struct weston_compositor *compositor);
/** Allocate a new output
*
@ -142,6 +138,10 @@ weston_head_set_subpixel(struct weston_head *head,
void
weston_head_set_transform(struct weston_head *head, uint32_t transform);
void
weston_head_set_supported_eotf_mask(struct weston_head *head,
uint32_t eotf_mask);
/* weston_output */
void
@ -154,9 +154,6 @@ weston_output_damage(struct weston_output *output);
void
weston_output_release(struct weston_output *output);
void
weston_output_init_zoom(struct weston_output *output);
void
weston_output_finish_frame(struct weston_output *output,
const struct timespec *stamp,
@ -178,6 +175,9 @@ void
weston_output_region_from_global(struct weston_output *output,
pixman_region32_t *region);
const struct weston_hdr_metadata_type1 *
weston_output_get_hdr_metadata_type1(const struct weston_output *output);
/* weston_seat */
void

@ -310,8 +310,8 @@ weston_compositor_run_key_binding(struct weston_compositor *compositor,
/* If this was a key binding and it didn't
* install a keyboard grab, install one now to
* swallow the key press. */
if (keyboard->grab ==
&keyboard->default_grab)
if (keyboard->grab == &keyboard->default_grab ||
keyboard->grab == &keyboard->input_method_grab)
install_binding_grab(keyboard,
time,
key,

@ -110,7 +110,7 @@ clipboard_source_data(int fd, uint32_t mask, void *data)
static void
clipboard_source_accept(struct weston_data_source *source,
uint32_t time, const char *mime_type)
uint32_t serial, const char *mime_type)
{
}

@ -27,12 +27,37 @@
#include "config.h"
#include <assert.h>
#include <string.h>
#include <libweston/libweston.h>
#include "color.h"
#include "color-lcms.h"
#include "shared/helpers.h"
static cmsUInt32Number
cmlcms_get_render_intent(enum cmlcms_category cat,
struct weston_surface *surface,
struct weston_output *output)
{
/*
* TODO: Take into account client provided content profile,
* output profile, and the category of the wanted color
* transformation.
*/
cmsUInt32Number intent = INTENT_RELATIVE_COLORIMETRIC;
return intent;
}
static struct cmlcms_color_profile *
get_cprof_or_stock_sRGB(struct weston_color_manager_lcms *cm,
struct weston_color_profile *cprof_base)
{
if (cprof_base)
return get_cprof(cprof_base);
else
return cm->sRGB_profile;
}
static void
cmlcms_destroy_color_transform(struct weston_color_transform *xform_base)
{
@ -48,47 +73,52 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base,
struct weston_surface_color_transform *surf_xform)
{
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
struct cmlcms_color_transform_search_param param = {
/*
* Assumes both content and output color spaces are sRGB SDR.
* This defines the blending space as optical sRGB SDR.
*/
.type = CMLCMS_TYPE_EOTF_sRGB,
};
struct cmlcms_color_transform *xform;
/* TODO: use output color profile */
if (output->color_profile)
return false;
/* TODO: take weston_output::eotf_mode into account */
struct cmlcms_color_transform_search_param param = {
.category = CMLCMS_CATEGORY_INPUT_TO_BLEND,
.input_profile = get_cprof_or_stock_sRGB(cm, NULL /* TODO: surface->color_profile */),
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
};
param.intent_output = cmlcms_get_render_intent(param.category,
surface, output);
xform = cmlcms_color_transform_get(cm, &param);
if (!xform)
return false;
surf_xform->transform = &xform->base;
surf_xform->identity_pipeline = true;
/*
* When we introduce LCMS plug-in we can precisely answer this question
* by examining the color pipeline using precision parameters. For now
* we just compare if it is same pointer or not.
*/
if (xform->search_key.input_profile == xform->search_key.output_profile)
surf_xform->identity_pipeline = true;
else
surf_xform->identity_pipeline = false;
return true;
}
static bool
cmlcms_get_output_color_transform(struct weston_color_manager *cm_base,
struct weston_output *output,
struct weston_color_transform **xform_out)
cmlcms_get_blend_to_output_color_transform(struct weston_color_manager_lcms *cm,
struct weston_output *output,
struct weston_color_transform **xform_out)
{
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
struct cmlcms_color_transform_search_param param = {
/*
* Assumes blending space is optical sRGB SDR and
* output color space is sRGB SDR.
*/
.type = CMLCMS_TYPE_EOTF_sRGB_INV,
};
struct cmlcms_color_transform *xform;
/* TODO: use output color profile */
if (output->color_profile)
return false;
/* TODO: take weston_output::eotf_mode into account */
struct cmlcms_color_transform_search_param param = {
.category = CMLCMS_CATEGORY_BLEND_TO_OUTPUT,
.input_profile = NULL,
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
};
param.intent_output = cmlcms_get_render_intent(param.category,
NULL, output);
xform = cmlcms_color_transform_get(cm, &param);
if (!xform)
@ -99,37 +129,54 @@ cmlcms_get_output_color_transform(struct weston_color_manager *cm_base,
}
static bool
cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base,
cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager_lcms *cm,
struct weston_output *output,
struct weston_color_transform **xform_out)
{
/* Assumes output color space is sRGB SDR */
struct cmlcms_color_transform *xform;
/* TODO: use output color profile */
if (output->color_profile)
return false;
/* TODO: take weston_output::eotf_mode into account */
/* Identity transform */
*xform_out = NULL;
struct cmlcms_color_transform_search_param param = {
.category = CMLCMS_CATEGORY_INPUT_TO_OUTPUT,
.input_profile = cm->sRGB_profile,
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
};
param.intent_output = cmlcms_get_render_intent(param.category,
NULL, output);
/*
* Create a color transformation when output profile is not stock
* sRGB profile.
*/
if (param.output_profile != cm->sRGB_profile) {
xform = cmlcms_color_transform_get(cm, &param);
if (!xform)
return false;
*xform_out = &xform->base;
} else {
*xform_out = NULL; /* Identity transform */
}
return true;
}
static bool
cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base,
cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager_lcms *cm,
struct weston_output *output,
struct weston_color_transform **xform_out)
{
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
struct cmlcms_color_transform_search_param param = {
/* Assumes blending space is optical sRGB SDR */
.type = CMLCMS_TYPE_EOTF_sRGB,
};
struct cmlcms_color_transform *xform;
/* TODO: use output color profile */
if (output->color_profile)
return false;
/* TODO: take weston_output::eotf_mode into account */
struct cmlcms_color_transform_search_param param = {
.category = CMLCMS_CATEGORY_INPUT_TO_BLEND,
.input_profile = cm->sRGB_profile,
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
};
param.intent_output = cmlcms_get_render_intent(param.category,
NULL, output);
xform = cmlcms_color_transform_get(cm, &param);
if (!xform)
@ -139,6 +186,145 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base,
return true;
}
static float
meta_clamp(float value, const char *valname, float min, float max,
struct weston_output *output)
{
float ret = value;
/* Paranoia against NaN */
if (!(ret >= min))
ret = min;
if (!(ret <= max))
ret = max;
if (ret != value) {
weston_log("output '%s' clamping %s value from %f to %f.\n",
output->name, valname, value, ret);
}
return ret;
}
static bool
cmlcms_get_hdr_meta(struct weston_output *output,
struct weston_hdr_metadata_type1 *hdr_meta)
{
const struct weston_color_characteristics *cc;
hdr_meta->group_mask = 0;
/* Only SMPTE ST 2084 mode uses HDR Static Metadata Type 1 */
if (weston_output_get_eotf_mode(output) != WESTON_EOTF_MODE_ST2084)
return true;
/* ICC profile overrides color characteristics */
if (output->color_profile) {
/*
* TODO: extract characteristics from profile?
* Get dynamic range from weston_color_characteristics?
*/
return true;
}
cc = weston_output_get_color_characteristics(output);
/* Target content chromaticity */
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES) {
unsigned i;
for (i = 0; i < 3; i++) {
hdr_meta->primary[i].x = meta_clamp(cc->primary[i].x,
"primary", 0.0, 1.0,
output);
hdr_meta->primary[i].y = meta_clamp(cc->primary[i].y,
"primary", 0.0, 1.0,
output);
}
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES;
}
/* Target content white point */
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE) {
hdr_meta->white.x = meta_clamp(cc->white.x, "white",
0.0, 1.0, output);
hdr_meta->white.y = meta_clamp(cc->white.y, "white",
0.0, 1.0, output);
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_WHITE;
}
/* Target content peak and max mastering luminance */
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL) {
hdr_meta->maxDML = meta_clamp(cc->max_luminance, "maxDML",
1.0, 65535.0, output);
hdr_meta->maxCLL = meta_clamp(cc->max_luminance, "maxCLL",
1.0, 65535.0, output);
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML;
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL;
}
/* Target content min mastering luminance */
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MINL) {
hdr_meta->minDML = meta_clamp(cc->min_luminance, "minDML",
0.0001, 6.5535, output);
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MINDML;
}
/* Target content max frame-average luminance */
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL) {
hdr_meta->maxFALL = meta_clamp(cc->maxFALL, "maxFALL",
1.0, 65535.0, output);
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL;
}
return true;
}
static struct weston_output_color_outcome *
cmlcms_create_output_color_outcome(struct weston_color_manager *cm_base,
struct weston_output *output)
{
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
struct weston_output_color_outcome *co;
co = zalloc(sizeof *co);
if (!co)
return NULL;
if (!cmlcms_get_hdr_meta(output, &co->hdr_meta))
goto out_fail;
/*
* TODO: if output->color_profile is NULL, maybe manufacture a
* profile from weston_color_characteristics if it has enough
* information?
* Or let the frontend decide to call a "create a profile from
* characteristics" API?
*/
/* TODO: take container color space into account */
if (!cmlcms_get_blend_to_output_color_transform(cm, output,
&co->from_blend_to_output))
goto out_fail;
if (!cmlcms_get_sRGB_to_blend_color_transform(cm, output,
&co->from_sRGB_to_blend))
goto out_fail;
if (!cmlcms_get_sRGB_to_output_color_transform(cm, output,
&co->from_sRGB_to_output))
goto out_fail;
return co;
out_fail:
weston_output_color_outcome_destroy(&co);
return NULL;
}
static void
lcms_error_logger(cmsContext context_id,
cmsUInt32Number error_code,
@ -165,6 +351,10 @@ cmlcms_init(struct weston_color_manager *cm_base)
cmsSetLogErrorHandlerTHR(cm->lcms_ctx, lcms_error_logger);
if (!cmlcms_create_stock_profile(cm)) {
weston_log("color-lcms: error: cmlcms_create_stock_profile failed\n");
return false;
}
weston_log("LittleCMS %d initialized.\n", cmsGetEncodedCMMversion());
return true;
@ -175,6 +365,8 @@ cmlcms_destroy(struct weston_color_manager *cm_base)
{
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
if (cm->sRGB_profile)
cmlcms_color_profile_destroy(cm->sRGB_profile);
assert(wl_list_empty(&cm->color_transform_list));
assert(wl_list_empty(&cm->color_profile_list));
@ -199,13 +391,8 @@ weston_color_manager_create(struct weston_compositor *compositor)
cm->base.destroy_color_profile = cmlcms_destroy_color_profile;
cm->base.get_color_profile_from_icc = cmlcms_get_color_profile_from_icc;
cm->base.destroy_color_transform = cmlcms_destroy_color_transform;
cm->base.get_surface_color_transform =
cmlcms_get_surface_color_transform;
cm->base.get_output_color_transform = cmlcms_get_output_color_transform;
cm->base.get_sRGB_to_output_color_transform =
cmlcms_get_sRGB_to_output_color_transform;
cm->base.get_sRGB_to_blend_color_transform =
cmlcms_get_sRGB_to_blend_color_transform;
cm->base.get_surface_color_transform = cmlcms_get_surface_color_transform;
cm->base.create_output_color_outcome = cmlcms_create_output_color_outcome;
wl_list_init(&cm->color_transform_list);
wl_list_init(&cm->color_profile_list);

@ -39,6 +39,7 @@ struct weston_color_manager_lcms {
struct wl_list color_transform_list; /* cmlcms_color_transform::link */
struct wl_list color_profile_list; /* cmlcms_color_profile::link */
struct cmlcms_color_profile *sRGB_profile; /* stock profile */
};
static inline struct weston_color_manager_lcms *
@ -59,6 +60,58 @@ struct cmlcms_color_profile {
cmsHPROFILE profile;
struct cmlcms_md5_sum md5sum;
/**
* If the profile does support being an output profile and it is used as an
* output then this field represents a light linearizing transfer function
* and it can not be null. The field is null only if the profile is not
* usable as an output profile. The field is set when cmlcms_color_profile
* is created.
*/
cmsToneCurve *output_eotf[3];
/**
* If the profile does support being an output profile and it is used as an
* output then this field represents a concatenation of inverse EOTF + VCGT,
* if the tag exists and it can not be null.
* VCGT is part of monitor calibration which means: even though we must
* apply VCGT in the compositor, we pretend that it happens inside the
* monitor. This is how the classic color management and ICC profiles work.
* The ICC profile (ignoring the VCGT tag) characterizes the output which
* is VCGT + monitor behavior. The field is null only if the profile is not
* usable as an output profile. The field is set when cmlcms_color_profile
* is created.
*/
cmsToneCurve *output_inv_eotf_vcgt[3];
/**
* VCGT tag cached from output profile, it could be null if not exist
*/
cmsToneCurve *vcgt[3];
};
/**
* Type of LCMS transforms
*/
enum cmlcms_category {
/**
* Uses combination of input profile with output profile, but
* without INV EOTF or with additional EOTF in the transform pipeline
* inputblend = input profile + output profile + output EOTF
*/
CMLCMS_CATEGORY_INPUT_TO_BLEND = 0,
/**
* Uses INV EOTF only concatenated with VCGT tag if present
* blendoutput = output inverse EOTF + VCGT
*/
CMLCMS_CATEGORY_BLEND_TO_OUTPUT,
/**
* Transform uses input profile and output profile as is
* inputoutput = input profile + output profile + VCGT
*/
CMLCMS_CATEGORY_INPUT_TO_OUTPUT,
};
static inline struct cmlcms_color_profile *
@ -78,18 +131,12 @@ cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm,
void
cmlcms_destroy_color_profile(struct weston_color_profile *cprof_base);
/*
* Perhaps a placeholder, until we get actual color spaces involved and
* see how this would work better.
*/
enum cmlcms_color_transform_type {
CMLCMS_TYPE_EOTF_sRGB = 0,
CMLCMS_TYPE_EOTF_sRGB_INV,
CMLCMS_TYPE__END,
};
struct cmlcms_color_transform_search_param {
enum cmlcms_color_transform_type type;
enum cmlcms_category category;
struct cmlcms_color_profile *input_profile;
struct cmlcms_color_profile *output_profile;
cmsUInt32Number intent_output; /* selected intent from output profile */
};
struct cmlcms_color_transform {
@ -100,8 +147,15 @@ struct cmlcms_color_transform {
struct cmlcms_color_transform_search_param search_key;
/* for EOTF types */
cmsToneCurve *curve;
/**
* 3D LUT color mapping part of the transformation, if needed.
* For category CMLCMS_CATEGORY_INPUT_TO_OUTPUT it includes pre-curve and
* post-curve.
* For category CMLCMS_CATEGORY_INPUT_TO_BLEND it includes pre-curve.
* For category CMLCMS_CATEGORY_BLEND_TO_OUTPUT and when identity it is
* not used
*/
cmsHTRANSFORM cmap_3dlut;
};
static inline struct cmlcms_color_transform *
@ -117,4 +171,27 @@ cmlcms_color_transform_get(struct weston_color_manager_lcms *cm,
void
cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform);
struct cmlcms_color_profile *
ref_cprof(struct cmlcms_color_profile *cprof);
void
unref_cprof(struct cmlcms_color_profile *cprof);
bool
cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm);
void
cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof);
bool
retrieve_eotf_and_output_inv_eotf(cmsContext lcms_ctx,
cmsHPROFILE hProfile,
cmsToneCurve *output_eotf[3],
cmsToneCurve *output_inv_eotf_vcgt[3],
cmsToneCurve *vcgt[3],
unsigned int num_points);
unsigned int
cmlcms_reasonable_1D_points(void);
#endif /* WESTON_COLOR_LCMS_H */

@ -36,6 +36,223 @@
#include "shared/helpers.h"
#include "shared/string-helpers.h"
struct xyz_arr_flt {
float v[3];
};
static double
xyz_dot_prod(const struct xyz_arr_flt a, const struct xyz_arr_flt b)
{
return (double)a.v[0] * b.v[0] +
(double)a.v[1] * b.v[1] +
(double)a.v[2] * b.v[2];
}
/**
* Graeme sketched a linearization method there:
* https://lists.freedesktop.org/archives/wayland-devel/2019-March/040171.html
*/
static bool
build_eotf_from_clut_profile(cmsContext lcms_ctx,
cmsHPROFILE profile,
cmsToneCurve *output_eotf[3],
int num_points)
{
int ch, point;
float *curve_array[3];
float *red = NULL;
cmsHPROFILE xyz_profile = NULL;
cmsHTRANSFORM transform_rgb_to_xyz = NULL;
bool ret = false;
const float div = num_points - 1;
red = malloc(sizeof(float) * num_points * 3);
if (!red)
goto release;
curve_array[0] = red;
curve_array[1] = red + num_points;
curve_array[2] = red + 2 * num_points;
xyz_profile = cmsCreateXYZProfileTHR(lcms_ctx);
if (!xyz_profile)
goto release;
transform_rgb_to_xyz = cmsCreateTransformTHR(lcms_ctx, profile,
TYPE_RGB_FLT, xyz_profile,
TYPE_XYZ_FLT,
INTENT_ABSOLUTE_COLORIMETRIC,
0);
if (!transform_rgb_to_xyz)
goto release;
for (ch = 0; ch < 3; ch++) {
struct xyz_arr_flt prim_xyz_max;
struct xyz_arr_flt prim_xyz;
double xyz_square_magnitude;
float rgb[3] = { 0.0f, 0.0f, 0.0f };
rgb[ch] = 1.0f;
cmsDoTransform(transform_rgb_to_xyz, rgb, prim_xyz_max.v, 1);
/**
* Calculate xyz square of magnitude uses single channel 100% and
* others are zero.
*/
xyz_square_magnitude = xyz_dot_prod(prim_xyz_max, prim_xyz_max);
/**
* Build rgb tone curves
*/
for (point = 0; point < num_points; point++) {
rgb[ch] = (float)point / div;
cmsDoTransform(transform_rgb_to_xyz, rgb, prim_xyz.v, 1);
curve_array[ch][point] = xyz_dot_prod(prim_xyz,
prim_xyz_max) /
xyz_square_magnitude;
}
/**
* Create LCMS object of rgb tone curves and validate whether
* monotonic
*/
output_eotf[ch] = cmsBuildTabulatedToneCurveFloat(lcms_ctx,
num_points,
curve_array[ch]);
if (!output_eotf[ch])
goto release;
if (!cmsIsToneCurveMonotonic(output_eotf[ch])) {
/**
* It is interesting to see how this profile was created.
* We assume that such a curve could not be used for linearization
* of arbitrary profile.
*/
goto release;
}
}
ret = true;
release:
if (transform_rgb_to_xyz)
cmsDeleteTransform(transform_rgb_to_xyz);
if (xyz_profile)
cmsCloseProfile(xyz_profile);
free(red);
if (ret == false)
cmsFreeToneCurveTriple(output_eotf);
return ret;
}
/**
* Concatenation of two monotonic tone curves.
* LCMS API cmsJoinToneCurve does y = Y^-1(X(t)),
* but want to have y = Y^(X(t))
*/
static cmsToneCurve *
lcmsJoinToneCurve(cmsContext context_id, const cmsToneCurve *X,
const cmsToneCurve *Y, unsigned int resulting_points)
{
cmsToneCurve *out = NULL;
float t, x;
float *res = NULL;
unsigned int i;
res = zalloc(resulting_points * sizeof(float));
if (res == NULL)
goto error;
for (i = 0; i < resulting_points; i++) {
t = (float)i / (resulting_points - 1);
x = cmsEvalToneCurveFloat(X, t);
res[i] = cmsEvalToneCurveFloat(Y, x);
}
out = cmsBuildTabulatedToneCurveFloat(context_id, resulting_points, res);
error:
if (res != NULL)
free(res);
return out;
}
/**
* Extract EOTF from matrix-shaper and cLUT profiles,
* then invert and concatenate with 'vcgt' curve if it
* is available.
*/
bool
retrieve_eotf_and_output_inv_eotf(cmsContext lcms_ctx,
cmsHPROFILE hProfile,
cmsToneCurve *output_eotf[3],
cmsToneCurve *output_inv_eotf_vcgt[3],
cmsToneCurve *vcgt[3],
unsigned int num_points)
{
cmsToneCurve *curve = NULL;
const cmsToneCurve * const *vcgt_curves;
unsigned i;
cmsTagSignature tags[] = {
cmsSigRedTRCTag, cmsSigGreenTRCTag, cmsSigBlueTRCTag
};
if (cmsIsMatrixShaper(hProfile)) {
/**
* Optimization for matrix-shaper profile
* May have 1DLUT->3x3->3x3->1DLUT, 1DLUT->3x3->1DLUT
*/
for (i = 0 ; i < 3; i++) {
curve = cmsReadTag(hProfile, tags[i]);
if (!curve)
goto fail;
output_eotf[i] = cmsDupToneCurve(curve);
if (!output_eotf[i])
goto fail;
}
} else {
/**
* Linearization of cLUT profile may have 1DLUT->3DLUT->1DLUT,
* 1DLUT->3DLUT, 3DLUT
*/
if (!build_eotf_from_clut_profile(lcms_ctx, hProfile,
output_eotf, num_points))
goto fail;
}
/**
* If the caller looking for eotf only then return early.
* It could be used for input profile when identity case: EOTF + INV_EOTF
* in pipeline only.
*/
if (output_inv_eotf_vcgt == NULL)
return true;
for (i = 0; i < 3; i++) {
curve = cmsReverseToneCurve(output_eotf[i]);
if (!curve)
goto fail;
output_inv_eotf_vcgt[i] = curve;
}
vcgt_curves = cmsReadTag(hProfile, cmsSigVcgtTag);
if (vcgt_curves && vcgt_curves[0] && vcgt_curves[1] && vcgt_curves[2]) {
for (i = 0; i < 3; i++) {
curve = lcmsJoinToneCurve(lcms_ctx,
output_inv_eotf_vcgt[i],
vcgt_curves[i], num_points);
if (!curve)
goto fail;
cmsFreeToneCurve(output_inv_eotf_vcgt[i]);
output_inv_eotf_vcgt[i] = curve;
if (vcgt)
vcgt[i] = cmsDupToneCurve(vcgt_curves[i]);
}
}
return true;
fail:
cmsFreeToneCurveTriple(output_eotf);
cmsFreeToneCurveTriple(output_inv_eotf_vcgt);
return false;
}
/* FIXME: sync with spec! */
static bool
validate_icc_profile(cmsHPROFILE profile, char **errmsg)
@ -102,15 +319,37 @@ cmlcms_color_profile_create(struct weston_color_manager_lcms *cm,
return cprof;
}
static void
void
cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof)
{
wl_list_remove(&cprof->link);
cmsFreeToneCurveTriple(cprof->vcgt);
cmsFreeToneCurveTriple(cprof->output_eotf);
cmsFreeToneCurveTriple(cprof->output_inv_eotf_vcgt);
cmsCloseProfile(cprof->profile);
free(cprof->base.description);
free(cprof);
}
struct cmlcms_color_profile *
ref_cprof(struct cmlcms_color_profile *cprof)
{
if (!cprof)
return NULL;
weston_color_profile_ref(&cprof->base);
return cprof;
}
void
unref_cprof(struct cmlcms_color_profile *cprof)
{
if (!cprof)
return;
weston_color_profile_unref(&cprof->base);
}
static char *
make_icc_file_description(cmsHPROFILE profile,
const struct cmlcms_md5_sum *md5sum,
@ -131,6 +370,52 @@ make_icc_file_description(cmsHPROFILE profile,
return desc;
}
/**
*
* Build stock profile which available for clients unaware of color management
*/
bool
cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm)
{
cmsHPROFILE profile;
struct cmlcms_md5_sum md5sum;
char *desc = NULL;
profile = cmsCreate_sRGBProfileTHR(cm->lcms_ctx);
if (!profile) {
weston_log("color-lcms: error: cmsCreate_sRGBProfileTHR failed\n");
return false;
}
if (!cmsMD5computeID(profile)) {
weston_log("Failed to compute MD5 for ICC profile\n");
goto err_close;
}
cmsGetHeaderProfileID(profile, md5sum.bytes);
desc = make_icc_file_description(profile, &md5sum, "sRGB stock");
if (!desc)
goto err_close;
cm->sRGB_profile = cmlcms_color_profile_create(cm, profile, desc, NULL);
if (!cm->sRGB_profile)
goto err_close;
if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx,
cm->sRGB_profile->profile,
cm->sRGB_profile->output_eotf,
cm->sRGB_profile->output_inv_eotf_vcgt,
cm->sRGB_profile->vcgt,
cmlcms_reasonable_1D_points()))
goto err_close;
return true;
err_close:
free(desc);
cmsCloseProfile(profile);
return false;
}
bool
cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm_base,
const void *icc_data,

@ -33,48 +33,98 @@
#include "color-lcms.h"
#include "shared/helpers.h"
/* Arguments to cmsBuildParametricToneCurve() */
struct tone_curve_def {
cmsInt32Number cmstype;
cmsFloat64Number params[5];
};
/*
* LCMS uses the required number of 'params' based on 'cmstype', the parametric
* tone curve number. LCMS honors negative 'cmstype' as inverse function.
* These are LCMS built-in parametric tone curves.
/**
* The method is used in linearization of an arbitrary color profile
* when EOTF is retrieved we want to know a generic way to decide the number
* of points
*/
static const struct tone_curve_def predefined_eotf_curves[] = {
[CMLCMS_TYPE_EOTF_sRGB] = {
.cmstype = 4,
.params = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 },
},
[CMLCMS_TYPE_EOTF_sRGB_INV] = {
.cmstype = -4,
.params = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 },
},
};
unsigned int
cmlcms_reasonable_1D_points(void)
{
return 1024;
}
static unsigned int
cmlcms_reasonable_3D_points(void)
{
return 33;
}
static void
cmlcms_fill_in_tone_curve(struct weston_color_transform *xform_base,
float *values, unsigned len)
fill_in_curves(cmsToneCurve *curves[3], float *values, unsigned len)
{
struct cmlcms_color_transform *xform = get_xform(xform_base);
float *R_lut = values;
float *G_lut = R_lut + len;
float *B_lut = G_lut + len;
unsigned i;
cmsFloat32Number x, y;
assert(xform->curve != NULL);
assert(len > 1);
cmsFloat32Number x;
for (i = 0; i < len; i++) {
x = (double)i / (len - 1);
y = cmsEvalToneCurveFloat(xform->curve, x);
R_lut[i] = y;
G_lut[i] = y;
B_lut[i] = y;
R_lut[i] = cmsEvalToneCurveFloat(curves[0], x);
G_lut[i] = cmsEvalToneCurveFloat(curves[1], x);
B_lut[i] = cmsEvalToneCurveFloat(curves[2], x);
}
}
static void
cmlcms_fill_in_pre_curve(struct weston_color_transform *xform_base,
float *values, unsigned len)
{
struct cmlcms_color_transform *xform = get_xform(xform_base);
assert(xform->search_key.category == CMLCMS_CATEGORY_BLEND_TO_OUTPUT);
assert(len > 1);
fill_in_curves(xform->search_key.output_profile->output_inv_eotf_vcgt,
values, len);
}
/**
* Clamp value to [0.0, 1.0], except pass NaN through.
*
* This function is not intended for hiding NaN.
*/
static float
ensure_unorm(float v)
{
if (v <= 0.0f)
return 0.0f;
if (v > 1.0f)
return 1.0f;
return v;
}
static void
cmlcms_fill_in_3dlut(struct weston_color_transform *xform_base,
float *lut, unsigned int len)
{
struct cmlcms_color_transform *xform = get_xform(xform_base);
float rgb_in[3];
float rgb_out[3];
unsigned int index;
unsigned int value_b, value_r, value_g;
float divider = len - 1;
assert(xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_BLEND ||
xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_OUTPUT);
for (value_b = 0; value_b < len; value_b++) {
for (value_g = 0; value_g < len; value_g++) {
for (value_r = 0; value_r < len; value_r++) {
rgb_in[0] = (float)value_r / divider;
rgb_in[1] = (float)value_g / divider;
rgb_in[2] = (float)value_b / divider;
cmsDoTransform(xform->cmap_3dlut, rgb_in, rgb_out, 1);
index = 3 * (value_r + len * (value_g + len * value_b));
lut[index ] = ensure_unorm(rgb_out[0]);
lut[index + 1] = ensure_unorm(rgb_out[1]);
lut[index + 2] = ensure_unorm(rgb_out[2]);
}
}
}
}
@ -82,55 +132,135 @@ void
cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform)
{
wl_list_remove(&xform->link);
if (xform->curve)
cmsFreeToneCurve(xform->curve);
if (xform->cmap_3dlut)
cmsDeleteTransform(xform->cmap_3dlut);
unref_cprof(xform->search_key.input_profile);
unref_cprof(xform->search_key.output_profile);
free(xform);
}
static bool
xform_set_cmap_3dlut(struct cmlcms_color_transform *xform,
cmsHPROFILE input_profile,
cmsHPROFILE output_profile,
cmsToneCurve *curves[3],
cmsUInt32Number intent)
{
struct weston_color_manager_lcms *cm = get_cmlcms(xform->base.cm);
cmsHPROFILE arr_prof[3] = { input_profile, output_profile, NULL };
int num_profiles = 2;
if (curves[0]) {
arr_prof[2] = cmsCreateLinearizationDeviceLinkTHR(cm->lcms_ctx,
cmsSigRgbData,
curves);
if (!arr_prof[2])
return false;
num_profiles = 3;
}
xform->cmap_3dlut = cmsCreateMultiprofileTransformTHR(cm->lcms_ctx,
arr_prof,
num_profiles,
TYPE_RGB_FLT,
TYPE_RGB_FLT,
intent,
0);
if (!xform->cmap_3dlut) {
cmsCloseProfile(arr_prof[2]);
weston_log("color-lcms error: fail cmsCreateMultiprofileTransformTHR.\n");
return false;
}
xform->base.mapping.type = WESTON_COLOR_MAPPING_TYPE_3D_LUT;
xform->base.mapping.u.lut3d.fill_in = cmlcms_fill_in_3dlut;
xform->base.mapping.u.lut3d.optimal_len =
cmlcms_reasonable_3D_points();
cmsCloseProfile(arr_prof[2]);
return true;
}
static struct cmlcms_color_transform *
cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
const struct cmlcms_color_transform_search_param *param)
const struct cmlcms_color_transform_search_param *search_param)
{
struct cmlcms_color_profile *input_profile = search_param->input_profile;
struct cmlcms_color_profile *output_profile = search_param->output_profile;
struct cmlcms_color_transform *xform;
const struct tone_curve_def *tonedef;
if (param->type < 0 || param->type >= CMLCMS_TYPE__END) {
weston_log("color-lcms error: bad color transform type in %s.\n",
__func__);
return NULL;
}
tonedef = &predefined_eotf_curves[param->type];
bool ok = false;
xform = zalloc(sizeof *xform);
if (!xform)
return NULL;
xform->curve = cmsBuildParametricToneCurve(cm->lcms_ctx,
tonedef->cmstype,
tonedef->params);
if (xform->curve == NULL) {
weston_log("color-lcms error: failed to build parametric tone curve.\n");
free(xform);
return NULL;
weston_color_transform_init(&xform->base, &cm->base);
wl_list_init(&xform->link);
xform->search_key = *search_param;
xform->search_key.input_profile = ref_cprof(input_profile);
xform->search_key.output_profile = ref_cprof(output_profile);
/* Ensure the linearization etc. have been extracted. */
if (!output_profile->output_eotf[0]) {
if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx,
output_profile->profile,
output_profile->output_eotf,
output_profile->output_inv_eotf_vcgt,
output_profile->vcgt,
cmlcms_reasonable_1D_points()))
goto error;
}
weston_color_transform_init(&xform->base, &cm->base);
xform->search_key = *param;
switch (search_param->category) {
case CMLCMS_CATEGORY_INPUT_TO_BLEND:
/* Use EOTF to linearize the result. */
ok = xform_set_cmap_3dlut(xform, input_profile->profile,
output_profile->profile,
output_profile->output_eotf,
search_param->intent_output);
break;
xform->base.pre_curve.type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D;
xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_tone_curve;
xform->base.pre_curve.u.lut_3x1d.optimal_len = 256;
case CMLCMS_CATEGORY_INPUT_TO_OUTPUT:
/* Apply also VCGT if it exists. */
ok = xform_set_cmap_3dlut(xform, input_profile->profile,
output_profile->profile,
output_profile->vcgt,
search_param->intent_output);
break;
wl_list_insert(&cm->color_transform_list, &xform->link);
case CMLCMS_CATEGORY_BLEND_TO_OUTPUT:
xform->base.pre_curve.type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D;
xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_pre_curve;
xform->base.pre_curve.u.lut_3x1d.optimal_len =
cmlcms_reasonable_1D_points();
ok = true;
break;
}
if (!ok)
goto error;
wl_list_insert(&cm->color_transform_list, &xform->link);
return xform;
error:
cmlcms_color_transform_destroy(xform);
weston_log("CM cmlcms_color_transform_create failed\n");
return NULL;
}
static bool
transform_matches_params(const struct cmlcms_color_transform *xform,
const struct cmlcms_color_transform_search_param *param)
{
if (xform->search_key.type != param->type)
if (xform->search_key.category != param->category)
return false;
if (xform->search_key.intent_output != param->intent_output ||
xform->search_key.output_profile != param->output_profile ||
xform->search_key.input_profile != param->input_profile)
return false;
return true;

@ -2,7 +2,6 @@ if not get_option('color-management-lcms')
subdir_done()
endif
dep_lcms2 = dependency('lcms2', version: '>= 2.9', required: false)
if not dep_lcms2.found()
error('color-lcms plugin requires lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.')
endif

@ -35,6 +35,18 @@ struct weston_color_manager_noop {
struct weston_color_manager base;
};
static bool
check_output_eotf_mode(struct weston_output *output)
{
if (output->eotf_mode == WESTON_EOTF_MODE_SDR)
return true;
weston_log("Error: color manager no-op does not support EOTF mode %s of output %s.\n",
weston_eotf_mode_to_str(output->eotf_mode),
output->name);
return false;
}
static struct weston_color_manager_noop *
get_cmnoop(struct weston_color_manager *cm_base)
{
@ -74,6 +86,9 @@ cmnoop_get_surface_color_transform(struct weston_color_manager *cm_base,
/* TODO: Assert surface has no colorspace set */
assert(output->color_profile == NULL);
if (!check_output_eotf_mode(output))
return false;
/* Identity transform */
surf_xform->transform = NULL;
surf_xform->identity_pipeline = true;
@ -81,43 +96,29 @@ cmnoop_get_surface_color_transform(struct weston_color_manager *cm_base,
return true;
}
static bool
cmnoop_get_output_color_transform(struct weston_color_manager *cm_base,
struct weston_output *output,
struct weston_color_transform **xform_out)
static struct weston_output_color_outcome *
cmnoop_create_output_color_outcome(struct weston_color_manager *cm_base,
struct weston_output *output)
{
assert(output->color_profile == NULL);
/* Identity transform */
*xform_out = NULL;
return true;
}
struct weston_output_color_outcome *co;
static bool
cmnoop_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base,
struct weston_output *output,
struct weston_color_transform **xform_out)
{
assert(output->color_profile == NULL);
/* Identity transform */
*xform_out = NULL;
if (!check_output_eotf_mode(output))
return NULL;
return true;
}
co = zalloc(sizeof *co);
if (!co)
return NULL;
static bool
cmnoop_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base,
struct weston_output *output,
struct weston_color_transform **xform_out)
{
assert(output->color_profile == NULL);
/* Identity transform on everything */
co->from_blend_to_output = NULL;
co->from_sRGB_to_blend = NULL;
co->from_sRGB_to_output = NULL;
/* Identity transform */
*xform_out = NULL;
co->hdr_meta.group_mask = 0;
return true;
return co;
}
static bool
@ -153,13 +154,8 @@ weston_color_manager_noop_create(struct weston_compositor *compositor)
cm->base.destroy_color_profile = cmnoop_destroy_color_profile;
cm->base.get_color_profile_from_icc = cmnoop_get_color_profile_from_icc;
cm->base.destroy_color_transform = cmnoop_destroy_color_transform;
cm->base.get_surface_color_transform =
cmnoop_get_surface_color_transform;
cm->base.get_output_color_transform = cmnoop_get_output_color_transform;
cm->base.get_sRGB_to_output_color_transform =
cmnoop_get_sRGB_to_output_color_transform;
cm->base.get_sRGB_to_blend_color_transform =
cmnoop_get_sRGB_to_blend_color_transform;
cm->base.get_surface_color_transform = cmnoop_get_surface_color_transform;
cm->base.create_output_color_outcome = cmnoop_create_output_color_outcome;
return &cm->base;
}

@ -297,3 +297,55 @@ out_close:
close(fd);
return cprof;
}
/** Get a string naming the EOTF mode
*
* \internal
*/
WL_EXPORT const char *
weston_eotf_mode_to_str(enum weston_eotf_mode e)
{
switch (e) {
case WESTON_EOTF_MODE_NONE: return "(none)";
case WESTON_EOTF_MODE_SDR: return "SDR";
case WESTON_EOTF_MODE_TRADITIONAL_HDR: return "traditional gamma HDR";
case WESTON_EOTF_MODE_ST2084: return "ST2084";
case WESTON_EOTF_MODE_HLG: return "HLG";
}
return "???";
}
/** A list of EOTF modes as a string
*
* \param eotf_mask Bitwise-or'd enum weston_eotf_mode values.
* \return Comma separated names of the listed EOTF modes. Must be free()'d by
* the caller.
*/
WL_EXPORT char *
weston_eotf_mask_to_str(uint32_t eotf_mask)
{
FILE *fp;
char *str = NULL;
size_t size = 0;
unsigned i;
const char *sep = "";
fp = open_memstream(&str, &size);
if (!fp)
return NULL;
for (i = 0; eotf_mask; i++) {
uint32_t bitmask = 1u << i;
if (eotf_mask & bitmask) {
fprintf(fp, "%s%s", sep,
weston_eotf_mode_to_str(bitmask));
sep = ", ";
}
eotf_mask &= ~bitmask;
}
fclose(fp);
return str;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save