./0000755000015600001650000000000012676616160011110 5ustar jenkinsjenkins./doc/0000755000015600001650000000000012676616160011655 5ustar jenkinsjenkins./doc/kernel_requirements.md0000644000015600001650000000073312676616125016266 0ustar jenkinsjenkinsLinux Kernel Requirements for Mir ================================= To run Mir with the default `mesa-kms` platform you need a linux kernel with at least: Modules: i915, radeon and nouveau, to support the broadest range of common desktop/laptop hardware. Version: Kernel version 3.11.0 or later, at least for radeon and nouveau to support fullscreen bypass correctly. Intel (i915) is known to work properly with even older kernels. Additional patches: None at this time. ./doc/footer.html.in0000644000015600001650000000016612676616125014452 0ustar jenkinsjenkins

Copyright © 2012-2015 Canonical Ltd.
Generated on @DATE_TODAY@

./doc/using_mir_on_android.md0000644000015600001650000000146312676616125016374 0ustar jenkinsjenkinsUsing Mir on an Android device {#using_mir_on_android} ============================== Mir is the default on Ubuntu Touch images, so if you're using Ubuntu Touch, you're already using Mir. If you would like to run a pre-release version of Mir on your device, you'll need to recompile our downstream dependencies (unity-system-compositor and qtmir) to ensure ABI compatibility with the pre-release version of Mir. Using some demo applications ---------------------------- Simple demos are available in the `mir-demos` package. First install the demos: $ sudo apt-get install mir-demos Next ensure that the Unity8 session is ended: $ sudo stop lightdm Finally, start mir and a client application: $ mir_demo_server --test-client mir_demo_client_egltriangle and you should see a triangle on screen. ./doc/android_new_device_bringup.md0000644000015600001650000001564412676616157017555 0ustar jenkinsjenkinsAndroid New Device Bringup {#android_new_device_bringup} =============================== Mir is a library, and is the building block that unity-system-compositor and unity8 are build upon. If the device is crashing or hanging in a Mir library when you try to start unity, the information listed below will help the Mir team diagnose and fix the issues that are seen. ##Mir Tests## Mir has a test suite available in the package mir-test-tools that checks the operation of Mir. Mir also provides a test suite in mir-android-diagnostics that is helpful in checking how a new device will work with Mir. ##Mir and libhybris## Vendor provided drivers are compiled against Android's bionic libc. Mir runs against glibc. To make the two libc's work together, Mir uses libhybris. libhybris has some internal tests (e.g., test_egl), but these are insufficient tests to see if mir will run on the device. The tests are aimed checking the operation of hybris on non-Mir graphics stacks. Furthermore, they test a more limited range of hardware module capabilities (e.g. they don't test HWC overlay capability) ###Mir Client Software Rendering### mir_android_diagnostics --gtest_filter="AndroidMirDiagnostics.client_can_draw_with_cpu" This test checks that the CPU can render pixels using software and checks that the pixels can be read back. ###Mir Client OpenGLES 2.0 Rendering (Android EGLNativeWindowType test)### mir_android_diagnostics --gtest_filter="AndroidMirDiagnostics.client_can_draw_with_gpu" This test checks that the GL api can be used to glClear() a buffer, and checks that the content is correct after render. ###Mir Display posting (HWC tests)### mir_android_diagnostics --gtest_filter="AndroidMirDiagnostics.display_can_post" mir_android_diagnostics --gtest_filter="AndroidMirDiagnostics.display_can_post_overlay" This test checks that the display can post content to the screen. It should flash the screen briefly and run without error. Since it is important that screen looks flawless, a visual inspection should also be perfomed using mir_demo_standalone_render_to_fb and mir_demo_standalone_render_overlays ###Mir GPU buffer allocations (gralloc tests)### mir_android_diagnostics --gtest_filter="AndroidMirDiagnostics.can_allocate_sw_buffer" mir_android_diagnostics --gtest_filter="AndroidMirDiagnostics.can_allocate_hw_buffer" This will test that Mir can access the gralloc module and allocate GPU buffers. Mir Demos --------- The Mir team ships certain demos that are useful for developing and improving Mir, as they operate with less complexity than the full unity stack. These are available in the 'mir-demos' debian package. ###Visual check of posting to the display using GLES### mir_demo_standalone_render_to_fb This will use the HWC module to drive the display. The program will display an animation to the screen until the program is stopped with Ctrl-C. This program forces HWC to display the OpenGLES 2.0 rendered image without using overlays. The animation should: - be a black, white, and purple image of the Mir space station scrolling on a green background - be smooth - be checked against visual artifacts (flickering, blockiness, tearing) - run indefinitely ###Visual check of posting to the display using Overlays### mir_demo_standalone_render_overlays This will use the HWC module to drive the display by displaying overlays. The animation should: - be a blue square on top of a red square. Both squares will quickly fade to white, reset to their respective colors, and repeat. - be smooth - be checked against visual artifacts (flickering, blockiness, tearing) - run indefinitely ###Visual check of demo servers### The demo servers provide a good way to check visually that clients can connect and display to the screen. In one terminal, run mir_demo_server and in another terminal, run mir_demo_client_egltriangle You should see: - an orange triangle smoothly rotating on a purple background on screen - the client terminal reporting an FPS that is close to the vsync rate of the system. - both client and server continuing to run until stopped with Ctrl-C. There are a variety of demos that should all work with each other. The demo shell is talked about further in \ref demo_shell_controls Collecting Additional Android Information From Mir -------------------------------------------------- Mir has some android specific options for watching HWC interactions. This option is available with mir_demo_standalone_* and mir_demo_server_* If you run mir_proving_server --hwc-report log You will get output similar to this: before prepare(): # | pos {l,t,r,b} | crop {l,t,r,b} | transform | blending | 0 | { 0, 0, 512, 512} | { 0, 0, 512, 512} | NONE | NONE | 1 | { 80, 80, 592, 592} | { 0, 0, 512, 512} | NONE | NONE | 2 | { 0, 0, 768,1280} | { 0, 0, 768,1280} | NONE | NONE | after prepare(): # | Type | 0 | OVERLAY | 1 | OVERLAY | 2 | FB_TARGET | set list(): # | handle 0 | 0x2183540 1 | 0x2183a00 2 | 0x202ea40 HWC: vsync signal off HWC: display off This is the list that Mir submits to HWC, the decision of hwc (overlay or GLES), and the final handles submitted to HWC during the display post. The HWC version vsync signal and the blanking are also logged. The "--hwc-report log" option should work with all android-based mir servers and demo standalone programs. If its more convenient, setting MIR_SERVER_HWC_REPORT=log to the environment of the running server will give the hwc report on stdout. ###Android platform quirks### If a driver has a bug that can be fixed, its best to fix the bug in the driver. However, if workarounds are needed due to source unavailability, they can be experimented with via quicks that can be activated via the command line. The list of quirks is printed with the --help flag on a mir_demo_server. ###Note on HWC versions### The "--hwc-report log" will log the HWC version at the beginning of the report, e.g. HWC version 1.2 If you see something like: HWC version unknown () This means that Mir does not support the version of hwc on the device. As of Feb 2016, Mir supports the legacy FB module, as well as HWC versions 1.0, 1.1, 1.2, 1.3, and 1.4. If you run mir_demo_standalone_render_overlays --display-report log You will get the EGL configuration that was selected for the framebuffer EGL context. Reporting Problems ----------------- If any of the above tests crash, hang, or experience a problem, the Mir team wants to help fix it. When diagnosing an android problem, these logs are most helpful: - The log of the Mir program when it crashes with the "Additional Android Information". - A stacktrace from where Mir has crashed or hung. - The logcat from /system/bin/logcat at the time of the problem - The kernel log from dmesg at the time of the problem. - A video of any visual glitches or corruption ./doc/component_reports.md0000644000015600001650000000605412676616125015765 0ustar jenkinsjenkinsMir component reports {#component_reports} ===================== Both the server library and the client library include facilities to provide debugging and tracing information at runtime. This is achieved through component reports, which are sets of interesting events provided by many Mir components. A component report can be usually handled in a number of different ways, configured using command-line options and/or environment variables. By default, component reports are turned off. Server reports -------------- The way component reports are handled on the server can be configured using either command-line options or environment variables. The environment variables are prefixed with `MIR_SERVER_` and contain underscores ('_') instead of dashes ('-'). The available component reports and handlers for the server are: Report | Handlers ---------------------------- | -------- connector-report | log,lttng compositor-report | log,lttng display-report | log,lttng input-report | log,lttng legacy-input-report | log msg-processor-report | log,lttng session-mediator-report | log,lttng scene-report | log,lttng shared-library-prober-report | log,lttng For example, to enable the LTTng input report, one could either use the `--input-report=lttng` command-line option to the server, or set the `MIR_SERVER_INPUT_REPORT=lttng` environment variable. Client reports -------------- Client side reports can be configured only using environment variables. The environment variables are prefixed with `MIR_CLIENT_` and contain only underscores. The available reports and handlers for the client are: Report | Handlers --------------------- | -------- rpc-report | log,lttng input-receiver-report | log,lttng shared-library-prober-report | log,lttng perf-report | log,lttng For example, to enable the logging RPC report, one should set the `MIR_CLIENT_RPC_REPORT=log` environment variable. LTTng support ------------- Mir provides LTTng tracepoints for various interesting events. You can enable LTTng tracing for a Mir component by using the corresponding command-line option or environment variable for that component's report: $ lttng create mirsession -o /tmp/mirsession $ lttng enable-event -u -a $ lttng start $ mir_demo_server --msg-processor-report=lttng $ lttng stop $ babeltrace /tmp/mirsession/ LTTng-UST versions up to and including 2.1.2, and up to and including 2.2-rc2 contain a bug (lttng #538) that prevents event recording if the tracepoint provider is dlopen()-ed at runtime, like in the case of Mir. If you have a version of LTTng affected by this bug, you need to pre-load the server tracepoint provider library: $ LD_PRELOAD=libmirserverlttng.so mir_demo_server --msg-processor-report=lttng The bug also affects client-side LTTng tracing, in which case you need to pre-load the client tracepoint provider library: $ LD_PRELOAD=libmirclientlttng.so MIR_CLIENT_RPC_REPORT=lttng myclient ./doc/extra.css0000644000015600001650000000127112676616125013514 0ustar jenkinsjenkinsbody, table, div, p, dl { font: 400 14px/19px Ubuntu,Arial,sans-serif; } #projectname { font: 300% Ubuntu,Arial,sans-serif; margin: 0px; padding: 2px 0px; } #projectbrief { font: 120% Ubuntu,Arial,sans-serif; margin: 0px; padding: 0px; } #projectnumber { font: 50% Ubuntu,Arial,sans-serif; margin: 0px; padding: 0px; } div.toc li { background: url("bdwn.png") no-repeat scroll 0 5px transparent; font: 10px/1.2 Ubuntu,Arial,sans-serif; margin-top: 5px; padding-left: 10px; padding-top: 2px; } div.toc h3 { font: bold 12px/1.2 Ubuntu,Arial,FreeSans,sans-serif; color: #E24106; border-bottom: 0 none; margin: 0; } ./doc/abi_compatibility_tools.md0000644000015600001650000000156712676616157017122 0ustar jenkinsjenkinsTracking ABI compatibility {#abi_compatibility_tools} ================================ A few make targets exist to help us track ABI compatibility across different Mir versions and ensure we increase the ABI version properly. These targets invoke the abi-compliance-checker tool for the actual ABI check. The targets are: * **make abi-check** Compiles all the public libraries in the current tree and checks their ABI against the latest released archive version * **make abi-check-** Compiles only the specified library in the current tree and checks its ABI against the latest released archive version - *library* can be any of the public library targets such as mirclient, mirserver, mirplatform, mircommon, etc. Sample usage ------------ $ bzr branch lp:mir && cd mir $ debian/rules override_dh_auto_configure $ cd $ make abi-check ./doc/using_mir_on_pc.md0000644000015600001650000000476112676616125015362 0ustar jenkinsjenkinsUsing Mir on a PC {#using_mir_on_pc} ================= Before you begin ---------------- Make sure your hardware is supported. That means you're using a Mesa driver, of which intel, radeon, and nouveau families are supported. If you're logged in to X then run this command to verify an appropriate DRI driver is active: sudo pmap `pidof X` | grep dri.so or lsmod | grep drm Before you can use Mir you need to ensure you have the proper custom Mesa build installed. If you are running Ubuntu 13.10 or later (see \ref installing_prebuilt_on_pc), you should be good to go. If you built Mir from source code (see \ref building_source_for_pc), you need to ensure you are using the proper Mesa at runtime. You can do that by installing the Mesa packages from Ubuntu 13.10 (or later) or by building the custom Mesa yourself and ensuring it can be found by Mir, e.g., by using `LD_LIBRARY_PATH`. ### Getting some example client applications You can get some example programs by installing the `mir-demos` package: $ sudo apt-get install mir-demos If you are building from source you can find client applications in the `bin/` subdirectory of the build directory. Running Mir ----------- Mir can run run either natively on mesa-kms or as an X application. ### Running Mir on X To run Mir as an X client start it from an X terminal: $ mir_demo_server --launch-client mir_demo_client_multiwin You can start additional Mir clients, for example (in a new terminal): $ mir_demo_egltriangle To exit from Mir: Note: up to Mir 0.18 it is also necessary to specify `--platform-input-lib` when starting the server: - for Mir-0.17 add: `--platform-input-lib server-mesa-x11.so.6` - for Mir-0.18 add: `--platform-input-lib server-mesa-x11.so.7` ### Running Mir natively To run Mir natively on a PC/desktop/laptop: $ sudo DISPLAY= mir_demo_server --vt 1 --arw-file This will switch you to a Mir session on VT1. Switch back to your X-based desktop: In a new terminal: $ mir_demo_client_multiwin -m /tmp/mir_socket Switch back to Mir. Watch your friends be amazed! To exit from Mir: In case you accidentally killed your X login and ended up with a failsafe screen, you might find on subsequent reboots you can't log in to X at all any more (it instantly and silently takes you back to the login screen). The fix for this is to log in to a VT and: $ rm .Xauthority $ sudo restart lightdm ./doc/installing_prebuilt_on_pc.md0000644000015600001650000000047712676616125017440 0ustar jenkinsjenkinsInstalling pre-built packages on a PC {#installing_prebuilt_on_pc} ===================================== Install Ubuntu 13.10 or later if you haven't done so already. Uninstall any proprietary drivers (-nvidia, -fglrx) and reboot on the FOSS drivers. sudo apt-get update sudo apt-get install mir-demos ./doc/android_technical_details.md0000644000015600001650000000353412676616125017344 0ustar jenkinsjenkinsAndroid Technical Details {#android_technical_details} =============================== Mir Usage of Android Drivers ---------------------------- Mir relies on the libhybris library to use the Android drivers. This allows the drivers to use the bionic libc while Mir itself uses the standard GNU libc. Mir Display Modes ----------------- When you're using Mir to drive the display of the Android device, Mir has a default way to display, as well as a backup mode that it can try if the default mode isn't working. * Default Mode The default display mode uses the HWC HAL module from the Android drivers. Depending on the version and device, the default mode may also use the framebuffer HAL module. These modules are used to determine the display information, to synchronize with vsync, and to post to the display. The HWC is not used at this time to provide overlay support. * Backup Mode The backup mode is used when the primary display mode is unavailable (due to system problems, missing shared libraries, or similar conditions) The backup mode will only use the framebuffer HAL module. This module is a bit more limited than the HWC module, and the driver support might be a bit less thoroughly tested. It still should give you a display though. Mir Device Support ------------------ In theory, all devices with that use the normal Android drivers abstractions should run Mir. Currently, we support HWC (hardware composer) version 1.0 and later. The deprecated FB HAL module from android's libhardware should also work. If you are attempting to get mir to work on a new device, check out \subpage android_new_device_bringup "this page" for suggestions on troubleshooting. You can also file a bug, being very specific about the chipset, GPU, and driver versions that your phone has. ./doc/demo_shell_controls.md0000644000015600001650000000366112676616125016244 0ustar jenkinsjenkinsDemo Shell Controls {#demo_shell_controls} =================== Mir's demo shell (`mir_proving_server`) is a basic environment for testing Mir server features as well as running natively ported apps/toolkits. It is still primitive and requires some explaining to make proper use of, so read on. Running Demo Shell ------------------ Remember to always run `mir_proving_server` as root on PC (not required on Android), as this is required for input device support (open bug https://bugs.launchpad.net/mir/+bug/1286252); sudo mir_proving_server And if you're not already on the VT you wish to use, that needs to be specified: sudo mir_proving_server --vt 1 There are plenty more options available if you run: mir_proving_server --help Controls -------- All controls have a keyboard/mouse combination and where possible also have touch alternatives for phones/tablets: - Quit the shell (shut down the Mir server): *Ctrl-Alt-Backspace* - Switch back to X: *Ctrl-Alt-F7* - Switch virtual terminals (VTs): *Ctrl-Alt-(F1-F12)* - Switch apps: *Alt-Tab* or *4-finger swipe left/right* - Switch windows of the current app: *Alt-grave* - Ask an app to close: *Alt-F4* (it may refuse or not understand) - Move window: *Alt-leftmousebutton* or *3-finger drag* - Resize window: *Alt-middlemousebutton* or *3-finger pinch/zoom* - Toggle negative rendering: *Super-N* - Toggle high-contrast rendering: *Super-C* - Sleep/wake all displays: *Alt-P* or *Android power button* - Rotate the focussed monitor: *Ctrl-Alt-(Left/Right/Up/Down)* or *Volume up/down while touching the screen* - Change display mode of the focussed monitor: *Ctrl-Alt-(=/-)* - Reset display mode to default, on focussed monitor: *Ctrl-Alt-0* - Adjust window opacity/alpha: *Alt-mousewheel* - Zoom in/out: *Super-mousewheel* *Super* means the Windows key on a PC, or the search key on a Chromebook. Want more? Log your requests at: https://bugs.launchpad.net/mir/+filebug ./doc/performance_framework.md0000644000015600001650000001257612676616125016571 0ustar jenkinsjenkinsMir performance framework {#performance_framework} ========================= In order to facilitate writing portable and easy to share performance tests, Mir provides a performance framework written in python3. Each test, along with code to process the test results, is contained in a single python3 script which uses the Mir performance framework modules. Invoking tests -------------- To run a test, just execute the python3 script containing it. We need to ensure that the Mir performance framework can be found by python. If you have installed the Mir performance framework to one of the standard python3 library locations (e.g., with make install or a package installation), then the framework should be automatically detected. If you are using the framework from within the source tree you need to add the directory containing the mir_perf_framework/ directory (i.e., its parent directory) to the PYTHONPATH env. variable: sudo PYTHONPATH=/path/to/mir/benchmarks python3 testscript.py If you are using an Ubuntu system the framework comes pre-packaged in the python3-mir-perf-framework package, which, besides the framework itself, also installs a few interesting tests in /usr/share/mir-perf-framework/. Writing test scripts -------------------- ### Specifying the test configuration To create a test we first need to instantiate objects representing the servers and clients we want to run as part of the test, and also the kind of LTTng reports we want to enable for each one of them: from mir_perf_framework import Server, Client host = Server() nested = Server(...) client = Client(...) ### Specifying the executable to run By default the servers use the 'mir_demo_server' executable and the clients the 'mir_demo_client_egltriangle' executable. We can override this by using the 'executable' option. We can also set custom command line arguments to use when invoking the executable (even the default ones). Finally we can set extra environment variables using the 'env' option, which is a dictionary of variable names and values. server = Server(executable=shutil.which("my_test_server"), options=["--window-manager", "fullscreen"], env = {"MIR_SERVER_MY_OPTION": "on"}) ### Specifying the host server a server should connect to By default a server is started in host mode. If we want to create a nested server we need to set the host server it will connect to by using the 'host' option: host = Server() nested = Server(host=host) ### Specifying the server a should connect to A client needs to know the server to connect to. This is set with the 'server' option: host = Server() client = Client(server=host) ### Specifying the lttng reports to enable To enable an LTTng report add it to the list passed to the 'reports' option. To enable a client report (which only makes sense for clients or nested servers), prefix them with 'client-': nested = Server(host=host, reports=["compositor", "client-input-receiver"]) client = Client(server=nested, reports=["client-input-receiver"]) Starting and stopping the test ------------------------------ After we have instantiated objects representing our test setup, we need to create a PerformanceTest using these objects: from mir_perf_framework import Server, Client, PerformanceTest ... test = PerformanceTest([host, nested, client]) Note that the order of the objects in the list matters, since server/clients are created and run in list order when the test is started. To start the test use the start() method, and after finishing stop it using the stop() method: test = PerformanceTest([host, nested, client]) test.start() ... test.stop() Simulating input events ----------------------- To simulate input events during tests, you can use the evdev python3 module. import evdev ui = evdev.UInput() # during testing ... ui.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_A, 1) ui.syn() Accessing LTTng events ----------------------- After the test has finished (i.e., after it has been stop()-ed), we can access the recorded LTTng events in two ways. The first is to get the raw babeltrace trace object and extract the data from it: ### NOT RECOMMENDED unless you know what you are doing ### use test.events() instead trace = test.babeltrace() for event in trace.events: print(event.name) interesting_event = event # This works, but interesting_event will # contain invalid contents later. print(trace.events[i].name) # Random access is not supported. Unfortunately, accessing events this way has two severe limitations. First, only sequential access is supported, and, second, we are allowed to access only one event at a time (the active iteration event), since references to other events do not remain valid after we have moved on. To make life easier, the PerformanceTest object offers the events() method, which returns all the LTTng events in a normal, random-access capable python list. events = test.events() for event in events: print(event.name) interesting_event = event # OK! Interesting event is always valid. print(events[i].name) # Random access is OK too! This is the recommend way of accessing the events, unless you need some functionality offered only by the babeltrace APIs. Note, however, that creating the list of events may incur a small delay. ./doc/demo_server_controls.md0000644000015600001650000000440412676616125016437 0ustar jenkinsjenkinsDemo Server {#demo_server} =========== Mir's demo server (`mir_demo_server`) is an example of using the Mir to build a server. It uses only functionality supported by the public Mir API. Running Demo Server ------------------- Remember to always run `mir_demo_server` as root on PC (not required on Android), as this is required for input device support (open bug https://bugs.launchpad.net/mir/+bug/1286252); sudo mir_demo_server And if you're not already on the VT you wish to use, that needs to be specified: sudo mir_demo_server --vt 1 There are plenty more options available if you run: mir_demo_server --help The following operations are supported: - Quit (shut down the Mir server): *Ctrl-Alt-Backspace* - Switch back to X: *Ctrl-Alt-F7* - Switch virtual terminals (VTs): *Ctrl-Alt-(F1-F12)* - Switch apps: *Alt-Tab*, tap or click on the corresponding app - Close app: *Alt-F4* - Switch window within app: *Alt-`*, tap or click on the window - Close surface: *Ctrl-F4* - Move window: *Alt-leftmousebutton* drag - Resize window: *Alt-middle_button* drag - Maximize/restore current window: Alt-F11 - Maximize/restore current window: Shift-F11 - Maximize/restore current window: Ctrl-F11 For those writing client code request to set the surface attribute `mir_surface_attrib_state` are honoured: - `mir_surface_state_restored`: restores the window - `mir_surface_state_maximized`: maximizes size - `mir_surface_state_vertmaximized`: maximizes height - `mir_surface_state_horizmaximized`: maximizes width For a quick demo try: sudo DISPLAY= mir_demo_server --vt 1 --launch bin/mir_demo_client_egltriangle\ --test-client bin/mir_demo_client_multiwin --test-timeout 60 (Remember to unwrap the line) ### Tiling Window Manager One option that needs elaboration is "--window-manager tiling". This starts a (rather primitive) tiling window manager. It tracks the available displays and splits the available workspace into "tiles" (one per client). For a quick demo try: sudo DISPLAY= mir_demo_server --vt 1 --launch bin/mir_demo_client_egltriangle\ --test-client bin/mir_demo_client_multiwin --test-timeout 60\ --window-manager tiling (Remember to unwrap the line) Want more? Log your requests at: https://bugs.launchpad.net/mir/+filebug ./doc/building_source_for_pc.md0000644000015600001650000000574412676616125016717 0ustar jenkinsjenkinsBuilding the source for a PC {#building_source_for_pc} ============================ Getting Mir ----------- Mir is a project on Launchpad (https://launchpad.net/mir). To grab a copy use the command: $ bzr branch lp:mir The command above will download the latest development version of Mir into the 'mir' directory (called the 'project directory' from now on). Getting dependencies -------------------- To succesfully build Mir there are a few packages required. The easiest way to get them is to use the packaging build dependencies: $ sudo apt-get install devscripts equivs cmake Then, in the project directory: $ sudo mk-build-deps --install --tool "apt-get -y" --build-dep debian/control Building Mir ------------ Mir is built using CMake. You first need to create the build directory and configure the build. In the project directory do: $ mkdir build $ cd build $ cmake .. (possibly passing configuration options to CMake) There are many configuration options for the Mir project. The default options will work fine, but you may want to customize the build depending on your needs. The best way to get an overview and set them is to use the cmake-gui tool: $ cmake-gui .. The next step is to build the source and run the tests: $ make (-j8) $ ctest Running Mir ----------- The binaries created in the bin subdirectory of the project directory can be used directly. For example, $ bin/mir_demo_server --launch_client bin/mir_demo_client_multiwin Other examples described elsewhere in this documentation assume you're using the installed version and simply need "bin/" adding to specify the local build. Install Mir ----------- *It should not be necessary to install Mir for experimental purposes (see "Running Mir" above).* Further, if you are using an Ubuntu derived disto then there's likely to be existing Mir binaries elsewhere that may interact badly with a second install. To install Mir just use the normal make install command: $ sudo make install This will install the Mir libraries, executable, example clients and header files to the configured installation location (/usr/local by default). NB You may need "sudo ldconfig" to refresh the cache before the installed programs work. If you install to a non-standard location, keep in mind that you will probably need to properly set the PKG_CONFIG_PATH environment variable to allow other applications to build against Mir, and LD_LIBRARY_PATH to allow applications to find the Mir libraries at runtime. Building Mesa ------------- *The Mesa packages shipped with Ubuntu are already built with the relevant Mir patches and should work out of the box with Mir.* For GL accelerated clients to use Mir they need to use a patched version of Mesa that supports Mir. The patch is hosted on GitHub: $ git clone https://github.com/RAOF/mesa.git Compile as per normal instructions and pass --with-egl-platforms="mir,drm" to the configure options. You will need libmirclient installed as shown above. ./doc/building_source_for_arm.md0000644000015600001650000000643412676616125017071 0ustar jenkinsjenkinsBuilding the Mir source for ARM {#building_source_for_arm} =============================== There are a few ways to compile Mir for an ARM device. You should only need to choose one of these approaches... Building on the ARM device -------------------------- If you have an ARM device you should be able to compile and install directly on the device. Although this will usually be significantly slower than using a desktop. On the armhf or arm64 target device just follow these steps: $ mk-build-deps --install --tool "apt-get -y" --build-dep debian/control $ cmake .. -DMIR_PLATFORM=android $ make $ make install The addional cmake option -DMIR_ENABLE_TESTS=off can be used to avoid building the test suite to save time. Building for ARM from a PC (cross compiling) -------------------------------------------- Using a current Ubuntu (15.04 or later) installation it's very simple to build binaries for armhf (32-bit) devices. Just follow these steps: $ sudo apt-get install g++-arm-linux-gnueabihf g++-4.9-arm-linux-gnueabihf multistrap $ cd mir_source_dir $ ./cross-compile-chroot.sh $ ls -l build-android-arm/* # binaries to copy to your device as you wish The special package 'g++-4.9-arm-linux-gnueabihf' above is required if you are building for vivid (-d vivid). If you wish to target arm64 (AArch64) then you can use: $ sudo apt-get install g++-aarch64-linux-gnu multistrap $ cd mir_source_dir $ ./cross-compile-chroot.sh -a arm64 $ ls -l build-arm64-*/bin Note: If your target device is running an Ubuntu version other than vivid then you will also need to specify the target distribution for correct library linkage. For example: $ ./cross-compile-chroot.sh -a arm64 -d wily More architectures like PowerPC are also supported. To see the full list of options, just run: $ ./cross-compile-chroot.sh -h To speed up the process for future runs of cross-compile-chroot.sh, some files are saved in ~/.cache/. To flush the cache and download new armhf packages just add the -u option to cross-compile-chroot.sh. Building armhf deb packages --------------------------- "sbuild" is recommended to compile Mir packages with armhf. Information on setting up sbuild can be found here: * https://wiki.debian.org/sbuild * https://wiki.ubuntu.com/SimpleSbuild If you do not wish to run the Mir test suite during package generation, set DEB_BUILD_OPTIONS=nocheck to your environment Emulated sbuild package generation ---------------------------------- This uses qemu to compile the package. Substitute for the .dsc file name generated by the debuild command. $ cd mir_source_dir $ debuild -S -uc -us $ cd .. $ sbuild -d vivid --arch armhf mir_.dsc Cross-compile sbuild package generation --------------------------------------- This uses a cross-compile toolchain to compile the package, and generally should be faster than the emulated sbuild package generation. Substitute for the .dsc file name generated by the debuild command. $ cd mir_source_dir $ debuild -S -uc -us $ cd .. $ sbuild -d vivid --host armhf --build amd64 mir_.dsc ./doc/dso_versioning_guide.md0000644000015600001650000002000612676616125016403 0ustar jenkinsjenkinsA brief guide for versioning symbols in the Mir DSOs {#dso_versioning_guide} ==================================================== So, what do I have to do? ------------------------- There are more detailed descriptions below, but as a general rule: - If you add a new symbol, add it to the `*_unreleased` version stanza, like `MIR_CLIENT_unreleased`, `MIR_PLATFORM_unreleased`, etc. - If you change the behaviour or signature of a symbol _and_ wish to preserve backward compatibility, see "Change symbols without breaking ABI" below. - At release time, rename the current `*_unversioned` stanzas to have the version of the current release, like `MIR_CLIENT_0.17`, `MIR_PLATFORM_0.17`, etc. Can I have some details? ------------------------ Sure. Mir is a set of libraries, one C++ library for writing display- server/compositor/shells and one C library for writing clients (or, more usually, toolkits for clients) that use a Mir display-server for output. Mir also has internal dynamic libraries for platform support - drivers - and may in future allow the same with extensions to the core functionality. As such, the ABI of these interfaces is important to keep in mind. Mir uses the ELF symbol versioning support. This provides three advantages: - Consumers of the Mir libraries can know at load time rather than symbol resolution time whether the library exposes all the symbols they expect. - We can drop or change the behaviour of symbols without breaking ABI by exposing multiple different implementations under different versions, and - We can (modulo protobuf singletons in our current implementation, and with some care) safely load multiple different versions of Mir libraries into the same process. When should I bump SONAME? -------------------------- There are varying standards for when to bump SONAME. In Mir we choose to bump the SONAME of a library whenever we make a change that could cause a binary linked to the library to fail _as long as_ the binary is using only public interfaces and (where applicable) relying on documented behaviour. In general, changes that make an interface work as described by its documentation will not result in SONAME bumps. With that explanation, you _should_ bump SONAME when: - You remove a public symbol from a library - You change the signature of a public symbol _without_ retaining the previous signature exposed under the old versioning. - You change the behaviour of a public symbol _without_ retaining the previous behaviour exposed with the old versioning. If you are changing the behaviour of an interface, think about whether it's easy to maintain the old interface in parallel. If it is, you should consider providing both under different versions. This should become easier over time as the Mir ABI becomes more stable and also more valuable over time as the Mir libraries become more widely used. Load-time version detection --------------------------- When using versioned symbols the linker adds an extra, special symbol containing the version(s) exported from the library. Consumers of the library resolve this on library load. For example: $ objdump -C -T lib/libmirclient.so … 00000000002a2080 w DO .data.rel.ro 0000000000000080 MIR_CLIENT_8 vtable for mir::client::DefaultConnectionConfiguration 0000000000000000 g DO *ABS* 0000000000000000 MIR_CLIENT_8 MIR_CLIENT_8 0000000000030ed2 g DF .text 0000000000000098 MIR_CLIENT_8 mir::client::DefaultConnectionConfiguration::the_rpc_report() … This shows the special `MIR_CLIENT_8` symbol of the current libmirclient, along with a versioned symbol in the read-only data segment (the vtable for `mir::client::DefaultConnectionConfiguration`) and a versioned symbol in the text segment (the implementation of `mir::client::DefaultConnectionConfiguration::the_rpc_report()`). If a client needed a symbol versioned with `MIR_CLIENT_9`, it would try to resolve this at load time and fail, rather than failing when the symbol was first referenced - possibly much later, and more confusingly. ### So what do I have to do to make this work? When you add new symbols, add them to a new `version` block in the relevant `symbols.map` file, like so: MIR_CLIENT_0.17 { global: mir_connect_sync; ... /* Other symbols go here */ }; MIR_CLIENT_unreleased { global: mir_connect_new_symbol; local: *; } MIR_CLIENT_0.17; Note that the script is read top to bottom; wildcards are greedily bound when first encountered, so to avoid surprises you should only have a wildcard in the final stanza. Change symbols without breaking ABI ----------------------------------- ELF DSOs can have multiple implementations for the same symbol with different versions. This means that you can change the signature or behaviour of a symbol without breaking dependants that use the old behaviour. While there can be as many different implementations with different versions as you want, there can only be one default implementation - this is what the linker will resolve to when building a dependant project. Binding different implementations to the versioned symbol is done with `__asm__` directives in the relevant source file(s). The default implementation is specified with `symbol_name@@VERSION`; other versions are specified with `symbol_name@VERSION`. Note that this does _not_ require a change in SONAME. Binaries that have been linked against the old library will continue to work and resolve to the old implementation. Binaries linked against the new library will resolve to the new (default) implementation. ### So, what do I have to do to make this work? For example, if you wanted to change the signature of `mir_connection_create_surface` to take a new parameter: `mir_connection_api.cpp`: __asm__(".symver old_mir_connection_create_surface,mir_connection_create_surface@MIR_CLIENT_0.17"); extern "C" MirWaitHandle* old_mir_connection_create_surface(...) /* The old implementation */ /* The @@ specifies that this is the default version */ __asm__(".symver mir_connection_create_surface,mir_connection_create_surface@@@MIR_CLIENT_unreleased"); MirWaitHandle* mir_connection_create_surface(...) /* The new implementation */ `symbols.map`: MIR_CLIENT_0.17 { global: ... mir_connection_create_surface; ... }; MIR_CLIENT_unreleased { global: ... mir_connection_create_surface; ... local: *; } MIR_CLIENT_0.17; Safely load multiple versions of a library into the same address space ---------------------------------------------------------------------- This benefit is currently theoretical, as there seems to be a Protobuf singleton that aborts if we try this. But should that be resolved, it's theoretically possible and of some benefit... This situation will come about - the Qtmir plugin links to libmirclient and also libEGL, and libEGL will link to libmirclient itself. There is no guarantee that Qtmir and libEGL will link to the same SONAME, and so a process can end up trying to load both `libmirclient.so.8` and `libmirclient.so.9` into its address space. Without symbol versioning this is potentially broken - there's no mechanism for libEGL to only resolve symbols from `libmirclient.so.8` and Qtmir to only resolve symbols from `libmirclient.so.9`, so in cases where symbols have changed use of those symbols will break. By versioning the symbols we ensure that code always gets exactly the symbol implementation it expects, even when multiple library versions are loaded. ### So, what do I have to do to make this work? Ensure that different implementations of a symbol have different versions. Additionally there's the complication of passing objects between different versions. For the moment, we can not bother trying to make this work. See also: --------- [Binutils manual](https://sourceware.org/binutils/docs/ld/VERSION.html) [Former glibc maintainer's DSO guide](http://www.akkadia.org/drepper/dsohowto.pdf) ./doc/setup_kvm_for_mir.md0000644000015600001650000000375612676616125015745 0ustar jenkinsjenkinsSetup KVM for Mir {#setup_kvm_for_mir} ================= At the time of writing not all necessary patches to run Mir inside KVM can be found in a vanilla kernel release. The steps below describe setting up KVM and installing the right version of Mesa and the Linux kernel. Install KVM and utilities ------------------------- The only way to run Mir inside KVM requires a setup with [SPICE](http://spice-space.org). So next to KVM a SPICE client is needed on the host to provide a monitor to the guest. The following description will use virt-manager for this task: $ apt-get install qemu-KVM virt-manager python-spice-client-gtk More details on setting up KVM can be found in the [wiki](https://help.ubuntu.com/community/KVM/Installation). Now create a new virtual machine with virt-manager and a current Linux boot iso image or reconfigure an existing installation. Configure the virtual machine ----------------------------- Launch virt-manager and open your virtual machine. Go to the configuration options through the info icon in the toolbar. There are now two relevant configuration entries: Video and Display. * Open the Display settings and select SPICE instead of KVM * Open the Video settings and select QXL as model. Now boot the machine and build a new kernel and verify that you have the right Mesa package. Verify Mesa and used Kernel --------------------------- Since the DRM QXL driver only provides KMS, GEM and dma-buf support, and no 3D GPU emulation or forwarding, Mesa will load the kms-swrast driver. This driver is available since Mesa 10.3.0. The necessary support for dmabuf fds and crtc handling in QXL is available since Linux 3.18. With that we have enough support for EGL and GLESv2 to run Mir. Additional steps ---------------- This is not necessary but helpful for day to day use: * Set up sharing on file system level using 9p as shown in [Fileystem Passthrough](http://www.linux-kvm.org/page/9p_virtio) Now you can finally install & run unity8-desktop-session-mir in KVM. ./doc/mainpage.md0000644000015600001650000000770712676616125013774 0ustar jenkinsjenkinsWelcome to Mir {#mainpage} ============== Mir is a next generation display server targeted as a replacement for the X window server system to unlock next-generation user experiences for devices ranging from Linux desktop to mobile devices powered by Ubuntu. The primary purpose of Mir is to enable the development of the next generation [Unity](http://unity.ubuntu.com). More detailed information about the motivation, scope, and high-level design of Mir can be found at http://wiki.ubuntu.com/MirSpec . Getting and installing Mir -------------------------- ### Using pre-built packages If you just want to try out mir, or write client applications, then the easiest way is to use the pre-built packages: - \ref installing_prebuilt_on_pc - \ref installing_prebuilt_on_android ### Building and installing from source If you are curious about Mir internals or intend to contribute to it, you should get the source and build it: - \ref building_source_for_pc - \ref building_source_for_arm ### Preparing a VM to run Mir Especially if you want to debug the shell without locking your system this might be a helpful setup: - \ref setup_kvm_for_mir - \ref setup_vmware_for_mir Using Mir --------- - \ref using_mir_on_pc - \ref using_mir_on_android - \ref demo_shell_controls - \ref demo_server Getting involved ---------------- The best place to ask questions and discuss about the Mir project is the #ubuntu-mir IRC channel on freenode. The Mir project is hosted on Launchpad: https://launchpad.net/mir Currently, the Mir code activity is performed on a development branch: lp:~mir-team/mir/development-branch Approximately fortnightly, this development branch is promoted to the branch used for the ubuntu archive and touch images. Please submit any merge proposals against the development branch. Please file bug reports at: https://bugs.launchpad.net/mir The Mir development mailing list can be found at: https://lists.ubuntu.com/mailman/listinfo/Mir-devel The Mir coding guidelines are [here](cppguide/index.html). Learn about Mir ---------------- Android technical info: - \ref android_technical_details Writing client applications --------------------------- - \ref mir_toolkit "Mir API Documentation" - \subpage basic.c "basic.c: A basic Mir client (which does nothing)" Writing server applications --------------------------- Mir server is written as a library which allows the server code to be adapted for bespoke applications. - \subpage server_example.cpp "server_example.cpp: a test executable hosting the following" - \subpage server_example_input_event_filter.cpp "server_example_input_event_filter.cpp: provide a Quit command" - \subpage server_example_display_configuration_policy.cpp "server_example_display_configuration_policy.cpp: configuring display layout" - \subpage server_example_input_filter.cpp "server_example_input_filter.cpp: print input events to stdout" - \subpage server_example_log_options.cpp "server_example_log_options.cpp: replace Mir logger with glog" - \subpage server_example_basic_window_manager.h "server_example_basic_window_manager.h: How to wire up a window manager" - \subpage server_example_window_management.cpp "server_example_window_management.cpp: simple window management examples" - \subpage server_example_canonical_window_manager.cpp "server_example_canonical_window_manager.cpp: canonical window management policy" - \subpage server_example_tiling_window_manager.cpp "server_example_tiling_window_manager.cpp: an alternative window management policy" - \subpage server_example_custom_compositor.cpp "server_example_custom_compositor.cpp: demonstrate writing an alternative GL rendering code" Working on Mir code ------------------- - \ref md_README "Mir Read me" - \ref md_HACKING "Mir hacking guide" - \subpage android_new_device_bringup "New android device bringup" - \ref component_reports - \ref dso_versioning_guide - \ref abi_compatibility_tools - \ref mir_performance_framework ./doc/setup_vmware_for_mir.md0000644000015600001650000000374112676616125016443 0ustar jenkinsjenkinsSetup VMware for Mir {#setup_vmware_for_mir} ============================================ Here is a quick-start guide for running Mir in VMware: 1. Go to https://my.vmware.com/web/vmware/downloads and download VMware Player (last in Desktop & End-User Computing section). 2. Install VMware player with: sudo bash VMware-Player-.x86_64.bundle 3. Get the latest vivid daily iso (*not* the unity-next iso!). 4. Install vivid into VM, restart and log in. 5. Install VMware tools from the menu "Virtual Machine->Install VMWare tools" and follow the instructions. If you get an error "VMware Tools installation cannot be started manually while the easy install is in progress see [1]. 6. If you are using Mesa drivers, stop the virtual machine, go to the folder where the VM is saved and add the following line to the .vmx file there: mks.gl.allowBlacklistedDrivers = "TRUE" 7. Restart the VM, log in and try `glmark2` to ensure it shows 'SVGA' as the GL renderer (*not* 'llvmpipe'!): $ sudo apt-get install glmark2 $ glmark2 8. `$ sudo apt-get unity8-desktop-session-mir mir-demos` 9. Try out a Mir demo server: 1. Change to VT2 and run `$ sudo mir_demo_server --launch-client mir_demo_client_egltriangle` 2. You should see a window with a triangle spinning fast 3. Use Alt + click + drag to move the window around 4. Use Ctrl+Alt+BkSp to stop the server 10. Try out the unity8 desktop session by selecting it in lightdm Unfortunately the VMware KMS support is still somewhat unstable. The KMS/DRM driver doesn't seem to respect vsync (hence the fast spinning egl triangle in step 9c). Also switching between VTs may occasionally make the VM unresponsive, and sometimes screen resizing causes problems. I strongly suggest you set up an ssh server in the VM so you can inspect the system over ssh from the host. [1] http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1017687 ./doc/Doxyfile.in0000644000015600001650000024141712676616125014002 0ustar jenkinsjenkins# Doxyfile 1.8.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed # in front of the TAG it is preceding . # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = Mir # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, # Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, # Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = @CMAKE_CURRENT_SOURCE_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES (the # default) will make doxygen replace the get and set methods by a property in # the documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields or simple typedef fields will be shown # inline in the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO (the default), structs, classes, and unions are shown on a separate # page (for HTML and Man pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can # be an expensive process and often the same symbol appear multiple times in # the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too # small doxygen will become slower. If the cache is too large, memory is wasted. # The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid # range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 # symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if section-label ... \endif # and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. Do not use # file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # 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 will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @CMAKE_CURRENT_SOURCE_DIR@/README.md \ @CMAKE_CURRENT_SOURCE_DIR@/HACKING.md \ @CMAKE_CURRENT_SOURCE_DIR@/doc \ @CMAKE_CURRENT_SOURCE_DIR@/src \ @CMAKE_CURRENT_SOURCE_DIR@/examples \ @CMAKE_CURRENT_SOURCE_DIR@/include \ @MIR_GENERATED_INCLUDE_DIRECTORIES@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.markdown \ *.md \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.for \ *.vhd \ *.vhdl # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = */include/test/* */3rd_party/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = google \ mfd \ mgg \ mp # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/examples # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be ignored. # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = @CMAKE_BINARY_DIR@/doc/footer.html # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefor more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = @CMAKE_BINARY_DIR@/doc/extra.css # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 16 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 240 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # 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. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. 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 before the changes have effect. 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 prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and # SVG. The default value is HTML-CSS, which is slower, but has the best # compatibility. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript # pieces of code that will be used on startup of the MathJax code. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. # There are two flavours of web server based search depending on the # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # searching and an index file used by the script. When EXTERNAL_SEARCH is # enabled the indexing and searching needs to be provided by external tools. # See the manual for details. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain # the search results. Doxygen ships with an example indexer (doxyindexer) and # search engine (doxysearch.cgi) which are based on the open source search # engine library Xapian. See the manual for configuration details. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will returned the search results when EXTERNAL_SEARCH is enabled. # Doxygen ships with an example search engine (doxysearch) which is based on # the open source search engine library Xapian. See the manual for configuration # details. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id # of to a relative location where the documentation can be found. # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. 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. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # 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. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. 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. 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, letter, legal and # executive. If left blank a4 will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. 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. Notice: only use this tag if you know what you are doing! 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. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # 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 directory. # Note that the files will be copied as-is; there are no commands or markers # available. 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). 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. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. 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. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE 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. 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. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # 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 very pretty with # other RTF readers or editors. 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. If left blank `rtf' will be used as the default path. 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. 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 other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet 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. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- # If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files # that can be used to generate PDF. GENERATE_DOCBOOK = NO # The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be put in # front of it. If left blank docbook will be used as the default path. DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = __cplusplus # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # If the EXTERNAL_PAGES tag is set to YES all external pages will be listed # in the related pages index. If set to NO, only the current project's # pages will be listed. EXTERNAL_PAGES = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF 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. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = @DOXYGEN_DOT_FOUND@ # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font 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. DOT_FONTNAME = Arial # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # manageable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = svg # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # 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 root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. 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). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES 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 this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES ./doc/installing_prebuilt_on_android.md0000644000015600001650000000131612676616125020447 0ustar jenkinsjenkinsInstalling pre-built packages on an Android device {#installing_prebuilt_on_android} ================================================== Supported Devices ----------------- Mir has been used on an array of devices powered by the factory-provided android drivers. Mir currently supports the fb module and the hwc module up to version 1.3. If you encounter a problem with your device, please file a bug and we'll see if we can help. Installing Mir -------------- Mir should be installed and used by default on Ubuntu Touch images. Mir is available in the Ubuntu archive. Ensure that the android platform drivers, available in the mir-graphics-drivers-android metapackage, are installed for an android-based device. ./native-compile.sh0000755000015600001650000000050612676616125014365 0ustar jenkinsjenkins#!/bin/sh # Native build script for Mir # set -e BUILD_DIR=build-linux-x86 if [ ! -e ${BUILD_DIR} ]; then mkdir ${BUILD_DIR} ( cd ${BUILD_DIR} && cmake ..) fi cmake --build ${BUILD_DIR} GTEST_OUTPUT=xml:./ ${BUILD_DIR}/bin/mir_acceptance_tests ${BUILD_DIR}/bin/mir_integration_tests ${BUILD_DIR}/bin/mir_unit_tests ./cmake/0000755000015600001650000000000012676616160012170 5ustar jenkinsjenkins./cmake/FindAndroidProperties.cmake0000644000015600001650000000166112676616125017435 0ustar jenkinsjenkins# Variables defined by this module: # ANDROID_PROPERTIES_FOUND # ANDROID_PROPERTIES_LIBRARIES # ANDROID_PROPERTIES_INCLUDE_DIRS INCLUDE(FindPackageHandleStandardArgs) find_package( PkgConfig ) pkg_check_modules(ANDROID_PROPERTIES REQUIRED libandroid-properties) find_path(ANDROID_PROPERTIES_INCLUDE_DIR hybris/properties/properties.h HINTS ${PC_ANDROID_PROPERTIES_INCLUDEDIR} ${PC_ANDROID_PROPERTIES_INCLUDE_DIRS}) find_library(ANDROID_PROPERTIES_LIBRARIES NAMES libandroid-properties.so HINTS ${PC_ANDROID_PROPERTIES_LIBDIR} ${PC_ANDROID_PROPERTIES_LIBRARY_DIRS}) # handle the QUIETLY and REQUIRED arguments and set ANDROID_PROPERTIES_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(ANDROID_PROPERTIES DEFAULT_MSG ANDROID_PROPERTIES_LIBRARIES) mark_as_advanced(ANDROID_PROPERTIES_INCLUDE_DIR ANDROID_PROPERTIES_LIBRARY ) ./cmake/FindGFlags.cmake0000644000015600001650000000064112676616125015140 0ustar jenkinsjenkinsif (GFlags_INCLUDE_DIR) # Already in cache, be silent set(GFlags_FIND_QUIETLY TRUE) endif () find_path(GFlags_INCLUDE_DIR gflags/gflags.h) find_library(GFlags_LIBRARY libgflags.so HINTS /usr/lib/arm-linux-gnueabihf/) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GFlags DEFAULT_MSG GFlags_LIBRARY GFlags_INCLUDE_DIR) mark_as_advanced(GFlags_LIBRARY GFlags_INCLUDE_DIR) ./cmake/FindBoost.cmake0000644000015600001650000015377712676616125015106 0ustar jenkinsjenkins# - Try to find Boost include dirs and libraries # Usage of this module as follows: # # NOTE: Take note of the Boost_ADDITIONAL_VERSIONS variable below. # Due to Boost naming conventions and limitations in CMake this find # module is NOT future safe with respect to Boost version numbers, # and may break. # # == Using Header-Only libraries from within Boost: == # # find_package( Boost 1.36.0 ) # if(Boost_FOUND) # include_directories(${Boost_INCLUDE_DIRS}) # add_executable(foo foo.cc) # endif() # # # == Using actual libraries from within Boost: == # # set(Boost_USE_STATIC_LIBS ON) # set(Boost_USE_MULTITHREADED ON) # set(Boost_USE_STATIC_RUNTIME OFF) # find_package( Boost 1.36.0 COMPONENTS date_time filesystem system ... ) # # if(Boost_FOUND) # include_directories(${Boost_INCLUDE_DIRS}) # add_executable(foo foo.cc) # target_link_libraries(foo ${Boost_LIBRARIES}) # endif() # # # The components list needs to contain actual names of boost libraries only, # such as "date_time" for "libboost_date_time". If you're using parts of # Boost that contain header files only (e.g. foreach) you do not need to # specify COMPONENTS. # # You should provide a minimum version number that should be used. If you provide this # version number and specify the REQUIRED attribute, this module will fail if it # can't find the specified or a later version. If you specify a version number this is # automatically put into the considered list of version numbers and thus doesn't need # to be specified in the Boost_ADDITIONAL_VERSIONS variable (see below). # # NOTE for Visual Studio Users: # Automatic linking is used on MSVC & Borland compilers by default when # #including things in Boost. It's important to note that setting # Boost_USE_STATIC_LIBS to OFF is NOT enough to get you dynamic linking, # should you need this feature. Automatic linking typically uses static # libraries with a few exceptions (Boost.Python is one). # # Please see the section below near Boost_LIB_DIAGNOSTIC_DEFINITIONS for # more details. Adding a TARGET_LINK_LIBRARIES() as shown in the example # above appears to cause VS to link dynamically if Boost_USE_STATIC_LIBS # gets set to OFF. It is suggested you avoid automatic linking since it # will make your application less portable. # # =========== The mess that is Boost_ADDITIONAL_VERSIONS (sorry?) ============ # # OK, so the Boost_ADDITIONAL_VERSIONS variable can be used to specify a list of # boost version numbers that should be taken into account when searching # for Boost. Unfortunately boost puts the version number into the # actual filename for the libraries, so this variable will certainly be needed # in the future when new Boost versions are released. # # Currently this module searches for the following version numbers: # 1.33, 1.33.0, 1.33.1, 1.34, 1.34.0, 1.34.1, 1.35, 1.35.0, 1.35.1, # 1.36, 1.36.0, 1.36.1, 1.37, 1.37.0, 1.38, 1.38.0, 1.39, 1.39.0, # 1.40, 1.40.0, 1.41, 1.41.0, 1.42, 1.42.0, 1.43, 1.43.0, 1.44, 1.44.0, # 1.45, 1.45.0, 1.46, 1.46.0, 1.46.1, 1.47, 1.47.0, 1.48, 1.48.0 # # NOTE: If you add a new major 1.x version in Boost_ADDITIONAL_VERSIONS you should # add both 1.x and 1.x.0 as shown above. Official Boost include directories # omit the 3rd version number from include paths if it is 0 although not all # binary Boost releases do so. # # set(Boost_ADDITIONAL_VERSIONS "1.78" "1.78.0" "1.79" "1.79.0") # # ===================================== ============= ======================== # # Variables used by this module, they can change the default behaviour and # need to be set before calling find_package: # # Boost_USE_MULTITHREADED Can be set to OFF to use the non-multithreaded # boost libraries. If not specified, defaults # to ON. # # Boost_USE_STATIC_LIBS Can be set to ON to force the use of the static # boost libraries. Defaults to OFF. # # Boost_NO_SYSTEM_PATHS Set to TRUE to suppress searching in system # paths (or other locations outside of BOOST_ROOT # or BOOST_INCLUDEDIR). Useful when specifying # BOOST_ROOT. Defaults to OFF. # [Since CMake 2.8.3] # # Boost_NO_BOOST_CMAKE Do not do a find_package call in config mode # before searching for a regular boost install. # This will avoid finding boost-cmake installs. # Defaults to OFF. # [Since CMake 2.8.6] # # Boost_USE_STATIC_RUNTIME If enabled, searches for boost libraries # linked against a static C++ standard library # ('s' ABI tag). This option should be set to # ON or OFF because the default behavior # if not specified is platform dependent # for backwards compatibility. # [Since CMake 2.8.3] # # Boost_USE_DEBUG_PYTHON If enabled, searches for boost libraries # compiled against a special debug build of # Python ('y' ABI tag). Defaults to OFF. # [Since CMake 2.8.3] # # Boost_USE_STLPORT If enabled, searches for boost libraries # compiled against the STLPort standard # library ('p' ABI tag). Defaults to OFF. # [Since CMake 2.8.3] # # Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS # If enabled, searches for boost libraries # compiled against the deprecated STLPort # "native iostreams" feature ('n' ABI tag). # Defaults to OFF. # [Since CMake 2.8.3] # # Other Variables used by this module which you may want to set. # # Boost_ADDITIONAL_VERSIONS A list of version numbers to use for searching # the boost include directory. Please see # the documentation above regarding this # annoying, but necessary variable :( # # Boost_DEBUG Set this to TRUE to enable debugging output # of FindBoost.cmake if you are having problems. # Please enable this before filing any bug # reports. # # Boost_DETAILED_FAILURE_MSG FindBoost doesn't output detailed information # about why it failed or how to fix the problem # unless this is set to TRUE or the REQUIRED # keyword is specified in find_package(). # [Since CMake 2.8.0] # # Boost_COMPILER Set this to the compiler suffix used by Boost # (e.g. "-gcc43") if FindBoost has problems finding # the proper Boost installation # # Boost_THREADAPI When building boost.thread, sometimes the name of the # library contains an additional "pthread" or "win32" # string known as the threadapi. This can happen when # compiling against pthreads on Windows or win32 threads # on Cygwin. You may specify this variable and if set # when FindBoost searches for the Boost threading library # it will first try to match the threadapi you specify. # For Example: libboost_thread_win32-mgw45-mt-1_43.a # might be found if you specified "win32" here before # falling back on libboost_thread-mgw45-mt-1_43.a. # [Since CMake 2.8.3] # # Boost_REALPATH Resolves symbolic links for discovered boost libraries # to assist with packaging. For example, instead of # Boost_SYSTEM_LIBRARY_RELEASE being resolved to # "/usr/lib/libboost_system.so" it would be # "/usr/lib/libboost_system.so.1.42.0" instead. # This does not affect linking and should not be # enabled unless the user needs this information. # [Since CMake 2.8.3] # # # These last three variables are available also as environment variables: # Also, note they are completely UPPERCASE, except Boost_DIR. # # Boost_DIR or The preferred installation prefix for searching for # BOOST_ROOT or BOOSTROOT Boost. Set this if the module has problems finding # the proper Boost installation. # # Note that Boost_DIR behaves exactly as _DIR # variables are documented to behave in find_package's # Config mode. That is, if it is set as a -D argument # to CMake, it must point to the location of the # BoostConfig.cmake or Boost-config.cmake file. If it # is set as an environment variable, it must point to # the root of the boost installation. BOOST_ROOT and # BOOSTROOT, on the other hand, will point to the root # in either case. # # To prevent falling back on the system paths, set # Boost_NO_SYSTEM_PATHS to true. # # To avoid finding boost-cmake installations, set # Boost_NO_BOOST_CMAKE to true. # # BOOST_INCLUDEDIR Set this to the include directory of Boost, if the # module has problems finding the proper Boost installation # # BOOST_LIBRARYDIR Set this to the lib directory of Boost, if the # module has problems finding the proper Boost installation # # Variables defined by this module: # # Boost_FOUND System has Boost, this means the include dir was # found, as well as all the libraries specified in # the COMPONENTS list. # # Boost_INCLUDE_DIRS Boost include directories: not cached # # Boost_INCLUDE_DIR This is almost the same as above, but this one is # cached and may be modified by advanced users # # Boost_LIBRARIES Link to these to use the Boost libraries that you # specified: not cached # # Boost_LIBRARY_DIRS The path to where the Boost library files are. # # Boost_VERSION The version number of the boost libraries that # have been found, same as in version.hpp from Boost # # Boost_LIB_VERSION The version number in filename form as # it's appended to the library filenames # # Boost_MAJOR_VERSION major version number of boost # Boost_MINOR_VERSION minor version number of boost # Boost_SUBMINOR_VERSION subminor version number of boost # # Boost_LIB_DIAGNOSTIC_DEFINITIONS [WIN32 Only] You can call # add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS}) # to have diagnostic information about Boost's # automatic linking outputted during compilation time. # # For each component you specify in find_package(), the following (UPPER-CASE) # variables are set. You can use these variables if you would like to pick and # choose components for your targets instead of just using Boost_LIBRARIES. # # Boost_${COMPONENT}_FOUND True IF the Boost library "component" was found. # # Boost_${COMPONENT}_LIBRARY Contains the libraries for the specified Boost # "component" (includes debug and optimized keywords # when needed). #============================================================================= # Copyright 2006-2009 Kitware, Inc. # Copyright 2006-2008 Andreas Schneider # Copyright 2007 Wengo # Copyright 2007 Mike Jackson # Copyright 2008 Andreas Pakulat # Copyright 2008-2010 Philip Lowman # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) #------------------------------------------------------------------------------- # Before we go searching, check whether boost-cmake is avaialble, unless the # user specifically asked NOT to search for boost-cmake. # # If Boost_DIR is set, this behaves as any find_package call would. If not, # it looks at BOOST_ROOT and BOOSTROOT to find Boost. # if (NOT Boost_NO_BOOST_CMAKE) # If Boost_DIR is not set, look for BOOSTROOT and BOOST_ROOT as alternatives, # since these are more conventional for Boost. if ("$ENV{Boost_DIR}" STREQUAL "") if (NOT "$ENV{BOOST_ROOT}" STREQUAL "") set(ENV{Boost_DIR} $ENV{BOOST_ROOT}) elseif (NOT "$ENV{BOOSTROOT}" STREQUAL "") set(ENV{Boost_DIR} $ENV{BOOSTROOT}) endif() endif() # Do the same find_package call but look specifically for the CMake version. # Note that args are passed in the Boost_FIND_xxxxx variables, so there is no # need to delegate them to this find_package call. find_package(Boost QUIET NO_MODULE) # If we found boost-cmake, then we're done. Print out what we found. # Otherwise let the rest of the module try to find it. if (Boost_FOUND) message("Boost ${Boost_FIND_VERSION} found.") if (Boost_FIND_COMPONENTS) message("Found Boost components:") message(" ${Boost_FIND_COMPONENTS}") endif() return() endif() endif() #------------------------------------------------------------------------------- # FindBoost functions & macros # ############################################ # # Check the existence of the libraries. # ############################################ # This macro was taken directly from the FindQt4.cmake file that is included # with the CMake distribution. This is NOT my work. All work was done by the # original authors of the FindQt4.cmake file. Only minor modifications were # made to remove references to Qt and make this file more generally applicable # And ELSE/ENDIF pairs were removed for readability. ######################################################################### macro(_Boost_ADJUST_LIB_VARS basename) if(Boost_INCLUDE_DIR ) if(Boost_${basename}_LIBRARY_DEBUG AND Boost_${basename}_LIBRARY_RELEASE) # if the generator supports configuration types then set # optimized and debug libraries, or if the CMAKE_BUILD_TYPE has a value if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE) set(Boost_${basename}_LIBRARY optimized ${Boost_${basename}_LIBRARY_RELEASE} debug ${Boost_${basename}_LIBRARY_DEBUG}) else() # if there are no configuration types and CMAKE_BUILD_TYPE has no value # then just use the release libraries set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE} ) endif() # FIXME: This probably should be set for both cases set(Boost_${basename}_LIBRARIES optimized ${Boost_${basename}_LIBRARY_RELEASE} debug ${Boost_${basename}_LIBRARY_DEBUG}) endif() # if only the release version was found, set the debug variable also to the release version if(Boost_${basename}_LIBRARY_RELEASE AND NOT Boost_${basename}_LIBRARY_DEBUG) set(Boost_${basename}_LIBRARY_DEBUG ${Boost_${basename}_LIBRARY_RELEASE}) set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE}) set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE}) endif() # if only the debug version was found, set the release variable also to the debug version if(Boost_${basename}_LIBRARY_DEBUG AND NOT Boost_${basename}_LIBRARY_RELEASE) set(Boost_${basename}_LIBRARY_RELEASE ${Boost_${basename}_LIBRARY_DEBUG}) set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_DEBUG}) set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_DEBUG}) endif() # If the debug & release library ends up being the same, omit the keywords if(${Boost_${basename}_LIBRARY_RELEASE} STREQUAL ${Boost_${basename}_LIBRARY_DEBUG}) set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE} ) set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE} ) endif() if(Boost_${basename}_LIBRARY) set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY} CACHE FILEPATH "The Boost ${basename} library") # Remove superfluous "debug" / "optimized" keywords from # Boost_LIBRARY_DIRS foreach(_boost_my_lib ${Boost_${basename}_LIBRARY}) get_filename_component(_boost_my_lib_path "${_boost_my_lib}" PATH) list(APPEND Boost_LIBRARY_DIRS ${_boost_my_lib_path}) endforeach() list(REMOVE_DUPLICATES Boost_LIBRARY_DIRS) set(Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIRS} CACHE FILEPATH "Boost library directory") set(Boost_${basename}_FOUND ON CACHE INTERNAL "Whether the Boost ${basename} library found") endif(Boost_${basename}_LIBRARY) endif(Boost_INCLUDE_DIR ) # Make variables changeble to the advanced user mark_as_advanced( Boost_${basename}_LIBRARY Boost_${basename}_LIBRARY_RELEASE Boost_${basename}_LIBRARY_DEBUG ) endmacro(_Boost_ADJUST_LIB_VARS) #------------------------------------------------------------------------------- # # Runs compiler with "-dumpversion" and parses major/minor # version with a regex. # function(_Boost_COMPILER_DUMPVERSION _OUTPUT_VERSION) exec_program(${CMAKE_CXX_COMPILER} ARGS ${CMAKE_CXX_COMPILER_ARG1} -dumpversion OUTPUT_VARIABLE _boost_COMPILER_VERSION ) string(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" _boost_COMPILER_VERSION ${_boost_COMPILER_VERSION}) set(${_OUTPUT_VERSION} ${_boost_COMPILER_VERSION} PARENT_SCOPE) endfunction() # # A convenience function for marking desired components # as found or not # function(_Boost_MARK_COMPONENTS_FOUND _yes_or_no) foreach(COMPONENT ${Boost_FIND_COMPONENTS}) string(TOUPPER ${COMPONENT} UPPERCOMPONENT) set(Boost_${UPPERCOMPONENT}_FOUND ${_yes_or_no} CACHE INTERNAL "Whether the Boost ${COMPONENT} library found" FORCE) endforeach() endfunction() # # Take a list of libraries with "thread" in it # and prepend duplicates with "thread_${Boost_THREADAPI}" # at the front of the list # function(_Boost_PREPEND_LIST_WITH_THREADAPI _output) set(_orig_libnames ${ARGN}) string(REPLACE "thread" "thread_${Boost_THREADAPI}" _threadapi_libnames "${_orig_libnames}") set(${_output} ${_threadapi_libnames} ${_orig_libnames} PARENT_SCOPE) endfunction() # # If a library is found, replace its cache entry with its REALPATH # function(_Boost_SWAP_WITH_REALPATH _library _docstring) if(${_library}) get_filename_component(_boost_filepathreal ${${_library}} REALPATH) unset(${_library} CACHE) set(${_library} ${_boost_filepathreal} CACHE FILEPATH "${_docstring}") endif() endfunction() function(_Boost_CHECK_SPELLING _var) if(${_var}) string(TOUPPER ${_var} _var_UC) message(FATAL_ERROR "ERROR: ${_var} is not the correct spelling. The proper spelling is ${_var_UC}.") endif() endfunction() # # End functions/macros # #------------------------------------------------------------------------------- if(NOT DEFINED Boost_USE_MULTITHREADED) set(Boost_USE_MULTITHREADED TRUE) endif() if(Boost_FIND_VERSION_EXACT) # The version may appear in a directory with or without the patch # level, even when the patch level is non-zero. set(_boost_TEST_VERSIONS "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}.${Boost_FIND_VERSION_PATCH}" "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") else(Boost_FIND_VERSION_EXACT) # The user has not requested an exact version. Among known # versions, find those that are acceptable to the user request. set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS} "1.48.0" "1.48" "1.47.0" "1.47" "1.46.1" "1.46.0" "1.46" "1.45.0" "1.45" "1.44.0" "1.44" "1.43.0" "1.43" "1.42.0" "1.42" "1.41.0" "1.41" "1.40.0" "1.40" "1.39.0" "1.39" "1.38.0" "1.38" "1.37.0" "1.37" "1.36.1" "1.36.0" "1.36" "1.35.1" "1.35.0" "1.35" "1.34.1" "1.34.0" "1.34" "1.33.1" "1.33.0" "1.33") set(_boost_TEST_VERSIONS) if(Boost_FIND_VERSION) set(_Boost_FIND_VERSION_SHORT "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") # Select acceptable versions. foreach(version ${_Boost_KNOWN_VERSIONS}) if(NOT "${version}" VERSION_LESS "${Boost_FIND_VERSION}") # This version is high enough. list(APPEND _boost_TEST_VERSIONS "${version}") elseif("${version}.99" VERSION_EQUAL "${_Boost_FIND_VERSION_SHORT}.99") # This version is a short-form for the requested version with # the patch level dropped. list(APPEND _boost_TEST_VERSIONS "${version}") endif() endforeach(version) else(Boost_FIND_VERSION) # Any version is acceptable. set(_boost_TEST_VERSIONS "${_Boost_KNOWN_VERSIONS}") endif(Boost_FIND_VERSION) endif(Boost_FIND_VERSION_EXACT) # The reason that we failed to find Boost. This will be set to a # user-friendly message when we fail to find some necessary piece of # Boost. set(Boost_ERROR_REASON) set( _boost_IN_CACHE TRUE) if(Boost_INCLUDE_DIR) # On versions < 1.35, remove the System library from the considered list # since it wasn't added until 1.35. if(Boost_VERSION AND Boost_FIND_COMPONENTS) if(Boost_VERSION LESS 103500) list(REMOVE_ITEM Boost_FIND_COMPONENTS system) endif() endif() foreach(COMPONENT ${Boost_FIND_COMPONENTS}) string(TOUPPER ${COMPONENT} COMPONENT) if(NOT Boost_${COMPONENT}_FOUND) set( _boost_IN_CACHE FALSE) endif(NOT Boost_${COMPONENT}_FOUND) endforeach(COMPONENT) else(Boost_INCLUDE_DIR) set( _boost_IN_CACHE FALSE) endif(Boost_INCLUDE_DIR) if(_boost_IN_CACHE) # in cache already set(Boost_FOUND TRUE) foreach(COMPONENT ${Boost_FIND_COMPONENTS}) string(TOUPPER ${COMPONENT} COMPONENT) _Boost_ADJUST_LIB_VARS( ${COMPONENT} ) set(Boost_LIBRARIES ${Boost_LIBRARIES} ${Boost_${COMPONENT}_LIBRARY}) endforeach(COMPONENT) set(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR}) if(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") math(EXPR Boost_MAJOR_VERSION "${Boost_VERSION} / 100000") math(EXPR Boost_MINOR_VERSION "${Boost_VERSION} / 100 % 1000") math(EXPR Boost_SUBMINOR_VERSION "${Boost_VERSION} % 100") endif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "boost ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION} " "is already in the cache. To view debugging messages, please clear the cache.") endif() else(_boost_IN_CACHE) # Need to search for boost if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "Boost not in cache") # Output some of their choices message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "_boost_TEST_VERSIONS = ${_boost_TEST_VERSIONS}") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "Boost_USE_MULTITHREADED = ${Boost_USE_MULTITHREADED}") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "Boost_USE_STATIC_LIBS = ${Boost_USE_STATIC_LIBS}") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "Boost_USE_STATIC_RUNTIME = ${Boost_USE_STATIC_RUNTIME}") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "Boost_ADDITIONAL_VERSIONS = ${Boost_ADDITIONAL_VERSIONS}") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "Boost_NO_SYSTEM_PATHS = ${Boost_NO_SYSTEM_PATHS}") endif() if(WIN32) # In windows, automatic linking is performed, so you do not have # to specify the libraries. If you are linking to a dynamic # runtime, then you can choose to link to either a static or a # dynamic Boost library, the default is to do a static link. You # can alter this for a specific library "whatever" by defining # BOOST_WHATEVER_DYN_LINK to force Boost library "whatever" to be # linked dynamically. Alternatively you can force all Boost # libraries to dynamic link by defining BOOST_ALL_DYN_LINK. # This feature can be disabled for Boost library "whatever" by # defining BOOST_WHATEVER_NO_LIB, or for all of Boost by defining # BOOST_ALL_NO_LIB. # If you want to observe which libraries are being linked against # then defining BOOST_LIB_DIAGNOSTIC will cause the auto-linking # code to emit a #pragma message each time a library is selected # for linking. set(Boost_LIB_DIAGNOSTIC_DEFINITIONS "-DBOOST_LIB_DIAGNOSTIC" CACHE STRING "Boost diagnostic define") endif(WIN32) set(_boost_INCLUDE_SEARCH_DIRS_SYSTEM C:/boost/include C:/boost "$ENV{ProgramFiles}/boost/include" "$ENV{ProgramFiles}/boost" /sw/local/include ) _Boost_CHECK_SPELLING(Boost_ROOT) _Boost_CHECK_SPELLING(Boost_LIBRARYDIR) _Boost_CHECK_SPELLING(Boost_INCLUDEDIR) # If BOOST_ROOT was defined in the environment, use it. if (NOT BOOST_ROOT AND NOT $ENV{Boost_DIR} STREQUAL "") set(BOOST_ROOT $ENV{Boost_DIR}) endif() # If BOOST_ROOT was defined in the environment, use it. if (NOT BOOST_ROOT AND NOT $ENV{BOOST_ROOT} STREQUAL "") set(BOOST_ROOT $ENV{BOOST_ROOT}) endif() # If BOOSTROOT was defined in the environment, use it. if (NOT BOOST_ROOT AND NOT $ENV{BOOSTROOT} STREQUAL "") set(BOOST_ROOT $ENV{BOOSTROOT}) endif() # If BOOST_INCLUDEDIR was defined in the environment, use it. if( NOT $ENV{BOOST_INCLUDEDIR} STREQUAL "" ) set(BOOST_INCLUDEDIR $ENV{BOOST_INCLUDEDIR}) endif() # If BOOST_LIBRARYDIR was defined in the environment, use it. if( NOT $ENV{BOOST_LIBRARYDIR} STREQUAL "" ) set(BOOST_LIBRARYDIR $ENV{BOOST_LIBRARYDIR}) endif() if( BOOST_ROOT ) file(TO_CMAKE_PATH ${BOOST_ROOT} BOOST_ROOT) endif() if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "Declared as CMake or Environmental Variables:") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " " BOOST_ROOT = ${BOOST_ROOT}") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " " BOOST_INCLUDEDIR = ${BOOST_INCLUDEDIR}") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " " BOOST_LIBRARYDIR = ${BOOST_LIBRARYDIR}") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "_boost_TEST_VERSIONS = ${_boost_TEST_VERSIONS}") endif() if( Boost_NO_SYSTEM_PATHS) set(_boost_FIND_OPTIONS NO_CMAKE_SYSTEM_PATH) else() set(_boost_INCLUDE_SEARCH_DIRS ${_boost_INCLUDE_SEARCH_DIRS_SYSTEM}) endif() if( BOOST_ROOT ) set(_boost_INCLUDE_SEARCH_DIRS ${BOOST_ROOT}/include ${BOOST_ROOT} ${_boost_INCLUDE_SEARCH_DIRS}) endif() # prepend BOOST_INCLUDEDIR to search path if specified if( BOOST_INCLUDEDIR ) file(TO_CMAKE_PATH ${BOOST_INCLUDEDIR} BOOST_INCLUDEDIR) set(_boost_INCLUDE_SEARCH_DIRS ${BOOST_INCLUDEDIR} ${_boost_INCLUDE_SEARCH_DIRS}) endif( BOOST_INCLUDEDIR ) # ------------------------------------------------------------------------ # Search for Boost include DIR # ------------------------------------------------------------------------ # Try to find Boost by stepping backwards through the Boost versions # we know about. if( NOT Boost_INCLUDE_DIR ) # Build a list of path suffixes for each version. set(_boost_PATH_SUFFIXES) foreach(_boost_VER ${_boost_TEST_VERSIONS}) # Add in a path suffix, based on the required version, ideally # we could read this from version.hpp, but for that to work we'd # need to know the include dir already set(_boost_BOOSTIFIED_VERSION) # Transform 1.35 => 1_35 and 1.36.0 => 1_36_0 if(_boost_VER MATCHES "[0-9]+\\.[0-9]+\\.[0-9]+") string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1_\\2_\\3" _boost_BOOSTIFIED_VERSION ${_boost_VER}) elseif(_boost_VER MATCHES "[0-9]+\\.[0-9]+") string(REGEX REPLACE "([0-9]+)\\.([0-9]+)" "\\1_\\2" _boost_BOOSTIFIED_VERSION ${_boost_VER}) endif() list(APPEND _boost_PATH_SUFFIXES "boost-${_boost_BOOSTIFIED_VERSION}") list(APPEND _boost_PATH_SUFFIXES "boost_${_boost_BOOSTIFIED_VERSION}") endforeach(_boost_VER) if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "Include debugging info:") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " " _boost_INCLUDE_SEARCH_DIRS = ${_boost_INCLUDE_SEARCH_DIRS}") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " " _boost_PATH_SUFFIXES = ${_boost_PATH_SUFFIXES}") endif() # Look for a standard boost header file. find_path(Boost_INCLUDE_DIR NAMES boost/config.hpp HINTS ${_boost_INCLUDE_SEARCH_DIRS} PATH_SUFFIXES ${_boost_PATH_SUFFIXES} ${_boost_FIND_OPTIONS} ) endif( NOT Boost_INCLUDE_DIR ) # ------------------------------------------------------------------------ # Extract version information from version.hpp # ------------------------------------------------------------------------ if(Boost_INCLUDE_DIR) # Extract Boost_VERSION and Boost_LIB_VERSION from version.hpp # Read the whole file: # set(BOOST_VERSION 0) set(BOOST_LIB_VERSION "") file(READ "${Boost_INCLUDE_DIR}/boost/version.hpp" _boost_VERSION_HPP_CONTENTS) if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "location of version.hpp: ${Boost_INCLUDE_DIR}/boost/version.hpp") endif() string(REGEX REPLACE ".*#define BOOST_VERSION ([0-9]+).*" "\\1" Boost_VERSION "${_boost_VERSION_HPP_CONTENTS}") string(REGEX REPLACE ".*#define BOOST_LIB_VERSION \"([0-9_]+)\".*" "\\1" Boost_LIB_VERSION "${_boost_VERSION_HPP_CONTENTS}") set(Boost_LIB_VERSION ${Boost_LIB_VERSION} CACHE INTERNAL "The library version string for boost libraries") set(Boost_VERSION ${Boost_VERSION} CACHE INTERNAL "The version number for boost libraries") if(NOT "${Boost_VERSION}" STREQUAL "0") math(EXPR Boost_MAJOR_VERSION "${Boost_VERSION} / 100000") math(EXPR Boost_MINOR_VERSION "${Boost_VERSION} / 100 % 1000") math(EXPR Boost_SUBMINOR_VERSION "${Boost_VERSION} % 100") set(Boost_ERROR_REASON "${Boost_ERROR_REASON}Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}\nBoost include path: ${Boost_INCLUDE_DIR}") endif(NOT "${Boost_VERSION}" STREQUAL "0") if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "version.hpp reveals boost " "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") endif() else(Boost_INCLUDE_DIR) set(Boost_ERROR_REASON "${Boost_ERROR_REASON}Unable to find the Boost header files. Please set BOOST_ROOT to the root directory containing Boost or BOOST_INCLUDEDIR to the directory containing Boost's headers.") endif(Boost_INCLUDE_DIR) # ------------------------------------------------------------------------ # Suffix initialization and compiler suffix detection. # ------------------------------------------------------------------------ # Setting some more suffixes for the library set(Boost_LIB_PREFIX "") if ( WIN32 AND Boost_USE_STATIC_LIBS AND NOT CYGWIN) set(Boost_LIB_PREFIX "lib") endif() if (Boost_COMPILER) set(_boost_COMPILER ${Boost_COMPILER}) if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "using user-specified Boost_COMPILER = ${_boost_COMPILER}") endif() else(Boost_COMPILER) # Attempt to guess the compiler suffix # NOTE: this is not perfect yet, if you experience any issues # please report them and use the Boost_COMPILER variable # to work around the problems. if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" OR "${CMAKE_CXX_COMPILER}" MATCHES "icl" OR "${CMAKE_CXX_COMPILER}" MATCHES "icpc") if(WIN32) set (_boost_COMPILER "-iw") else() set (_boost_COMPILER "-il") endif() elseif (MSVC11) set(_boost_COMPILER "-vc110") elseif (MSVC10) set(_boost_COMPILER "-vc100") elseif (MSVC90) set(_boost_COMPILER "-vc90") elseif (MSVC80) set(_boost_COMPILER "-vc80") elseif (MSVC71) set(_boost_COMPILER "-vc71") elseif (MSVC70) # Good luck! set(_boost_COMPILER "-vc7") # yes, this is correct elseif (MSVC60) # Good luck! set(_boost_COMPILER "-vc6") # yes, this is correct elseif (BORLAND) set(_boost_COMPILER "-bcb") elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "SunPro") set(_boost_COMPILER "-sw") elseif (MINGW) if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34) set(_boost_COMPILER "-mgw") # no GCC version encoding prior to 1.34 else() _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION) set(_boost_COMPILER "-mgw${_boost_COMPILER_VERSION}") endif() elseif (UNIX) if (CMAKE_COMPILER_IS_GNUCXX) if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34) set(_boost_COMPILER "-gcc") # no GCC version encoding prior to 1.34 else() _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION) # Determine which version of GCC we have. if(APPLE) if(Boost_MINOR_VERSION) if(${Boost_MINOR_VERSION} GREATER 35) # In Boost 1.36.0 and newer, the mangled compiler name used # on Mac OS X/Darwin is "xgcc". set(_boost_COMPILER "-xgcc${_boost_COMPILER_VERSION}") else(${Boost_MINOR_VERSION} GREATER 35) # In Boost <= 1.35.0, there is no mangled compiler name for # the Mac OS X/Darwin version of GCC. set(_boost_COMPILER "") endif(${Boost_MINOR_VERSION} GREATER 35) else(Boost_MINOR_VERSION) # We don't know the Boost version, so assume it's # pre-1.36.0. set(_boost_COMPILER "") endif(Boost_MINOR_VERSION) else() set(_boost_COMPILER "-gcc${_boost_COMPILER_VERSION}") endif() endif() endif (CMAKE_COMPILER_IS_GNUCXX) endif() if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "guessed _boost_COMPILER = ${_boost_COMPILER}") endif() endif(Boost_COMPILER) set (_boost_MULTITHREADED "-mt") if( NOT Boost_USE_MULTITHREADED ) set (_boost_MULTITHREADED "") endif() if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "_boost_MULTITHREADED = ${_boost_MULTITHREADED}") endif() #====================== # Systematically build up the Boost ABI tag # http://boost.org/doc/libs/1_41_0/more/getting_started/windows.html#library-naming set( _boost_RELEASE_ABI_TAG "-") set( _boost_DEBUG_ABI_TAG "-") # Key Use this library when: # s linking statically to the C++ standard library and # compiler runtime support libraries. if(Boost_USE_STATIC_RUNTIME) set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}s") set( _boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}s") endif() # g using debug versions of the standard and runtime # support libraries if(WIN32) if(MSVC OR "${CMAKE_CXX_COMPILER}" MATCHES "icl" OR "${CMAKE_CXX_COMPILER}" MATCHES "icpc") set(_boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}g") endif() endif() # y using special debug build of python if(Boost_USE_DEBUG_PYTHON) set(_boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}y") endif() # d using a debug version of your code set(_boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}d") # p using the STLport standard library rather than the # default one supplied with your compiler if(Boost_USE_STLPORT) set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}p") set( _boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}p") endif() # n using the STLport deprecated "native iostreams" feature if(Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS) set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}n") set( _boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}n") endif() if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "_boost_RELEASE_ABI_TAG = ${_boost_RELEASE_ABI_TAG}") message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "_boost_DEBUG_ABI_TAG = ${_boost_DEBUG_ABI_TAG}") endif() # ------------------------------------------------------------------------ # Begin finding boost libraries # ------------------------------------------------------------------------ if(BOOST_ROOT) set(_boost_LIBRARY_SEARCH_DIRS_ALWAYS ${BOOST_ROOT}/lib ${BOOST_ROOT}/stage/lib) endif() set(_boost_LIBRARY_SEARCH_DIRS_ALWAYS ${_boost_LIBRARY_SEARCH_DIRS_ALWAYS} ${Boost_INCLUDE_DIR}/lib ${Boost_INCLUDE_DIR}/../lib ${Boost_INCLUDE_DIR}/stage/lib ) set(_boost_LIBRARY_SEARCH_DIRS_SYSTEM C:/boost/lib C:/boost "$ENV{ProgramFiles}/boost/boost_${Boost_MAJOR_VERSION}_${Boost_MINOR_VERSION}_${Boost_SUBMINOR_VERSION}/lib" "$ENV{ProgramFiles}/boost/boost_${Boost_MAJOR_VERSION}_${Boost_MINOR_VERSION}/lib" "$ENV{ProgramFiles}/boost/lib" "$ENV{ProgramFiles}/boost" /sw/local/lib ) set(_boost_LIBRARY_SEARCH_DIRS ${_boost_LIBRARY_SEARCH_DIRS_ALWAYS}) if( Boost_NO_SYSTEM_PATHS ) set(_boost_FIND_OPTIONS NO_CMAKE_SYSTEM_PATH) else() list(APPEND _boost_LIBRARY_SEARCH_DIRS ${_boost_LIBRARY_SEARCH_DIRS_SYSTEM}) endif() # prepend BOOST_LIBRARYDIR to search path if specified if( BOOST_LIBRARYDIR ) file(TO_CMAKE_PATH ${BOOST_LIBRARYDIR} BOOST_LIBRARYDIR) set(_boost_LIBRARY_SEARCH_DIRS ${BOOST_LIBRARYDIR} ${_boost_LIBRARY_SEARCH_DIRS}) endif() if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "_boost_LIBRARY_SEARCH_DIRS = ${_boost_LIBRARY_SEARCH_DIRS}") endif() # Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES if( Boost_USE_STATIC_LIBS ) set( _boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) if(WIN32) set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) else() set(CMAKE_FIND_LIBRARY_SUFFIXES .a ) endif() endif() # We want to use the tag inline below without risking double dashes if(_boost_RELEASE_ABI_TAG) if(${_boost_RELEASE_ABI_TAG} STREQUAL "-") set(_boost_RELEASE_ABI_TAG "") endif() endif() if(_boost_DEBUG_ABI_TAG) if(${_boost_DEBUG_ABI_TAG} STREQUAL "-") set(_boost_DEBUG_ABI_TAG "") endif() endif() # The previous behavior of FindBoost when Boost_USE_STATIC_LIBS was enabled # on WIN32 was to: # 1. Search for static libs compiled against a SHARED C++ standard runtime library (use if found) # 2. Search for static libs compiled against a STATIC C++ standard runtime library (use if found) # We maintain this behavior since changing it could break people's builds. # To disable the ambiguous behavior, the user need only # set Boost_USE_STATIC_RUNTIME either ON or OFF. set(_boost_STATIC_RUNTIME_WORKAROUND false) if(WIN32 AND Boost_USE_STATIC_LIBS) if(NOT DEFINED Boost_USE_STATIC_RUNTIME) set(_boost_STATIC_RUNTIME_WORKAROUND true) endif() endif() foreach(COMPONENT ${Boost_FIND_COMPONENTS}) string(TOUPPER ${COMPONENT} UPPERCOMPONENT) set( Boost_${UPPERCOMPONENT}_LIBRARY "Boost_${UPPERCOMPONENT}_LIBRARY-NOTFOUND" ) set( Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE "Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE-NOTFOUND" ) set( Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG "Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG-NOTFOUND") set( _boost_docstring_release "Boost ${COMPONENT} library (release)") set( _boost_docstring_debug "Boost ${COMPONENT} library (debug)") # # Find RELEASE libraries # set(_boost_RELEASE_NAMES ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} ${Boost_LIB_PREFIX}boost_${COMPONENT} ) if(_boost_STATIC_RUNTIME_WORKAROUND) set(_boost_RELEASE_STATIC_ABI_TAG "-s${_boost_RELEASE_ABI_TAG}") list(APPEND _boost_RELEASE_NAMES ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} ) endif() if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") _Boost_PREPEND_LIST_WITH_THREADAPI(_boost_RELEASE_NAMES ${_boost_RELEASE_NAMES}) endif() if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "Searching for ${UPPERCOMPONENT}_LIBRARY_RELEASE: ${_boost_RELEASE_NAMES}") endif() find_library(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE NAMES ${_boost_RELEASE_NAMES} PATHS ${_boost_LIBRARY_SEARCH_DIRS} ${_boost_FIND_OPTIONS} DOC "${_boost_docstring_release}" ) # # Find DEBUG libraries # set(_boost_DEBUG_NAMES ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED} ${Boost_LIB_PREFIX}boost_${COMPONENT} ) if(_boost_STATIC_RUNTIME_WORKAROUND) set(_boost_DEBUG_STATIC_ABI_TAG "-s${_boost_DEBUG_ABI_TAG}") list(APPEND _boost_DEBUG_NAMES ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} ) endif() if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") _Boost_PREPEND_LIST_WITH_THREADAPI(_boost_DEBUG_NAMES ${_boost_DEBUG_NAMES}) endif() if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "Searching for ${UPPERCOMPONENT}_LIBRARY_DEBUG: ${_boost_DEBUG_NAMES}") endif() find_library(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG NAMES ${_boost_DEBUG_NAMES} HINTS ${_boost_LIBRARY_SEARCH_DIRS} ${_boost_FIND_OPTIONS} DOC "${_boost_docstring_debug}" ) if(Boost_REALPATH) _Boost_SWAP_WITH_REALPATH(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE "${_boost_docstring_release}") _Boost_SWAP_WITH_REALPATH(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG "${_boost_docstring_debug}" ) endif() _Boost_ADJUST_LIB_VARS(${UPPERCOMPONENT}) endforeach(COMPONENT) # Restore the original find library ordering if( Boost_USE_STATIC_LIBS ) set(CMAKE_FIND_LIBRARY_SUFFIXES ${_boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) endif() # ------------------------------------------------------------------------ # End finding boost libraries # ------------------------------------------------------------------------ set(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR} ) set(Boost_FOUND FALSE) if(Boost_INCLUDE_DIR) set( Boost_FOUND TRUE ) # Check the version of Boost against the requested version. if (Boost_FIND_VERSION AND NOT Boost_FIND_VERSION_MINOR) message(SEND_ERROR "When requesting a specific version of Boost, you must provide at least the major and minor version numbers, e.g., 1.34") endif (Boost_FIND_VERSION AND NOT Boost_FIND_VERSION_MINOR) if(Boost_MAJOR_VERSION LESS "${Boost_FIND_VERSION_MAJOR}" ) set( Boost_FOUND FALSE ) set(_Boost_VERSION_AGE "old") elseif(Boost_MAJOR_VERSION EQUAL "${Boost_FIND_VERSION_MAJOR}" ) if(Boost_MINOR_VERSION LESS "${Boost_FIND_VERSION_MINOR}" ) set( Boost_FOUND FALSE ) set(_Boost_VERSION_AGE "old") elseif(Boost_MINOR_VERSION EQUAL "${Boost_FIND_VERSION_MINOR}" ) if( Boost_FIND_VERSION_PATCH AND Boost_SUBMINOR_VERSION LESS "${Boost_FIND_VERSION_PATCH}" ) set( Boost_FOUND FALSE ) set(_Boost_VERSION_AGE "old") endif( Boost_FIND_VERSION_PATCH AND Boost_SUBMINOR_VERSION LESS "${Boost_FIND_VERSION_PATCH}" ) endif( Boost_MINOR_VERSION LESS "${Boost_FIND_VERSION_MINOR}" ) endif( Boost_MAJOR_VERSION LESS "${Boost_FIND_VERSION_MAJOR}" ) if (NOT Boost_FOUND) _Boost_MARK_COMPONENTS_FOUND(OFF) endif() if (Boost_FOUND AND Boost_FIND_VERSION_EXACT) # If the user requested an exact version of Boost, check # that. We already know that the Boost version we have is >= the # requested version. set(_Boost_VERSION_AGE "new") # If the user didn't specify a patchlevel, it's 0. if (NOT Boost_FIND_VERSION_PATCH) set(Boost_FIND_VERSION_PATCH 0) endif (NOT Boost_FIND_VERSION_PATCH) # We'll set Boost_FOUND true again if we have an exact version match. set(Boost_FOUND FALSE) _Boost_MARK_COMPONENTS_FOUND(OFF) if(Boost_MAJOR_VERSION EQUAL "${Boost_FIND_VERSION_MAJOR}" ) if(Boost_MINOR_VERSION EQUAL "${Boost_FIND_VERSION_MINOR}" ) if(Boost_SUBMINOR_VERSION EQUAL "${Boost_FIND_VERSION_PATCH}" ) set( Boost_FOUND TRUE ) _Boost_MARK_COMPONENTS_FOUND(ON) endif(Boost_SUBMINOR_VERSION EQUAL "${Boost_FIND_VERSION_PATCH}" ) endif( Boost_MINOR_VERSION EQUAL "${Boost_FIND_VERSION_MINOR}" ) endif( Boost_MAJOR_VERSION EQUAL "${Boost_FIND_VERSION_MAJOR}" ) endif (Boost_FOUND AND Boost_FIND_VERSION_EXACT) if(NOT Boost_FOUND) # State that we found a version of Boost that is too new or too old. set(Boost_ERROR_REASON "${Boost_ERROR_REASON}\nDetected version of Boost is too ${_Boost_VERSION_AGE}. Requested version was ${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") if (Boost_FIND_VERSION_PATCH) set(Boost_ERROR_REASON "${Boost_ERROR_REASON}.${Boost_FIND_VERSION_PATCH}") endif (Boost_FIND_VERSION_PATCH) if (NOT Boost_FIND_VERSION_EXACT) set(Boost_ERROR_REASON "${Boost_ERROR_REASON} (or newer)") endif (NOT Boost_FIND_VERSION_EXACT) set(Boost_ERROR_REASON "${Boost_ERROR_REASON}.") endif (NOT Boost_FOUND) # Always check for missing components set(_boost_CHECKED_COMPONENT FALSE) set(_Boost_MISSING_COMPONENTS "") foreach(COMPONENT ${Boost_FIND_COMPONENTS}) string(TOUPPER ${COMPONENT} COMPONENT) set(_boost_CHECKED_COMPONENT TRUE) if(NOT Boost_${COMPONENT}_FOUND) string(TOLOWER ${COMPONENT} COMPONENT) list(APPEND _Boost_MISSING_COMPONENTS ${COMPONENT}) set( Boost_FOUND FALSE) endif() endforeach(COMPONENT) if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] Boost_FOUND = ${Boost_FOUND}") endif() if (_Boost_MISSING_COMPONENTS) # We were unable to find some libraries, so generate a sensible # error message that lists the libraries we were unable to find. set(Boost_ERROR_REASON "${Boost_ERROR_REASON}\nThe following Boost libraries could not be found:\n") foreach(COMPONENT ${_Boost_MISSING_COMPONENTS}) set(Boost_ERROR_REASON "${Boost_ERROR_REASON} boost_${COMPONENT}\n") endforeach(COMPONENT) list(LENGTH Boost_FIND_COMPONENTS Boost_NUM_COMPONENTS_WANTED) list(LENGTH _Boost_MISSING_COMPONENTS Boost_NUM_MISSING_COMPONENTS) if (${Boost_NUM_COMPONENTS_WANTED} EQUAL ${Boost_NUM_MISSING_COMPONENTS}) set(Boost_ERROR_REASON "${Boost_ERROR_REASON}No Boost libraries were found. You may need to set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.") else (${Boost_NUM_COMPONENTS_WANTED} EQUAL ${Boost_NUM_MISSING_COMPONENTS}) set(Boost_ERROR_REASON "${Boost_ERROR_REASON}Some (but not all) of the required Boost libraries were found. You may need to install these additional Boost libraries. Alternatively, set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.") endif (${Boost_NUM_COMPONENTS_WANTED} EQUAL ${Boost_NUM_MISSING_COMPONENTS}) endif (_Boost_MISSING_COMPONENTS) if( NOT Boost_LIBRARY_DIRS AND NOT _boost_CHECKED_COMPONENT ) # Compatibility Code for backwards compatibility with CMake # 2.4's FindBoost module. # Look for the boost library path. # Note that the user may not have installed any libraries # so it is quite possible the Boost_LIBRARY_PATH may not exist. set(_boost_LIB_DIR ${Boost_INCLUDE_DIR}) if("${_boost_LIB_DIR}" MATCHES "boost-[0-9]+") get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH) endif() if("${_boost_LIB_DIR}" MATCHES "/include$") # Strip off the trailing "/include" in the path. get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH) endif() if(EXISTS "${_boost_LIB_DIR}/lib") set(_boost_LIB_DIR ${_boost_LIB_DIR}/lib) else() if(EXISTS "${_boost_LIB_DIR}/stage/lib") set(_boost_LIB_DIR ${_boost_LIB_DIR}/stage/lib) else() set(_boost_LIB_DIR "") endif() endif() if(_boost_LIB_DIR AND EXISTS "${_boost_LIB_DIR}") set(Boost_LIBRARY_DIRS ${_boost_LIB_DIR} CACHE FILEPATH "Boost library directory") endif() endif( NOT Boost_LIBRARY_DIRS AND NOT _boost_CHECKED_COMPONENT ) else(Boost_INCLUDE_DIR) set( Boost_FOUND FALSE) endif(Boost_INCLUDE_DIR) if(Boost_FOUND) if(NOT Boost_FIND_QUIETLY) message(STATUS "Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") if(Boost_FIND_COMPONENTS) message(STATUS "Found the following Boost libraries:") endif() endif(NOT Boost_FIND_QUIETLY) foreach( COMPONENT ${Boost_FIND_COMPONENTS} ) string( TOUPPER ${COMPONENT} UPPERCOMPONENT ) if( Boost_${UPPERCOMPONENT}_FOUND ) if(NOT Boost_FIND_QUIETLY) message (STATUS " ${COMPONENT}") endif(NOT Boost_FIND_QUIETLY) set(Boost_LIBRARIES ${Boost_LIBRARIES} ${Boost_${UPPERCOMPONENT}_LIBRARY}) if(${UPPERCOMPONENT} STREQUAL "THREAD" AND UNIX) find_package (Threads) set(Boost_LIBRARIES ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) endif(${UPPERCOMPONENT} STREQUAL "THREAD" AND UNIX) endif( Boost_${UPPERCOMPONENT}_FOUND ) endforeach(COMPONENT) else() if(Boost_FIND_REQUIRED) message(SEND_ERROR "Unable to find the requested Boost libraries.\n${Boost_ERROR_REASON}") else() if(NOT Boost_FIND_QUIETLY) # we opt not to automatically output Boost_ERROR_REASON here as # it could be quite lengthy and somewhat imposing in it's requests # Since Boost is not always a required dependency we'll leave this # up to the end-user. if(Boost_DEBUG OR Boost_DETAILED_FAILURE_MSG) message(STATUS "Could NOT find Boost\n${Boost_ERROR_REASON}") else() message(STATUS "Could NOT find Boost") endif() endif() endif(Boost_FIND_REQUIRED) endif() # show the Boost_INCLUDE_DIRS AND Boost_LIBRARIES variables only in the advanced view mark_as_advanced(Boost_INCLUDE_DIR Boost_INCLUDE_DIRS Boost_LIBRARY_DIRS ) endif(_boost_IN_CACHE) ./cmake/MirCommon.cmake0000644000015600001650000003160712676616157015107 0ustar jenkinsjenkinscmake_minimum_required (VERSION 2.6) # Create target to discover tests include (CMakeParseArguments) include(CMakeDependentOption) file(REMOVE ${CMAKE_BINARY_DIR}/discover_all_tests.sh) option( ENABLE_MEMCHECK_OPTION "If set to ON, enables automatic creation of memcheck targets" OFF ) option( MIR_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON ) if(ENABLE_MEMCHECK_OPTION) find_program( VALGRIND_EXECUTABLE valgrind) if(VALGRIND_EXECUTABLE) set(VALGRIND_CMD "${VALGRIND_EXECUTABLE}" "--error-exitcode=1" "--trace-children=yes") set(VALGRIND_CMD ${VALGRIND_CMD} "--leak-check=full" "--show-leak-kinds=definite" "--errors-for-leak-kinds=definite") set(VALGRIND_CMD ${VALGRIND_CMD} "--track-fds=yes") set(VALGRIND_CMD ${VALGRIND_CMD} "--num-callers=128") set(VALGRIND_CMD ${VALGRIND_CMD} "--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions_generic") set(VALGRIND_CMD ${VALGRIND_CMD} "--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions_glibc_2.21") if (TARGET_ARCH STREQUAL "arm-linux-gnueabihf") set(VALGRIND_CMD ${VALGRIND_CMD} "--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions_armhf") endif() else(VALGRIND_EXECUTABLE) message("Not enabling memcheck as valgrind is missing on your system") endif(VALGRIND_EXECUTABLE) endif(ENABLE_MEMCHECK_OPTION) if(CMAKE_CROSSCOMPILING) set(SYSTEM_SUPPORTS_O_TMPFILE 0) else() try_run(SYSTEM_SUPPORTS_O_TMPFILE SYSTEM_HEADERS_SUPPORT_O_TMPFILE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/src/mir/mir_test_tmpfile.cpp ) endif() function (list_to_string LIST_VAR PREFIX STR_VAR) foreach (value ${LIST_VAR}) set(tmp_str "${tmp_str} ${PREFIX} ${value}") endforeach() set(${STR_VAR} "${tmp_str}" PARENT_SCOPE) endfunction() function (mir_discover_tests_internal EXECUTABLE TEST_ENV_OPTIONS DETECT_FD_LEAKS ) # Set vars set(test_cmd_no_memcheck "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${EXECUTABLE}") set(test_cmd "${test_cmd_no_memcheck}") set(test_env ${ARGN} ${TEST_ENV_OPTIONS}) if (TEST_ENV_OPTIONS) set(test_name ${EXECUTABLE}---${TEST_ENV_OPTIONS}---) else() set(test_name ${EXECUTABLE}) endif() set(test_no_memcheck_filter) set(test_exclusion_filter) if(ENABLE_MEMCHECK_OPTION) set(test_cmd ${VALGRIND_CMD} ${test_cmd_no_memcheck}) set(test_no_memcheck_filter "*DeathTest.*:ClientLatency.*") endif() if(cmake_build_type_lower MATCHES "threadsanitizer") if (NOT CMAKE_COMPILER_IS_GNUCXX) find_program(LLVM_SYMBOLIZER llvm-symbolizer-3.6) if (LLVM_SYMBOLIZER) set(TSAN_EXTRA_OPTIONS "external_symbolizer_path=${LLVM_SYMBOLIZER}") endif() endif() # Space after ${TSAN_EXTRA_OPTIONS} works around bug in TSAN env. variable parsing list(APPEND test_env "TSAN_OPTIONS=\"suppressions=${CMAKE_SOURCE_DIR}/tools/tsan-suppressions second_deadlock_stack=1 halt_on_error=1 history_size=7 die_after_fork=0 ${TSAN_EXTRA_OPTIONS} \"") # TSan does not support starting threads after fork set(test_exclusion_filter "${test_exclusion_filter}:ThreadedDispatcherSignalTest.keeps_dispatching_after_signal_interruption") # tsan "eats" SIGQUIT, so ignore two more tests that involve it set(test_exclusion_filter "${test_exclusion_filter}:ServerSignal/AbortDeathTest.cleanup_handler_is_called_for/0") set(test_exclusion_filter "${test_exclusion_filter}:ServerShutdown/OnSignalDeathTest.removes_endpoint/0") endif() if(cmake_build_type_lower MATCHES "ubsanitizer") list(APPEND test_env "UBSAN_OPTIONS=\"suppressions=${CMAKE_SOURCE_DIR}/tools/ubsan-suppressions print_stacktrace=1 die_after_fork=0\"") set(test_exclusion_filter "${test_exclusion_filter}:*DeathTest*") endif() if(SYSTEM_SUPPORTS_O_TMPFILE EQUAL 1) set(test_exclusion_filter "${test_exclusion_filter}:AnonymousShmFile.*:MesaBufferAllocatorTest.software_buffers_dont_bypass:MesaBufferAllocatorTest.creates_software_rendering_buffer") endif() # Final commands set(test_cmd "${test_cmd}" "--gtest_filter=-${test_no_memcheck_filter}:${test_exclusion_filter}") set(test_cmd_no_memcheck "${test_cmd_no_memcheck}" "--gtest_filter=${test_no_memcheck_filter}:-${test_exclusion_filter}") if(DETECT_FD_LEAKS) set(test_cmd ${CMAKE_SOURCE_DIR}/tools/detect_fd_leaks.bash ${test_cmd}) endif() # Normal add_test(${test_name} ${test_cmd}) set_property(TEST ${test_name} PROPERTY ENVIRONMENT ${test_env}) if (test_no_memcheck_filter) add_test(${test_name}_no_memcheck ${test_cmd_no_memcheck}) set_property(TEST ${test_name}_no_memcheck PROPERTY ENVIRONMENT ${test_env}) endif() # ptest list_to_string("${test_env}" "--env" discover_env) list_to_string("${test_cmd}" "" discover_cmd) list_to_string("${test_cmd_no_memcheck}" "" discover_cmd_no_memcheck) file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh "sh ${CMAKE_SOURCE_DIR}/tools/discover_gtests.sh ${discover_env} -- ${discover_cmd}\n") if (test_no_memcheck_filter) file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh "sh ${CMAKE_SOURCE_DIR}/tools/discover_gtests.sh ${discover_env} -- ${discover_cmd_no_memcheck}\n") endif() endfunction () function (mir_discover_tests EXECUTABLE) mir_discover_tests_internal(${EXECUTABLE} "" FALSE ${ARGN}) endfunction() function (mir_discover_tests_with_fd_leak_detection EXECUTABLE) mir_discover_tests_internal(${EXECUTABLE} "" TRUE ${ARGN}) endfunction() function (mir_discover_tests_with_fd_leak_detection_and_env EXECUTABLE TEST_ENV_OPTION) mir_discover_tests_internal(${EXECUTABLE} ${TEST_ENV_OPTION} TRUE ${ARGN}) endfunction() function (mir_add_memcheck_test) if (ENABLE_MEMCHECK_OPTION) add_custom_target(memcheck_test ALL) mir_add_test(NAME "memcheck-test" COMMAND ${CMAKE_BINARY_DIR}/mir_gtest/fail_on_success.sh ${VALGRIND_CMD} ${CMAKE_BINARY_DIR}/mir_gtest/mir_test_memory_error) add_dependencies(memcheck_test mir_test_memory_error) endif() endfunction() function (mir_add_detect_fd_leaks_test) if (ENABLE_MEMCHECK_OPTION) add_custom_target(detect_fd_leaks_catches_fd_leak_test ALL) mir_add_test(NAME "detect-fd-leaks-catches-fd-leak" COMMAND ${CMAKE_BINARY_DIR}/mir_gtest/fail_on_success.sh ${CMAKE_SOURCE_DIR}/tools/detect_fd_leaks.bash ${VALGRIND_CMD} ${CMAKE_BINARY_DIR}/mir_gtest/mir_test_fd_leak) add_dependencies(detect_fd_leaks_catches_fd_leak_test mir_test_fd_leak) add_custom_target(detect_fd_leaks_propagates_test_failure_test ALL) mir_add_test(NAME "detect-fd-leaks-propagates-test-failure" COMMAND ${CMAKE_BINARY_DIR}/mir_gtest/fail_on_success.sh ${CMAKE_SOURCE_DIR}/tools/detect_fd_leaks.bash ${VALGRIND_CMD} ${CMAKE_BINARY_DIR}/mir_gtest/mir_test_memory_error) add_dependencies(detect_fd_leaks_propagates_test_failure_test mir_test_memory_error) endif() endfunction() function (mir_precompiled_header TARGET HEADER) if (MIR_USE_PRECOMPILED_HEADERS) get_filename_component(HEADER_NAME ${HEADER} NAME) get_property(TARGET_INCLUDE_DIRECTORIES TARGET ${TARGET} PROPERTY INCLUDE_DIRECTORIES) set(TARGET_COMPILE_DEFINITIONS "$") set(TARGET_COMPILE_DEFINITIONS "$<$:-D$\n>") foreach(dir ${TARGET_INCLUDE_DIRECTORIES}) if (${dir} MATCHES "usr/include") set(TARGET_INCLUDE_DIRECTORIES_STRING "${TARGET_INCLUDE_DIRECTORIES_STRING} -isystem ${dir}") else() set(TARGET_INCLUDE_DIRECTORIES_STRING "${TARGET_INCLUDE_DIRECTORIES_STRING} -I${dir}") endif() endforeach() # So. # ${CMAKE_CXX_FLAGS} *only* includes the base flags, not any extra flags set by the build target. # The build targets set flags which affect the precompiled headers - -g verses no debug for the Debug build, # -g -O2 -NDEBUG versus no specified optimisation for RelWithDebugInfo, etc. # # The various CMAKE_CXX_FLAGS_DEBUG, CMAKE_CXX_FLAGS_RELWITHDEBUGINFO, etc variables have the extra flags # to add. CMAKE_BUILD_TYPE contains "Debug" or "RelWithDebugInfo" or "Release" etc, however, so first # we need to uppercase CMAKE_BUILD_TYPE, then dereference ${CMAKE_CXX_FLAGS_${UC_BUILD_TYPE}}. # # I'm unaware of a less roundabout method of getting the *actual* build flags for a target. string(TOUPPER "${CMAKE_BUILD_TYPE}" UC_BUILD_TYPE) # Lllloook at you, haaacker. A pa-pa-pathetic creature of meat and bone. # # It appears that we can *only* get the COMPILE_DEFINITIONS as a generator expression. # This wouldn't be so bad if http://www.kwwidgets.org/Bug/view.php?id=14353#c33712 didn't mean # that you can't use generator expressions in custom commands. # # So! # What we *can* do is generate a file with the contents of the generator expressions, # then use gcc's @file mechanism... set(FLAGS_FILE "${CMAKE_CURRENT_BINARY_DIR}/${HEADER_NAME}.compileflags") file( GENERATE OUTPUT "${FLAGS_FILE}" CONTENT " ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${UC_BUILD_TYPE}} ${TARGET_INCLUDE_DIRECTORIES_STRING} ${TARGET_COMPILE_DEFINITIONS}" ) # HA HA! # # Of course, that has unescaped all the escaped \"s we have in the compile definitions. # gcc treats the contents of @file exactly as if it came from the command line, so we need to # re-escape them. add_custom_command( OUTPUT ${FLAGS_FILE}.processed DEPENDS ${FLAGS_FILE} # ESCAPE ALL THE THINGS! COMMAND sh -c "sed s_\\\"_\\\\\\\\\\\"_g ${FLAGS_FILE} > ${FLAGS_FILE}.processed" VERBATIM ) add_custom_command( OUTPUT ${TARGET}_precompiled.hpp.gch DEPENDS ${HEADER} ${FLAGS_FILE}.processed COMMAND ${CMAKE_CXX_COMPILER} @${FLAGS_FILE}.processed -x c++-header -c ${HEADER} -o ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_precompiled.hpp.gch ) set_property(TARGET ${TARGET} APPEND_STRING PROPERTY COMPILE_FLAGS " -include ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_precompiled.hpp -Winvalid-pch ") add_custom_target(${TARGET}_pch DEPENDS ${TARGET}_precompiled.hpp.gch) add_dependencies(${TARGET} ${TARGET}_pch) endif() endfunction() function (mir_add_wrapped_executable TARGET) set(REAL_EXECUTABLE ${TARGET}.bin) list(GET ARGN 0 modifier) if ("${modifier}" STREQUAL "NOINSTALL") list(REMOVE_AT ARGN 0) else() install(PROGRAMS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${REAL_EXECUTABLE} DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME ${TARGET} ) endif() add_executable(${TARGET} ${ARGN}) set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME ${REAL_EXECUTABLE} SKIP_BUILD_RPATH TRUE ) add_custom_target(${TARGET}-wrapped ln -fs wrapper ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET} ) add_dependencies(${TARGET} ${TARGET}-wrapped) endfunction() function (mir_add_test) # Add test normally add_test(${ARGN}) # Add to to discovery for parallel test running set(one_value_args "NAME" WORKING_DIRECTORY) set(multi_value_args "COMMAND") cmake_parse_arguments(MAT "" "${one_value_args}" "${multi_value_args}" ${ARGN}) foreach (cmd ${MAT_COMMAND}) set(cmdstr "${cmdstr} \\\"${cmd}\\\"") endforeach() file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh "echo \"add_test(${MAT_NAME} ${cmdstr})\"\n") if (MAT_WORKING_DIRECTORY) file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh "echo \"set_tests_properties(${MAT_NAME} PROPERTIES WORKING_DIRECTORY \\\"${MAT_WORKING_DIRECTORY}\\\")\"\n") endif() endfunction() function (mir_check_no_unreleased_symbols TARGET DEPENDENT_TARGET) set(TARGET_NAME "Checking-${TARGET}-for-unreleased-symbols") add_custom_target( ${TARGET_NAME} # Some sort of documentation for this monstrosity: # # Objdump format is: # # $ADDRESS $FLAGS $SECTION $SIZE $SYMVER $NAME # # Cut first five lines which don't contain any symbol information # Cut any lines for undefined symbols # Cut adress and flags which are fixed width # Convert tabs to spaces # Whitespace between fields is collapsed to one character # Extract the symbol version (3rd field) # Check for unreleased symbols - grep will set exit code to 0 if any are found # finally invert the exit code COMMAND /bin/sh -c "objdump -T $ | tail -n+5 | grep -v 'UND' | cut -c 26- | sed $'s/\t/ /g' | tr -s ' ' | cut -d ' ' -f 3 | grep -q unreleased ; test $? -gt 0" VERBATIM ) add_dependencies(${DEPENDENT_TARGET} ${TARGET_NAME}) endfunction() function (mir_add_library_with_symbols TARGET TYPE SYMBOLS_FILE) # Bask in the majesty of CMake! # # You can't just depend on an arbitary file. Oh, no! # # Instead, we add a custom command to generate an empty C++ source # file, depending on the symbols file, and then add that empty C++ # source to the library. set(HACK_OUTPUT ${TARGET}_abysmal_hack.cpp) add_custom_command(OUTPUT ${HACK_OUTPUT} COMMAND touch ${HACK_OUTPUT} DEPENDS ${SYMBOLS_FILE} ) add_library(${TARGET} ${TYPE} ${HACK_OUTPUT} ${ARGN}) endfunction() ./cmake/src/0000755000015600001650000000000012676616125012760 5ustar jenkinsjenkins./cmake/src/mir/0000755000015600001650000000000012676616125013547 5ustar jenkinsjenkins./cmake/src/mir/fail_on_success.sh0000755000015600001650000000007512676616125017247 0ustar jenkinsjenkins#!/bin/sh $@ if [ $? -eq 0 ] ; then exit 1; fi exit 0; ./cmake/src/mir/mir_test_fd_leak.cpp0000644000015600001650000000010412676616125017541 0ustar jenkinsjenkins#include int main() { open("/dev/zero", O_RDONLY); } ./cmake/src/mir/mir_test_memory_error.cpp0000644000015600001650000000015512676616125020703 0ustar jenkinsjenkins#include int main() { auto x = new int{5}; delete x; std::cout << *x << std::endl; } ./cmake/src/mir/CMakeLists.txt0000644000015600001650000000111212676616125016302 0ustar jenkinsjenkinsadd_executable( mir_test_memory_error EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/mir_test_memory_error.cpp) set_target_properties( mir_test_memory_error PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/mir_gtest ) add_executable( mir_test_fd_leak EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/mir_test_fd_leak.cpp) set_target_properties( mir_test_fd_leak PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/mir_gtest ) file(INSTALL ${CMAKE_CURRENT_SOURCE_DIR}/fail_on_success.sh DESTINATION ${CMAKE_BINARY_DIR}/mir_gtest USE_SOURCE_PERMISSIONS ) ./cmake/src/mir/mir_test_tmpfile.cpp0000644000015600001650000000035612676616125017625 0ustar jenkinsjenkins#include #include #include #include int main() { int ret = open("/dev/shm", O_TMPFILE | O_RDWR | O_EXCL, S_IRWXU); if (ret == -1) return 1; close(ret); return 0; } ./cmake/src/CMakeLists.txt0000644000015600001650000000002712676616125015517 0ustar jenkinsjenkinsadd_subdirectory (mir) ./cmake/EnableCoverageReport.cmake0000644000015600001650000001520212676616125017231 0ustar jenkinsjenkins# - Creates a special coverage build type and target on GCC. # # Defines a function ENABLE_COVERAGE_REPORT which generates the coverage target # for selected targets. Optional arguments to this function are used to filter # unwanted results using globbing expressions. Moreover targets with tests for # the source code can be specified to trigger regenerating the report if the # test has changed # # ENABLE_COVERAGE_REPORT(TARGETS target... [FILTER filter...] [TESTS test targets...]) # # To generate a coverage report first build the project with # CMAKE_BUILD_TYPE=coverage, then call make test and afterwards make coverage. # # The coverage report is based on gcov. Depending on the availability of lcov # a HTML report will be generated and/or an XML report of gcovr is found. # The generated coverage target executes all found solutions. Special targets # exist to create e.g. only the xml report: coverage-xml. # # Copyright (C) 2010 by Johannes Wienke # # This program is free software; you can redistribute it # and/or modify it under the terms of the GNU General # Public License as published by the Free Software Foundation; # either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # INCLUDE(ParseArguments) FIND_PACKAGE(Lcov) FIND_PACKAGE(gcovr) FUNCTION(ENABLE_COVERAGE_REPORT) # argument parsing PARSE_ARGUMENTS(ARG "FILTER;TARGETS;TESTS" "" ${ARGN}) SET(COVERAGE_RAW_FILE "${CMAKE_BINARY_DIR}/coverage.raw.info") SET(COVERAGE_FILTERED_FILE "${CMAKE_BINARY_DIR}/coverage.info") SET(COVERAGE_REPORT_DIR "${CMAKE_BINARY_DIR}/coveragereport") SET(COVERAGE_XML_FILE "${CMAKE_BINARY_DIR}/coverage.xml") SET(COVERAGE_XML_COMMAND_FILE "${CMAKE_BINARY_DIR}/coverage-xml.cmake") # decide if there is any tool to create coverage data SET(TOOL_FOUND FALSE) IF(LCOV_FOUND OR GCOVR_FOUND) SET(TOOL_FOUND TRUE) ENDIF() IF(NOT TOOL_FOUND) MESSAGE(STATUS "Cannot enable coverage targets because neither lcov nor gcovr are found.") ENDIF() STRING(TOLOWER "${CMAKE_BUILD_TYPE}" COVERAGE_BUILD_TYPE) IF(CMAKE_COMPILER_IS_GNUCXX AND TOOL_FOUND AND "${COVERAGE_BUILD_TYPE}" MATCHES "coverage") MESSAGE(STATUS "Coverage support enabled for targets: ${ARG_TARGETS}") # create coverage build type SET(CMAKE_CXX_FLAGS_COVERAGE ${CMAKE_CXX_FLAGS_DEBUG} PARENT_SCOPE) SET(CMAKE_C_FLAGS_COVERAGE ${CMAKE_C_FLAGS_DEBUG} PARENT_SCOPE) SET(CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES} coverage PARENT_SCOPE) # instrument targets SET_TARGET_PROPERTIES(${ARG_TARGETS} PROPERTIES COMPILE_FLAGS --coverage LINK_FLAGS --coverage) # html report IF (LCOV_FOUND) MESSAGE(STATUS "Enabling HTML coverage report") # set up coverage target ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_RAW_FILE} COMMAND ${LCOV_EXECUTABLE} -c -d ${CMAKE_BINARY_DIR} -o ${COVERAGE_RAW_FILE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMENT "Collecting coverage data" DEPENDS ${ARG_TARGETS} ${ARG_TESTS} VERBATIM) # filter unwanted stuff LIST(LENGTH ARG_FILTER FILTER_LENGTH) IF(${FILTER_LENGTH} GREATER 0) SET(FILTER COMMAND ${LCOV_EXECUTABLE}) FOREACH(F ${ARG_FILTER}) SET(FILTER ${FILTER} -r ${COVERAGE_FILTERED_FILE} ${F}) ENDFOREACH() SET(FILTER ${FILTER} -o ${COVERAGE_FILTERED_FILE}) ELSE() SET(FILTER "") ENDIF() ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_FILTERED_FILE} COMMAND ${LCOV_EXECUTABLE} -e ${COVERAGE_RAW_FILE} "${CMAKE_SOURCE_DIR}*" -o ${COVERAGE_FILTERED_FILE} ${FILTER} DEPENDS ${COVERAGE_RAW_FILE} COMMENT "Filtering recorded coverage data for project-relevant entries" VERBATIM) ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_REPORT_DIR} COMMAND ${CMAKE_COMMAND} -E make_directory ${COVERAGE_REPORT_DIR} COMMAND ${GENHTML_EXECUTABLE} --legend --show-details -t "${PROJECT_NAME} test coverage" -o ${COVERAGE_REPORT_DIR} ${COVERAGE_FILTERED_FILE} DEPENDS ${COVERAGE_FILTERED_FILE} COMMENT "Generating HTML coverage report in ${COVERAGE_REPORT_DIR}" VERBATIM) ADD_CUSTOM_TARGET(coverage-html DEPENDS ${COVERAGE_REPORT_DIR}) ENDIF() # xml coverage report IF(GCOVR_FOUND) MESSAGE(STATUS "Enabling XML coverage report") # gcovr cannot write directly to a file so the execution needs to # be wrapped in a cmake file that generates the file output FILE(WRITE ${COVERAGE_XML_COMMAND_FILE} "SET(ENV{LANG} en)\n") FILE(APPEND ${COVERAGE_XML_COMMAND_FILE} "EXECUTE_PROCESS(COMMAND \"${GCOVR_EXECUTABLE}\" --exclude=3rd_party.* --exclude=tests.* --exclude=obj-.* --exclude=cmake.* --exclude=include.mir_test.* --exclude=include.mir_test_doubles.* --exclude=include.mir_test_framework.* -x -r \"${CMAKE_BINARY_DIR}\" OUTPUT_FILE \"${COVERAGE_XML_FILE}\" WORKING_DIRECTORY \"${CMAKE_BINARY_DIR}\")\n") ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_XML_FILE} COMMAND ${CMAKE_COMMAND} ARGS -P ${COVERAGE_XML_COMMAND_FILE} COMMENT "Generating coverage XML report" VERBATIM) ADD_CUSTOM_TARGET(coverage-xml DEPENDS ${COVERAGE_XML_FILE}) ENDIF() # provide a global coverage target executing both steps if available SET(GLOBAL_DEPENDS "") IF(LCOV_FOUND) LIST(APPEND GLOBAL_DEPENDS ${COVERAGE_REPORT_DIR}) ENDIF() IF(GCOVR_FOUND) LIST(APPEND GLOBAL_DEPENDS ${COVERAGE_XML_FILE}) ENDIF() IF(LCOV_FOUND OR GCOVR_FOUND) ADD_CUSTOM_TARGET(coverage DEPENDS ${GLOBAL_DEPENDS}) ENDIF() ENDIF() ENDFUNCTION() ./cmake/Findgcovr.cmake0000644000015600001650000000170212676616125015114 0ustar jenkinsjenkins# - Find gcovr scrip # Will define: # # GCOVR_EXECUTABLE - the gcovr script # # Uses: # # GCOVR_ROOT - root to search for the script # # Copyright (C) 2011 by Johannes Wienke # # This program is free software; you can redistribute it # and/or modify it under the terms of the GNU General # Public License as published by the Free Software Foundation; # either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # INCLUDE(FindPackageHandleStandardArgs) FIND_PROGRAM(GCOVR_EXECUTABLE gcovr HINTS ${GCOVR_ROOT} "${GCOVR_ROOT}/bin") FIND_PACKAGE_HANDLE_STANDARD_ARGS(gcovr DEFAULT_MSG GCOVR_EXECUTABLE) # only visible in advanced view MARK_AS_ADVANCED(GCOVR_EXECUTABLE) ./cmake/FindGtestGmock.cmake0000644000015600001650000000532212676616157016052 0ustar jenkinsjenkinsinclude(ExternalProject) include(FindPackageHandleStandardArgs) #gtest set(GTEST_INSTALL_DIR /usr/src/gmock/gtest/include) find_path(GTEST_INCLUDE_DIR gtest/gtest.h HINTS ${GTEST_INSTALL_DIR}) #gmock find_path(GMOCK_INSTALL_DIR gmock/CMakeLists.txt HINTS /usr/src) if(${GMOCK_INSTALL_DIR} STREQUAL "GMOCK_INSTALL_DIR-NOTFOUND") message(FATAL_ERROR "google-mock package not found") endif() set(GMOCK_INSTALL_DIR ${GMOCK_INSTALL_DIR}/gmock) find_path(GMOCK_INCLUDE_DIR gmock/gmock.h) set(GMOCK_PREFIX gmock) set(GMOCK_BINARY_DIR ${CMAKE_BINARY_DIR}/${GMOCK_PREFIX}/libs) set(GTEST_BINARY_DIR ${GMOCK_BINARY_DIR}/gtest) set(GTEST_CXX_FLAGS "-fPIC -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64") if (cmake_build_type_lower MATCHES "threadsanitizer") set(GTEST_CXX_FLAGS "${GTEST_CXX_FLAGS} -fsanitize=thread") elseif (cmake_build_type_lower MATCHES "ubsanitizer") set(GTEST_CXX_FLAGS "${GTEST_CXX_FLAGS} -fsanitize=undefined") endif() set(GTEST_CMAKE_ARGS "-DCMAKE_CXX_FLAGS=${GTEST_CXX_FLAGS}") list(APPEND GTEST_CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}) list(APPEND GTEST_CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}) if (cmake_build_type_lower MATCHES "threadsanitizer") #Skip compiler check, since if GCC is the compiler, we need to link against -ltsan #explicitly; specifying additional linker flags doesn't seem possible for external projects list(APPEND GTEST_CMAKE_ARGS -DCMAKE_CXX_COMPILER_WORKS=1) endif() if (${CMAKE_CROSSCOMPILING}) if(DEFINED MIR_NDK_PATH) list(APPEND GTEST_CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_MODULE_PATH}/LinuxCrossCompile.cmake) else() list(APPEND GTEST_CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}) list(APPEND GTEST_CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}) endif() endif() ExternalProject_Add( GMock #where to build in source tree PREFIX ${GMOCK_PREFIX} #where the source is external to the project SOURCE_DIR ${GMOCK_INSTALL_DIR} #forward the compilers to the subproject so cross-arch builds work CMAKE_ARGS ${GTEST_CMAKE_ARGS} BINARY_DIR ${GMOCK_BINARY_DIR} #we don't need to install, so skip INSTALL_COMMAND "" ) set(GMOCK_LIBRARY ${GMOCK_BINARY_DIR}/libgmock.a) set(GMOCK_MAIN_LIBRARY ${GMOCK_BINARY_DIR}/libgmock_main.a) set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY}) set(GTEST_LIBRARY ${GTEST_BINARY_DIR}/libgtest.a) set(GTEST_MAIN_LIBRARY ${GTEST_BINARY_DIR}/libgtest_main.a) set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) set(GTEST_ALL_LIBRARIES ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES}) find_package_handle_standard_args(GTest DEFAULT_MSG GMOCK_INCLUDE_DIR GTEST_INCLUDE_DIR) ./cmake/FindGLog.cmake0000644000015600001650000000061412676616125014625 0ustar jenkinsjenkinsif (GLog_INCLUDE_DIR) # Already in cache, be silent set(GLog_FIND_QUIETLY TRUE) endif () find_path(GLog_INCLUDE_DIR glog/logging.h) find_library(GLog_LIBRARY libglog.so HINTS /usr/lib/arm-linux-gnueabihf/) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GLog DEFAULT_MSG GLog_LIBRARY GLog_INCLUDE_DIR) mark_as_advanced(GLog_LIBRARY GLog_INCLUDE_DIR) ./cmake/FindEGL.cmake0000644000015600001650000000145312676616125014406 0ustar jenkinsjenkins# - Try to find EGL # Once done this will define # EGL_FOUND - System has EGL # EGL_INCLUDE_DIRS - The EGL include directories # EGL_LIBRARIES - The libraries needed to use EGL find_package(PkgConfig) pkg_check_modules(PC_EGL QUIET egl) find_path(EGL_INCLUDE_DIR EGL/egl.h HINTS ${PC_EGL_INCLUDEDIR} ${PC_EGL_INCLUDE_DIRS}) find_library(EGL_LIBRARY EGL HINTS ${PC_EGL_LIBDIR} ${PC_EGL_LIBRARY_DIRS}) set(EGL_LIBRARIES ${EGL_LIBRARY}) set(EGL_INCLUDE_DIRS ${EGL_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set EGL_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(EGL DEFAULT_MSG EGL_LIBRARY EGL_INCLUDE_DIR) mark_as_advanced(EGL_INCLUDE_DIR EGL_LIBRARY) ./cmake/LinuxCrossCompile.cmake0000644000015600001650000000351112676616125016615 0ustar jenkinsjenkinsset(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_VERSION 1) set(MIR_NDK_PATH $ENV{MIR_NDK_PATH} CACHE STRING "path of mir android bundle") if (NOT DEFINED MIR_TARGET_MACHINE) set(MIR_TARGET_MACHINE $ENV{MIR_TARGET_MACHINE} CACHE STRING "target machine") endif() if (NOT DEFINED MIR_GCC_VARIANT) set(MIR_GCC_VARIANT $ENV{MIR_GCC_VARIANT} CACHE STRING "gcc variant required") endif() set(CMAKE_C_COMPILER /usr/bin/${MIR_TARGET_MACHINE}-gcc${MIR_GCC_VARIANT}) set(CMAKE_CXX_COMPILER /usr/bin/${MIR_TARGET_MACHINE}-g++${MIR_GCC_VARIANT}) # where to look to find dependencies in the target environment set(CMAKE_FIND_ROOT_PATH "${MIR_NDK_PATH}") #treat the chroot's includes as system includes include_directories(SYSTEM "${MIR_NDK_PATH}/usr/include" "${MIR_NDK_PATH}/usr/include/${MIR_TARGET_MACHINE}") list(APPEND CMAKE_SYSTEM_INCLUDE_PATH "${MIR_NDK_PATH}/usr/include" "${MIR_NDK_PATH}/usr/include/${MIR_TARGET_MACHINE}" ) # Add the chroot libraries as system libraries list(APPEND CMAKE_SYSTEM_LIBRARY_PATH "${MIR_NDK_PATH}/lib" "${MIR_NDK_PATH}/lib/${MIR_TARGET_MACHINE}" "${MIR_NDK_PATH}/usr/lib" "${MIR_NDK_PATH}/usr/lib/${MIR_TARGET_MACHINE}" ) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) set(CMAKE_EXECUTABLE_RUNTIME_C_FLAG "-Wl,-rpath-link,") set(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG "-Wl,-rpath-link,") set(CMAKE_INSTALL_RPATH "${MIR_NDK_PATH}/lib:${MIR_NDK_PATH}/lib/${MIR_TARGET_MACHINE}:${MIR_NDK_PATH}/usr/lib:${MIR_NDK_PATH}/usr/lib/${MIR_TARGET_MACHINE}") set(ENV{PKG_CONFIG_PATH} "${MIR_NDK_PATH}/usr/lib/pkgconfig:${MIR_NDK_PATH}/usr/lib/${MIR_TARGET_MACHINE}/pkgconfig") set(ENV{PKG_CONFIG_SYSROOT_DIR} "${MIR_NDK_PATH}") #use only the cross compile system set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) ./cmake/ABICheck.cmake0000644000015600001650000001130712676616157014533 0ustar jenkinsjenkinscmake_minimum_required (VERSION 2.6) find_program(ABI_COMPLIANCE_CHECKER abi-compliance-checker) if (NOT ABI_COMPLIANCE_CHECKER) message(WARNING "no ABI checks possible: abi-compliance-checker was not found") return() endif() set(ENABLE_ABI_CHECK_TEST $ENV{MIR_ENABLE_ABI_CHECK_TEST}) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpmachine OUTPUT_VARIABLE ABI_CHECK_TARGET_MACH OUTPUT_STRIP_TRAILING_WHITESPACE) set(ABI_DUMPS_DIR "${CMAKE_BINARY_DIR}/abi_dumps/${ABI_CHECK_TARGET_MACH}") # Given a list of key value pairs such as "key1 value1 key2 value2...keyN valueN" # extract the value corresponding to the given key function(get_value_for_key a_list key value) list(FIND a_list ${key} idx) if (idx GREATER -1) math(EXPR idx "${idx} + 1") list(GET a_list ${idx} tmp_value) set(${value} "${tmp_value}" PARENT_SCOPE) endif() endfunction() # Makes a one-entry per line list of all include paths used # to compile the given library target function(get_includes libname output) get_property(lib_includes TARGET ${libname} PROPERTY INCLUDE_DIRECTORIES) list(REMOVE_DUPLICATES lib_includes) string(REPLACE ";" "\n " tmp_out "${lib_includes}") set(${output} "${tmp_out}" PARENT_SCOPE) endfunction() # Creates the XML descriptor file that describes the given library target # suitable for abi-compliance-checker function(make_lib_descriptor name) set(libname "mir${name}") # Optional argument LIBRARY_HEADER - use the given header to describe # the binary library instead of assuming its described by include/ get_value_for_key("${ARGN}" "LIBRARY_HEADER" library_header) if ("${library_header}" STREQUAL "") set(LIB_DESC_HEADERS "${CMAKE_SOURCE_DIR}/include/${name}\n ${private_headers}") else() set(LIB_DESC_HEADERS ${library_header}) endif() # FIXME: Property "LOCATION" is now deprecated if (NOT ${CMAKE_MAJOR_VERSION} LESS 3) cmake_policy(SET CMP0026 OLD) endif() get_property(LIB_DESC_LIBS TARGET ${libname} PROPERTY LOCATION) get_includes(${libname} LIB_DESC_INCLUDE_PATHS) set(LIB_DESC_GCC_OPTS "${CMAKE_CXX_FLAGS}") # Optional EXCLUDE_HEADERS - a list # while attempting an abi dump get_value_for_key("${ARGN}" "EXCLUDE_HEADERS" LIB_DESC_SKIP_HEADERS) configure_file(${CMAKE_SOURCE_DIR}/tools/lib_descriptor.xml.skel ${libname}_desc.xml) endfunction() #These headers are not part of the libmirplatform ABI set(mirplatform-exclude-headers "${CMAKE_SOURCE_DIR}/include/platform/mir/input") make_lib_descriptor(client) make_lib_descriptor(server) make_lib_descriptor(common) make_lib_descriptor(cookie) make_lib_descriptor(platform EXCLUDE_HEADERS ${mirplatform-exclude-headers}) if(MIR_BUILD_PLATFORM_MESA_KMS) make_lib_descriptor(clientplatformmesa LIBRARY_HEADER ${CMAKE_SOURCE_DIR}/src/include/client/mir/client_platform_factory.h) make_lib_descriptor(platformgraphicsmesakms LIBRARY_HEADER ${CMAKE_SOURCE_DIR}/include/platform/mir/graphics/platform.h) endif() if(MIR_BUILD_PLATFORM_ANDROID) make_lib_descriptor(clientplatformandroid LIBRARY_HEADER ${CMAKE_SOURCE_DIR}/src/include/client/mir/client_platform_factory.h) make_lib_descriptor(platformgraphicsandroid LIBRARY_HEADER ${CMAKE_SOURCE_DIR}/include/platform/mir/graphics/platform.h) endif() make_lib_descriptor(platforminputevdev LIBRARY_HEADER ${CMAKE_SOURCE_DIR}/include/platform/mir/input/) macro(_define_abi_dump_for libname) set(ABI_DUMP_NAME ${ABI_DUMPS_DIR}/${libname}_next.abi.tar.gz) add_custom_command(OUTPUT ${ABI_DUMP_NAME} COMMAND abi-compliance-checker -gcc-path ${CMAKE_CXX_COMPILER} -l ${libname} -v1 next -dump-path ${ABI_DUMP_NAME} -dump-abi ${libname}_desc.xml DEPENDS ${libname} ) add_custom_target(abi-dump-${libname} DEPENDS ${ABI_DUMP_NAME}) endmacro(_define_abi_dump_for) macro(_define_abi_check_for libname) add_custom_target(abi-check-${libname} COMMAND /bin/bash -c '${CMAKE_SOURCE_DIR}/tools/abi_check.sh ${libname} ${ABI_DUMPS_DIR} ${CMAKE_SOURCE_DIR}' DEPENDS abi-dump-${libname} ) endmacro(_define_abi_check_for) set(the_libs mirserver mirclient mircommon mirplatform mircookie mirplatforminputevdev) if(MIR_BUILD_PLATFORM_MESA_KMS) set(the_libs ${the_libs} mirclientplatformmesa mirplatformgraphicsmesakms) endif() if(MIR_BUILD_PLATFORM_ANDROID) set(the_libs ${the_libs} mirclientplatformandroid mirplatformgraphicsandroid) endif() foreach(libname ${the_libs}) _define_abi_dump_for(${libname}) _define_abi_check_for(${libname}) list(APPEND abi-dump-list abi-dump-${libname}) list(APPEND abi-check-list abi-check-${libname}) endforeach(libname) add_custom_target(abi-dump DEPENDS ${abi-dump-list}) add_custom_target(abi-check DEPENDS ${abi-check-list}) if (MIR_ENABLE_TESTS AND ENABLE_ABI_CHECK_TEST) add_test(abi-compliance-check make abi-check) endif() ./cmake/FindGLESv2.cmake0000644000015600001650000000157312676616125015004 0ustar jenkinsjenkins# - Try to find GLESv2 # Once done this will define # GLESv2_FOUND - System has GLESv2 # GLESv2_INCLUDE_DIRS - The GLESv2 include directories # GLESv2_LIBRARIES - The libraries needed to use GLESv2 find_package(PkgConfig) pkg_check_modules(PC_GLESv2 QUIET glesv2) find_path(GLESv2_INCLUDE_DIR GLES2/gl2.h HINTS ${PC_GLESv2_INCLUDEDIR} ${PC_GLESv2_INCLUDE_DIRS}) find_library(GLESv2_LIBRARY GLESv2 HINTS ${PC_GLESv2_LIBDIR} ${PC_GLESv2_LIBRARY_DIRS}) set(GLESv2_LIBRARIES ${GLESv2_LIBRARY}) set(GLESv2_INCLUDE_DIRS ${GLESv2_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set GLESv2_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(GLESv2 DEFAULT_MSG GLESv2_LIBRARY GLESv2_INCLUDE_DIR) mark_as_advanced(GLESv2_INCLUDE_DIR GLESv2_LIBRARY) ./cmake/FindLTTngUST.cmake0000644000015600001650000000244312676616125015363 0ustar jenkinsjenkins# - Try to find LTTng-UST # Once done this will define # LTTNG_UST_FOUND - System has LTTng-UST # LTTNG_UST_INCLUDE_DIRS - The LTTng-UST include directories # LTTNG_UST_LIBRARIES - The libraries needed to use LTTng-UST find_package(PkgConfig) # pkgconfig is currently broken for LTTng-UST and urcu-bp #pkg_check_modules(PC_LTTNG_UST QUIET lttng-ust) #pkg_check_modules(PC_LIBURCU_BP QUIET liburcu-bp) find_path(LTTNG_UST_INCLUDE_DIR lttng/tracepoint.h HINTS ${PC_LTTNG_UST_INCLUDEDIR} ${PC_LTTNG_UST_INCLUDE_DIRS}) find_library(LTTNG_UST_LIBRARY lttng-ust HINTS ${PC_LTTNG_UST_LIBDIR} ${PC_LTTNG_UST_LIBRARY_DIRS}) find_library(LIBURCU_BP_LIBRARY urcu-bp HINTS ${PC_LIBURCU_BP_LIBDIR} ${PC_LIBURCU_BP_LIBRARY_DIRS}) set(LTTNG_UST_LIBRARIES ${LTTNG_UST_LIBRARY} ${LIBURCU_BP_LIBRARY} -ldl) set(LTTNG_UST_INCLUDE_DIRS ${LTTNG_UST_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LTTNG_UST_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(LTTNG_UST DEFAULT_MSG LTTNG_UST_LIBRARY LIBURCU_BP_LIBRARY LTTNG_UST_INCLUDE_DIR) mark_as_advanced(LTTNG_UST_INCLUDE_DIR LTTNG_UST_LIBRARY) ./cmake/FindGLM.cmake0000644000015600001650000000046712676616125014422 0ustar jenkinsjenkins# - Try to find GLM # Once done this will define # GLM_FOUND - System has GLM # GLM_INCLUDE_DIRS - The GLM include directories find_path(GLM_INCLUDE_DIR glm/glm.hpp) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GLM DEFAULT_MSG GLM_INCLUDE_DIR) mark_as_advanced(GLM_INCLUDE_DIR) ./cmake/FindXKBCOMMON.cmake0000644000015600001650000000133612676616125015334 0ustar jenkinsjenkinspkg_check_modules( PC_XKBCOMMON QUIET xkbcommon ) find_path(XKBCOMMON_INCLUDE_DIR xkbcommon/xkbcommon.h HINTS ${PC_XKBCOMMON_INCLUDEDIR} ${PC_XKBCOMMON_INCLUDE_DIRS}) find_library(XKBCOMMON_LIBRARY xkbcommon HINTS ${PC_XKBCOMMON_LIBDIR} ${PC_XKBCOMMON_LIBRARY_DIRS}) set(XKBCOMMON_LIBRARIES ${XKBCOMMON_LIBRARY}) set(XKBCOMMON_INCLUDE_DIRS ${XKBCOMMON_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set XKBCOMMON_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(XKBCOMMON DEFAULT_MSG XKBCOMMON_LIBRARY XKBCOMMON_INCLUDE_DIR) mark_as_advanced(XKBCOMMON_INCLUDE_DIR XKBCOMMON_LIBRARY) ./cmake/ParseArguments.cmake0000644000015600001650000000340612676616125016136 0ustar jenkinsjenkins# Parse arguments passed to a function into several lists separated by # upper-case identifiers and options that do not have an associated list e.g.: # # SET(arguments # hello OPTION3 world # LIST3 foo bar # OPTION2 # LIST1 fuz baz # ) # PARSE_ARGUMENTS(ARG "LIST1;LIST2;LIST3" "OPTION1;OPTION2;OPTION3" ${arguments}) # # results in 7 distinct variables: # * ARG_DEFAULT_ARGS: hello;world # * ARG_LIST1: fuz;baz # * ARG_LIST2: # * ARG_LIST3: foo;bar # * ARG_OPTION1: FALSE # * ARG_OPTION2: TRUE # * ARG_OPTION3: TRUE # # taken from http://www.cmake.org/Wiki/CMakeMacroParseArguments MACRO(PARSE_ARGUMENTS prefix arg_names option_names) SET(DEFAULT_ARGS) FOREACH(arg_name ${arg_names}) SET(${prefix}_${arg_name}) ENDFOREACH(arg_name) FOREACH(option ${option_names}) SET(${prefix}_${option} FALSE) ENDFOREACH(option) SET(current_arg_name DEFAULT_ARGS) SET(current_arg_list) FOREACH(arg ${ARGN}) SET(larg_names ${arg_names}) LIST(FIND larg_names "${arg}" is_arg_name) IF (is_arg_name GREATER -1) SET(${prefix}_${current_arg_name} ${current_arg_list}) SET(current_arg_name ${arg}) SET(current_arg_list) ELSE (is_arg_name GREATER -1) SET(loption_names ${option_names}) LIST(FIND loption_names "${arg}" is_option) IF (is_option GREATER -1) SET(${prefix}_${arg} TRUE) ELSE (is_option GREATER -1) SET(current_arg_list ${current_arg_list} ${arg}) ENDIF (is_option GREATER -1) ENDIF (is_arg_name GREATER -1) ENDFOREACH(arg) SET(${prefix}_${current_arg_name} ${current_arg_list}) ENDMACRO(PARSE_ARGUMENTS) ./cmake/CMakeLists.txt0000644000015600001650000000002712676616125014730 0ustar jenkinsjenkinsadd_subdirectory (src) ./cmake/FindLcov.cmake0000644000015600001650000000172012676616125014677 0ustar jenkinsjenkins# - Find lcov # Will define: # # LCOV_EXECUTABLE - the lcov binary # GENHTML_EXECUTABLE - the genhtml executable # # Copyright (C) 2010 by Johannes Wienke # # This program is free software; you can redistribute it # and/or modify it under the terms of the GNU General # Public License as published by the Free Software Foundation; # either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # INCLUDE(FindPackageHandleStandardArgs) FIND_PROGRAM(LCOV_EXECUTABLE lcov) FIND_PROGRAM(GENHTML_EXECUTABLE genhtml) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lcov DEFAULT_MSG LCOV_EXECUTABLE GENHTML_EXECUTABLE) # only visible in advanced view MARK_AS_ADVANCED(LCOV_EXECUTABLE GENHTML_EXECUTABLE) ./cmake/FindLibHardware.cmake0000644000015600001650000000141412676616157016165 0ustar jenkinsjenkins# Variables defined by this module: #message(${LIBHARDWARE_LIBRARY}) # LIBHARDWARE_FOUND # LIBHARDWARE_LIBRARIES # LIBHARDWARE_INCLUDE_DIRS INCLUDE(FindPackageHandleStandardArgs) find_package( PkgConfig ) pkg_search_module(ANDROID_HEADERS REQUIRED android-headers) set(LIBHARDWARE_INCLUDE_DIRS ${ANDROID_HEADERS_INCLUDE_DIRS}) find_library(LIBHARDWARE_LIBRARY NAMES libhardware.so.2 libhardware.so ) set(LIBHARDWARE_LIBRARIES ${LIBHARDWARE_LIBRARY}) # handle the QUIETLY and REQUIRED arguments and set LIBHARDWARE_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(LIBHARDWARE DEFAULT_MSG LIBHARDWARE_LIBRARY) mark_as_advanced(LIBHARDWARE_INCLUDE_DIR LIBHARDWARE_LIBRARY ) ./cmake/Debian.cmake0000644000015600001650000000310012676616125014347 0ustar jenkinsjenkins# Check if dpkg-buildflags is available and adjust cmake buildflags find_program(DPKG_BUILDFLAGS dpkg-buildflags) if (DPKG_BUILDFLAGS) message(STATUS "dpkg-buildflags available, adjusting compiler flags.") #dpkg-buildflags is available, adjust cmake buildflags now. execute_process( COMMAND ${DPKG_BUILDFLAGS} "--get" "CFLAGS" OUTPUT_VARIABLE DPKG_BUILDFLAGS_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${DPKG_BUILDFLAGS} "--get" "CPPFLAGS" OUTPUT_VARIABLE DPKG_BUILDFLAGS_CPPFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${DPKG_BUILDFLAGS} "--get" "CXXFLAGS" OUTPUT_VARIABLE DPKG_BUILDFLAGS_CXXFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND ${DPKG_BUILDFLAGS} "--get" "LDFLAGS" OUTPUT_VARIABLE DPKG_BUILDFLAGS_LDFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE ) message(STATUS "DPKG_BUILDFLAGS_CFLAGS: " ${DPKG_BUILDFLAGS_CFLAGS}) message(STATUS "DPKG_BUILDFLAGS_CPPFLAGS: " ${DPKG_BUILDFLAGS_CPPFLAGS}) message(STATUS "DPKG_BUILDFLAGS_CXXFLAGS: " ${DPKG_BUILDFLAGS_CXXFLAGS}) message(STATUS "DPKG_BUILDFLAGS_LDFLAGS: " ${DPKG_BUILDFLAGS_LDFLAGS}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DPKG_BUILDFLAGS_CFLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DPKG_BUILDFLAGS_CXXFLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${DPKG_BUILDFLAGS_LDFLAGS}") add_definitions("${DPKG_BUILDFLAGS_CPPFLAGS}") else() message(WARNING "Could not find dpkg-buildflags, not building with packaging setup C/C++/LD-Flags.") endif(DPKG_BUILDFLAGS)./cmake/Doxygen.cmake0000644000015600001650000000207212676616125014611 0ustar jenkinsjenkins# Check if doxygen is present and add 'make doc' target find_package(Doxygen) option( BUILD_DOXYGEN "Build Doxygen documentation as part of the default build" OFF ) if(DOXYGEN_FOUND AND (DOXYGEN_VERSION VERSION_GREATER "1.8")) message(STATUS "doxygen ${DOXYGEN_VERSION} (>= 1.8.0) available - enabling make target doc") EXECUTE_PROCESS(COMMAND "date" "-u" OUTPUT_VARIABLE DATE_TODAY) configure_file(doc/Doxyfile.in ${PROJECT_BINARY_DIR}/Doxyfile @ONLY IMMEDIATE) configure_file(doc/footer.html.in ${PROJECT_BINARY_DIR}/doc/footer.html @ONLY IMMEDIATE) configure_file(doc/extra.css ${PROJECT_BINARY_DIR}/doc/extra.css @ONLY IMMEDIATE) if (BUILD_DOXYGEN) set(ALL "ALL") endif() add_custom_target(doc ${ALL} COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxyfile SOURCES ${PROJECT_BINARY_DIR}/Doxyfile DEPENDS guides) install(DIRECTORY ${CMAKE_BINARY_DIR}/doc/html DESTINATION ${CMAKE_INSTALL_PREFIX}/share/doc/mir-doc/ OPTIONAL) endif() ./tests/0000755000015600001650000000000012676616125012253 5ustar jenkinsjenkins./tests/mir_test/0000755000015600001650000000000012676616160014100 5ustar jenkinsjenkins./tests/mir_test/cross_process_action.cpp0000644000015600001650000000210112676616125021023 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/test/cross_process_action.h" namespace mt = mir::test; void mt::CrossProcessAction::exec(std::function const& f) { start_sync.wait_for_signal_ready(); f(); finish_sync.signal_ready(); } void mt::CrossProcessAction::operator()(std::chrono::milliseconds timeout) { start_sync.signal_ready(); finish_sync.wait_for_signal_ready_for(timeout); } ./tests/mir_test/cross_process_sync.cpp0000644000015600001650000001073712676616125020540 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #include "mir/test/cross_process_sync.h" #include #include #include #include namespace mt = mir::test; namespace { const int read_fd = 0; const int write_fd = 1; struct UnexpectedValueErrorInfoTag {}; typedef boost::error_info errinfo_unexpected_value; } mt::CrossProcessSync::CrossProcessSync() : counter(0) { if (::pipe(fds) < 0) { BOOST_THROW_EXCEPTION(std::system_error(errno, std::system_category(), "Failed to create pipe")); } } mt::CrossProcessSync::CrossProcessSync(const CrossProcessSync& rhs) : counter(rhs.counter) { fds[0] = ::dup(rhs.fds[0]); fds[1] = ::dup(rhs.fds[1]); } mt::CrossProcessSync::~CrossProcessSync() noexcept { ::close(fds[0]); ::close(fds[1]); } mt::CrossProcessSync& mt::CrossProcessSync::operator=(const mt::CrossProcessSync& rhs) { ::close(fds[0]); ::close(fds[1]); fds[0] = ::dup(rhs.fds[0]); fds[1] = ::dup(rhs.fds[1]); counter = rhs.counter; return *this; } void mt::CrossProcessSync::try_signal_ready_for() { try_signal_ready_for(std::chrono::minutes(2)); } void mt::CrossProcessSync::try_signal_ready_for(const std::chrono::milliseconds& duration) { static const short empty_revents = 0; pollfd poll_fd[1] = { { fds[write_fd], POLLOUT, empty_revents } }; int rc = -1; while ((rc = ::poll(poll_fd, 1, duration.count())) <= 0) { if (errno == EINTR) { continue; } if (rc == 0) { BOOST_THROW_EXCEPTION(std::runtime_error("Poll on writefd for pipe timed out")); } else { BOOST_THROW_EXCEPTION(std::system_error(errno, std::system_category(), "Error while polling pipe to become writable")); } } int value = 1; if (sizeof(value) != write(fds[write_fd], std::addressof(value), sizeof(value))) { BOOST_THROW_EXCEPTION(std::system_error(errno, std::system_category(), "Error while writing to pipe")); } } unsigned int mt::CrossProcessSync::wait_for_signal_ready_for() { return wait_for_signal_ready_for(std::chrono::minutes(2)); } unsigned int mt::CrossProcessSync::wait_for_signal_ready_for(const std::chrono::milliseconds& duration) { static const short empty_revents = 0; pollfd poll_fd[1] = { { fds[read_fd], POLLIN, empty_revents } }; int rc = -1; while ((rc = ::poll(poll_fd, 1, duration.count())) <= 0) { if (errno == EINTR) { continue; } if (rc == 0) { BOOST_THROW_EXCEPTION(std::runtime_error("Poll on readfd for pipe timed out")); } else { BOOST_THROW_EXCEPTION(std::system_error(errno, std::system_category(), "Error while polling pipe to become readable")); } } int value; if (sizeof(value) != read(fds[read_fd], std::addressof(value), sizeof(value))) { BOOST_THROW_EXCEPTION(std::system_error(errno, std::system_category(), "Error while reading from pipe")); } if (value != 1) { BOOST_THROW_EXCEPTION( ::boost::enable_error_info(std::runtime_error("Read an unexpected value from pipe")) << errinfo_unexpected_value(value)); } counter += value; return counter; } void mt::CrossProcessSync::signal_ready() { try_signal_ready_for(std::chrono::milliseconds{-1}); } unsigned int mt::CrossProcessSync::wait_for_signal_ready() { return wait_for_signal_ready_for(std::chrono::milliseconds{-1}); } ./tests/mir_test/signal.cpp0000644000015600001650000000232312676616125016062 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/test/signal.h" namespace mt = mir::test; void mt::Signal::raise() { std::lock_guard lock(mutex); signalled = true; cv.notify_all(); } bool mt::Signal::raised() { std::lock_guard lock(mutex); return signalled; } void mt::Signal::wait() { std::unique_lock lock(mutex); cv.wait(lock, [this]() { return signalled; }); } void mt::Signal::reset() { std::lock_guard lock(mutex); signalled = false; } ./tests/mir_test/pipe.cpp0000644000015600001650000000270012676616125015541 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include #include #include #include #include #include namespace mt = mir::test; mt::Pipe::Pipe() : Pipe(0) { } mt::Pipe::Pipe(int flags) { int pipefd[2]; if (pipe2(pipefd, flags)) { BOOST_THROW_EXCEPTION( boost::enable_error_info(std::system_error(errno, std::system_category(), "Failed to create pipe"))); } reader = mir::Fd{pipefd[0]}; writer = mir::Fd{pipefd[1]}; } mir::Fd mt::Pipe::read_fd() const { return reader; } mir::Fd mt::Pipe::write_fd() const { return writer; } ./tests/mir_test/current_thread_name.cpp0000644000015600001650000000176612676616157020635 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/test/current_thread_name.h" #include std::string mir::test::current_thread_name() { static size_t const max_thread_name_size = 16; char thread_name[max_thread_name_size]; pthread_getname_np(pthread_self(), thread_name, sizeof thread_name); return {thread_name}; } ./tests/mir_test/validity_matchers.cpp0000644000015600001650000000142312676616125020320 0ustar jenkinsjenkins#include "mir/test/validity_matchers.h" #include "mir_toolkit/mir_client_library.h" template<> bool IsValidMatcher::MatchAndExplain(MirConnection* connection, MatchResultListener* listener) const { if (connection == nullptr) { return false; } if (!mir_connection_is_valid(connection)) { *listener << "error is: " << mir_connection_get_error_message(connection); return false; } return true; } template<> bool IsValidMatcher::MatchAndExplain(MirSurface* surface, MatchResultListener* listener) const { if (surface == nullptr) { return false; } if (!mir_surface_is_valid(surface)) { *listener << "error is: " << mir_surface_get_error_message(surface); return false; } return true; } ./tests/mir_test/fd_utils.cpp0000644000015600001650000000250012676616125016413 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/test/fd_utils.h" ::testing::AssertionResult mir::test::std_call_succeeded(int retval) { if (retval >= 0) { return ::testing::AssertionSuccess(); } else { return ::testing::AssertionFailure() << "errno: " << errno << " [" << strerror(errno) << "]"; } } ::testing::AssertionResult mir::test::fd_is_readable(mir::Fd const& fd) { return fd_becomes_readable(fd, std::chrono::seconds{0}); } ./tests/mir_test/event_matchers.cpp0000644000015600001650000000175712676616125017626 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/test/event_matchers.h" #include "mir/event_printer.h" void PrintTo(MirEvent const& event, std::ostream *os) { using mir::operator<<; *os << event; } void PrintTo(MirEvent const* event, std::ostream *os) { if (event) PrintTo(*event, os); else *os << "nullptr"; } ./tests/mir_test/event_factory.cpp0000644000015600001650000000643112676616125017461 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/test/event_factory.h" namespace mis = mir::input::synthesis; namespace geom = mir::geometry; mis::KeyParameters::KeyParameters() : device_id(0), scancode(0), action(mis::EventAction::Down) { } mis::KeyParameters& mis::KeyParameters::from_device(int new_device_id) { device_id = new_device_id; return *this; } mis::KeyParameters& mis::KeyParameters::of_scancode(int new_scancode) { scancode = new_scancode; return *this; } mis::KeyParameters& mis::KeyParameters::with_action(mis::EventAction new_action) { action = new_action; return *this; } mis::KeyParameters mis::a_key_down_event() { return mis::KeyParameters().with_action(mis::EventAction::Down); } mis::KeyParameters mis::a_key_up_event() { return mis::KeyParameters().with_action(mis::EventAction::Up); } mis::ButtonParameters::ButtonParameters() : device_id(0), button(0), action(mis::EventAction::Down) { } mis::ButtonParameters& mis::ButtonParameters::from_device(int new_device_id) { device_id = new_device_id; return *this; } mis::ButtonParameters& mis::ButtonParameters::of_button(int new_button) { button = new_button; return *this; } mis::ButtonParameters& mis::ButtonParameters::with_action(mis::EventAction new_action) { action = new_action; return *this; } mis::ButtonParameters mis::a_button_down_event() { return mis::ButtonParameters().with_action(mis::EventAction::Down); } mis::ButtonParameters mis::a_button_up_event() { return mis::ButtonParameters().with_action(mis::EventAction::Up); } mis::MotionParameters::MotionParameters() : device_id(0), rel_x(0), rel_y(0) { } mis::MotionParameters& mis::MotionParameters::from_device(int new_device_id) { device_id = new_device_id; return *this; } mis::MotionParameters& mis::MotionParameters::with_movement(int new_rel_x, int new_rel_y) { rel_x = new_rel_x; rel_y = new_rel_y; return *this; } mis::MotionParameters mis::a_pointer_event() { return mis::MotionParameters(); } mis::TouchParameters::TouchParameters() : device_id(0), abs_x(0), abs_y(0), action(Action::Tap) { } mis::TouchParameters& mis::TouchParameters::from_device(int new_device_id) { device_id = new_device_id; return *this; } mis::TouchParameters& mis::TouchParameters::at_position(geom::Point abs_pos) { abs_x = abs_pos.x.as_int(); abs_y = abs_pos.y.as_int(); return *this; } mis::TouchParameters& mis::TouchParameters::with_action(Action touch_action) { action = touch_action; return *this; } mis::TouchParameters mis::a_touch_event() { return mis::TouchParameters(); } ./tests/mir_test/wait_object.cpp0000644000015600001650000000203012676616125017072 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis * Andreas Pokorny */ #include "mir/test/wait_object.h" namespace mt = mir::test; void mt::WaitObject::notify_ready() { std::unique_lock lock{mutex}; ready = true; cv.notify_all(); } void mt::WaitObject::wait_until_ready() { wait_until_ready(std::chrono::seconds{10}); } ./tests/mir_test/fake_clock.cpp0000644000015600001650000000260612676616125016672 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/test/fake_clock.h" namespace mt = mir::test; mt::FakeClock::FakeClock() : current_time{std::chrono::nanoseconds::zero()} { } mt::FakeClock::time_point mt::FakeClock::now() const { return time_point{current_time}; } void mt::FakeClock::register_time_change_callback(std::function cb) { callbacks.push_back(cb); } void mt::FakeClock::advance_time_ns(std::chrono::nanoseconds by) { current_time += by; auto next = callbacks.begin(); while (next != callbacks.end()) { if (!(*next)(now())) { next = callbacks.erase(next); } else { next++; } } } ./tests/mir_test/test_dispatchable.cpp0000644000015600001650000000503012676616125020265 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include #include "mir/test/pipe.h" #include "mir/test/test_dispatchable.h" #include #include namespace mt = mir::test; namespace md = mir::dispatch; mt::TestDispatchable::TestDispatchable(std::function const& target, md::FdEvents relevant_events) : target{target}, eventmask{relevant_events} { // Need to use O_NONBLOCK here to ensure concurrent dispatch doesn't block indefinitely // in read() mt::Pipe pipe{O_NONBLOCK}; read_fd = pipe.read_fd(); write_fd = pipe.write_fd(); } mt::TestDispatchable::TestDispatchable(std::function const& target) : TestDispatchable(target, md::FdEvent::readable) { } mt::TestDispatchable::TestDispatchable(std::function const& target) : TestDispatchable([target](md::FdEvents) { target(); return true; }) { } mir::Fd mt::TestDispatchable::watch_fd() const { return read_fd; } bool mt::TestDispatchable::dispatch(md::FdEvents events) { auto continue_dispatch = target(events); if (!(events & md::FdEvent::remote_closed)) { // There's no way to untrigger remote hangup :) untrigger(); } return continue_dispatch; } md::FdEvents mt::TestDispatchable::relevant_events() const { return eventmask; } void mt::TestDispatchable::trigger() { using namespace testing; char dummy{0}; EXPECT_THAT(::write(write_fd, &dummy, sizeof(dummy)), Eq(sizeof(dummy))); } void mt::TestDispatchable::untrigger() { using namespace testing; char dummy{0}; auto val = ::read(read_fd, &dummy, sizeof(dummy)); if (val < 0) { EXPECT_THAT(errno, Eq(EAGAIN)); } else { EXPECT_THAT(val, Eq(sizeof(dummy))); } } void mt::TestDispatchable::hangup() { write_fd = mir::Fd{}; } ./tests/mir_test/CMakeLists.txt0000644000015600001650000000063212676616125016642 0ustar jenkinsjenkinsadd_library(mir-public-test OBJECT cross_process_action.cpp cross_process_sync.cpp current_thread_name.cpp display_config_matchers.cpp event_factory.cpp event_matchers.cpp pipe.cpp popen.cpp signal.cpp spin_wait.cpp validity_matchers.cpp ) add_library(mir-test-static STATIC fake_clock.cpp fd_utils.cpp test_dispatchable.cpp wait_object.cpp $ ) ./tests/mir_test/spin_wait.cpp0000644000015600001650000000221512676616125016602 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/test/spin_wait.h" #include bool mir::test::spin_wait_for_condition_or_timeout( std::function const& condition, std::chrono::milliseconds timeout, std::chrono::milliseconds spin_period) { auto const end = std::chrono::steady_clock::now() + timeout; while (std::chrono::steady_clock::now() < end && !condition()) { std::this_thread::sleep_for(spin_period); } return condition(); } ./tests/mir_test/display_config_matchers.cpp0000644000015600001650000003602512676616157021500 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/test/display_config_matchers.h" #include "mir/graphics/display_configuration.h" #include "mir_protobuf.pb.h" #include "mir_toolkit/client_types.h" #include "mir_toolkit/mir_display_configuration.h" #include namespace mg = mir::graphics; namespace mp = mir::protobuf; namespace geom = mir::geometry; namespace mt = mir::test; namespace { size_t find_mode_index(MirOutput const* output, MirOutputMode const* mode) { for (int i = 0; i < mir_output_get_num_modes(output); ++i) { if (mir_output_get_mode(output, i) == mode) { return i; } } return static_cast(-1); } class TestDisplayConfiguration : public mg::DisplayConfiguration { public: TestDisplayConfiguration(mp::DisplayConfiguration const& protobuf_config) { /* Cards */ for (int i = 0; i < protobuf_config.display_card_size(); i++) { auto const& protobuf_card = protobuf_config.display_card(i); mg::DisplayConfigurationCard display_card { mg::DisplayConfigurationCardId(protobuf_card.card_id()), protobuf_card.max_simultaneous_outputs(), }; cards.push_back(display_card); } /* Outputs */ for (int i = 0; i < protobuf_config.display_output_size(); i++) { auto const& protobuf_output = protobuf_config.display_output(i); mg::DisplayConfigurationOutput display_output { mg::DisplayConfigurationOutputId(protobuf_output.output_id()), mg::DisplayConfigurationCardId(protobuf_output.card_id()), static_cast(protobuf_output.type()), {}, {}, protobuf_output.preferred_mode(), geom::Size{protobuf_output.physical_width_mm(), protobuf_output.physical_height_mm()}, static_cast(protobuf_output.connected()), static_cast(protobuf_output.used()), geom::Point{protobuf_output.position_x(), protobuf_output.position_y()}, protobuf_output.current_mode(), static_cast(protobuf_output.current_format()), static_cast(protobuf_output.power_mode()), static_cast(protobuf_output.orientation()), 1.0f, mir_form_factor_monitor }; /* Modes */ std::vector modes; for (int n = 0; n < protobuf_output.mode_size(); n++) { auto const& protobuf_mode = protobuf_output.mode(n); modes.push_back( { geom::Size{protobuf_mode.horizontal_resolution(), protobuf_mode.vertical_resolution()}, protobuf_mode.refresh_rate() }); } display_output.modes = modes; /* Pixel formats */ std::vector pixel_formats; for (int n = 0; n < protobuf_output.pixel_format_size(); n++) { pixel_formats.push_back( static_cast(protobuf_output.pixel_format(n))); } display_output.pixel_formats = pixel_formats; outputs.push_back(display_output); } } TestDisplayConfiguration(MirDisplayConfiguration const& client_config) { /* Cards */ for (size_t i = 0; i < client_config.num_cards; i++) { auto const& client_card = client_config.cards[i]; mg::DisplayConfigurationCard display_card { mg::DisplayConfigurationCardId(client_card.card_id), client_card.max_simultaneous_outputs, }; cards.push_back(display_card); } /* Outputs */ for (size_t i = 0; i < client_config.num_outputs; i++) { auto const& client_output = client_config.outputs[i]; mg::DisplayConfigurationOutput display_output { mg::DisplayConfigurationOutputId(client_output.output_id), mg::DisplayConfigurationCardId(client_output.card_id), static_cast(client_output.type), {}, {}, client_output.preferred_mode, geom::Size{client_output.physical_width_mm, client_output.physical_height_mm}, static_cast(client_output.connected), static_cast(client_output.used), geom::Point{client_output.position_x, client_output.position_y}, client_output.current_mode, client_output.current_format, static_cast(client_output.power_mode), static_cast(client_output.orientation), 1.0f, mir_form_factor_monitor }; /* Modes */ std::vector modes; for (size_t n = 0; n < client_output.num_modes; n++) { auto const& client_mode = client_output.modes[n]; modes.push_back( { geom::Size{client_mode.horizontal_resolution, client_mode.vertical_resolution}, client_mode.refresh_rate }); } display_output.modes = modes; /* Pixel formats */ std::vector pixel_formats; for (size_t n = 0; n < client_output.num_output_formats; n++) { pixel_formats.push_back(client_output.output_formats[n]); } display_output.pixel_formats = pixel_formats; outputs.push_back(display_output); } } TestDisplayConfiguration(MirDisplayConfig const* config) { /* Cards; fake it, 'cause we only ever support 1 card at the moment */ cards.push_back( mg::DisplayConfigurationCard{ mg::DisplayConfigurationCardId{1}, static_cast(mir_display_config_get_max_simultaneous_outputs(config)) }); /* Outputs */ for (int i = 0; i < mir_display_config_get_num_outputs(config); i++) { auto const client_output = mir_display_config_get_output(config, i); mg::DisplayConfigurationOutput display_output { mg::DisplayConfigurationOutputId(mir_output_get_id(client_output)), mg::DisplayConfigurationCardId(1), static_cast(mir_output_get_type(client_output)), {}, {}, static_cast(find_mode_index(client_output, mir_output_get_preferred_mode(client_output))), geom::Size{mir_output_get_physical_width_mm(client_output), mir_output_get_physical_height_mm(client_output)}, mir_output_get_connection_state(client_output) == mir_output_connection_state_connected, mir_output_is_enabled(client_output), geom::Point{mir_output_get_position_x(client_output), mir_output_get_position_y(client_output)}, static_cast(find_mode_index(client_output, (mir_output_get_current_mode(client_output)))), mir_output_get_current_pixel_format(client_output), mir_output_get_power_mode(client_output), mir_output_get_orientation(client_output), 1.0f, mir_form_factor_monitor }; /* Modes */ std::vector modes; for (int n = 0; n < mir_output_get_num_modes(client_output); n++) { auto const client_mode = mir_output_get_mode(client_output, n); modes.push_back( { geom::Size{ mir_output_mode_get_width(client_mode), mir_output_mode_get_height(client_mode)}, mir_output_mode_get_refresh_rate(client_mode) }); } display_output.modes = modes; /* Pixel formats */ std::vector pixel_formats; for (int n = 0; n < mir_output_get_num_pixel_formats(client_output); n++) { pixel_formats.push_back(mir_output_get_pixel_format(client_output, n)); } display_output.pixel_formats = pixel_formats; outputs.push_back(display_output); } } TestDisplayConfiguration(TestDisplayConfiguration const& other) : mg::DisplayConfiguration(), cards{other.cards}, outputs{other.outputs} { } void for_each_card(std::function f) const override { for (auto const& card : cards) f(card); } void for_each_output(std::function f) const override { for (auto const& output : outputs) f(output); } void for_each_output(std::function f) override { for (auto& output : outputs) { mg::UserDisplayConfigurationOutput user(output); f(user); } } std::unique_ptr clone() const override { return std::make_unique(*this); } private: std::vector cards; std::vector outputs; }; } bool mt::compare_display_configurations( testing::MatchResultListener* listener, mg::DisplayConfiguration const& config1, mg::DisplayConfiguration const& config2) { using namespace testing; bool failure = false; /* cards */ std::vector cards1; std::vector cards2; config1.for_each_card( [&cards1](mg::DisplayConfigurationCard const& card) { cards1.push_back(card); }); config2.for_each_card( [&cards2](mg::DisplayConfigurationCard const& card) { cards2.push_back(card); }); failure |= !ExplainMatchResult(UnorderedElementsAreArray(cards1), cards2, listener); /* Outputs */ std::vector outputs1; std::vector outputs2; config1.for_each_output( [&outputs1](mg::DisplayConfigurationOutput const& output) { outputs1.push_back(output); }); config2.for_each_output( [&outputs2](mg::DisplayConfigurationOutput const& output) { outputs2.push_back(output); }); failure |= !ExplainMatchResult(UnorderedElementsAreArray(outputs1), outputs2, listener); return !failure; } bool mt::compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfiguration const& client_config, mg::DisplayConfiguration const& display_config) { TestDisplayConfiguration config1{client_config}; return compare_display_configurations(listener, config1, display_config); } bool mt::compare_display_configurations( testing::MatchResultListener* listener, mp::DisplayConfiguration const& protobuf_config, mg::DisplayConfiguration const& display_config) { TestDisplayConfiguration config1{protobuf_config}; return compare_display_configurations(listener, config1, display_config); } bool mt::compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfiguration const* client_config1, MirDisplayConfiguration const* client_config2) { TestDisplayConfiguration config1{*client_config1}; TestDisplayConfiguration config2{*client_config2}; return compare_display_configurations(listener, config1, config2); } bool mt::compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfiguration const& client_config, mp::DisplayConfiguration const& protobuf_config) { TestDisplayConfiguration config1{client_config}; TestDisplayConfiguration config2{protobuf_config}; return compare_display_configurations(listener, config1, config2); } bool mt::compare_display_configurations( testing::MatchResultListener* listener, graphics::DisplayConfiguration const& display_config1, MirDisplayConfiguration const* display_config2) { TestDisplayConfiguration config2{*display_config2}; return compare_display_configurations(listener, display_config1, config2); } bool mt::compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfiguration const* display_config2, graphics::DisplayConfiguration const& display_config1) { TestDisplayConfiguration config2{*display_config2}; return compare_display_configurations(listener, display_config1, config2); } bool mt::compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfig const* client_config, mg::DisplayConfiguration const& server_config) { TestDisplayConfiguration translated_config{client_config}; return compare_display_configurations(listener, server_config, translated_config); } bool mt::compare_display_configurations( testing::MatchResultListener* listener, mg::DisplayConfiguration const& server_config, MirDisplayConfig const* client_config) { TestDisplayConfiguration translated_config{client_config}; return compare_display_configurations(listener, server_config, translated_config); } bool mt::compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfig const* config1, MirDisplayConfig const* config2) { TestDisplayConfiguration translated_config_one{config1}; TestDisplayConfiguration translated_config_two{config2}; return compare_display_configurations(listener, translated_config_one, translated_config_two); } ./tests/mir_test/popen.cpp0000644000015600001650000000303412676616125015726 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre * Daniel van Vugt */ #include #include namespace mt = mir::test; mt::Popen::Popen(std::string const& cmd) : raw_stream{popen(cmd.c_str(), "r"), [](FILE* f){ pclose(f); }} { if (!raw_stream) throw std::system_error(errno, std::system_category(), "popen failed for `"+cmd+"'"); } bool mt::Popen::get_line(std::string& line) { FILE* in = raw_stream.get(); if (!in) return false; char* got = 0; char buf[1024]; line.clear(); do { got = fgets(buf, sizeof buf, in); if (got) line.append(got); } while (got && !feof(in) && !line.empty() && line.back() != '\n'); if (!line.empty() && line.back() == '\n') line.pop_back(); return !line.empty() || !feof(in); } ./tests/unit-tests/0000755000015600001650000000000012676616160014371 5ustar jenkinsjenkins./tests/unit-tests/gl/0000755000015600001650000000000012676616126014775 5ustar jenkinsjenkins./tests/unit-tests/gl/test_gl_texture_cache.cpp0000644000015600001650000001131612676616125022046 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois * Originally by: Daniel van Vugt */ #include "src/gl/recently_used_cache.h" #include "mir/test/doubles/mock_gl_buffer.h" #include "mir/test/doubles/mock_renderable.h" #include "mir/test/doubles/mock_gl.h" #include namespace mtd=mir::test::doubles; namespace mgl=mir::gl; namespace mg=mir::graphics; namespace { class RecentlyUsedCache : public testing::Test { public: RecentlyUsedCache() { using namespace testing; mock_buffer = std::make_shared>(); renderable = std::make_shared>(); ON_CALL(*renderable, buffer()) .WillByDefault(Return(mock_buffer)); ON_CALL(*mock_buffer, id()) .WillByDefault(Return(mg::BufferID(123))); } testing::NiceMock mock_gl; std::shared_ptr mock_buffer; std::shared_ptr> renderable; GLuint const stub_texture{1}; }; } TEST_F(RecentlyUsedCache, caches_and_uploads_texture_only_on_buffer_changes) { using namespace testing; InSequence seq; // Frame 1: Texture generated and uploaded EXPECT_CALL(*mock_buffer, id()) .WillOnce(Return(mg::BufferID(123))); EXPECT_CALL(mock_gl, glGenTextures(1, _)) .WillOnce(SetArgPointee<1>(stub_texture)); EXPECT_CALL(mock_gl, glBindTexture(GL_TEXTURE_2D, stub_texture)); EXPECT_CALL(mock_gl, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _)); EXPECT_CALL(mock_gl, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _)); EXPECT_CALL(mock_gl, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _)); EXPECT_CALL(mock_gl, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _)); EXPECT_CALL(mock_gl, glBindTexture(GL_TEXTURE_2D, stub_texture)); EXPECT_CALL(*mock_buffer,gl_bind_to_texture()); // Frame 2: Texture found in cache and not re-uploaded EXPECT_CALL(*mock_buffer, id()) .WillOnce(Return(mg::BufferID(123))); EXPECT_CALL(mock_gl, glBindTexture(GL_TEXTURE_2D, stub_texture)); EXPECT_CALL(*mock_buffer,gl_bind_to_texture()) .Times(0); EXPECT_CALL(*mock_buffer, used_as_texture()); // Frame 3: Texture found in cache but refreshed with new buffer EXPECT_CALL(*mock_buffer, id()) .WillOnce(Return(mg::BufferID(456))); EXPECT_CALL(mock_gl, glBindTexture(GL_TEXTURE_2D, stub_texture)); EXPECT_CALL(*mock_buffer,gl_bind_to_texture()); // Frame 4: Stale texture reuploaded following bypass EXPECT_CALL(*mock_buffer, id()) .WillOnce(Return(mg::BufferID(456))); EXPECT_CALL(mock_gl, glBindTexture(GL_TEXTURE_2D, stub_texture)); EXPECT_CALL(*mock_buffer,gl_bind_to_texture()); // Frame 5: Texture found in cache and not re-uploaded EXPECT_CALL(*mock_buffer, id()) .WillOnce(Return(mg::BufferID(456))); EXPECT_CALL(mock_gl, glBindTexture(GL_TEXTURE_2D, stub_texture)); EXPECT_CALL(*mock_buffer, used_as_texture()); EXPECT_CALL(mock_gl, glDeleteTextures(1, Pointee(stub_texture))); mgl::RecentlyUsedCache cache; cache.load(*renderable); cache.drop_unused(); cache.load(*renderable); cache.drop_unused(); cache.load(*renderable); cache.drop_unused(); cache.invalidate(); cache.load(*renderable); cache.drop_unused(); cache.load(*renderable); cache.drop_unused(); } TEST_F(RecentlyUsedCache, holds_buffers_till_the_end) { auto old_use_count = mock_buffer.use_count(); mgl::RecentlyUsedCache cache; cache.load(*renderable); EXPECT_EQ(old_use_count+1, mock_buffer.use_count()); cache.drop_unused(); EXPECT_EQ(old_use_count, mock_buffer.use_count()); } //LP: #1362444 TEST_F(RecentlyUsedCache, invalidated_buffers_are_reloaded) { ON_CALL(*mock_buffer, id()) .WillByDefault(testing::Return(mg::BufferID(0))); EXPECT_CALL(*mock_buffer,gl_bind_to_texture()) .Times(2); mgl::RecentlyUsedCache cache; cache.load(*renderable); cache.load(*renderable); cache.invalidate(); cache.load(*renderable); } ./tests/unit-tests/gl/test_program_factory.cpp0000644000015600001650000001465612676616125021751 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury * Kevin DuBois */ #include #include #include #include #include #include #include #include "mir/gl/default_program_factory.h" #include #include #include #include #include #include using testing::SetArgPointee; using testing::InSequence; using testing::Return; using testing::ReturnRef; using testing::Pointee; using testing::AnyNumber; using testing::AtLeast; using testing::_; namespace mt=mir::test; namespace mtd=mir::test::doubles; namespace mc=mir::compositor; namespace mg=mir::graphics; namespace { const GLint stub_v_shader = 1; const GLint stub_f_shader = 2; const GLint stub_program = 1; const std::string stub_info_log = "something failed!"; const size_t stub_info_log_length = stub_info_log.size(); void ExpectShaderCompileFailure(const GLint shader, mtd::MockGL &mock_gl) { EXPECT_CALL(mock_gl, glGetShaderiv(shader, GL_COMPILE_STATUS, _)) .WillOnce(SetArgPointee<2>(GL_FALSE)); } void ExpectShaderCompileSuccess(const GLint shader, mtd::MockGL &mock_gl) { EXPECT_CALL(mock_gl, glGetShaderiv(shader, GL_COMPILE_STATUS, _)) .WillRepeatedly(SetArgPointee<2>(GL_TRUE)); } void SetUpMockVertexShader(mtd::MockGL &mock_gl, const std::function &shader_compile_expectation) { /* Vertex Shader */ ON_CALL(mock_gl, glCreateShader(GL_VERTEX_SHADER)) .WillByDefault(Return(stub_v_shader)); shader_compile_expectation(stub_v_shader, mock_gl); } void SetUpMockFragmentShader(mtd::MockGL &mock_gl, const std::function &shader_compile_expectation) { /* Fragment Shader */ ON_CALL(mock_gl, glCreateShader(GL_FRAGMENT_SHADER)) .WillByDefault(Return(stub_f_shader)); shader_compile_expectation(stub_f_shader, mock_gl); } void ExpectProgramLinkFailure(const GLint program, mtd::MockGL &mock_gl) { EXPECT_CALL(mock_gl, glGetProgramiv(program, GL_LINK_STATUS, _)) .WillOnce(SetArgPointee<2>(GL_FALSE)); } void ExpectProgramLinkSuccess(const GLint program, mtd::MockGL &mock_gl) { EXPECT_CALL(mock_gl, glGetProgramiv(program, GL_LINK_STATUS, _)) .WillRepeatedly(SetArgPointee<2>(GL_TRUE)); } void SetUpMockGraphicsProgram(mtd::MockGL &mock_gl, const std::function &program_link_expectation) { /* Graphics Program */ ON_CALL(mock_gl, glCreateProgram()) .WillByDefault(Return(stub_program)); program_link_expectation(stub_program, mock_gl); } class ProgramFactory : public testing::Test { public: testing::NiceMock mock_gl; mir::gl::DefaultProgramFactory factory; }; ACTION_P2(CopyString, str, len) { memcpy(arg3, str, len); arg3[len] = '\0'; } MATCHER_P(NthCharacterIsNul, n, "specified character is the nul-byte") { return arg[n] == '\0'; } TEST_F(ProgramFactory, vertex_shader_compiler_failure_recovers_and_throws) { using namespace std::placeholders; SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileFailure, _1, _2)); EXPECT_CALL(mock_gl, glGetShaderiv(stub_v_shader, GL_INFO_LOG_LENGTH, _)) .WillOnce(SetArgPointee<2>(stub_info_log_length)); EXPECT_CALL(mock_gl, glGetShaderInfoLog(stub_v_shader, stub_info_log_length, _, NthCharacterIsNul(stub_info_log_length))) .WillOnce(CopyString(stub_info_log.c_str(), stub_info_log.size())); EXPECT_THROW({ auto p = factory.create_gl_program("foo", "bar"); }, std::runtime_error); } TEST_F(ProgramFactory, fragment_shader_compiler_failure_recovers_and_throw) { using namespace std::placeholders; SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2)); SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileFailure, _1, _2)); EXPECT_CALL(mock_gl, glGetShaderiv(stub_f_shader, GL_INFO_LOG_LENGTH, _)) .WillOnce(SetArgPointee<2>(stub_info_log_length)); EXPECT_CALL(mock_gl, glGetShaderInfoLog(stub_f_shader, stub_info_log_length, _, NthCharacterIsNul(stub_info_log_length))) .WillOnce(CopyString(stub_info_log.c_str(), stub_info_log.size())); EXPECT_THROW({ auto p = factory.create_gl_program("foo", "bar"); }, std::runtime_error); } TEST_F(ProgramFactory, graphics_program_linker_failure_recovers_and_throw) { using namespace std::placeholders; SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2)); SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2)); SetUpMockGraphicsProgram(mock_gl, std::bind(ExpectProgramLinkFailure, _1, _2)); EXPECT_CALL(mock_gl, glGetProgramiv(stub_program, GL_INFO_LOG_LENGTH, _)) .WillOnce(SetArgPointee<2>(stub_info_log_length)); EXPECT_CALL(mock_gl, glGetProgramInfoLog(stub_program, stub_info_log_length, _, NthCharacterIsNul(stub_info_log_length))) .WillOnce(CopyString(stub_info_log.c_str(), stub_info_log.size())); EXPECT_THROW({ auto p = factory.create_gl_program("foo", "bar"); }, std::runtime_error); } TEST_F(ProgramFactory, graphics_program_creation_success) { using namespace std::placeholders; SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2)); SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2)); SetUpMockGraphicsProgram(mock_gl, std::bind(ExpectProgramLinkSuccess, _1, _2)); auto p = factory.create_gl_program("foo", "bar"); } } ./tests/unit-tests/gl/CMakeLists.txt0000644000015600001650000000031112676616125017527 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_gl_texture_cache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_program_factory.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/android_input/0000755000015600001650000000000012676616125017231 5ustar jenkinsjenkins./tests/unit-tests/android_input/string8.cpp0000644000015600001650000000233412676616125021335 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include #include #include using namespace android; TEST(AndroidString8, appendFormat) { String8 string; EXPECT_STREQ("", c_str(string)); appendFormat(string, "%s", "Hello"); EXPECT_STREQ("Hello", c_str(string)); u_char digits = 0x12; appendFormat(string, "%02x", digits); EXPECT_STREQ("Hello12", c_str(string)); } TEST(AndroidString8, formatString8) { auto string = formatString8("Hello %s #%02x", "world", 0x42); EXPECT_STREQ("Hello world #42", c_str(string)); } ./tests/unit-tests/android_input/property_map.cpp0000644000015600001650000000637612676616125022472 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include #include #include #include using namespace android; namespace { static char const* test_file = "AndroidInputPropertyMap.ini"; struct AndroidInputPropertyMap : public ::testing::Test { PropertyMap* test_map; static void SetUpTestCase() { ASSERT_TRUE((std::ofstream(test_file) << "test.string=a_string\n" "#test.bool.true=true\n" "test.bool.true=1\n" "test.bool.false=0\n" "#test.bool.true=true\n" "# a comment\n" "#test.int.ignored=1\n" "test.int_32=123\n" "test.float=0.5\n").good()); } void SetUp() { ASSERT_EQ(NO_ERROR, PropertyMap::load(String8(test_file), &test_map)); } void TearDown() { delete test_map; } }; } TEST_F(AndroidInputPropertyMap, test_map_created) { EXPECT_TRUE(test_map); } TEST_F(AndroidInputPropertyMap, test_map_has_a_string) { String8 result; EXPECT_TRUE(test_map->tryGetProperty(String8("test.string"), result)); EXPECT_EQ(String8("a_string"), result); } TEST_F(AndroidInputPropertyMap, test_map_has_an_int32_t) { int32_t result; EXPECT_TRUE(test_map->tryGetProperty(String8("test.int_32"), result)); EXPECT_EQ(123, result); } TEST_F(AndroidInputPropertyMap, test_map_has_bools_true_and_false) { bool result{}; EXPECT_TRUE(test_map->tryGetProperty(String8("test.bool.true"), result)); EXPECT_TRUE(result); EXPECT_TRUE(test_map->tryGetProperty(String8("test.bool.false"), result)); EXPECT_FALSE(result); } TEST_F(AndroidInputPropertyMap, test_map_has_a_float) { float result; EXPECT_TRUE(test_map->tryGetProperty(String8("test.float"), result)); EXPECT_EQ(0.5, result); } TEST_F(AndroidInputPropertyMap, test_map_fails_to_get_unknown_string) { String8 result; EXPECT_FALSE(test_map->tryGetProperty(String8("unknown"), result)); } TEST_F(AndroidInputPropertyMap, test_map_fails_to_get_unknown_bool) { bool result{}; EXPECT_FALSE(test_map->tryGetProperty(String8("unknown"), result)); } TEST_F(AndroidInputPropertyMap, test_map_fails_to_get_unknown_int32_t) { int32_t result; EXPECT_FALSE(test_map->tryGetProperty(String8("unknown"), result)); } TEST_F(AndroidInputPropertyMap, test_map_fails_to_get_unknown_float) { float result; EXPECT_FALSE(test_map->tryGetProperty(String8("unknown"), result)); } TEST_F(AndroidInputPropertyMap, test_map_ignores_comment) { int32_t result; EXPECT_FALSE(test_map->tryGetProperty(String8("test.int.ignored"), result)); } ./tests/unit-tests/android_input/int_set.cpp0000644000015600001650000000600612676616125021404 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel d'Andrada */ // Need a different implementation of IntSet with ANDROID_INPUT_INTSET_TEST // defined. It will fall under test::android namespace instead of in android // to avoid clashing with the production IntSet from libmirserver #define ANDROID_INPUT_INTSET_TEST #include #include int test::android::IntSet::constructionCount; int test::android::IntSet::destructionCount; #include #include #include using std::vector; using std::string; namespace { struct AndroidInputIntSet : public ::testing::Test { void SetUp() { } }; } TEST_F(AndroidInputIntSet, difference) { IntSet a = {1, 2, 3, 6}; IntSet b = { 2, 3, 4, 5}; IntSet c = a - b; IntSet expected_c = {1, 6}; EXPECT_EQ(expected_c, c); } TEST_F(AndroidInputIntSet, intersection) { IntSet a = {1, 2, 3}; IntSet b = { 2, 3, 4, 5}; IntSet c = a & b; IntSet expected_c = {2, 3}; EXPECT_EQ(expected_c, c); } TEST_F(AndroidInputIntSet, first) { IntSet a = {4, 2, 1, 3}; EXPECT_EQ(1, a.first()); } TEST_F(AndroidInputIntSet, remove_set) { IntSet a = {1, 2, 3, 4}; IntSet b = { 2, 3, 5}; a.remove(b); IntSet expected_a = {1, 4}; EXPECT_EQ(expected_a, a); } TEST_F(AndroidInputIntSet, index_of) { IntSet a = {5, 6, 10, 15}; EXPECT_EQ(1u, a.indexOf(6)); EXPECT_EQ(2u, a.indexOf(10)); } TEST_F(AndroidInputIntSet, for_each) { IntSet a = {5, 6, 10, 15}; std::vector expected_values = {5, 6, 10, 15}; std::vector actual_values; a.forEach([&](int32_t value) { actual_values.push_back(value); }); EXPECT_EQ(expected_values, actual_values); } TEST_F(AndroidInputIntSet, to_string) { IntSet a = {1, 2, 3}; string expected_str = "1, 2, 3"; EXPECT_EQ(expected_str, a.toString()); } TEST_F(AndroidInputIntSet, unnecessary_constructions) { IntSet a = {1, 2, 3, 6}; IntSet b = { 2, 3, 4, 5}; IntSet::constructionCount = 0; IntSet::destructionCount = 0; { IntSet c = a - b; } EXPECT_EQ(1, IntSet::constructionCount); EXPECT_EQ(1, IntSet::destructionCount); IntSet::constructionCount = 0; IntSet::destructionCount = 0; { IntSet c = a & b; } EXPECT_EQ(1, IntSet::constructionCount); EXPECT_EQ(1, IntSet::destructionCount); } ./tests/unit-tests/android_input/CMakeLists.txt0000644000015600001650000000042412676616125021771 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/int_set.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sorted_vector.cpp ${CMAKE_CURRENT_SOURCE_DIR}/property_map.cpp ${CMAKE_CURRENT_SOURCE_DIR}/string8.cpp ) set( UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/android_input/sorted_vector.cpp0000644000015600001650000001250012676616125022615 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include #include #include namespace { struct AndroidInputSortedVector : public ::testing::Test { android::SortedVector test_vector; void SetUp() { test_vector.clear(); } }; } TEST_F(AndroidInputSortedVector, empty_vector_isEmpty) { EXPECT_TRUE(test_vector.isEmpty()); } TEST_F(AndroidInputSortedVector, non_empty_vector_not_isEmpty) { test_vector.add(1); EXPECT_FALSE(test_vector.isEmpty()); } TEST_F(AndroidInputSortedVector, items_found_at_expected_indexes) { test_vector.add(3); test_vector.add(1); test_vector.add(2); test_vector.add(0); EXPECT_EQ(0, test_vector.indexOf(0)); EXPECT_EQ(1, test_vector.indexOf(1)); EXPECT_EQ(2, test_vector.indexOf(2)); EXPECT_EQ(3, test_vector.indexOf(3)); EXPECT_EQ(android::NAME_NOT_FOUND, test_vector.indexOf(4)); } TEST_F(AndroidInputSortedVector, duplicate_adds_create_single_entry) { test_vector.add(3); test_vector.add(1); test_vector.add(2); test_vector.add(0); test_vector.add(3); test_vector.add(1); test_vector.add(2); test_vector.add(0); EXPECT_EQ(0, test_vector.indexOf(0)); EXPECT_EQ(1, test_vector.indexOf(1)); EXPECT_EQ(2, test_vector.indexOf(2)); EXPECT_EQ(3, test_vector.indexOf(3)); EXPECT_EQ(4u, test_vector.size()); EXPECT_EQ(android::NAME_NOT_FOUND, test_vector.indexOf(4)); } TEST_F(AndroidInputSortedVector, adds_returns_correct_index) { EXPECT_EQ(0, test_vector.add(3)); EXPECT_EQ(0, test_vector.add(1)); EXPECT_EQ(1, test_vector.add(2)); EXPECT_EQ(0, test_vector.add(0)); EXPECT_EQ(3, test_vector.add(3)); EXPECT_EQ(1, test_vector.add(1)); EXPECT_EQ(2, test_vector.add(2)); EXPECT_EQ(0, test_vector.add(0)); } TEST_F(AndroidInputSortedVector, missing_items_are_not_found) { test_vector.add(3); test_vector.add(1); test_vector.add(2); test_vector.add(0); EXPECT_EQ(android::NAME_NOT_FOUND, test_vector.indexOf(4)); } TEST_F(AndroidInputSortedVector, index_gets_expected_items) { test_vector.add(30); test_vector.add(10); test_vector.add(20); test_vector.add(00); EXPECT_EQ(00, test_vector.itemAt(0)); EXPECT_EQ(10, test_vector.itemAt(1)); EXPECT_EQ(20, test_vector.itemAt(2)); EXPECT_EQ(30, test_vector.itemAt(3)); } TEST_F(AndroidInputSortedVector, edit_changes_item) { test_vector.add(30); test_vector.add(10); test_vector.add(20); test_vector.add(00); ASSERT_EQ(10, test_vector.itemAt(1)); test_vector.editItemAt(1) = 12; EXPECT_EQ(12, test_vector.itemAt(1)); } TEST_F(AndroidInputSortedVector, removed_items_are_not_found) { test_vector.add(3); test_vector.add(1); test_vector.add(2); test_vector.add(0); auto const old_index = test_vector.remove(1); EXPECT_EQ(1, old_index); EXPECT_EQ(android::NAME_NOT_FOUND, test_vector.indexOf(1)); } TEST_F(AndroidInputSortedVector, remove_unknown_items_is_noop) { test_vector.add(3); test_vector.add(2); test_vector.add(0); EXPECT_EQ(android::NAME_NOT_FOUND, test_vector.remove(1)); EXPECT_EQ(3u, test_vector.size()); } TEST_F(AndroidInputSortedVector, item_at_removed_index_are_not_found) { test_vector.add(30); test_vector.add(10); test_vector.add(20); test_vector.add(00); ASSERT_EQ(10, test_vector.itemAt(1)); auto const old_index = test_vector.removeItemsAt(1); EXPECT_EQ(1, old_index); EXPECT_EQ(android::NAME_NOT_FOUND, test_vector.indexOf(10)); } TEST_F(AndroidInputSortedVector, count_items_at_removed_index_are_not_found) { test_vector.add(30); test_vector.add(10); test_vector.add(20); test_vector.add(00); ASSERT_EQ(10, test_vector.itemAt(1)); auto const old_index = test_vector.removeItemsAt(1, 2); EXPECT_EQ(1, old_index); EXPECT_EQ(android::NAME_NOT_FOUND, test_vector.indexOf(10)); EXPECT_EQ(android::NAME_NOT_FOUND, test_vector.indexOf(20)); EXPECT_NE(android::NAME_NOT_FOUND, test_vector.indexOf(00)); EXPECT_NE(android::NAME_NOT_FOUND, test_vector.indexOf(30)); } // Android utils use ALOG_ASSERT - which tends to bomb the program TEST_F(AndroidInputSortedVector, remove_beyond_end_fails) { test_vector.add(30); test_vector.add(10); test_vector.add(20); test_vector.add(00); EXPECT_EQ(android::BAD_VALUE, test_vector.removeItemsAt(4, 1)); EXPECT_EQ(android::BAD_VALUE, test_vector.removeItemsAt(3, 2)); EXPECT_EQ(android::BAD_VALUE, test_vector.removeItemsAt(2, 3)); EXPECT_EQ(android::BAD_VALUE, test_vector.removeItemsAt(1, 4)); EXPECT_EQ(android::BAD_VALUE, test_vector.removeItemsAt(0, 5)); EXPECT_EQ(4u, test_vector.size()); } ./tests/unit-tests/test_variable_length_array.cpp0000644000015600001650000000613312676616125022464 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * * Authored by: Alexandros Frantzis */ #include "mir/variable_length_array.h" #include #include TEST(VariableLengthArray, has_correct_size_if_using_builtin) { using namespace testing; size_t const builtin_size{100}; size_t const vla_size_builtin{builtin_size - 1}; mir::VariableLengthArray vla{vla_size_builtin}; EXPECT_THAT(vla.size(), Eq(vla_size_builtin)); memset(vla.data(), 0x55, vla.size()); } TEST(VariableLengthArray, has_correct_size_if_using_heap) { using namespace testing; size_t const builtin_size{100}; size_t const vla_size_heap{builtin_size + 1}; mir::VariableLengthArray vla{vla_size_heap}; EXPECT_THAT(vla.size(), Eq(vla_size_heap)); memset(vla.data(), 0x55, vla.size()); } TEST(VariableLengthArray, resizes_from_builtin_to_heap) { using namespace testing; size_t const builtin_size{100}; size_t const vla_size_builtin{builtin_size - 1}; size_t const vla_size_heap{builtin_size + 1}; mir::VariableLengthArray vla{vla_size_builtin}; vla.resize(vla_size_heap); EXPECT_THAT(vla.size(), Eq(vla_size_heap)); memset(vla.data(), 0x55, vla.size()); } TEST(VariableLengthArray, resizes_from_builtin_to_builtin) { using namespace testing; size_t const builtin_size{100}; size_t const vla_size_builtin1{builtin_size - 1}; size_t const vla_size_builtin2{builtin_size}; mir::VariableLengthArray vla{vla_size_builtin1}; vla.resize(vla_size_builtin2); EXPECT_THAT(vla.size(), Eq(vla_size_builtin2)); memset(vla.data(), 0x55, vla.size()); } TEST(VariableLengthArray, resizes_from_heap_to_builtin) { using namespace testing; size_t const builtin_size{100}; size_t const vla_size_builtin{builtin_size - 1}; size_t const vla_size_heap{builtin_size + 1}; mir::VariableLengthArray vla{vla_size_heap}; vla.resize(vla_size_builtin); EXPECT_THAT(vla.size(), Eq(vla_size_builtin)); memset(vla.data(), 0x55, vla.size()); } TEST(VariableLengthArray, resizes_from_heap_to_heap) { using namespace testing; size_t const builtin_size{100}; size_t const vla_size_heap1{builtin_size + 1}; size_t const vla_size_heap2{builtin_size + 2}; mir::VariableLengthArray vla{vla_size_heap1}; vla.resize(vla_size_heap2); EXPECT_THAT(vla.size(), Eq(vla_size_heap2)); memset(vla.data(), 0x55, vla.size()); } ./tests/unit-tests/test_flags.cpp0000644000015600001650000000512312676616125017232 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Andreas Pokorny */ #include "mir/flags.h" #include "mir_toolkit/common.h" #include #include namespace arbitrary_namespace { enum class Character : uint32_t { Empty = 0, Logical = 1<<0, Idealistic = 1<<1, Englihtened = 1<<2, Curious = 1<<3, Paranoid = 1<<16, Unstable = 1<<17, Reckless = 1<<18, Positive = (1<<4) - 1, Negative = 0xFFFF0000 }; Character mir_enable_enum_bit_operators(Character); using Profile = mir::Flags; } namespace mir { namespace ns_inside_mir { enum class Capability : uint8_t { Pointer = 1<<4, Touchpad = 1<<3}; Capability mir_enable_enum_bit_operators(Capability); using Capabilities = mir::Flags; } } namespace nested = mir::ns_inside_mir; namespace arb = arbitrary_namespace; TEST(MirFlags,lookup_rules_work_in_mir_nested_namespace) { using namespace testing; nested::Capabilities cap = nested::Capability::Pointer | nested::Capability::Touchpad; EXPECT_THAT(cap.value(), (1<<3) | (1<<4)); } TEST(MirFlags,lookup_rules_work_in_arbitrary_namespace) { using namespace testing; arb::Profile empty = arb::Character::Curious & arb::Character::Reckless; EXPECT_THAT(empty.value(),Eq(0)); } TEST(MirFlags,contains_check_works_for_masks) { using namespace testing; arb::Profile mostly_positive; mostly_positive |= arb::Character::Curious | arb::Character::Logical; EXPECT_THAT(contains(mostly_positive,arb::Character::Positive),Eq(false)); mostly_positive = mostly_positive | arb::Character::Idealistic | arb::Character::Englihtened; EXPECT_THAT(contains(mostly_positive,arb::Character::Positive),Eq(true)); } TEST(MirFlags, toggling_bits) { using namespace testing; arb::Profile negative{arb::Character::Negative}; EXPECT_THAT(contains(negative,arb::Character::Paranoid),Eq(true)); EXPECT_THAT(contains(negative^arb::Character::Paranoid,arb::Character::Paranoid),Eq(false)); } ./tests/unit-tests/thread/0000755000015600001650000000000012676616126015642 5ustar jenkinsjenkins./tests/unit-tests/thread/CMakeLists.txt0000644000015600001650000000022312676616125020376 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_thread_pool.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/thread/test_basic_thread_pool.cpp0000644000015600001650000001201512676616125023044 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #include "mir/thread/basic_thread_pool.h" #include "mir/thread_name.h" #include "mir/test/current_thread_name.h" #include "mir/test/signal.h" #include #include #include #include namespace mt = mir::test; namespace mth = mir::thread; namespace { class BasicThreadPool : public testing::Test { public: BasicThreadPool() : default_task_id{nullptr}, default_num_threads{3}, expected_name{"test_thread"} { } protected: mth::BasicThreadPool::TaskId default_task_id; int default_num_threads; std::string expected_name; }; class TestTask { public: TestTask() : TestTask("") { } TestTask(std::string name) : called{false}, block{false}, name{name} { } ~TestTask() { unblock(); } void operator()() noexcept { called = true; if (!name.empty()) mir::set_thread_name(name); else name = mt::current_thread_name(); if (block) signal.wait(); } std::string thread_name() const { return name; } bool was_called() const { return called; } void block_on_execution() { block = true; } void unblock() { signal.raise(); } private: std::atomic called; bool block; std::string name; mt::Signal signal; }; } TEST_F(BasicThreadPool, executes_given_functor) { using namespace testing; mth::BasicThreadPool p{default_num_threads}; TestTask task; auto future = p.run(std::ref(task)); future.wait(); EXPECT_TRUE(task.was_called()); } TEST_F(BasicThreadPool, executes_on_preferred_thread) { using namespace testing; mth::BasicThreadPool p{default_num_threads}; TestTask task1{expected_name}; task1.block_on_execution(); auto future1 = p.run(std::ref(task1), default_task_id); // This task should be queued to the thread associated with the given id // even when its busy TestTask task2; auto future2 = p.run(std::ref(task2), default_task_id); task1.unblock(); future1.wait(); future2.wait(); // Check if the second task executed on the same thread as the first task EXPECT_TRUE(task1.was_called()); EXPECT_TRUE(task2.was_called()); EXPECT_THAT(task2.thread_name(), Eq(expected_name)); } TEST_F(BasicThreadPool, recycles_threads) { using namespace testing; mth::BasicThreadPool p{2}; std::string const thread1_name = "thread1"; std::string const thread2_name = "thread2"; //Create a couple of blocking tasks, so that we force the pool //to assign new threads to each task TestTask task1{thread1_name}; task1.block_on_execution(); auto future1 = p.run(std::ref(task1)); TestTask task2{thread2_name}; task2.block_on_execution(); auto future2 = p.run(std::ref(task2)); task1.unblock(); task2.unblock(); future1.wait(); future2.wait(); // Now we should have some idle threads, the next task should // run in any of the two previously created threads TestTask task3; auto future = p.run(std::ref(task3)); future.wait(); EXPECT_TRUE(task1.was_called()); EXPECT_TRUE(task2.was_called()); EXPECT_TRUE(task3.was_called()); EXPECT_THAT(task3.thread_name(), AnyOf(Eq(thread1_name), Eq(thread2_name))); } TEST_F(BasicThreadPool, creates_new_threads) { using namespace testing; mth::BasicThreadPool p{1}; TestTask task1{expected_name}; task1.block_on_execution(); auto future1 = p.run(std::ref(task1)); TestTask task2; auto future2 = p.run(std::ref(task2)); task1.unblock(); future1.wait(); future2.wait(); EXPECT_TRUE(task1.was_called()); EXPECT_TRUE(task2.was_called()); EXPECT_THAT(task2.thread_name(), Ne(expected_name)); } TEST_F(BasicThreadPool, can_shrink) { using namespace testing; mth::BasicThreadPool p{0}; TestTask task1{expected_name}; auto future = p.run(std::ref(task1)); future.wait(); // This should delete all threads since we specified a minimum of 0 threads for our pool p.shrink(); TestTask task2; future = p.run(std::ref(task2)); future.wait(); EXPECT_TRUE(task1.was_called()); EXPECT_TRUE(task2.was_called()); EXPECT_THAT(task2.thread_name(), Ne(expected_name)); } ./tests/unit-tests/test_gmock_fixes.cpp0000644000015600001650000000314612676616125020437 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/test/gmock_fixes.h" #include #include TEST(GMock, return_by_move) { struct Interface { virtual ~Interface() = default; virtual std::unique_ptr function() const = 0; }; struct MockImplementation : Interface { MOCK_CONST_METHOD0(function, std::unique_ptr()); ~MockImplementation() noexcept {} }; MockImplementation mi; EXPECT_CALL(mi, function()); mi.function(); } TEST(GMock, return_by_move_with_deleter_function) { struct Interface { virtual ~Interface() = default; virtual std::unique_ptr function() const = 0; }; struct MockImplementation : Interface { MOCK_CONST_METHOD0(function, std::unique_ptr()); ~MockImplementation() noexcept {} }; MockImplementation mi; EXPECT_CALL(mi, function()); mi.function(); } ./tests/unit-tests/scene/0000755000015600001650000000000012676616160015466 5ustar jenkinsjenkins./tests/unit-tests/scene/test_prompt_session_impl.cpp0000644000015600001650000000623212676616125023342 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #include "src/server/scene/prompt_session_impl.h" #include "mir/test/doubles/mock_scene_session.h" #include "mir/test/fake_shared.h" #include #include namespace ms = mir::scene; namespace mt = mir::test; namespace mtd = mir::test::doubles; using namespace ::testing; namespace { struct PromptSession : public testing::Test { std::shared_ptr const helper{std::make_shared<::testing::NiceMock>()}; }; } TEST_F(PromptSession, start_when_stopped) { ms::PromptSessionImpl prompt_session; EXPECT_EQ(prompt_session.state(), mir_prompt_session_state_stopped); EXPECT_CALL(*helper, start_prompt_session()).Times(1); prompt_session.start(helper); EXPECT_EQ(prompt_session.state(), mir_prompt_session_state_started); } TEST_F(PromptSession, stop_when_started) { ms::PromptSessionImpl prompt_session; prompt_session.start(helper); EXPECT_EQ(prompt_session.state(), mir_prompt_session_state_started); EXPECT_CALL(*helper, stop_prompt_session()).Times(1); prompt_session.stop(helper); EXPECT_EQ(prompt_session.state(), mir_prompt_session_state_stopped); } TEST_F(PromptSession, suspend_when_started) { ms::PromptSessionImpl prompt_session; prompt_session.start(helper); EXPECT_EQ(prompt_session.state(), mir_prompt_session_state_started); EXPECT_CALL(*helper, suspend_prompt_session()).Times(1); prompt_session.suspend(helper); EXPECT_EQ(prompt_session.state(), mir_prompt_session_state_suspended); } TEST_F(PromptSession, suspend_fails_to_stop_helper_when_not_started) { ms::PromptSessionImpl prompt_session; EXPECT_CALL(*helper, suspend_prompt_session()).Times(0); prompt_session.suspend(helper); EXPECT_EQ(prompt_session.state(), mir_prompt_session_state_stopped); } TEST_F(PromptSession, resume_when_suspended) { ms::PromptSessionImpl prompt_session; prompt_session.start(helper); prompt_session.suspend(helper); EXPECT_EQ(prompt_session.state(), mir_prompt_session_state_suspended); EXPECT_CALL(*helper, resume_prompt_session()).Times(1); prompt_session.resume(helper); EXPECT_EQ(prompt_session.state(), mir_prompt_session_state_started); } TEST_F(PromptSession, resume_fails_to_stop_helper_when_not_started) { ms::PromptSessionImpl prompt_session; EXPECT_CALL(*helper, resume_prompt_session()).Times(0); prompt_session.resume(helper); EXPECT_EQ(prompt_session.state(), mir_prompt_session_state_stopped); } ./tests/unit-tests/scene/test_broadcasting_session_event_sink.cpp0000644000015600001650000000527312676616125025671 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #include "src/server/scene/broadcasting_session_event_sink.h" #include "mir/test/doubles/stub_session.h" #include "mir/test/fake_shared.h" #include namespace ms = mir::scene; namespace mtd = mir::test::doubles; namespace mt = mir::test; TEST(BroadcastingSessionEventSinkTest, emits_and_handles_focus_change) { mtd::StubSession session1; std::vector handler_called(3, nullptr); ms::BroadcastingSessionEventSink events; for (auto& h : handler_called) { events.register_focus_change_handler( [&h](std::shared_ptr const& session) { h = session.get(); }); } events.handle_focus_change(mt::fake_shared(session1)); for (unsigned int i = 0; i < handler_called.size(); i++) { EXPECT_EQ(&session1, handler_called[i]) << " i = " << i; } } TEST(BroadcastingSessionEventSinkTest, emits_and_handles_no_focus) { mtd::StubSession session1; std::vector handler_called(3, 0); ms::BroadcastingSessionEventSink events; for (auto& h : handler_called) { events.register_no_focus_handler( [&h] { h = 1; }); } events.handle_no_focus(); for (unsigned int i = 0; i < handler_called.size(); i++) { EXPECT_EQ(1, handler_called[i]) << " i = " << i; } } TEST(BroadcastingSessionEventSinkTest, emits_and_handles_session_stopping) { mtd::StubSession session1; std::vector handler_called(3, nullptr); ms::BroadcastingSessionEventSink events; for (auto& h : handler_called) { events.register_session_stopping_handler( [&h](std::shared_ptr const& session) { h = session.get(); }); } events.handle_session_stopping(mt::fake_shared(session1)); for (unsigned int i = 0; i < handler_called.size(); i++) { EXPECT_EQ(&session1, handler_called[i]) << " i = " << i; } } ./tests/unit-tests/scene/test_global_event_sender.cpp0000644000015600001650000000413212676616125023233 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/server/scene/global_event_sender.h" #include "src/server/scene/session_container.h" #include "mir/test/doubles/mock_scene_session.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/fake_shared.h" #include #include namespace mt=mir::test; namespace mtd=mir::test::doubles; namespace ms = mir::scene; namespace { class MockSessionStorage : public ms::SessionContainer { public: MOCK_METHOD1(insert_session, void(std::shared_ptr const&)); MOCK_METHOD1(remove_session, void(std::shared_ptr const&)); MOCK_CONST_METHOD1(for_each, void(std::function const&)>)); MOCK_CONST_METHOD1(successor_of, std::shared_ptr(std::shared_ptr const&)); }; } TEST(GlobalEventSender, sender) { using namespace testing; MockSessionStorage mock_storage; std::function const&)> called_fn; EXPECT_CALL(mock_storage, for_each(_)) .Times(1) .WillOnce(SaveArg<0>(&called_fn)); ms::GlobalEventSender g_sender(mt::fake_shared(mock_storage)); mtd::StubDisplayConfig stub_display_config; g_sender.handle_display_config_change(stub_display_config); auto mock_session = std::make_shared(); EXPECT_CALL(*mock_session, send_display_config(_)) .Times(1); called_fn(mock_session); } ./tests/unit-tests/scene/test_surface.cpp0000644000015600001650000003201112676616125020657 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #include "mir/events/event_private.h" #include "mir/events/event_builders.h" #include "src/server/scene/basic_surface.h" #include "src/server/scene/legacy_surface_change_notification.h" #include "src/server/report/null_report_factory.h" #include "src/server/scene/output_properties_cache.h" #include "mir/frontend/event_sink.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/scene/surface_event_source.h" #include "mir/input/input_channel.h" #include "mir/test/doubles/mock_buffer_stream.h" #include "mir/test/doubles/mock_input_surface.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/mock_input_sender.h" #include "mir/test/doubles/stub_input_channel.h" #include "mir/test/doubles/stub_input_sender.h" #include "mir/test/doubles/null_event_sink.h" #include "mir/test/fake_shared.h" #include "mir/test/event_matchers.h" #include "gmock_set_arg.h" #include #include #include #include namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell; namespace mg = mir::graphics; namespace mi = mir::input; namespace mev = mir::events; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mt::doubles; namespace mr = mir::report; namespace { struct MockInputChannel : public mi::InputChannel { MOCK_CONST_METHOD0(server_fd, int()); MOCK_CONST_METHOD0(client_fd, int()); }; } TEST(SurfaceCreationParametersTest, default_creation_parameters) { using namespace geom; ms::SurfaceCreationParameters params; geom::Point const default_point{geom::X{0}, geom::Y{0}}; EXPECT_EQ(std::string(), params.name); EXPECT_EQ(Width(0), params.size.width); EXPECT_EQ(Height(0), params.size.height); EXPECT_EQ(default_point, params.top_left); EXPECT_EQ(mg::BufferUsage::undefined, params.buffer_usage); EXPECT_EQ(mir_pixel_format_invalid, params.pixel_format); EXPECT_FALSE(params.type.is_set()); EXPECT_FALSE(params.state.is_set()); EXPECT_FALSE(params.preferred_orientation.is_set()); EXPECT_FALSE(params.parent_id.is_set()); EXPECT_EQ(ms::a_surface(), params); } TEST(SurfaceCreationParametersTest, builder_mutators) { using namespace geom; Size const size{1024, 768}; mg::BufferUsage const usage{mg::BufferUsage::hardware}; MirPixelFormat const format{mir_pixel_format_abgr_8888}; std::string name{"surface"}; MirSurfaceState state{mir_surface_state_fullscreen}; MirSurfaceType type{mir_surface_type_dialog}; MirOrientationMode mode{mir_orientation_mode_landscape}; mf::SurfaceId surf_id{1000}; auto params = ms::a_surface() .of_name(name) .of_size(size) .of_buffer_usage(usage) .of_pixel_format(format) .of_type(type) .with_parent_id(surf_id) .with_preferred_orientation(mode) .with_state(state); EXPECT_EQ(name, params.name); EXPECT_EQ(size, params.size); EXPECT_EQ(usage, params.buffer_usage); EXPECT_EQ(format, params.pixel_format); EXPECT_EQ(type, params.type); EXPECT_EQ(state, params.state); EXPECT_EQ(mode, params.preferred_orientation); EXPECT_EQ(surf_id, params.parent_id); } TEST(SurfaceCreationParametersTest, equality) { using namespace geom; Size const size{1024, 768}; mg::BufferUsage const usage{mg::BufferUsage::hardware}; MirPixelFormat const format{mir_pixel_format_abgr_8888}; auto params0 = ms::a_surface().of_name("surface") .of_size(size) .of_buffer_usage(usage) .of_pixel_format(format); auto params1 = ms::a_surface().of_name("surface") .of_size(size) .of_buffer_usage(usage) .of_pixel_format(format); EXPECT_EQ(params0, params1); EXPECT_EQ(params1, params0); } TEST(SurfaceCreationParametersTest, inequality) { using namespace geom; std::vector const sizes{{1024, 768}, {1025, 768}}; std::vector const usages{mg::BufferUsage::hardware, mg::BufferUsage::software}; std::vector const formats{mir_pixel_format_abgr_8888, mir_pixel_format_bgr_888}; std::vector params_vec; for (auto const& size : sizes) { for (auto const& usage : usages) { for (auto const& format : formats) { auto cur_params = ms::a_surface().of_name("surface0") .of_size(size) .of_buffer_usage(usage) .of_pixel_format(format); params_vec.push_back(cur_params); size_t cur_index = params_vec.size() - 1; /* * Compare the current SurfaceCreationParameters with all the previously * created ones. */ for (size_t i = 0; i < cur_index; i++) { EXPECT_NE(params_vec[i], params_vec[cur_index]) << "cur_index: " << cur_index << " i: " << i; EXPECT_NE(params_vec[cur_index], params_vec[i]) << "cur_index: " << cur_index << " i: " << i; } } } } } namespace { struct MockEventSink : mtd::NullEventSink { MOCK_METHOD1(handle_event, void(MirEvent const&)); }; struct SurfaceCreation : public ::testing::Test { SurfaceCreation() : surface(surface_name, rect, false, mock_buffer_stream, std::make_shared(), std::make_shared(), nullptr /* cursor_image */, report) { } virtual void SetUp() { using namespace testing; notification_count = 0; change_notification = [this]() { notification_count++; }; ON_CALL(*mock_buffer_stream, acquire_client_buffer(_)) .WillByDefault(InvokeArgument<0>(&stub_buffer)); } std::shared_ptr> mock_buffer_stream = std::make_shared>(); std::function change_notification; int notification_count = 0; mtd::StubBuffer stub_buffer; std::string surface_name = "test_surfaceA"; MirPixelFormat pf = mir_pixel_format_abgr_8888; geom::Size size = geom::Size{43, 420}; geom::Stride stride = geom::Stride{4 * size.width.as_uint32_t()}; geom::Rectangle rect = geom::Rectangle{geom::Point{geom::X{0}, geom::Y{0}}, size}; std::shared_ptr const report = mr::null_scene_report(); ms::BasicSurface surface; }; } TEST_F(SurfaceCreation, test_surface_gets_right_name) { EXPECT_EQ(surface_name, surface.name()); } TEST_F(SurfaceCreation, test_surface_queries_state_for_size) { EXPECT_EQ(size, surface.size()); } TEST_F(SurfaceCreation, constructed_stream_is_primary) { using namespace testing; EXPECT_THAT(surface.primary_buffer_stream(), Eq(mock_buffer_stream)); } TEST_F(SurfaceCreation, test_surface_gets_top_left) { auto ret_top_left = surface.top_left(); EXPECT_EQ(geom::Point(), ret_top_left); } TEST_F(SurfaceCreation, test_surface_move_to) { geom::Point p{55, 66}; surface.move_to(p); EXPECT_EQ(p, surface.top_left()); } TEST_F(SurfaceCreation, resize_updates_stream_and_state) { using namespace testing; geom::Size const new_size{123, 456}; EXPECT_CALL(*mock_buffer_stream, resize(new_size)) .Times(1); auto const mock_event_sink = std::make_shared(); ms::OutputPropertiesCache cache; auto const observer = std::make_shared(mf::SurfaceId(), surface, cache, mock_event_sink); surface.add_observer(observer); ASSERT_THAT(surface.size(), Ne(new_size)); EXPECT_CALL(*mock_event_sink, handle_event(_)).Times(1); surface.resize(new_size); EXPECT_THAT(surface.size(), Eq(new_size)); } TEST_F(SurfaceCreation, duplicate_resize_ignored) { using namespace testing; geom::Size const new_size{123, 456}; auto const mock_event_sink = std::make_shared(); ms::OutputPropertiesCache cache; auto const observer = std::make_shared(mf::SurfaceId(), surface, cache, mock_event_sink); surface.add_observer(observer); ASSERT_THAT(surface.size(), Ne(new_size)); EXPECT_CALL(*mock_buffer_stream, resize(new_size)).Times(1); EXPECT_CALL(*mock_event_sink, handle_event(_)).Times(1); surface.resize(new_size); EXPECT_THAT(surface.size(), Eq(new_size)); Mock::VerifyAndClearExpectations(mock_buffer_stream.get()); Mock::VerifyAndClearExpectations(mock_event_sink.get()); EXPECT_CALL(*mock_buffer_stream, resize(_)).Times(0); EXPECT_CALL(*mock_event_sink, handle_event(_)).Times(0); surface.resize(new_size); EXPECT_THAT(surface.size(), Eq(new_size)); } TEST_F(SurfaceCreation, unsuccessful_resize_does_not_update_state) { using namespace testing; geom::Size const new_size{123, 456}; EXPECT_CALL(*mock_buffer_stream, resize(new_size)) .Times(1) .WillOnce(Throw(std::runtime_error("bad resize"))); EXPECT_THROW({ surface.resize(new_size); }, std::runtime_error); EXPECT_EQ(size, surface.size()); } TEST_F(SurfaceCreation, impossible_resize_clamps) { using namespace testing; geom::Size const bad_sizes[] = { {0, 123}, {456, 0}, {-1, -1}, {78, -10}, {0, 0} }; for (auto &size : bad_sizes) { geom::Size expect_size = size; if (expect_size.width <= geom::Width{0}) expect_size.width = geom::Width{1}; if (expect_size.height <= geom::Height{0}) expect_size.height = geom::Height{1}; EXPECT_CALL(*mock_buffer_stream, resize(expect_size)).Times(1); EXPECT_NO_THROW({ surface.resize(size); }); EXPECT_EQ(expect_size, surface.size()); } } TEST_F(SurfaceCreation, test_surface_set_alpha) { using namespace testing; float alpha = 0.5f; surface.set_alpha(alpha); EXPECT_FLOAT_EQ(alpha, surface.alpha()); auto renderables = surface.generate_renderables(nullptr); ASSERT_THAT(renderables.size(), Ge(1)); EXPECT_FLOAT_EQ(alpha, renderables[0]->alpha()); alpha = 0.1; surface.set_alpha(alpha); EXPECT_FLOAT_EQ(alpha, surface.alpha()); renderables = surface.generate_renderables(nullptr); ASSERT_THAT(renderables.size(), Ge(1)); EXPECT_FLOAT_EQ(alpha, renderables[0]->alpha()); } TEST_F(SurfaceCreation, input_fds) { using namespace testing; MockInputChannel channel; int const client_fd = 13; EXPECT_CALL(channel, client_fd()).Times(AnyNumber()).WillRepeatedly(Return(client_fd)); ms::BasicSurface input_surf( surface_name, rect, false, mock_buffer_stream, mt::fake_shared(channel), std::make_shared(), std::shared_ptr(), report); EXPECT_EQ(client_fd, input_surf.client_input_fd()); } TEST_F(SurfaceCreation, consume_calls_send_event) { using namespace testing; NiceMock mock_sender; ms::BasicSurface surface( surface_name, rect, false, mock_buffer_stream, std::make_shared(), mt::fake_shared(mock_sender), std::shared_ptr(), report); auto key_event = mev::make_event(MirInputDeviceId(0), std::chrono::nanoseconds(0), std::vector{}, mir_keyboard_action_down, 0, 0, mir_input_event_modifier_none); auto touch_event = mev::make_event(MirInputDeviceId(0), std::chrono::nanoseconds(0), std::vector{}, mir_input_event_modifier_none); mev::add_touch(*touch_event, 0, mir_touch_action_down, mir_touch_tooltype_finger, 0, 0, 0, 0, 0, 0); EXPECT_CALL(mock_sender, send_event(mt::MirKeyboardEventMatches(*key_event), _)).Times(1); EXPECT_CALL(mock_sender, send_event(mt::MirTouchEventMatches(*touch_event), _)).Times(1); surface.consume(key_event.get()); surface.consume(touch_event.get()); } ./tests/unit-tests/scene/test_gl_pixel_buffer.cpp0000644000015600001650000001631512676616125022374 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/scene/gl_pixel_buffer.h" #include "mir/graphics/gl_context.h" #include "mir/test/doubles/mock_gl_buffer.h" #include "mir/test/doubles/mock_gl.h" #include #include #include namespace mg = mir::graphics; namespace geom = mir::geometry; namespace ms = mir::scene; namespace mtd = mir::test::doubles; namespace { struct MockGLContext : public mg::GLContext { ~MockGLContext() noexcept {} MOCK_CONST_METHOD0(make_current, void()); MOCK_CONST_METHOD0(release_current, void()); }; struct WrappingGLContext : public mg::GLContext { WrappingGLContext(mg::GLContext& wrapped) : wrapped(wrapped) { } ~WrappingGLContext() noexcept {} void make_current() const { wrapped.make_current(); } void release_current() const { wrapped.release_current(); } mg::GLContext& wrapped; }; class GLPixelBufferTest : public ::testing::Test { public: GLPixelBufferTest() : context{new WrappingGLContext{mock_context}} { using namespace testing; ON_CALL(mock_buffer, size()) .WillByDefault(Return(geom::Size{51, 71})); } testing::NiceMock mock_gl; testing::NiceMock mock_buffer; MockGLContext mock_context; std::unique_ptr context; }; ACTION(FillPixels) { auto const pixels = static_cast(arg6); size_t const width = arg2; size_t const height = arg3; for (uint32_t i = 0; i < width * height; ++i) { pixels[i] = i; } } ACTION(FillPixelsRGBA) { auto const pixels = static_cast(arg6); size_t const width = arg2; size_t const height = arg3; for (uint32_t i = 0; i < width * height; ++i) { pixels[i] = ((i << 16) & 0x00ff0000) | /* Move R to new position */ ((i) & 0x0000ff00) | /* G remains at same position */ ((i >> 16) & 0x000000ff) | /* Move B to new position */ ((i) & 0xff000000); /* A remains at same position */ } } } TEST_F(GLPixelBufferTest, returns_empty_if_not_initialized) { ms::GLPixelBuffer pixels{std::move(context)}; EXPECT_EQ(geom::Size(), pixels.size()); EXPECT_EQ(geom::Stride(), pixels.stride()); } TEST_F(GLPixelBufferTest, returns_data_from_bgra_buffer_texture) { using namespace testing; GLuint const tex{10}; GLuint const fbo{20}; uint32_t const width{mock_buffer.size().width.as_uint32_t()}; uint32_t const height{mock_buffer.size().height.as_uint32_t()}; { InSequence s; /* The GL context is made current */ EXPECT_CALL(mock_context, make_current()); /* The texture and framebuffer are prepared */ EXPECT_CALL(mock_gl, glGenTextures(_,_)) .WillOnce(SetArgPointee<1>(tex)); EXPECT_CALL(mock_gl, glBindTexture(_,tex)); EXPECT_CALL(mock_gl, glGenFramebuffers(_,_)) .WillOnce(SetArgPointee<1>(fbo)); EXPECT_CALL(mock_gl, glBindFramebuffer(_,fbo)); /* The buffer texture is read as BGRA */ EXPECT_CALL(mock_buffer, gl_bind_to_texture()); EXPECT_CALL(mock_gl, glFramebufferTexture2D(_,_,_,tex,0)); EXPECT_CALL(mock_gl, glReadPixels(0, 0, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, _)) .WillOnce(FillPixels()); /* at destruction */ EXPECT_CALL(mock_context, make_current()); EXPECT_CALL(mock_gl, glDeleteTextures(_,_)); EXPECT_CALL(mock_gl, glDeleteFramebuffers(_,_)); } ms::GLPixelBuffer pixels{std::move(context)}; pixels.fill_from(mock_buffer); auto data = pixels.as_argb_8888(); EXPECT_EQ(mock_buffer.size(), pixels.size()); EXPECT_EQ(geom::Stride{width * 4}, pixels.stride()); /* Check that data has been properly y-flipped */ EXPECT_EQ(1, static_cast(data)[width * (height - 1) + 1]); EXPECT_EQ(width * (height / 2), static_cast(data)[width * (height / 2)]); EXPECT_EQ(width * (height - 1), static_cast(data)[0]); EXPECT_EQ(width - 1, static_cast(data)[width * height - 1]); } TEST_F(GLPixelBufferTest, returns_data_from_rgba_buffer_texture) { using namespace testing; GLuint const tex{10}; GLuint const fbo{20}; uint32_t const width{mock_buffer.size().width.as_uint32_t()}; uint32_t const height{mock_buffer.size().height.as_uint32_t()}; { InSequence s; /* The GL context is made current */ EXPECT_CALL(mock_context, make_current()); /* The texture and framebuffer are prepared */ EXPECT_CALL(mock_gl, glGenTextures(_,_)) .WillOnce(SetArgPointee<1>(tex)); EXPECT_CALL(mock_gl, glBindTexture(_,tex)); EXPECT_CALL(mock_gl, glGenFramebuffers(_,_)) .WillOnce(SetArgPointee<1>(fbo)); EXPECT_CALL(mock_gl, glBindFramebuffer(_,fbo)); /* Try to read the FBO as BGRA but fail */ EXPECT_CALL(mock_buffer, gl_bind_to_texture()); EXPECT_CALL(mock_gl, glFramebufferTexture2D(_,_,_,tex,0)); EXPECT_CALL(mock_gl, glGetError()) .WillOnce(Return(GL_NO_ERROR)); EXPECT_CALL(mock_gl, glReadPixels(0, 0, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, _)); EXPECT_CALL(mock_gl, glGetError()) .WillOnce(Return(GL_INVALID_ENUM)); /* Read as RGBA */ EXPECT_CALL(mock_gl, glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, _)) .WillOnce(FillPixelsRGBA()); /* at destruction */ EXPECT_CALL(mock_context, make_current()); EXPECT_CALL(mock_gl, glDeleteTextures(_,_)); EXPECT_CALL(mock_gl, glDeleteFramebuffers(_,_)); } ms::GLPixelBuffer pixels{std::move(context)}; pixels.fill_from(mock_buffer); auto data = pixels.as_argb_8888(); EXPECT_EQ(mock_buffer.size(), pixels.size()); EXPECT_EQ(geom::Stride{width * 4}, pixels.stride()); /* Check that data has been properly y-flipped and converted to argb_8888 */ EXPECT_EQ(1, static_cast(data)[width * (height - 1) + 1]); EXPECT_EQ(width * (height / 2), static_cast(data)[width * (height / 2)]); EXPECT_EQ(width * (height - 1), static_cast(data)[0]); EXPECT_EQ(width - 1, static_cast(data)[width * height - 1]); } ./tests/unit-tests/scene/test_prompt_session_manager.cpp0000644000015600001650000002147212676616125024016 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "src/server/scene/prompt_session_manager_impl.h" #include "src/server/scene/session_container.h" #include "mir/scene/prompt_session.h" #include "mir/scene/prompt_session_creation_parameters.h" #include "mir/test/doubles/mock_prompt_session_listener.h" #include "mir/test/doubles/stub_session.h" #include "mir/test/fake_shared.h" #include #include namespace ms = mir::scene; namespace mt = mir::test; namespace mtd = mir::test::doubles; using namespace ::testing; namespace { struct StubSessionContainer : ms::SessionContainer { void insert_session(std::shared_ptr const& session) { sessions.push_back(session); } void remove_session(std::shared_ptr const&) { } void for_each(std::function const&)> f) const { for (auto const& session : sessions) f(session); } std::shared_ptr successor_of(std::shared_ptr const&) const { return {}; } std::vector> sessions; }; struct PromptSessionManager : public testing::Test { pid_t const helper_pid = __LINE__; pid_t const application_pid = __LINE__; pid_t const prompt_provider_pid = __LINE__; std::shared_ptr const helper{std::make_shared(helper_pid)}; std::shared_ptr const application_session{std::make_shared(application_pid)}; std::shared_ptr const provider_session{std::make_shared(prompt_provider_pid)}; std::shared_ptr const another_prompt_provider{std::make_shared(__LINE__)}; ms::PromptSessionCreationParameters parameters; StubSessionContainer existing_sessions; NiceMock prompt_session_listener; ms::PromptSessionManagerImpl session_manager{mt::fake_shared(existing_sessions), mt::fake_shared(prompt_session_listener)}; std::shared_ptr prompt_session; void SetUp() { existing_sessions.insert_session(application_session); parameters.application_pid = application_pid; prompt_session = session_manager.start_prompt_session_for(helper, parameters); } std::vector> list_providers_for(std::shared_ptr const& prompt_session) { std::vector> results; auto providers_fn = [&results](std::weak_ptr const& session) { results.push_back(session.lock()); }; session_manager.for_each_provider_in(prompt_session, providers_fn); return results; } void helper_for() { session_manager.helper_for(prompt_session); } void application_for() { session_manager.application_for(prompt_session); } void iterate_providers() { session_manager.for_each_provider_in(prompt_session, [](std::shared_ptr const&) {}); } }; } TEST_F(PromptSessionManager, notifies_provider_of_start_and_stop) { InSequence seq; EXPECT_CALL(prompt_session_listener, starting(_)).Times(1); auto const prompt_session = session_manager.start_prompt_session_for(helper, parameters); EXPECT_CALL(prompt_session_listener, stopping(Eq(prompt_session))).Times(1); session_manager.stop_prompt_session(prompt_session); } TEST_F(PromptSessionManager, notifies_provider_of_suspend_and_resume) { InSequence seq; EXPECT_CALL(prompt_session_listener, suspending(_)).Times(1); session_manager.suspend_prompt_session(prompt_session); EXPECT_CALL(prompt_session_listener, resuming(Eq(prompt_session))).Times(1); session_manager.resume_prompt_session(prompt_session); } TEST_F(PromptSessionManager, sets_helper_for) { EXPECT_EQ(session_manager.helper_for(prompt_session), helper); } TEST_F(PromptSessionManager, sets_application_for) { EXPECT_EQ(session_manager.application_for(prompt_session), application_session); } TEST_F(PromptSessionManager, successfully_adds_a_provider) { session_manager.add_prompt_provider(prompt_session, provider_session); EXPECT_THAT(list_providers_for(prompt_session), ElementsAre(provider_session)); } TEST_F(PromptSessionManager, no_exception_when_adding_a_prompt_provider_twice) { session_manager.add_prompt_provider(prompt_session, provider_session); EXPECT_NO_THROW(session_manager.add_prompt_provider(prompt_session, provider_session)); } TEST_F(PromptSessionManager, thows_exception_when_adding_a_prompt_provider_with_stopped_prompt_session) { session_manager.stop_prompt_session(prompt_session); EXPECT_THROW( session_manager.add_prompt_provider(prompt_session, provider_session), std::runtime_error); } TEST_F(PromptSessionManager, can_iterate_over_prompt_providers_in_a_prompt_session) { session_manager.add_prompt_provider(prompt_session, provider_session); session_manager.add_prompt_provider(prompt_session, another_prompt_provider); struct { MOCK_METHOD1(enumerate, void(std::shared_ptr const& prompt_provider)); } mock; EXPECT_CALL(mock, enumerate(provider_session)); EXPECT_CALL(mock, enumerate(another_prompt_provider)); session_manager.for_each_provider_in( prompt_session, [&](std::shared_ptr const& prompt_provider) { mock.enumerate(prompt_provider); }); } TEST_F(PromptSessionManager, can_fetch_application_during_listener_notifications) { ON_CALL(prompt_session_listener, starting(_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::application_for)); ON_CALL(prompt_session_listener, stopping(_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::application_for)); ON_CALL(prompt_session_listener, prompt_provider_added(_,_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::application_for)); ON_CALL(prompt_session_listener, prompt_provider_removed(_,_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::application_for)); auto const prompt_session = session_manager.start_prompt_session_for(helper, parameters); session_manager.add_prompt_provider(prompt_session, provider_session); session_manager.remove_session(provider_session); session_manager.stop_prompt_session(prompt_session); } TEST_F(PromptSessionManager, can_fetch_helper_during_listener_notifications) { ON_CALL(prompt_session_listener, starting(_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::helper_for)); ON_CALL(prompt_session_listener, stopping(_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::helper_for)); ON_CALL(prompt_session_listener, prompt_provider_added(_,_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::helper_for)); ON_CALL(prompt_session_listener, prompt_provider_removed(_,_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::helper_for)); auto const prompt_session = session_manager.start_prompt_session_for(helper, parameters); session_manager.add_prompt_provider(prompt_session, provider_session); session_manager.remove_session(provider_session); session_manager.stop_prompt_session(prompt_session); } TEST_F(PromptSessionManager, can_iterating_over_prompt_providers_during_listener_notifications) { ON_CALL(prompt_session_listener, starting(_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::iterate_providers)); ON_CALL(prompt_session_listener, stopping(_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::iterate_providers)); ON_CALL(prompt_session_listener, prompt_provider_added(_,_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::iterate_providers)); ON_CALL(prompt_session_listener, prompt_provider_removed(_,_)).WillByDefault(InvokeWithoutArgs(this, &PromptSessionManager::iterate_providers)); auto const prompt_session = session_manager.start_prompt_session_for(helper, parameters); session_manager.add_prompt_provider(prompt_session, provider_session); session_manager.remove_session(provider_session); session_manager.stop_prompt_session(prompt_session); } ./tests/unit-tests/scene/test_the_session_container_implementation.cpp0000644000015600001650000000517012676616125026727 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #include "src/server/scene/application_session.h" #include "src/server/scene/default_session_container.h" #include "mir/test/doubles/stub_session.h" #include #include #include namespace mf = mir::frontend; namespace ms = mir::scene; namespace mtd = mir::test::doubles; TEST(DefaultSessionContainer, for_each) { using namespace ::testing; ms::DefaultSessionContainer container; auto session1 = std::make_shared(); auto session2 = std::make_shared(); container.insert_session(session1); container.insert_session(session2); struct local { MOCK_METHOD1(see, void(std::shared_ptr const&)); void operator()(std::shared_ptr const& session) { see(session); } } functor; InSequence seq; EXPECT_CALL(functor, see(Eq(session1))); EXPECT_CALL(functor, see(Eq(session2))); container.for_each(std::ref(functor)); } TEST(DefaultSessionContainer, successor_of) { using namespace ::testing; ms::DefaultSessionContainer container; auto session1 = std::make_shared(); auto session2 = std::make_shared(); auto session3 = std::make_shared(); container.insert_session(session1); container.insert_session(session2); container.insert_session(session3); EXPECT_EQ(session2, container.successor_of(session1)); EXPECT_EQ(session3, container.successor_of(session2)); EXPECT_EQ(session1, container.successor_of(session3)); // Successor of no session is the last session. EXPECT_EQ(session3, container.successor_of(std::shared_ptr())); } TEST(DefaultSessionContainer, invalid_session_throw_behavior) { using namespace ::testing; ms::DefaultSessionContainer container; EXPECT_THROW({ container.remove_session(std::make_shared()); }, std::logic_error); } ./tests/unit-tests/scene/test_basic_surface.cpp0000644000015600001650000010430412676616157022032 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/server/scene/basic_surface.h" #include "src/server/scene/legacy_surface_change_notification.h" #include "mir/events/event_private.h" #include "mir/frontend/event_sink.h" #include "mir/geometry/rectangle.h" #include "mir/geometry/displacement.h" #include "mir/scene/null_surface_observer.h" #include "mir/events/event_builders.h" #include "mir/test/doubles/stub_cursor_image.h" #include "mir/test/doubles/mock_buffer_stream.h" #include "mir/test/doubles/mock_input_sender.h" #include "mir/test/doubles/stub_input_sender.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/fake_shared.h" #include "src/server/report/null_report_factory.h" #include #include #include #include namespace mc = mir::compositor; namespace mf = mir::frontend; namespace mi = mir::input; namespace mr = mir::report; namespace ms = mir::scene; namespace mg = mir::graphics; namespace msh = mir::shell; namespace mev = mir::events; namespace mt = mir::test; namespace mtd = mt::doubles; namespace geom = mir::geometry; namespace { class MockCallback { public: MOCK_METHOD0(call, void()); }; class MockSurfaceObserver : public ms::NullSurfaceObserver { public: MOCK_METHOD2(attrib_changed, void(MirSurfaceAttrib, int)); MOCK_METHOD1(hidden_set_to, void(bool)); MOCK_METHOD1(renamed, void(char const*)); MOCK_METHOD0(client_surface_close_requested, void()); MOCK_METHOD1(cursor_image_set_to, void(mir::graphics::CursorImage const& image)); MOCK_METHOD0(cursor_image_removed, void()); }; void post_a_frame(ms::BasicSurface& surface) { /* * Make sure there's a frame ready. Otherwise visible()==false and the * input_area will never report it containing anything for all the tests * that use it. */ mtd::StubBuffer buffer; surface.primary_buffer_stream()->swap_buffers(&buffer, [&](mir::graphics::Buffer*){}); } struct BasicSurfaceTest : public testing::Test { std::string const name{"aa"}; geom::Rectangle const rect{{4,7},{5,9}}; testing::NiceMock mock_callback; std::function null_change_cb{[]{}}; std::function mock_change_cb{std::bind(&MockCallback::call, &mock_callback)}; std::shared_ptr> mock_buffer_stream = std::make_shared>(); std::shared_ptr const report = mr::null_scene_report(); void const* compositor_id{nullptr}; std::shared_ptr observer = std::make_shared(mock_change_cb, [this](int){mock_change_cb();}); std::shared_ptr const stub_input_sender = std::make_shared(); testing::NiceMock mock_sender; ms::BasicSurface surface{ name, rect, false, mock_buffer_stream, std::shared_ptr(), stub_input_sender, std::shared_ptr(), report}; BasicSurfaceTest() { post_a_frame(surface); } }; } TEST_F(BasicSurfaceTest, basics) { EXPECT_EQ(name, surface.name()); EXPECT_EQ(rect.size, surface.size()); EXPECT_EQ(rect.top_left, surface.top_left()); for (auto& renderable : surface.generate_renderables(this)) EXPECT_FALSE(renderable->shaped()); } TEST_F(BasicSurfaceTest, primary_buffer_stream) { EXPECT_THAT(surface.primary_buffer_stream(), Eq(mock_buffer_stream)); } TEST_F(BasicSurfaceTest, buffer_stream_ids_always_unique) { int const n = 10; std::array, n> surfaces; std::multiset ids; for (auto& surface : surfaces) { surface = std::make_unique( name, rect, false, std::make_shared>(), std::shared_ptr(), stub_input_sender, std::shared_ptr(), report); for (auto& renderable : surface->generate_renderables(this)) ids.insert(renderable->id()); } for (auto& it : ids) EXPECT_THAT(ids.count(it), testing::Eq(1)); } TEST_F(BasicSurfaceTest, id_never_invalid) { int const n = 10; std::array, n> surfaces; std::multiset ids; for (auto& surface : surfaces) { surface = std::make_unique( name, rect, false, mock_buffer_stream, std::shared_ptr(), stub_input_sender, std::shared_ptr(), report); for (auto& renderable : surface->generate_renderables(this)) EXPECT_THAT(renderable->id(), testing::Ne(nullptr)); } } TEST_F(BasicSurfaceTest, update_top_left) { EXPECT_CALL(mock_callback, call()) .Times(1); surface.add_observer(observer); post_a_frame(surface); EXPECT_EQ(rect.top_left, surface.top_left()); auto new_top_left = geom::Point{geom::X{6}, geom::Y{10}}; surface.move_to(new_top_left); EXPECT_EQ(new_top_left, surface.top_left()); } TEST_F(BasicSurfaceTest, update_size) { geom::Size const new_size{34, 56}; EXPECT_CALL(mock_callback, call()) .Times(1); surface.add_observer(observer); post_a_frame(surface); EXPECT_EQ(rect.size, surface.size()); EXPECT_NE(new_size, surface.size()); auto renderables = surface.generate_renderables(compositor_id); ASSERT_THAT(renderables.size(), testing::Eq(1)); auto old_transformation = renderables[0]->transformation(); surface.resize(new_size); EXPECT_EQ(new_size, surface.size()); // Size no longer affects transformation: renderables = surface.generate_renderables(compositor_id); ASSERT_THAT(renderables.size(), testing::Eq(1)); EXPECT_THAT(renderables[0]->transformation(), testing::Eq(old_transformation)); } /* * Until logic is implemented to separate size() from client_size(), verify * they do return the same thing for backward compatibility. */ TEST_F(BasicSurfaceTest, size_equals_client_size) { geom::Size const new_size{34, 56}; EXPECT_EQ(rect.size, surface.size()); EXPECT_EQ(rect.size, surface.client_size()); EXPECT_NE(new_size, surface.size()); EXPECT_NE(new_size, surface.client_size()); surface.resize(new_size); EXPECT_EQ(new_size, surface.size()); EXPECT_EQ(new_size, surface.client_size()); } TEST_F(BasicSurfaceTest, test_surface_set_transformation_updates_transform) { EXPECT_CALL(mock_callback, call()) .Times(1); surface.add_observer(observer); post_a_frame(surface); auto renderables = surface.generate_renderables(compositor_id); ASSERT_THAT(renderables.size(), testing::Eq(1)); auto original_transformation = renderables[0]->transformation(); glm::mat4 trans{0.1f, 0.5f, 0.9f, 1.3f, 0.2f, 0.6f, 1.0f, 1.4f, 0.3f, 0.7f, 1.1f, 1.5f, 0.4f, 0.8f, 1.2f, 1.6f}; surface.set_transformation(trans); renderables = surface.generate_renderables(compositor_id); ASSERT_THAT(renderables.size(), testing::Eq(1)); auto got = renderables[0]->transformation(); EXPECT_NE(original_transformation, got); EXPECT_EQ(trans, got); } TEST_F(BasicSurfaceTest, test_surface_set_alpha_notifies_changes) { using namespace testing; EXPECT_CALL(mock_callback, call()) .Times(1); surface.add_observer(observer); post_a_frame(surface); float alpha = 0.5f; surface.set_alpha(0.5f); EXPECT_THAT(alpha, FloatEq(surface.alpha())); } TEST_F(BasicSurfaceTest, test_surface_is_opaque_by_default) { using namespace testing; EXPECT_THAT(1.0f, FloatEq(surface.alpha())); auto renderables = surface.generate_renderables(compositor_id); ASSERT_THAT(renderables.size(), testing::Eq(1)); EXPECT_FALSE(renderables[0]->shaped()); } TEST_F(BasicSurfaceTest, test_surface_visibility) { using namespace testing; mtd::StubBuffer mock_buffer; auto submitted_buffer = false; ON_CALL(*mock_buffer_stream, has_submitted_buffer()) .WillByDefault(Invoke([&submitted_buffer] { return submitted_buffer; })); // Must be a fresh surface to guarantee no frames posted yet... ms::BasicSurface surface{ name, rect, false, mock_buffer_stream, std::shared_ptr(), stub_input_sender, std::shared_ptr(), report}; //not visible by default EXPECT_FALSE(surface.visible()); surface.set_hidden(false); //not renderable if no first frame has been posted by client, regardless of hide state EXPECT_FALSE(surface.visible()); surface.set_hidden(true); EXPECT_FALSE(surface.visible()); surface.set_hidden(false); submitted_buffer = true; EXPECT_TRUE(surface.visible()); } TEST_F(BasicSurfaceTest, test_surface_hidden_notifies_changes) { using namespace testing; EXPECT_CALL(mock_callback, call()) .Times(1); surface.add_observer(observer); post_a_frame(surface); surface.set_hidden(true); } // a 1x1 window at (1,1) will get events at (1,1) TEST_F(BasicSurfaceTest, default_region_is_surface_rectangle) { geom::Point pt(1,1); geom::Size one_by_one{geom::Width{1}, geom::Height{1}}; ms::BasicSurface surface{ name, geom::Rectangle{pt, one_by_one}, false, mock_buffer_stream, std::shared_ptr(), stub_input_sender, std::shared_ptr(), report}; surface.add_observer(observer); post_a_frame(surface); std::vector contained_pt { geom::Point{geom::X{1}, geom::Y{1}} }; for(auto x = 0; x <= 3; x++) { for(auto y = 0; y <= 3; y++) { auto test_pt = geom::Point{x, y}; auto contains = surface.input_area_contains(test_pt); if (std::find(contained_pt.begin(), contained_pt.end(), test_pt) != contained_pt.end()) { EXPECT_TRUE(contains); } else { EXPECT_FALSE(contains); } } } } TEST_F(BasicSurfaceTest, default_invisible_surface_doesnt_get_input) { EXPECT_CALL(*mock_buffer_stream, has_submitted_buffer()) .WillOnce(testing::Return(false)) .WillOnce(testing::Return(true)); ms::BasicSurface surface{ name, geom::Rectangle{{0,0}, {100,100}}, false, mock_buffer_stream, std::shared_ptr(), stub_input_sender, std::shared_ptr(), report}; EXPECT_FALSE(surface.input_area_contains({50,50})); EXPECT_TRUE(surface.input_area_contains({50,50})); } TEST_F(BasicSurfaceTest, set_input_region) { std::vector const rectangles = { {{geom::X{0}, geom::Y{0}}, {geom::Width{1}, geom::Height{1}}}, //region0 {{geom::X{1}, geom::Y{1}}, {geom::Width{1}, geom::Height{1}}} //region1 }; surface.add_observer(observer); surface.set_input_region(rectangles); std::vector contained_pt { //region0 points geom::Point{geom::X{4}, geom::Y{7}}, //region1 points geom::Point{geom::X{5}, geom::Y{8}}, }; for(auto x = 0; x <= 3; x++) { for(auto y = 0; y <= 3; y++) { auto test_pt = rect.top_left + geom::Displacement{x, y}; auto contains = surface.input_area_contains(test_pt); if (std::find(contained_pt.begin(), contained_pt.end(), test_pt) != contained_pt.end()) { EXPECT_TRUE(contains); } else { EXPECT_FALSE(contains); } } } } TEST_F(BasicSurfaceTest, updates_default_input_region_when_surface_is_resized_to_larger_size) { geom::Rectangle const new_rect{rect.top_left,{10,10}}; surface.resize(new_rect.size); for (auto x = new_rect.top_left.x.as_int() - 1; x <= new_rect.top_right().x.as_int(); x++) { for (auto y = new_rect.top_left.y.as_int() - 1; y <= new_rect.bottom_left().y.as_int(); y++) { auto const test_pt = geom::Point{x, y}; auto const contains = surface.input_area_contains(test_pt); if (new_rect.contains(test_pt)) { EXPECT_TRUE(contains) << " point = " << test_pt; } else { EXPECT_FALSE(contains) << " point = " << test_pt; } } } } TEST_F(BasicSurfaceTest, updates_default_input_region_when_surface_is_resized_to_smaller_size) { geom::Rectangle const new_rect{rect.top_left,{2,2}}; surface.resize(new_rect.size); for (auto x = rect.top_left.x.as_int() - 1; x <= rect.top_right().x.as_int(); x++) { for (auto y = rect.top_left.y.as_int() - 1; y <= rect.bottom_left().y.as_int(); y++) { auto const test_pt = geom::Point{x, y}; auto const contains = surface.input_area_contains(test_pt); if (new_rect.contains(test_pt)) { EXPECT_TRUE(contains) << " point = " << test_pt; } else { EXPECT_FALSE(contains) << " point = " << test_pt; } } } } TEST_F(BasicSurfaceTest, restores_default_input_region_when_setting_empty_input_region) { std::vector const rectangles = { {{geom::X{0}, geom::Y{0}}, {geom::Width{1}, geom::Height{1}}}, //region0 }; surface.set_input_region(rectangles); EXPECT_FALSE(surface.input_area_contains(rect.bottom_right() - geom::Displacement{1,1})); surface.set_input_region({}); EXPECT_TRUE(surface.input_area_contains(rect.bottom_right() - geom::Displacement{1,1})); } TEST_F(BasicSurfaceTest, disables_input_when_setting_input_region_with_empty_rectangle) { surface.set_input_region({geom::Rectangle()}); EXPECT_FALSE(surface.input_area_contains(rect.top_left)); } TEST_F(BasicSurfaceTest, reception_mode_is_normal_by_default) { EXPECT_EQ(mi::InputReceptionMode::normal, surface.reception_mode()); } TEST_F(BasicSurfaceTest, reception_mode_can_be_changed) { surface.set_reception_mode(mi::InputReceptionMode::receives_all_input); EXPECT_EQ(mi::InputReceptionMode::receives_all_input, surface.reception_mode()); } TEST_F(BasicSurfaceTest, stores_parent) { auto parent = mt::fake_shared(surface); ms::BasicSurface child{ name, geom::Rectangle{{0,0}, {100,100}}, parent, false, mock_buffer_stream, std::shared_ptr(), stub_input_sender, std::shared_ptr(), report}; EXPECT_EQ(child.parent(), parent); } namespace { struct AttributeTestParameters { MirSurfaceAttrib attribute; int default_value; int a_valid_value; int an_invalid_value; }; struct BasicSurfaceAttributeTest : public BasicSurfaceTest, public ::testing::WithParamInterface { }; AttributeTestParameters const surface_visibility_test_parameters{ mir_surface_attrib_visibility, mir_surface_visibility_occluded, mir_surface_visibility_exposed, -1 }; AttributeTestParameters const surface_type_test_parameters{ mir_surface_attrib_type, mir_surface_type_normal, mir_surface_type_freestyle, -1 }; AttributeTestParameters const surface_state_test_parameters{ mir_surface_attrib_state, mir_surface_state_restored, mir_surface_state_fullscreen, 1178312 }; AttributeTestParameters const surface_swapinterval_test_parameters{ mir_surface_attrib_swapinterval, 1, 0, -1 }; AttributeTestParameters const surface_dpi_test_parameters{ mir_surface_attrib_dpi, 0, 90, -1 }; AttributeTestParameters const surface_focus_test_parameters{ mir_surface_attrib_focus, mir_surface_unfocused, mir_surface_focused, -1 }; } TEST_P(BasicSurfaceAttributeTest, default_value) { auto const& params = GetParam(); auto const& attribute = params.attribute; auto const& default_value = params.default_value; EXPECT_EQ(default_value, surface.query(attribute)); } TEST_P(BasicSurfaceAttributeTest, notifies_about_attrib_changes) { using namespace testing; auto const& params = GetParam(); auto const& attribute = params.attribute; auto const& value1 = params.a_valid_value; auto const& value2 = params.default_value; NiceMock mock_surface_observer; InSequence s; EXPECT_CALL(mock_surface_observer, attrib_changed(attribute, value1)) .Times(1); EXPECT_CALL(mock_surface_observer, attrib_changed(attribute, value2)) .Times(1); surface.add_observer(mt::fake_shared(mock_surface_observer)); surface.configure(attribute, value1); surface.configure(attribute, value2); } TEST_P(BasicSurfaceAttributeTest, does_not_notify_if_attrib_is_unchanged) { using namespace testing; auto const& params = GetParam(); auto const& attribute = params.attribute; auto const& default_value = params.default_value; auto const& another_value = params.a_valid_value; NiceMock mock_surface_observer; EXPECT_CALL(mock_surface_observer, attrib_changed(attribute, another_value)) .Times(1); surface.add_observer(mt::fake_shared(mock_surface_observer)); surface.configure(attribute, default_value); surface.configure(attribute, another_value); surface.configure(attribute, another_value); } TEST_P(BasicSurfaceAttributeTest, throws_on_invalid_value) { using namespace testing; auto const& params = GetParam(); auto const& attribute = params.attribute; auto const& invalid_value = params.an_invalid_value; EXPECT_THROW({ surface.configure(attribute, invalid_value); }, std::logic_error); } INSTANTIATE_TEST_CASE_P(SurfaceTypeAttributeTest, BasicSurfaceAttributeTest, ::testing::Values(surface_type_test_parameters)); INSTANTIATE_TEST_CASE_P(SurfaceVisibilityAttributeTest, BasicSurfaceAttributeTest, ::testing::Values(surface_visibility_test_parameters)); INSTANTIATE_TEST_CASE_P(SurfaceStateAttributeTest, BasicSurfaceAttributeTest, ::testing::Values(surface_state_test_parameters)); INSTANTIATE_TEST_CASE_P(SurfaceSwapintervalAttributeTest, BasicSurfaceAttributeTest, ::testing::Values(surface_swapinterval_test_parameters)); INSTANTIATE_TEST_CASE_P(SurfaceDPIAttributeTest, BasicSurfaceAttributeTest, ::testing::Values(surface_dpi_test_parameters)); INSTANTIATE_TEST_CASE_P(SurfaceFocusAttributeTest, BasicSurfaceAttributeTest, ::testing::Values(surface_focus_test_parameters)); TEST_F(BasicSurfaceTest, notifies_about_cursor_image_change) { using namespace testing; NiceMock mock_surface_observer; auto cursor_image = std::make_shared(); EXPECT_CALL(mock_surface_observer, cursor_image_set_to(_)); surface.add_observer(mt::fake_shared(mock_surface_observer)); surface.set_cursor_image(cursor_image); } TEST_F(BasicSurfaceTest, notifies_about_cursor_image_removal) { using namespace testing; NiceMock mock_surface_observer; EXPECT_CALL(mock_surface_observer, cursor_image_set_to(_)).Times(0); EXPECT_CALL(mock_surface_observer, cursor_image_removed()); surface.add_observer(mt::fake_shared(mock_surface_observer)); surface.set_cursor_image({}); } TEST_F(BasicSurfaceTest, calls_send_event_on_consume) { using namespace ::testing; ms::BasicSurface surface{ name, rect, false, mock_buffer_stream, std::shared_ptr(), mt::fake_shared(mock_sender), nullptr, report}; EXPECT_CALL(mock_sender, send_event(_,_)); surface.consume(mev::make_event(mir_prompt_session_state_started).get()); } TEST_F(BasicSurfaceTest, observer_can_trigger_state_change_within_notification) { using namespace testing; auto const state_changer = [&]{ surface.set_hidden(false); }; //Make sure another thread can also change state auto const async_state_changer = [&]{ std::async(std::launch::async, state_changer); }; EXPECT_CALL(mock_callback, call()).Times(3) .WillOnce(InvokeWithoutArgs(state_changer)) .WillOnce(InvokeWithoutArgs(async_state_changer)) .WillOnce(Return()); surface.add_observer(observer); surface.set_hidden(true); } TEST_F(BasicSurfaceTest, observer_can_remove_itself_within_notification) { using namespace testing; MockSurfaceObserver observer1; MockSurfaceObserver observer2; MockSurfaceObserver observer3; //Both of these observers should still get their notifications //regardless of the unregistration of observer2 EXPECT_CALL(observer1, hidden_set_to(true)).Times(2); EXPECT_CALL(observer3, hidden_set_to(true)).Times(2); auto const remove_observer = [&]{ surface.remove_observer(mt::fake_shared(observer2)); }; EXPECT_CALL(observer2, hidden_set_to(true)).Times(1) .WillOnce(InvokeWithoutArgs(remove_observer)); surface.add_observer(mt::fake_shared(observer1)); surface.add_observer(mt::fake_shared(observer2)); surface.add_observer(mt::fake_shared(observer3)); surface.set_hidden(true); surface.set_hidden(true); } TEST_F(BasicSurfaceTest, notifies_of_client_close_request) { using namespace testing; MockSurfaceObserver mock_surface_observer; EXPECT_CALL(mock_surface_observer, client_surface_close_requested()).Times(1); surface.add_observer(mt::fake_shared(mock_surface_observer)); surface.request_client_surface_close(); } TEST_F(BasicSurfaceTest, notifies_of_rename) { using namespace testing; MockSurfaceObserver mock_surface_observer; surface.add_observer(mt::fake_shared(mock_surface_observer)); EXPECT_CALL(mock_surface_observer, renamed(StrEq("Steve"))); surface.rename("Steve"); } MATCHER_P(IsRenderableOfPosition, pos, "is renderable with position") { return (pos == arg->screen_position().top_left); } MATCHER_P(IsRenderableOfSize, size, "is renderable with size") { return (size == arg->screen_position().size); } MATCHER_P(IsRenderableOfAlpha, alpha, "is renderable with alpha") { EXPECT_THAT(static_cast(alpha), testing::FloatEq(arg->alpha())); return !(::testing::Test::HasFailure()); } TEST_F(BasicSurfaceTest, adds_buffer_streams) { using namespace testing; geom::Displacement d0{19,99}; geom::Displacement d1{21,101}; geom::Displacement d2{20,9}; auto buffer_stream0 = std::make_shared>(); auto buffer_stream1 = std::make_shared>(); auto buffer_stream2 = std::make_shared>(); std::list streams = { { mock_buffer_stream, {0,0}}, { buffer_stream0, d0 }, { buffer_stream1, d1 }, { buffer_stream2, d2 } }; surface.set_streams(streams); auto renderables = surface.generate_renderables(this); ASSERT_THAT(renderables.size(), Eq(4)); EXPECT_THAT(renderables[0], IsRenderableOfPosition(rect.top_left)); EXPECT_THAT(renderables[1], IsRenderableOfPosition(rect.top_left + d0)); EXPECT_THAT(renderables[2], IsRenderableOfPosition(rect.top_left + d1)); EXPECT_THAT(renderables[3], IsRenderableOfPosition(rect.top_left + d2)); } TEST_F(BasicSurfaceTest, moving_surface_repositions_all_associated_streams) { using namespace testing; geom::Point pt{10, 20}; geom::Displacement d{19,99}; auto buffer_stream = std::make_shared>(); std::list streams = { { mock_buffer_stream, {0,0} }, { buffer_stream, d } }; surface.set_streams(streams); auto renderables = surface.generate_renderables(this); ASSERT_THAT(renderables.size(), Eq(2)); EXPECT_THAT(renderables[0], IsRenderableOfPosition(rect.top_left)); EXPECT_THAT(renderables[1], IsRenderableOfPosition(rect.top_left + d)); surface.move_to(pt); renderables = surface.generate_renderables(this); ASSERT_THAT(renderables.size(), Eq(2)); EXPECT_THAT(renderables[0], IsRenderableOfPosition(pt)); EXPECT_THAT(renderables[1], IsRenderableOfPosition(pt + d)); } TEST_F(BasicSurfaceTest, can_remove_all_streams) { using namespace testing; surface.set_streams({}); auto renderables = surface.generate_renderables(this); EXPECT_THAT(renderables.size(), Eq(0)); } TEST_F(BasicSurfaceTest, can_set_streams_not_containing_originally_created_with_stream) { using namespace testing; auto buffer_stream0 = std::make_shared>(); auto buffer_stream1 = std::make_shared>(); std::list streams = { { buffer_stream0, {0,0} }, { buffer_stream1, {0,0} } }; surface.set_streams(streams); auto renderables = surface.generate_renderables(this); EXPECT_THAT(renderables.size(), Eq(2)); } TEST_F(BasicSurfaceTest, stream_observers_are_added_and_removed_appropriately) { using namespace testing; surface.add_observer(observer); auto buffer_stream0 = std::make_shared>(); auto buffer_stream1 = std::make_shared>(); Sequence seq0; EXPECT_CALL(*buffer_stream0, add_observer(_)) .InSequence(seq0); EXPECT_CALL(*buffer_stream0, remove_observer(_)) .InSequence(seq0); EXPECT_CALL(*buffer_stream0, add_observer(_)) .InSequence(seq0); EXPECT_CALL(*buffer_stream0, remove_observer(_)) .InSequence(seq0); EXPECT_CALL(*buffer_stream0, add_observer(_)) .InSequence(seq0); Sequence seq1; EXPECT_CALL(*buffer_stream1, add_observer(_)) .InSequence(seq1); EXPECT_CALL(*buffer_stream1, remove_observer(_)) .InSequence(seq1); EXPECT_CALL(*buffer_stream1, add_observer(_)) .InSequence(seq1); EXPECT_CALL(*buffer_stream1, remove_observer(_)) .InSequence(seq1); std::list streams = { { buffer_stream0, {0,0} }, { buffer_stream1, {0,0} }, }; surface.set_streams(streams); streams = { { buffer_stream0, {0,0} } }; surface.set_streams(streams); streams = { { buffer_stream1, {0,0} } }; surface.set_streams(streams); streams = { { buffer_stream0, {0,0} } }; surface.set_streams(streams); } TEST_F(BasicSurfaceTest, showing_brings_all_streams_up_to_date) { using namespace testing; auto buffer_stream = std::make_shared>(); std::list streams = { { mock_buffer_stream, {0,0} }, { buffer_stream, {0,0} } }; surface.set_streams(streams); EXPECT_CALL(*buffer_stream, drop_old_buffers()).Times(Exactly(1)); EXPECT_CALL(*mock_buffer_stream, drop_old_buffers()).Times(Exactly(1)); surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded); surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed); surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed); } //TODO: per-stream alpha and swapinterval seems useful TEST_F(BasicSurfaceTest, changing_alpha_effects_all_streams) { using namespace testing; auto alpha = 0.3; auto buffer_stream = std::make_shared>(); std::list streams = { { mock_buffer_stream, {0,0} }, { buffer_stream, {0,0} } }; surface.set_streams(streams); auto renderables = surface.generate_renderables(this); ASSERT_THAT(renderables.size(), Eq(2)); EXPECT_THAT(renderables[0], IsRenderableOfAlpha(1.0f)); EXPECT_THAT(renderables[1], IsRenderableOfAlpha(1.0f)); surface.set_alpha(alpha); renderables = surface.generate_renderables(this); ASSERT_THAT(renderables.size(), Eq(2)); EXPECT_THAT(renderables[0], IsRenderableOfAlpha(alpha)); EXPECT_THAT(renderables[1], IsRenderableOfAlpha(alpha)); } TEST_F(BasicSurfaceTest, DISABLED_setting_streams_with_size_changes_sizes) { using namespace testing; geom::Size size0 {100, 25 }; geom::Size size1 { 32, 44 }; geom::Size bad_size { 12, 11 }; auto buffer_stream = std::make_shared>(); ON_CALL(*mock_buffer_stream, stream_size()) .WillByDefault(Return(bad_size)); ON_CALL(*buffer_stream, stream_size()) .WillByDefault(Return(bad_size)); std::list streams = { { mock_buffer_stream, {0,0} }, { buffer_stream, {0,0} } }; surface.set_streams(streams); auto renderables = surface.generate_renderables(this); ASSERT_THAT(renderables.size(), Eq(2)); EXPECT_THAT(renderables[0], IsRenderableOfSize(size0)); EXPECT_THAT(renderables[1], IsRenderableOfSize(size1)); } TEST_F(BasicSurfaceTest, changing_inverval_effects_all_streams) { using namespace testing; auto buffer_stream = std::make_shared>(); std::list streams = { { mock_buffer_stream, {0,0} }, { buffer_stream, {0,0} } }; EXPECT_CALL(*mock_buffer_stream, allow_framedropping(true)); EXPECT_CALL(*buffer_stream, allow_framedropping(true)); surface.set_streams(streams); surface.configure(mir_surface_attrib_swapinterval, 0); } TEST_F(BasicSurfaceTest, visibility_matches_produced_list) { using namespace testing; auto stream1_visible = false; auto stream2_visible = false; geom::Displacement displacement{3,-2}; auto mock_buffer_stream1 = std::make_shared>(); ON_CALL(*mock_buffer_stream, has_submitted_buffer()) .WillByDefault(Invoke([&stream1_visible] { return stream1_visible; })); ON_CALL(*mock_buffer_stream1, has_submitted_buffer()) .WillByDefault(Invoke([&stream2_visible] { return stream2_visible; })); std::list streams = { { mock_buffer_stream, {0,0} }, { mock_buffer_stream1, displacement }, }; surface.set_streams(streams); auto renderables = surface.generate_renderables(this); EXPECT_FALSE(surface.visible()); EXPECT_THAT(renderables.size(), Eq(0)); stream2_visible = true; renderables = surface.generate_renderables(this); EXPECT_TRUE(surface.visible()); EXPECT_THAT(renderables.size(), Eq(1)); stream1_visible = true; renderables = surface.generate_renderables(this); EXPECT_TRUE(surface.visible()); EXPECT_THAT(renderables.size(), Eq(2)); } TEST_F(BasicSurfaceTest, buffers_ready_correctly_reported) { using namespace testing; auto buffer_stream0 = std::make_shared>(); auto buffer_stream1 = std::make_shared>(); EXPECT_CALL(*mock_buffer_stream, buffers_ready_for_compositor(_)) .WillOnce(Return(0)) .WillOnce(Return(0)) .WillOnce(Return(2)); EXPECT_CALL(*buffer_stream0, buffers_ready_for_compositor(_)) .WillOnce(Return(1)) .WillOnce(Return(0)) .WillOnce(Return(1)); EXPECT_CALL(*buffer_stream1, buffers_ready_for_compositor(_)) .WillOnce(Return(3)) .WillOnce(Return(0)) .WillOnce(Return(1)); std::list streams = { { mock_buffer_stream, {0,0} }, { buffer_stream0, {0,0} }, { buffer_stream1, {0,0} }, }; surface.set_streams(streams); EXPECT_THAT(surface.buffers_ready_for_compositor(this), Eq(3)); EXPECT_THAT(surface.buffers_ready_for_compositor(this), Eq(0)); EXPECT_THAT(surface.buffers_ready_for_compositor(this), Eq(2)); } TEST_F(BasicSurfaceTest, buffer_streams_produce_correctly_sized_renderables) { using namespace testing; auto buffer_stream = std::make_shared>(); geom::Displacement d0{0,0}; geom::Displacement d1{19,99}; geom::Size size0 {100, 101}; geom::Size size1 {102, 103}; ON_CALL(*mock_buffer_stream, stream_size()) .WillByDefault(Return(size0)); ON_CALL(*buffer_stream, stream_size()) .WillByDefault(Return(size1)); std::list streams = { { mock_buffer_stream, d0 }, { buffer_stream, d1 }, }; surface.set_streams(streams); auto renderables = surface.generate_renderables(this); ASSERT_THAT(renderables.size(), Eq(2)); EXPECT_THAT(renderables[0], IsRenderableOfSize(size0)); EXPECT_THAT(renderables[1], IsRenderableOfSize(size1)); } namespace { struct VisibilityObserver : ms::NullSurfaceObserver { void attrib_changed(MirSurfaceAttrib attrib, int value) override { if (attrib == mir_surface_attrib_visibility) { if (value == mir_surface_visibility_occluded) hides_++; else if (value == mir_surface_visibility_exposed) exposes_++; } } unsigned int exposes() { return exposes_; } unsigned int hides() { return hides_; } private: unsigned int exposes_{0}; unsigned int hides_{0}; }; } TEST_F(BasicSurfaceTest, notifies_when_first_visible) { using namespace testing; auto observer = std::make_shared(); surface.add_observer(observer); EXPECT_THAT(observer->exposes(), Eq(0)); EXPECT_THAT(observer->hides(), Eq(0)); post_a_frame(surface); surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed); EXPECT_THAT(observer->exposes(), Eq(1)); EXPECT_THAT(observer->hides(), Eq(0)); } ./tests/unit-tests/scene/test_mediating_display_changer.cpp0000644000015600001650000005257512676616125024425 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/server/scene/mediating_display_changer.h" #include "src/server/scene/session_container.h" #include "mir/graphics/display_configuration_policy.h" #include "mir/graphics/display_configuration_report.h" #include "mir/geometry/rectangles.h" #include "src/server/scene/broadcasting_session_event_sink.h" #include "mir/server_action_queue.h" #include "mir/test/doubles/mock_display.h" #include "mir/test/doubles/mock_compositor.h" #include "mir/test/doubles/null_display_configuration.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/mock_scene_session.h" #include "mir/test/doubles/mock_input_region.h" #include "mir/test/doubles/stub_session.h" #include "mir/test/fake_shared.h" #include "mir/test/display_config_matchers.h" #include #include #include namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mf = mir::frontend; namespace ms = mir::scene; namespace mg = mir::graphics; using namespace testing; namespace { class MockDisplayConfigurationPolicy : public mg::DisplayConfigurationPolicy { public: ~MockDisplayConfigurationPolicy() noexcept {} MOCK_METHOD1(apply_to, void(mg::DisplayConfiguration&)); }; class StubSessionContainer : public ms::SessionContainer { public: void insert_session(std::shared_ptr const& session) { sessions.push_back(session); } void remove_session(std::shared_ptr const&) { } void for_each(std::function const&)> f) const { for (auto const& session : sessions) f(session); } std::shared_ptr successor_of(std::shared_ptr const&) const { return {}; } private: std::vector> sessions; }; struct MockDisplay : public mtd::MockDisplay { std::unique_ptr configuration() const override { conf_ptr = new mtd::StubDisplayConfig{}; return std::unique_ptr(conf_ptr); } mutable mg::DisplayConfiguration* conf_ptr; }; struct StubServerActionQueue : mir::ServerActionQueue { void enqueue(void const* /*owner*/, mir::ServerAction const& action) override { action(); } void pause_processing_for(void const* /*owner*/) override {} void resume_processing_for(void const* /*owner*/) override {} }; struct MockServerActionQueue : mir::ServerActionQueue { MockServerActionQueue() { ON_CALL(*this, enqueue(_, _)).WillByDefault(InvokeArgument<1>()); } MOCK_METHOD2(enqueue, void(void const*, mir::ServerAction const&)); MOCK_METHOD1(pause_processing_for, void(void const*)); MOCK_METHOD1(resume_processing_for, void(void const*)); }; struct StubDisplayConfigurationReport : mg::DisplayConfigurationReport { void initial_configuration(mg::DisplayConfiguration const&) override {} void new_configuration(mg::DisplayConfiguration const&) override {} }; struct MediatingDisplayChangerTest : public ::testing::Test { MediatingDisplayChangerTest() { using namespace testing; changer = std::make_shared( mt::fake_shared(mock_display), mt::fake_shared(mock_compositor), mt::fake_shared(mock_conf_policy), mt::fake_shared(stub_session_container), mt::fake_shared(session_event_sink), mt::fake_shared(server_action_queue), mt::fake_shared(display_configuration_report), mt::fake_shared(mock_input_region)); } testing::NiceMock mock_display; testing::NiceMock mock_compositor; testing::NiceMock mock_conf_policy; StubSessionContainer stub_session_container; ms::BroadcastingSessionEventSink session_event_sink; mtd::StubDisplayConfig base_config; StubServerActionQueue server_action_queue; StubDisplayConfigurationReport display_configuration_report; testing::NiceMock mock_input_region; std::shared_ptr changer; }; } TEST_F(MediatingDisplayChangerTest, returns_base_configuration_from_display_at_startup) { using namespace testing; auto const base_conf = changer->base_configuration(); EXPECT_THAT(*base_conf, mt::DisplayConfigMatches(std::ref(*mock_display.conf_ptr))); } TEST_F(MediatingDisplayChangerTest, pauses_system_when_applying_new_configuration_for_focused_session) { using namespace testing; mtd::NullDisplayConfiguration conf; auto session = std::make_shared(); InSequence s; EXPECT_CALL(mock_compositor, stop()); EXPECT_CALL(mock_display, configure(Ref(conf))); EXPECT_CALL(mock_compositor, start()); session_event_sink.handle_focus_change(session); changer->configure(session, mt::fake_shared(conf)); } TEST_F(MediatingDisplayChangerTest, doesnt_apply_config_for_unfocused_session) { using namespace testing; mtd::NullDisplayConfiguration conf; EXPECT_CALL(mock_compositor, stop()).Times(0); EXPECT_CALL(mock_display, configure(Ref(conf))).Times(0); EXPECT_CALL(mock_compositor, start()).Times(0); changer->configure(std::make_shared(), mt::fake_shared(conf)); } TEST_F(MediatingDisplayChangerTest, returns_updated_base_configuration_after_hardware_change) { using namespace testing; mtd::StubDisplayConfig conf{2}; changer->configure_for_hardware_change( mt::fake_shared(conf), mir::DisplayChanger::PauseResumeSystem); auto const base_conf = changer->base_configuration(); EXPECT_THAT(*base_conf, mt::DisplayConfigMatches(conf)); } TEST_F(MediatingDisplayChangerTest, handles_hardware_change_properly_when_pausing_system) { using namespace testing; mtd::NullDisplayConfiguration conf; InSequence s; EXPECT_CALL(mock_conf_policy, apply_to(Ref(conf))); EXPECT_CALL(mock_compositor, stop()); EXPECT_CALL(mock_display, configure(Ref(conf))); EXPECT_CALL(mock_compositor, start()); changer->configure_for_hardware_change(mt::fake_shared(conf), mir::DisplayChanger::PauseResumeSystem); } TEST_F(MediatingDisplayChangerTest, handles_hardware_change_properly_when_retaining_system_state) { using namespace testing; mtd::NullDisplayConfiguration conf; EXPECT_CALL(mock_compositor, stop()).Times(0); EXPECT_CALL(mock_compositor, start()).Times(0); InSequence s; EXPECT_CALL(mock_conf_policy, apply_to(Ref(conf))); EXPECT_CALL(mock_display, configure(Ref(conf))); changer->configure_for_hardware_change(mt::fake_shared(conf), mir::DisplayChanger::RetainSystemState); } TEST_F(MediatingDisplayChangerTest, hardware_change_doesnt_apply_base_config_if_per_session_config_is_active) { using namespace testing; auto conf = std::make_shared(); auto session1 = std::make_shared(); stub_session_container.insert_session(session1); changer->configure(session1, conf); session_event_sink.handle_focus_change(session1); Mock::VerifyAndClearExpectations(&mock_compositor); Mock::VerifyAndClearExpectations(&mock_display); InSequence s; EXPECT_CALL(mock_compositor, stop()).Times(0); EXPECT_CALL(mock_display, configure(_)).Times(0); EXPECT_CALL(mock_compositor, start()).Times(0); changer->configure_for_hardware_change(conf, mir::DisplayChanger::PauseResumeSystem); } TEST_F(MediatingDisplayChangerTest, notifies_all_sessions_on_hardware_config_change) { using namespace testing; mtd::NullDisplayConfiguration conf; mtd::MockSceneSession mock_session1; mtd::MockSceneSession mock_session2; stub_session_container.insert_session(mt::fake_shared(mock_session1)); stub_session_container.insert_session(mt::fake_shared(mock_session2)); EXPECT_CALL(mock_session1, send_display_config(_)); EXPECT_CALL(mock_session2, send_display_config(_)); changer->configure_for_hardware_change(mt::fake_shared(conf), mir::DisplayChanger::PauseResumeSystem); } TEST_F(MediatingDisplayChangerTest, focusing_a_session_with_attached_config_applies_config) { using namespace testing; auto conf = std::make_shared(); auto session1 = std::make_shared(); stub_session_container.insert_session(session1); changer->configure(session1, conf); InSequence s; EXPECT_CALL(mock_compositor, stop()); EXPECT_CALL(mock_display, configure(Ref(*conf))); EXPECT_CALL(mock_compositor, start()); session_event_sink.handle_focus_change(session1); } TEST_F(MediatingDisplayChangerTest, focusing_a_session_without_attached_config_applies_base_config) { using namespace testing; auto conf = std::make_shared(); auto session1 = std::make_shared(); auto session2 = std::make_shared(); stub_session_container.insert_session(session1); changer->configure(session1, conf); session_event_sink.handle_focus_change(session1); Mock::VerifyAndClearExpectations(&mock_compositor); Mock::VerifyAndClearExpectations(&mock_display); InSequence s; EXPECT_CALL(mock_compositor, stop()); EXPECT_CALL(mock_display, configure(mt::DisplayConfigMatches(std::cref(base_config)))); EXPECT_CALL(mock_compositor, start()); session_event_sink.handle_focus_change(session2); } TEST_F(MediatingDisplayChangerTest, losing_focus_applies_base_config) { using namespace testing; auto conf = std::make_shared(); auto session1 = std::make_shared(); stub_session_container.insert_session(session1); changer->configure(session1, conf); session_event_sink.handle_focus_change(session1); Mock::VerifyAndClearExpectations(&mock_compositor); Mock::VerifyAndClearExpectations(&mock_display); InSequence s; EXPECT_CALL(mock_compositor, stop()); EXPECT_CALL(mock_display, configure(mt::DisplayConfigMatches(std::cref(base_config)))); EXPECT_CALL(mock_compositor, start()); session_event_sink.handle_no_focus(); } TEST_F(MediatingDisplayChangerTest, base_config_is_not_applied_if_already_active) { using namespace testing; auto conf = std::make_shared(); auto session1 = std::make_shared(); auto session2 = std::make_shared(); EXPECT_CALL(mock_compositor, stop()).Times(0); EXPECT_CALL(mock_display, configure(_)).Times(0); EXPECT_CALL(mock_compositor, start()).Times(0); stub_session_container.insert_session(session1); stub_session_container.insert_session(session2); session_event_sink.handle_focus_change(session1); session_event_sink.handle_focus_change(session2); session_event_sink.handle_no_focus(); } TEST_F(MediatingDisplayChangerTest, hardware_change_invalidates_session_configs) { using namespace testing; auto conf = std::make_shared(); auto session1 = std::make_shared(); stub_session_container.insert_session(session1); changer->configure(session1, conf); changer->configure_for_hardware_change(conf, mir::DisplayChanger::PauseResumeSystem); Mock::VerifyAndClearExpectations(&mock_compositor); Mock::VerifyAndClearExpectations(&mock_display); /* * Session1 had a config, but it should have been invalidated by the hardware * change, so expect no reconfiguration. */ EXPECT_CALL(mock_compositor, stop()).Times(0); EXPECT_CALL(mock_display, configure(_)).Times(0); EXPECT_CALL(mock_compositor, start()).Times(0); session_event_sink.handle_focus_change(session1); } TEST_F(MediatingDisplayChangerTest, session_stopping_invalidates_session_config) { using namespace testing; auto conf = std::make_shared(); auto session1 = std::make_shared(); stub_session_container.insert_session(session1); changer->configure(session1, conf); session_event_sink.handle_session_stopping(session1); Mock::VerifyAndClearExpectations(&mock_compositor); Mock::VerifyAndClearExpectations(&mock_display); /* * Session1 had a config, but it should have been invalidated by the * session stopping event, so expect no reconfiguration. */ EXPECT_CALL(mock_compositor, stop()).Times(0); EXPECT_CALL(mock_display, configure(_)).Times(0); EXPECT_CALL(mock_compositor, start()).Times(0); session_event_sink.handle_focus_change(session1); } TEST_F(MediatingDisplayChangerTest, uses_server_action_queue_for_configuration_actions) { using namespace testing; auto const conf = std::make_shared(); auto const session1 = std::make_shared(); auto const session2 = std::make_shared(); MockServerActionQueue mock_server_action_queue; stub_session_container.insert_session(session1); stub_session_container.insert_session(session2); ms::MediatingDisplayChanger display_changer( mt::fake_shared(mock_display), mt::fake_shared(mock_compositor), mt::fake_shared(mock_conf_policy), mt::fake_shared(stub_session_container), mt::fake_shared(session_event_sink), mt::fake_shared(mock_server_action_queue), mt::fake_shared(display_configuration_report), mt::fake_shared(mock_input_region)); void const* owner{nullptr}; EXPECT_CALL(mock_server_action_queue, enqueue(_, _)) .WillOnce(DoAll(SaveArg<0>(&owner), InvokeArgument<1>())); session_event_sink.handle_focus_change(session1); Mock::VerifyAndClearExpectations(&mock_server_action_queue); EXPECT_CALL(mock_server_action_queue, enqueue(owner, _)); display_changer.configure(session1, conf); Mock::VerifyAndClearExpectations(&mock_server_action_queue); EXPECT_CALL(mock_server_action_queue, enqueue(owner, _)); display_changer.configure_for_hardware_change( conf, mir::DisplayChanger::PauseResumeSystem); Mock::VerifyAndClearExpectations(&mock_server_action_queue); EXPECT_CALL(mock_server_action_queue, enqueue(owner, _)); session_event_sink.handle_focus_change(session2); Mock::VerifyAndClearExpectations(&mock_server_action_queue); EXPECT_CALL(mock_server_action_queue, enqueue(owner, _)); session_event_sink.handle_no_focus(); Mock::VerifyAndClearExpectations(&mock_server_action_queue); EXPECT_CALL(mock_server_action_queue, pause_processing_for(owner)); display_changer.pause_display_config_processing(); Mock::VerifyAndClearExpectations(&mock_server_action_queue); EXPECT_CALL(mock_server_action_queue, resume_processing_for(owner)); display_changer.resume_display_config_processing(); Mock::VerifyAndClearExpectations(&mock_server_action_queue); } TEST_F(MediatingDisplayChangerTest, does_not_block_IPC_thread_for_inactive_sessions) { using namespace testing; auto const conf = std::make_shared(); auto const active_session = std::make_shared(); auto const inactive_session = std::make_shared(); MockServerActionQueue mock_server_action_queue; stub_session_container.insert_session(active_session); stub_session_container.insert_session(inactive_session); ms::MediatingDisplayChanger display_changer( mt::fake_shared(mock_display), mt::fake_shared(mock_compositor), mt::fake_shared(mock_conf_policy), mt::fake_shared(stub_session_container), mt::fake_shared(session_event_sink), mt::fake_shared(mock_server_action_queue), mt::fake_shared(display_configuration_report), mt::fake_shared(mock_input_region)); EXPECT_CALL(mock_server_action_queue, enqueue(_, _)); session_event_sink.handle_focus_change(active_session); Mock::VerifyAndClearExpectations(&mock_server_action_queue); EXPECT_CALL(mock_server_action_queue, enqueue(_, _)).Times(0); display_changer.configure(inactive_session, conf); } TEST_F(MediatingDisplayChangerTest, set_base_configuration_doesnt_override_session_configuration) { using namespace testing; auto conf = std::make_shared(); auto session1 = std::make_shared(); stub_session_container.insert_session(session1); changer->configure(session1, conf); session_event_sink.handle_focus_change(session1); Mock::VerifyAndClearExpectations(&mock_compositor); Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(_)).Times(0); changer->set_base_configuration(conf); } TEST_F(MediatingDisplayChangerTest, set_base_configuration_overrides_base_configuration) { using namespace testing; auto conf = std::make_shared(); auto session1 = std::make_shared(); stub_session_container.insert_session(session1); Mock::VerifyAndClearExpectations(&mock_compositor); Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(_)).Times(1); changer->set_base_configuration(conf); } TEST_F(MediatingDisplayChangerTest, stores_new_base_config_on_set_default_configuration) { using namespace testing; auto default_conf = std::make_shared(2); auto session_conf = std::make_shared(4); auto mock_session1 = std::make_shared>(); auto mock_session2 = std::make_shared>(); auto mock_session3 = std::make_shared>(); stub_session_container.insert_session(mock_session1); stub_session_container.insert_session(mock_session2); session_event_sink.handle_focus_change(mock_session2); changer->configure(mock_session2, session_conf); stub_session_container.insert_session(mock_session3); Mock::VerifyAndClearExpectations(&mock_display); InSequence s; EXPECT_CALL(mock_display, configure(mt::DisplayConfigMatches(std::cref(*default_conf)))); EXPECT_CALL(mock_display, configure(mt::DisplayConfigMatches(std::cref(*session_conf)))); EXPECT_CALL(mock_display, configure(mt::DisplayConfigMatches(std::cref(*default_conf)))); changer->set_base_configuration(default_conf); session_event_sink.handle_focus_change(mock_session1); session_event_sink.handle_focus_change(mock_session2); session_event_sink.handle_focus_change(mock_session3); } TEST_F(MediatingDisplayChangerTest, returns_updated_base_configuration_after_set_base_configuration) { using namespace testing; mtd::StubDisplayConfig conf{2}; changer->set_base_configuration(mt::fake_shared(conf)); auto const base_conf = changer->base_configuration(); EXPECT_THAT(*base_conf, mt::DisplayConfigMatches(conf)); } TEST_F(MediatingDisplayChangerTest, notifies_all_sessions_on_set_base_configuration) { using namespace testing; mtd::NullDisplayConfiguration conf; mtd::MockSceneSession mock_session1; mtd::MockSceneSession mock_session2; stub_session_container.insert_session(mt::fake_shared(mock_session1)); stub_session_container.insert_session(mt::fake_shared(mock_session2)); EXPECT_CALL(mock_session1, send_display_config(_)); EXPECT_CALL(mock_session2, send_display_config(_)); changer->set_base_configuration(mt::fake_shared(conf)); } TEST_F(MediatingDisplayChangerTest, input_region_receives_display_configuration_on_start) { using namespace testing; EXPECT_CALL(mock_input_region, set_input_rectangles(_)); ms::MediatingDisplayChanger display_changer( mt::fake_shared(mock_display), mt::fake_shared(mock_compositor), mt::fake_shared(mock_conf_policy), mt::fake_shared(stub_session_container), mt::fake_shared(session_event_sink), mt::fake_shared(server_action_queue), mt::fake_shared(display_configuration_report), mt::fake_shared(mock_input_region)); } TEST_F(MediatingDisplayChangerTest, notifies_input_region_on_new_configuration) { using namespace testing; mtd::NullDisplayConfiguration conf; mir::geometry::Rectangles expected_rectangles; EXPECT_CALL(mock_input_region, set_input_rectangles(expected_rectangles)); auto session = std::make_shared(); session_event_sink.handle_focus_change(session); changer->configure(session, mt::fake_shared(conf)); } ./tests/unit-tests/scene/test_surface_stack.cpp0000644000015600001650000011207412676616125022054 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #include "src/server/scene/surface_stack.h" #include "src/server/compositor/buffer_stream_surfaces.h" #include "mir/graphics/buffer_properties.h" #include "mir/geometry/rectangle.h" #include "mir/scene/observer.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/compositor/scene_element.h" #include "mir/compositor/decoration.h" #include "src/server/report/null_report_factory.h" #include "src/server/scene/basic_surface.h" #include "mir/input/input_channel_factory.h" #include "mir/test/doubles/stub_input_channel.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/stub_buffer_stream.h" #include "mir/test/doubles/stub_renderable.h" #include "mir/test/doubles/mock_buffer_stream.h" #include "mir/test/doubles/mock_buffer_bundle.h" #include #include #include #include #include #include #include namespace mc = mir::compositor; namespace mg = mir::graphics; namespace ms = mir::scene; namespace msh = mir::shell; namespace mi = mir::input; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mr = mir::report; namespace { void post_a_frame(ms::Surface& s) { mtd::StubBuffer old_buffer; s.primary_buffer_stream()->swap_buffers(&old_buffer, [](mg::Buffer*){}); } MATCHER_P(SurfaceWithInputReceptionMode, mode, "") { return arg->reception_mode() == mode; } MATCHER_P(SceneElementForSurface, surface, "") { return arg->renderable()->id() == surface->primary_buffer_stream().get(); } MATCHER_P(SceneElementForStream, stream, "") { return arg->renderable()->id() == stream.get(); } struct StubInputChannelFactory : public mi::InputChannelFactory { std::shared_ptr make_input_channel() { return std::make_shared(); } }; struct StubInputChannel : public mi::InputChannel { StubInputChannel(int server_fd, int client_fd) : s_fd(server_fd), c_fd(client_fd) { } int client_fd() const { return c_fd; } int server_fd() const { return s_fd; } int const s_fd; int const c_fd; }; struct MockCallback { MOCK_METHOD0(call, void()); }; struct MockSceneObserver : public ms::Observer { MOCK_METHOD1(surface_added, void(ms::Surface*)); MOCK_METHOD1(surface_removed, void(ms::Surface*)); MOCK_METHOD0(surfaces_reordered, void()); MOCK_METHOD0(scene_changed, void()); MOCK_METHOD1(surface_exists, void(ms::Surface*)); MOCK_METHOD0(end_observation, void()); }; struct SurfaceStack : public ::testing::Test { void SetUp() { using namespace testing; default_params = ms::a_surface().of_size(geom::Size{geom::Width{1024}, geom::Height{768}}); stub_surface1 = std::make_shared( std::string("stub"), geom::Rectangle{{},{}}, false, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); post_a_frame(*stub_surface1); stub_surface2 = std::make_shared( std::string("stub"), geom::Rectangle{{},{}}, false, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); post_a_frame(*stub_surface2); stub_surface3 = std::make_shared( std::string("stub"), geom::Rectangle{{},{}}, false, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); post_a_frame(*stub_surface3); invisible_stub_surface = std::make_shared( std::string("stub"), geom::Rectangle{{},{}}, false, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); invisible_stub_surface->set_hidden(true); } ms::SurfaceCreationParameters default_params; std::shared_ptr stub_surface1; std::shared_ptr stub_surface2; std::shared_ptr stub_surface3; std::shared_ptr invisible_stub_surface; std::shared_ptr const report = mr::null_scene_report(); ms::SurfaceStack stack{report}; void const* compositor_id{&default_params}; }; } TEST_F(SurfaceStack, owns_surface_from_add_to_remove) { using namespace testing; auto const use_count = stub_surface1.use_count(); stack.add_surface(stub_surface1, default_params.input_mode); EXPECT_THAT(stub_surface1.use_count(), Gt(use_count)); stack.remove_surface(stub_surface1); EXPECT_THAT(stub_surface1.use_count(), Eq(use_count)); } TEST_F(SurfaceStack, stacking_order) { using namespace testing; stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); stack.add_surface(stub_surface3, default_params.input_mode); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface1), SceneElementForSurface(stub_surface2), SceneElementForSurface(stub_surface3))); } TEST_F(SurfaceStack, stacking_order_with_multiple_buffer_streams) { using namespace testing; auto stub_stream0 = std::make_shared(); auto stub_stream1 = std::make_shared(); auto stub_stream2 = std::make_shared(); std::list streams = { { stub_surface1->buffer_stream(), {0,0} }, { stub_stream0, {2,2} }, { stub_stream1, {2,3} }, }; stub_surface1->set_streams(streams); streams = { { stub_stream2, {2,4} }, { stub_surface3->buffer_stream(), {0,0} } }; stub_surface3->set_streams(streams); stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); stack.add_surface(stub_surface3, default_params.input_mode); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface1), SceneElementForStream(stub_stream0), SceneElementForStream(stub_stream1), SceneElementForSurface(stub_surface2), SceneElementForStream(stub_stream2), SceneElementForSurface(stub_surface3) )); } TEST_F(SurfaceStack, scene_snapshot_omits_invisible_surfaces) { using namespace testing; stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(invisible_stub_surface, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface1), SceneElementForSurface(stub_surface2))); } TEST_F(SurfaceStack, decor_name_is_surface_name) { using namespace testing; ms::SurfaceStack stack{report}; auto surface = std::make_shared( std::string("Mary had a little lamb"), geom::Rectangle{{},{}}, false, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); stack.add_surface(surface, default_params.input_mode); surface->configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed); post_a_frame(*surface); auto elements = stack.scene_elements_for(compositor_id); ASSERT_EQ(1, elements.size()); auto& element = elements.front(); auto decor = element->decoration(); ASSERT_THAT(decor, Ne(nullptr)); EXPECT_EQ(mc::Decoration::Type::surface, decor->type); EXPECT_EQ("Mary had a little lamb", decor->name); } TEST_F(SurfaceStack, gets_surface_renames) { using namespace testing; ms::SurfaceStack stack{report}; auto surface = std::make_shared( std::string("username@hostname: /"), geom::Rectangle{{},{}}, false, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); stack.add_surface(surface, default_params.input_mode); surface->configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed); post_a_frame(*surface); // (change directory in shell app) surface->rename("username@hostname: ~/Documents"); auto elements = stack.scene_elements_for(compositor_id); ASSERT_EQ(1, elements.size()); auto& element = elements.front(); auto decor = element->decoration(); ASSERT_THAT(decor, Ne(nullptr)); EXPECT_EQ(mc::Decoration::Type::surface, decor->type); EXPECT_EQ("username@hostname: ~/Documents", decor->name); } TEST_F(SurfaceStack, scene_counts_pending_accurately) { using namespace testing; ms::SurfaceStack stack{report}; stack.register_compositor(this); mtd::StubBuffer stub_buffer; int ready = 0; auto mock_queue = std::make_shared>(); ON_CALL(*mock_queue, buffers_ready_for_compositor(_)) .WillByDefault(InvokeWithoutArgs([&]{return ready;})); ON_CALL(*mock_queue, client_release(_)) .WillByDefault(InvokeWithoutArgs([&]{ready++;})); ON_CALL(*mock_queue, compositor_acquire(_)) .WillByDefault(InvokeWithoutArgs([&]{ready--; return mt::fake_shared(stub_buffer); })); auto surface = std::make_shared( std::string("stub"), geom::Rectangle{{},{}}, false, std::make_shared(mock_queue), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); stack.add_surface(surface, default_params.input_mode); surface->configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed); EXPECT_EQ(0, stack.frames_pending(this)); post_a_frame(*surface); post_a_frame(*surface); post_a_frame(*surface); EXPECT_EQ(3, stack.frames_pending(this)); for (int expect = 3; expect >= 0; --expect) { ASSERT_EQ(expect, stack.frames_pending(this)); auto snap = stack.scene_elements_for(compositor_id); for (auto& element : snap) { auto consumed = element->renderable()->buffer(); } } } TEST_F(SurfaceStack, scene_doesnt_count_pending_frames_from_occluded_surfaces) { // Regression test for LP: #1418081 using namespace testing; ms::SurfaceStack stack{report}; stack.register_compositor(this); auto surface = std::make_shared( std::string("stub"), geom::Rectangle{{},{}}, false, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); stack.add_surface(surface, default_params.input_mode); auto elements = stack.scene_elements_for(this); for (auto const& elem : elements) elem->occluded(); EXPECT_EQ(0, stack.frames_pending(this)); post_a_frame(*surface); post_a_frame(*surface); post_a_frame(*surface); EXPECT_EQ(0, stack.frames_pending(this)); } TEST_F(SurfaceStack, scene_doesnt_count_pending_frames_from_partially_exposed_surfaces) { // Regression test for LP: #1499039 using namespace testing; // Partially exposed means occluded in one compositor but not another ms::SurfaceStack stack{report}; auto const comp1 = reinterpret_cast(0); auto const comp2 = reinterpret_cast(1); stack.register_compositor(comp1); stack.register_compositor(comp2); auto surface = std::make_shared( std::string("stub"), geom::Rectangle{{},{}}, false, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); stack.add_surface(surface, default_params.input_mode); post_a_frame(*surface); post_a_frame(*surface); post_a_frame(*surface); EXPECT_EQ(3, stack.frames_pending(comp1)); EXPECT_EQ(3, stack.frames_pending(comp2)); auto elements = stack.scene_elements_for(comp1); for (auto const& elem : elements) { elem->rendered(); } elements = stack.scene_elements_for(comp2); for (auto const& elem : elements) { elem->occluded(); } EXPECT_EQ(3, stack.frames_pending(comp1)); EXPECT_EQ(0, stack.frames_pending(comp2)); } TEST_F(SurfaceStack, surfaces_are_emitted_by_layer) { using namespace testing; stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface3, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface1), SceneElementForSurface(stub_surface3), SceneElementForSurface(stub_surface2))); } TEST_F(SurfaceStack, input_registrar_is_notified_of_input_monitor_scene) { using namespace ::testing; MockSceneObserver observer; stack.add_observer(mt::fake_shared(observer)); Sequence seq; EXPECT_CALL(observer, surface_added(SurfaceWithInputReceptionMode(mi::InputReceptionMode::receives_all_input))) .InSequence(seq); EXPECT_CALL(observer, surface_removed(_)) .InSequence(seq); stack.add_surface(stub_surface1, mi::InputReceptionMode::receives_all_input); stack.remove_surface(stub_surface1); } TEST_F(SurfaceStack, raise_to_top_alters_render_ordering) { using namespace ::testing; stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); stack.add_surface(stub_surface3, default_params.input_mode); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface1), SceneElementForSurface(stub_surface2), SceneElementForSurface(stub_surface3))); stack.raise(stub_surface1); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface2), SceneElementForSurface(stub_surface3), SceneElementForSurface(stub_surface1))); } TEST_F(SurfaceStack, raise_throw_behavior) { using namespace ::testing; std::shared_ptr null_surface{nullptr}; EXPECT_THROW({ stack.raise(null_surface); }, std::runtime_error); } TEST_F(SurfaceStack, generate_elementelements) { using namespace testing; size_t num_surfaces{3}; std::vector> surfaces; for(auto i = 0u; i < num_surfaces; i++) { auto const surface = std::make_shared( std::string("stub"), geom::Rectangle{geom::Point{3 * i, 4 * i},geom::Size{1 * i, 2 * i}}, true, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); post_a_frame(*surface); surfaces.emplace_back(surface); stack.add_surface(surface, default_params.input_mode); } auto const elements = stack.scene_elements_for(compositor_id); ASSERT_THAT(elements.size(), Eq(num_surfaces)); auto surface_it = surfaces.begin(); for(auto& element : elements) { EXPECT_THAT(element->renderable()->screen_position().top_left, Eq((*surface_it++)->top_left())); } for(auto& surface : surfaces) stack.remove_surface(surface); } TEST_F(SurfaceStack, scene_observer_notified_of_add_and_remove) { using namespace ::testing; MockSceneObserver observer; InSequence seq; EXPECT_CALL(observer, surface_added(stub_surface1.get())).Times(1); EXPECT_CALL(observer, surface_removed(stub_surface1.get())) .Times(1); stack.add_observer(mt::fake_shared(observer)); stack.add_surface(stub_surface1, default_params.input_mode); stack.remove_surface(stub_surface1); } TEST_F(SurfaceStack, multiple_observers) { using namespace ::testing; MockSceneObserver observer1, observer2; InSequence seq; EXPECT_CALL(observer1, surface_added(stub_surface1.get())).Times(1); EXPECT_CALL(observer2, surface_added(stub_surface1.get())).Times(1); stack.add_observer(mt::fake_shared(observer1)); stack.add_observer(mt::fake_shared(observer2)); stack.add_surface(stub_surface1, default_params.input_mode); } TEST_F(SurfaceStack, remove_scene_observer) { using namespace ::testing; MockSceneObserver observer; InSequence seq; EXPECT_CALL(observer, surface_added(stub_surface1.get())).Times(1); // We remove the scene observer before removing the surface, and thus // expect to NOT see the surface_removed call EXPECT_CALL(observer, end_observation()).Times(1); EXPECT_CALL(observer, surface_removed(stub_surface1.get())) .Times(0); stack.add_observer(mt::fake_shared(observer)); stack.add_surface(stub_surface1, default_params.input_mode); stack.remove_observer(mt::fake_shared(observer)); stack.remove_surface(stub_surface1); } // Many clients of the scene observer wish to install surface observers to monitor surface // notifications. We offer them a surface_added event for existing surfaces to give them // a chance to do this. TEST_F(SurfaceStack, scene_observer_informed_of_existing_surfaces) { using namespace ::testing; MockSceneObserver observer; InSequence seq; EXPECT_CALL(observer, surface_exists(stub_surface1.get())).Times(1); EXPECT_CALL(observer, surface_exists(stub_surface2.get())).Times(1); stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); stack.add_observer(mt::fake_shared(observer)); } TEST_F(SurfaceStack, scene_observer_can_query_scene_within_surface_exists_notification) { using namespace ::testing; MockSceneObserver observer; auto const scene_query = [&]{ stack.for_each([&](std::shared_ptr const& surface){ EXPECT_THAT(surface.get(), Eq(stub_surface1.get())); }); }; EXPECT_CALL(observer, surface_exists(stub_surface1.get())).Times(1) .WillOnce(InvokeWithoutArgs(scene_query)); stack.add_surface(stub_surface1, default_params.input_mode); stack.add_observer(mt::fake_shared(observer)); } TEST_F(SurfaceStack, scene_observer_can_async_query_scene_within_surface_exists_notification) { using namespace ::testing; MockSceneObserver observer; auto const scene_query = [&]{ stack.for_each([&](std::shared_ptr const& surface){ EXPECT_THAT(surface.get(), Eq(stub_surface1.get())); }); }; auto const async_scene_query = [&]{ std::async(std::launch::async, scene_query); }; EXPECT_CALL(observer, surface_exists(stub_surface1.get())).Times(1) .WillOnce(InvokeWithoutArgs(async_scene_query)); stack.add_surface(stub_surface1, default_params.input_mode); stack.add_observer(mt::fake_shared(observer)); } TEST_F(SurfaceStack, scene_observer_can_remove_surface_from_scene_within_surface_exists_notification) { using namespace ::testing; MockSceneObserver observer; auto const surface_removal = [&]{ stack.remove_surface(stub_surface1); }; EXPECT_CALL(observer, surface_exists(stub_surface1.get())).Times(1) .WillOnce(InvokeWithoutArgs(surface_removal)); stack.add_surface(stub_surface1, default_params.input_mode); stack.add_observer(mt::fake_shared(observer)); } TEST_F(SurfaceStack, surfaces_reordered) { using namespace ::testing; MockSceneObserver observer; EXPECT_CALL(observer, surface_added(_)).Times(AnyNumber()); EXPECT_CALL(observer, surface_removed(_)).Times(AnyNumber()); EXPECT_CALL(observer, surfaces_reordered()).Times(1); stack.add_observer(mt::fake_shared(observer)); stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); stack.raise(stub_surface1); } TEST_F(SurfaceStack, scene_elements_hold_snapshot_of_positioning_info) { size_t num_surfaces{3}; std::vector> surfaces; for(auto i = 0u; i < num_surfaces; i++) { auto const surface = std::make_shared( std::string("stub"), geom::Rectangle{geom::Point{3 * i, 4 * i},geom::Size{1 * i, 2 * i}}, true, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); surfaces.emplace_back(surface); stack.add_surface(surface, default_params.input_mode); } auto const elements = stack.scene_elements_for(compositor_id); auto const changed_position = geom::Point{43,44}; for(auto const& surface : surfaces) surface->move_to(changed_position); //check that the renderables are not at changed_pos for(auto& element : elements) EXPECT_THAT(changed_position, testing::Ne(element->renderable()->screen_position().top_left)); } TEST_F(SurfaceStack, generates_scene_elements_that_delay_buffer_acquisition) { using namespace testing; auto mock_stream = std::make_shared>(); EXPECT_CALL(*mock_stream, lock_compositor_buffer(_)) .Times(0); auto const surface = std::make_shared( std::string("stub"), geom::Rectangle{geom::Point{3, 4},geom::Size{1, 2}}, true, mock_stream, std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); post_a_frame(*surface); stack.add_surface(surface, default_params.input_mode); auto const elements = stack.scene_elements_for(compositor_id); Mock::VerifyAndClearExpectations(mock_stream.get()); EXPECT_CALL(*mock_stream, lock_compositor_buffer(compositor_id)) .Times(1) .WillOnce(Return(std::make_shared())); ASSERT_THAT(elements.size(), Eq(1u)); elements.front()->renderable()->buffer(); } TEST_F(SurfaceStack, generates_scene_elements_that_allow_only_one_buffer_acquisition) { using namespace testing; auto mock_stream = std::make_shared>(); EXPECT_CALL(*mock_stream, lock_compositor_buffer(_)) .Times(1) .WillOnce(Return(std::make_shared())); auto const surface = std::make_shared( std::string("stub"), geom::Rectangle{geom::Point{3, 4},geom::Size{1, 2}}, true, mock_stream, std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), report); post_a_frame(*surface); stack.add_surface(surface, default_params.input_mode); auto const elements = stack.scene_elements_for(compositor_id); ASSERT_THAT(elements.size(), Eq(1u)); elements.front()->renderable()->buffer(); elements.front()->renderable()->buffer(); elements.front()->renderable()->buffer(); } namespace { struct MockConfigureSurface : public ms::BasicSurface { MockConfigureSurface() : ms::BasicSurface( {}, {{},{}}, true, std::make_shared(), {}, {}, {}, mir::report::null_scene_report()) { } MOCK_METHOD2(configure, int(MirSurfaceAttrib, int)); }; } TEST_F(SurfaceStack, occludes_not_rendered_surface) { using namespace testing; mc::CompositorID const compositor_id2{&compositor_id}; stack.register_compositor(compositor_id); stack.register_compositor(compositor_id2); auto const mock_surface = std::make_shared(); mock_surface->show(); post_a_frame(*mock_surface); stack.add_surface(mock_surface, default_params.input_mode); auto const elements = stack.scene_elements_for(compositor_id); ASSERT_THAT(elements.size(), Eq(1u)); auto const elements2 = stack.scene_elements_for(compositor_id2); ASSERT_THAT(elements2.size(), Eq(1u)); EXPECT_CALL(*mock_surface, configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded)); elements.back()->occluded(); elements2.back()->occluded(); } TEST_F(SurfaceStack, exposes_rendered_surface) { using namespace testing; mc::CompositorID const compositor_id2{&compositor_id}; stack.register_compositor(compositor_id); stack.register_compositor(compositor_id2); auto const mock_surface = std::make_shared(); post_a_frame(*mock_surface); stack.add_surface(mock_surface, default_params.input_mode); auto const elements = stack.scene_elements_for(compositor_id); ASSERT_THAT(elements.size(), Eq(1u)); auto const elements2 = stack.scene_elements_for(compositor_id2); ASSERT_THAT(elements2.size(), Eq(1u)); EXPECT_CALL(*mock_surface, configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed)); elements.back()->occluded(); elements2.back()->rendered(); } TEST_F(SurfaceStack, occludes_surface_when_unregistering_all_compositors_that_rendered_it) { using namespace testing; mc::CompositorID const compositor_id2{&compositor_id}; mc::CompositorID const compositor_id3{&compositor_id2}; stack.register_compositor(compositor_id); stack.register_compositor(compositor_id2); stack.register_compositor(compositor_id3); auto const mock_surface = std::make_shared(); post_a_frame(*mock_surface); stack.add_surface(mock_surface, default_params.input_mode); auto const elements = stack.scene_elements_for(compositor_id); ASSERT_THAT(elements.size(), Eq(1u)); auto const elements2 = stack.scene_elements_for(compositor_id2); ASSERT_THAT(elements2.size(), Eq(1u)); auto const elements3 = stack.scene_elements_for(compositor_id3); ASSERT_THAT(elements3.size(), Eq(1u)); EXPECT_CALL(*mock_surface, configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed)) .Times(2); elements.back()->occluded(); elements2.back()->rendered(); elements3.back()->rendered(); Mock::VerifyAndClearExpectations(mock_surface.get()); EXPECT_CALL(*mock_surface, configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded)); stack.unregister_compositor(compositor_id2); stack.unregister_compositor(compositor_id3); } TEST_F(SurfaceStack, observer_can_trigger_state_change_within_notification) { using namespace ::testing; MockSceneObserver observer; auto const state_changer = [&]{ stack.add_surface(stub_surface1, default_params.input_mode); }; //Make sure another thread can also change state auto const async_state_changer = [&]{ std::async(std::launch::async, state_changer); }; EXPECT_CALL(observer, surface_added(stub_surface1.get())).Times(3) .WillOnce(InvokeWithoutArgs(state_changer)) .WillOnce(InvokeWithoutArgs(async_state_changer)) .WillOnce(Return()); stack.add_observer(mt::fake_shared(observer)); state_changer(); } TEST_F(SurfaceStack, observer_can_remove_itself_within_notification) { using namespace testing; MockSceneObserver observer1; MockSceneObserver observer2; MockSceneObserver observer3; auto const remove_observer = [&]{ stack.remove_observer(mt::fake_shared(observer2)); }; //Both of these observers should still get their notifications //regardless of the removal of observer2 EXPECT_CALL(observer1, surface_added(stub_surface1.get())).Times(2); EXPECT_CALL(observer3, surface_added(stub_surface1.get())).Times(2); InSequence seq; EXPECT_CALL(observer2, surface_added(stub_surface1.get())).Times(1) .WillOnce(InvokeWithoutArgs(remove_observer)); EXPECT_CALL(observer2, end_observation()).Times(1); stack.add_observer(mt::fake_shared(observer1)); stack.add_observer(mt::fake_shared(observer2)); stack.add_observer(mt::fake_shared(observer3)); stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface1, default_params.input_mode); } TEST_F(SurfaceStack, scene_observer_notified_of_add_and_remove_input_visualization) { using namespace ::testing; MockSceneObserver observer; mtd::StubRenderable r; InSequence seq; EXPECT_CALL(observer, scene_changed()).Times(2); stack.add_observer(mt::fake_shared(observer)); stack.add_input_visualization(mt::fake_shared(r)); stack.remove_input_visualization(mt::fake_shared(r)); } TEST_F(SurfaceStack, overlays_do_not_appear_in_input_enumeration) { mtd::StubRenderable r; stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); // Configure surface1 and surface2 to appear in input enumeration. stub_surface1->configure(mir_surface_attrib_visibility, MirSurfaceVisibility::mir_surface_visibility_exposed); stub_surface2->configure(mir_surface_attrib_visibility, MirSurfaceVisibility::mir_surface_visibility_exposed); stack.add_input_visualization(mt::fake_shared(r)); unsigned int observed_input_targets = 0; stack.for_each([&observed_input_targets](std::shared_ptr const&) { observed_input_targets++; }); EXPECT_EQ(2, observed_input_targets); } TEST_F(SurfaceStack, overlays_appear_at_top_of_renderlist) { using namespace ::testing; mtd::StubRenderable r; stack.add_surface(stub_surface1, default_params.input_mode); stack.add_input_visualization(mt::fake_shared(r)); stack.add_surface(stub_surface2, default_params.input_mode); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface1), SceneElementForSurface(stub_surface2), SceneElementForStream(mt::fake_shared(r)))); } TEST_F(SurfaceStack, removed_overlays_are_removed) { using namespace ::testing; mtd::StubRenderable r; stack.add_surface(stub_surface1, default_params.input_mode); stack.add_input_visualization(mt::fake_shared(r)); stack.add_surface(stub_surface2, default_params.input_mode); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface1), SceneElementForSurface(stub_surface2), SceneElementForStream(mt::fake_shared(r)))); stack.remove_input_visualization(mt::fake_shared(r)); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface1), SceneElementForSurface(stub_surface2))); } TEST_F(SurfaceStack, scene_observers_notified_of_generic_scene_change) { MockSceneObserver o1, o2; EXPECT_CALL(o1, scene_changed()).Times(1); EXPECT_CALL(o2, scene_changed()).Times(1); stack.add_observer(mt::fake_shared(o1)); stack.add_observer(mt::fake_shared(o2)); stack.emit_scene_changed(); } TEST_F(SurfaceStack, only_enumerates_exposed_input_surfaces) { using namespace ::testing; stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); stack.add_surface(stub_surface3, default_params.input_mode); stub_surface1->configure(mir_surface_attrib_visibility, MirSurfaceVisibility::mir_surface_visibility_exposed); stub_surface2->configure(mir_surface_attrib_visibility, MirSurfaceVisibility::mir_surface_visibility_occluded); stub_surface3->configure(mir_surface_attrib_visibility, MirSurfaceVisibility::mir_surface_visibility_occluded); int num_exposed_surfaces = 0; auto const count_exposed_surfaces = [&num_exposed_surfaces](std::shared_ptr const&){ num_exposed_surfaces++; }; stack.for_each(count_exposed_surfaces); EXPECT_THAT(num_exposed_surfaces, Eq(1)); } using namespace ::testing; TEST_F(SurfaceStack, returns_top_surface_under_cursor) { geom::Point const cursor_over_all {100, 100}; geom::Point const cursor_over_12 {200, 100}; geom::Point const cursor_over_1 {600, 600}; geom::Point const cursor_over_none{999, 999}; stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); stack.add_surface(stub_surface3, default_params.input_mode); stub_surface1->resize({900, 900}); stub_surface2->resize({500, 200}); stub_surface3->resize({200, 500}); EXPECT_THAT(stack.surface_at(cursor_over_all), Eq(stub_surface3)); EXPECT_THAT(stack.surface_at(cursor_over_12), Eq(stub_surface2)); EXPECT_THAT(stack.surface_at(cursor_over_1), Eq(stub_surface1)); EXPECT_THAT(stack.surface_at(cursor_over_none).get(), IsNull()); } TEST_F(SurfaceStack, returns_top_visible_surface_under_cursor) { geom::Point const cursor_over_all {100, 100}; geom::Point const cursor_over_12 {200, 100}; geom::Point const cursor_over_1 {600, 600}; geom::Point const cursor_over_none{999, 999}; stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); stack.add_surface(stub_surface3, default_params.input_mode); stack.add_surface(invisible_stub_surface, default_params.input_mode); stub_surface1->resize({900, 900}); stub_surface2->resize({500, 200}); stub_surface3->resize({200, 500}); invisible_stub_surface->resize({999, 999}); EXPECT_THAT(stack.surface_at(cursor_over_all), Eq(stub_surface3)); EXPECT_THAT(stack.surface_at(cursor_over_12), Eq(stub_surface2)); EXPECT_THAT(stack.surface_at(cursor_over_1), Eq(stub_surface1)); EXPECT_THAT(stack.surface_at(cursor_over_none).get(), IsNull()); } TEST_F(SurfaceStack, raise_surfaces_to_top) { stack.add_surface(stub_surface1, default_params.input_mode); stack.add_surface(stub_surface2, default_params.input_mode); stack.add_surface(stub_surface3, default_params.input_mode); NiceMock observer; stack.add_observer(mt::fake_shared(observer)); EXPECT_CALL(observer, surfaces_reordered()).Times(1); stack.raise({stub_surface1, stub_surface3}); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface2), SceneElementForSurface(stub_surface1), SceneElementForSurface(stub_surface3))); Mock::VerifyAndClearExpectations(&observer); EXPECT_CALL(observer, surfaces_reordered()).Times(1); stack.raise({stub_surface2, stub_surface3}); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface1), SceneElementForSurface(stub_surface2), SceneElementForSurface(stub_surface3))); Mock::VerifyAndClearExpectations(&observer); EXPECT_CALL(observer, surfaces_reordered()).Times(0); stack.raise({stub_surface2, stub_surface1, stub_surface3}); EXPECT_THAT( stack.scene_elements_for(compositor_id), ElementsAre( SceneElementForSurface(stub_surface1), SceneElementForSurface(stub_surface2), SceneElementForSurface(stub_surface3))); } ./tests/unit-tests/scene/test_abstract_shell.cpp0000644000015600001650000005106312676616125022231 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/shell/abstract_shell.h" #include "mir/events/event_builders.h" #include "mir/scene/session.h" #include "mir/scene/null_session_listener.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/scene/surface_factory.h" #include "src/server/report/null/shell_report.h" #include "src/server/scene/default_session_container.h" #include "src/server/scene/session_event_sink.h" #include "src/server/scene/session_manager.h" #include "mir/test/doubles/mock_window_manager.h" #include "mir/test/doubles/mock_surface_stack.h" #include "mir/test/doubles/mock_surface.h" #include "mir/test/doubles/stub_surface.h" #include "mir/test/doubles/null_event_sink.h" #include "mir/test/doubles/null_snapshot_strategy.h" #include "mir/test/doubles/null_prompt_session_manager.h" #include "mir/test/doubles/stub_input_targeter.h" #include "mir/test/doubles/stub_buffer_stream_factory.h" #include "mir/test/doubles/null_application_not_responding_detector.h" #include "mir/test/doubles/stub_display.h" #include "mir/test/fake_shared.h" #include #include namespace mf = mir::frontend; namespace mi = mir::input; namespace ms = mir::scene; namespace msh = mir::shell; namespace geom = mir::geometry; namespace mg = mir::graphics; namespace mt = mir::test; namespace mtd = mir::test::doubles; using namespace ::testing; namespace { MATCHER_P(WeakPtrTo, p, "") { return !arg.owner_before(p) && !p.owner_before(arg); } struct MockSessionContainer : public ms::SessionContainer { MOCK_METHOD1(insert_session, void(std::shared_ptr const&)); MOCK_METHOD1(remove_session, void(std::shared_ptr const&)); MOCK_CONST_METHOD1(successor_of, std::shared_ptr(std::shared_ptr const&)); MOCK_CONST_METHOD1(for_each, void(std::function const&)>)); MOCK_METHOD0(lock, void()); MOCK_METHOD0(unlock, void()); ~MockSessionContainer() noexcept {} }; struct MockSessionEventSink : public ms::SessionEventSink { MOCK_METHOD1(handle_focus_change, void(std::shared_ptr const& session)); MOCK_METHOD0(handle_no_focus, void()); MOCK_METHOD1(handle_session_stopping, void(std::shared_ptr const& session)); }; struct MockSessionManager : ms::SessionManager { using ms::SessionManager::SessionManager; MOCK_METHOD1(set_focus_to, void (std::shared_ptr const& focus)); void unmocked_set_focus_to(std::shared_ptr const& focus) { ms::SessionManager::set_focus_to(focus); } }; struct MockSurfaceFactory : public ms::SurfaceFactory { MOCK_METHOD2(create_surface, std::shared_ptr( std::shared_ptr const&, ms::SurfaceCreationParameters const&)); }; using NiceMockWindowManager = NiceMock; struct AbstractShell : Test { NiceMock mock_surface; NiceMock surface_stack; NiceMock session_container; NiceMock session_event_sink; NiceMock surface_factory; mtd::StubDisplay display{3}; NiceMock session_manager{ mt::fake_shared(surface_stack), mt::fake_shared(surface_factory), std::make_shared(), mt::fake_shared(session_container), std::make_shared(), mt::fake_shared(session_event_sink), std::make_shared(), mt::fake_shared(display), std::make_shared()}; mtd::StubInputTargeter input_targeter; std::shared_ptr wm; msh::AbstractShell shell{ mt::fake_shared(input_targeter), mt::fake_shared(surface_stack), mt::fake_shared(session_manager), std::make_shared(), std::make_shared(), [this](msh::FocusController*) { return wm = std::make_shared(); }}; void SetUp() override { ON_CALL(session_container, successor_of(_)).WillByDefault(Return((std::shared_ptr()))); ON_CALL(session_manager, set_focus_to(_)). WillByDefault(Invoke(&session_manager, &MockSessionManager::unmocked_set_focus_to)); ON_CALL(mock_surface, size()) .WillByDefault(Return(geom::Size{})); ON_CALL(surface_factory, create_surface(_,_)) .WillByDefault(Return(mt::fake_shared(mock_surface))); } std::chrono::nanoseconds const event_timestamp = std::chrono::nanoseconds(0); std::vector const cookie; }; } TEST_F(AbstractShell, open_session_adds_session_to_window_manager) { std::shared_ptr new_session; InSequence s; EXPECT_CALL(session_container, insert_session(_)).Times(1); EXPECT_CALL(*wm, add_session(_)).WillOnce(SaveArg<0>(&new_session)); auto session = shell.open_session(__LINE__, "Visual Basic Studio", std::shared_ptr()); EXPECT_EQ(session, new_session); } TEST_F(AbstractShell, close_session_removes_session_from_window_manager) { std::shared_ptr old_session; InSequence s; EXPECT_CALL(session_container, insert_session(_)); EXPECT_CALL(*wm, remove_session(_)).WillOnce(SaveArg<0>(&old_session)); auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); shell.close_session(session); EXPECT_EQ(session, old_session); } TEST_F(AbstractShell, close_session_notifies_session_event_sink) { auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr()); InSequence s; EXPECT_CALL(session_event_sink, handle_session_stopping(session1)); EXPECT_CALL(session_event_sink, handle_session_stopping(session)); shell.close_session(session1); shell.close_session(session); } TEST_F(AbstractShell, close_session_removes_existing_session_surfaces_from_window_manager) { mtd::StubSurface surface1; mtd::StubSurface surface2; mtd::StubSurface surface3; EXPECT_CALL(surface_factory, create_surface(_,_)). WillOnce(Return(mt::fake_shared(surface1))). WillOnce(Return(mt::fake_shared(surface2))). WillOnce(Return(mt::fake_shared(surface3))); auto const session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto const surface_id1 = shell.create_surface(session, ms::a_surface(), nullptr); auto const surface_id2 = shell.create_surface(session, ms::a_surface(), nullptr); auto const surface_id3 = shell.create_surface(session, ms::a_surface(), nullptr); session->destroy_surface(surface_id2); Expectation remove1 = EXPECT_CALL( *wm, remove_surface(session, WeakPtrTo(session->surface(surface_id1)))); Expectation remove3 = EXPECT_CALL( *wm, remove_surface(session, WeakPtrTo(session->surface(surface_id3)))); EXPECT_CALL(*wm, remove_session(session)).After(remove1, remove3); shell.close_session(session); } TEST_F(AbstractShell, create_surface_provides_create_parameters_to_window_manager) { std::shared_ptr session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto params = ms::a_surface(); EXPECT_CALL(*wm, add_surface(session, Ref(params), _)); shell.create_surface(session, params, nullptr); } TEST_F(AbstractShell, create_surface_allows_window_manager_to_set_create_parameters) { std::shared_ptr const session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto params = ms::a_surface(); auto placed_params = params; placed_params.size.width = geom::Width{100}; EXPECT_CALL(surface_stack, add_surface(_,placed_params.input_mode)); EXPECT_CALL(*wm, add_surface(session, Ref(params), _)).WillOnce(Invoke( [&](std::shared_ptr const& session, ms::SurfaceCreationParameters const&, std::function const& session, ms::SurfaceCreationParameters const&)> const& build) { return build(session, placed_params); })); shell.create_surface(session, params, nullptr); } TEST_F(AbstractShell, destroy_surface_removes_surface_from_window_manager) { auto const params = ms::a_surface(); std::shared_ptr const session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto const surface_id = shell.create_surface(session, params, nullptr); auto const surface = session->surface(surface_id); EXPECT_CALL(*wm, remove_surface(session, WeakPtrTo(surface))); shell.destroy_surface(session, surface_id); } TEST_F(AbstractShell, add_display_adds_display_to_window_manager) { geom::Rectangle const arbitrary_area{{0,0}, {__LINE__,__LINE__}}; EXPECT_CALL(*wm, add_display(arbitrary_area)); shell.add_display(arbitrary_area); } TEST_F(AbstractShell, remove_display_adds_display_to_window_manager) { geom::Rectangle const arbitrary_area{{0,0}, {__LINE__,__LINE__}}; EXPECT_CALL(*wm, remove_display(arbitrary_area)); shell.remove_display(arbitrary_area); } TEST_F(AbstractShell, key_input_events_are_handled_by_window_manager) { MirKeyboardAction const action{mir_keyboard_action_down}; xkb_keysym_t const key_code{0}; int const scan_code{0}; MirInputEventModifiers const modifiers{mir_input_event_modifier_none}; auto const event = mir::events::make_event( mir_input_event_type_key, event_timestamp, cookie, action, key_code, scan_code, modifiers); EXPECT_CALL(*wm, handle_keyboard_event(_)) .WillOnce(Return(false)) .WillOnce(Return(true)); EXPECT_FALSE(shell.handle(*event)); EXPECT_TRUE(shell.handle(*event)); } TEST_F(AbstractShell, touch_input_events_are_handled_by_window_manager) { MirInputEventModifiers const modifiers{mir_input_event_modifier_none}; auto const event = mir::events::make_event( mir_input_event_type_touch, event_timestamp, cookie, modifiers); EXPECT_CALL(*wm, handle_touch_event(_)) .WillOnce(Return(false)) .WillOnce(Return(true)); EXPECT_FALSE(shell.handle(*event)); EXPECT_TRUE(shell.handle(*event)); } TEST_F(AbstractShell, pointer_input_events_are_handled_by_window_manager) { MirInputEventModifiers const modifiers{mir_input_event_modifier_none}; MirPointerAction const action{mir_pointer_action_button_down}; auto const buttons_pressed = mir_pointer_button_primary; float const x_axis_value{0.0}; float const y_axis_value{0.0}; float const hscroll_value{0.0}; float const vscroll_value{0.0}; float const relative_x_value{0.0}; float const relative_y_value{0.0}; auto const event = mir::events::make_event( mir_input_event_type_pointer, event_timestamp, cookie, modifiers, action, buttons_pressed, x_axis_value, y_axis_value, hscroll_value, vscroll_value, relative_x_value, relative_y_value); EXPECT_CALL(*wm, handle_pointer_event(_)) .WillOnce(Return(false)) .WillOnce(Return(true)); EXPECT_FALSE(shell.handle(*event)); EXPECT_TRUE(shell.handle(*event)); } TEST_F(AbstractShell, setting_surface_state_is_handled_by_window_manager) { std::shared_ptr const session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto const surface_id = shell.create_surface(session, ms::a_surface(), nullptr); auto const surface = session->surface(surface_id); MirSurfaceState const state{mir_surface_state_fullscreen}; EXPECT_CALL(*wm, set_surface_attribute(session, surface, mir_surface_attrib_state, state)) .WillOnce(WithArg<1>(Invoke([](std::shared_ptr const& surface) { surface->configure(mir_surface_attrib_state, mir_surface_state_maximized); return mir_surface_state_maximized; }))); EXPECT_CALL(mock_surface, configure(mir_surface_attrib_state, mir_surface_state_maximized)); shell.set_surface_attribute(session, surface, mir_surface_attrib_state, mir_surface_state_fullscreen); } TEST_F(AbstractShell, as_focus_controller_set_focus_to_notifies_session_event_sink) { auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr()); InSequence s; EXPECT_CALL(session_event_sink, handle_focus_change(session1)); EXPECT_CALL(session_event_sink, handle_focus_change(session)); EXPECT_CALL(session_event_sink, handle_no_focus()); msh::FocusController& focus_controller = shell; focus_controller.set_focus_to(session1, {}); focus_controller.set_focus_to(session, {}); focus_controller.set_focus_to({}, {}); } TEST_F(AbstractShell, as_focus_controller_focus_next_session_notifies_session_event_sink) { msh::FocusController& focus_controller = shell; auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr()); shell.create_surface(session, ms::a_surface(), nullptr); shell.create_surface(session1, ms::a_surface(), nullptr); focus_controller.set_focus_to(session, {}); EXPECT_CALL(session_container, successor_of(session)). WillOnce(Return(session1)); EXPECT_CALL(session_event_sink, handle_focus_change(session1)); focus_controller.focus_next_session(); } TEST_F(AbstractShell, as_focus_controller_focused_session_follows_focus) { auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr()); shell.create_surface(session, ms::a_surface(), nullptr); shell.create_surface(session1, ms::a_surface(), nullptr); EXPECT_CALL(session_container, successor_of(session1)). WillOnce(Return(session)); msh::FocusController& focus_controller = shell; focus_controller.set_focus_to(session, {}); EXPECT_THAT(focus_controller.focused_session(), Eq(session)); focus_controller.set_focus_to(session1, {}); EXPECT_THAT(focus_controller.focused_session(), Eq(session1)); focus_controller.focus_next_session(); EXPECT_THAT(focus_controller.focused_session(), Eq(session)); } TEST_F(AbstractShell, as_focus_controller_focused_surface_follows_focus) { auto const session0 = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto const session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr()); NiceMock dummy_surface; ON_CALL(dummy_surface, size()).WillByDefault(Return(geom::Size{})); EXPECT_CALL(surface_factory, create_surface(_,_)).Times(AnyNumber()) .WillOnce(Return(mt::fake_shared(dummy_surface))) .WillOnce(Return(mt::fake_shared(mock_surface))); EXPECT_CALL(session_container, successor_of(session1)). WillOnce(Return(session0)); auto const surface0_id = shell.create_surface(session0, ms::a_surface(), nullptr); auto const surface0 = session0->surface(surface0_id); auto const surface1_id = shell.create_surface(session1, ms::a_surface(), nullptr); auto const surface1 = session1->surface(surface1_id); msh::FocusController& focus_controller = shell; focus_controller.set_focus_to(session0, surface0); EXPECT_THAT(focus_controller.focused_surface(), Eq(surface0)); focus_controller.set_focus_to(session1, surface1); EXPECT_THAT(focus_controller.focused_surface(), Eq(surface1)); focus_controller.focus_next_session(); EXPECT_THAT(focus_controller.focused_surface(), Eq(surface0)); shell.destroy_surface(session0, surface0_id); shell.destroy_surface(session1, surface1_id); shell.close_session(session1); shell.close_session(session0); } TEST_F(AbstractShell, as_focus_controller_delegates_surface_at_to_surface_stack) { auto const surface = mt::fake_shared(mock_surface); geom::Point const cursor{__LINE__, __LINE__}; EXPECT_CALL(surface_stack, surface_at(cursor)). WillOnce(Return(surface)); msh::FocusController& focus_controller = shell; EXPECT_THAT(focus_controller.surface_at(cursor), Eq(surface)); } TEST_F(AbstractShell, as_focus_controller_focus_next_session_skips_surfaceless_sessions) { msh::FocusController& focus_controller = shell; auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto session1 = shell.open_session(__LINE__, "Surfaceless", std::shared_ptr()); auto session2 = shell.open_session(__LINE__, "Bla", std::shared_ptr()); auto surface_id = shell.create_surface(session, ms::a_surface(), nullptr); shell.create_surface(session2, ms::a_surface(), nullptr); focus_controller.set_focus_to(session, session->surface(surface_id)); EXPECT_CALL(session_container, successor_of(session)). WillOnce(Return(session1)); EXPECT_CALL(session_container, successor_of(session1)). WillOnce(Return(session2)); EXPECT_CALL(session_event_sink, handle_focus_change(session2)); focus_controller.focus_next_session(); } TEST_F(AbstractShell, as_focus_controller_focus_next_session_does_not_focus_any_session_if_no_session_has_surfaces) { msh::FocusController& focus_controller = shell; auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto session1 = shell.open_session(__LINE__, "Surfaceless", std::shared_ptr()); auto surface_id = shell.create_surface(session, ms::a_surface(), nullptr); focus_controller.set_focus_to(session, session->surface(surface_id)); session->destroy_surface(surface_id); EXPECT_CALL(session_container, successor_of(session)). WillOnce(Return(session1)); EXPECT_CALL(session_container, successor_of(session1)). WillOnce(Return(session)); EXPECT_CALL(session_event_sink, handle_no_focus()); focus_controller.focus_next_session(); } TEST_F(AbstractShell, modify_surface_with_only_streams_doesnt_call_into_wm) { std::shared_ptr session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto creation_params = ms::a_surface(); auto surface_id = shell.create_surface(session, creation_params, nullptr); auto surface = session->surface(surface_id); msh::SurfaceSpecification stream_modification; stream_modification.streams = std::vector{}; EXPECT_CALL(*wm, modify_surface(_,_,_)).Times(0); shell.modify_surface(session, surface, stream_modification); } TEST_F(AbstractShell, modify_surface_does_not_call_wm_for_empty_changes) { std::shared_ptr session = shell.open_session(__LINE__, "XPlane", std::shared_ptr()); auto creation_params = ms::a_surface(); auto surface_id = shell.create_surface(session, creation_params, nullptr); auto surface = session->surface(surface_id); msh::SurfaceSpecification stream_modification; EXPECT_CALL(*wm, modify_surface(_,_,_)).Times(0); shell.modify_surface(session, surface, stream_modification); } namespace mir { namespace scene { // The next test is easier to write if we can compare std::weak_ptr inline bool operator==( std::weak_ptr const& lhs, std::weak_ptr const& rhs) { return !lhs.owner_before(rhs) && !rhs.owner_before(lhs); } } } TEST_F(AbstractShell, as_focus_controller_delegates_raise_to_surface_stack) { msh::SurfaceSet const surfaces{mt::fake_shared(mock_surface)}; EXPECT_CALL(surface_stack, raise(surfaces)); msh::FocusController& focus_controller = shell; focus_controller.raise(surfaces); } ./tests/unit-tests/scene/test_prompt_session_container.cpp0000644000015600001650000002776212676616125024376 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #include "src/server/scene/prompt_session_container.h" #include "mir/test/doubles/mock_scene_session.h" #include "mir/test/doubles/null_prompt_session.h" #include "mir/test/fake_shared.h" #include #include namespace ms = mir::scene; namespace mf = mir::frontend; namespace mt = mir::test; namespace mtd = mir::test::doubles; using namespace testing; namespace { struct PromptSessionContainer : testing::Test { static constexpr pid_t process1 = 1; static constexpr pid_t process2 = 2; mtd::NullPromptSession null_prompt_session1; mtd::NullPromptSession null_prompt_session2; mtd::NullPromptSession null_prompt_session3; std::shared_ptr const prompt_session1 = mt::fake_shared(null_prompt_session1); std::shared_ptr const prompt_session2 = mt::fake_shared(null_prompt_session2); std::shared_ptr const prompt_session3 = mt::fake_shared(null_prompt_session3); testing::NiceMock mock_scene_session1; testing::NiceMock mock_scene_session2; testing::NiceMock mock_scene_session3; testing::NiceMock mock_scene_session4; std::shared_ptr const session1 = mt::fake_shared(mock_scene_session1); std::shared_ptr const session2 = mt::fake_shared(mock_scene_session2); std::shared_ptr const session3 = mt::fake_shared(mock_scene_session3); std::shared_ptr const session4 = mt::fake_shared(mock_scene_session4); ms::PromptSessionContainer container; void SetUp() { ON_CALL(mock_scene_session1, process_id()).WillByDefault(Return(process1)); ON_CALL(mock_scene_session2, process_id()).WillByDefault(Return(process1)); ON_CALL(mock_scene_session3, process_id()).WillByDefault(Return(process2)); ON_CALL(mock_scene_session4, process_id()).WillByDefault(Return(process2)); } std::vector> list_participants_for(std::shared_ptr const& prompt_session) { std::vector> results; auto list_participants = [&results](std::weak_ptr const& session, ms::PromptSessionContainer::ParticipantType) { results.push_back(session.lock()); }; container.for_each_participant_in_prompt_session(prompt_session.get(), list_participants); return results; } int count_participants_for(std::shared_ptr const& prompt_session) { return list_participants_for(prompt_session).size(); } std::vector> list_prompt_sessions_for(std::shared_ptr const& session) { std::vector> results; auto list_prompt_sessions = [&results](std::shared_ptr const& prompt_session, ms::PromptSessionContainer::ParticipantType) { results.push_back(prompt_session); }; container.for_each_prompt_session_with_participant(session, list_prompt_sessions); return results; } std::vector> list_prompt_sessions_for(std::shared_ptr const& session, ms::PromptSessionContainer::ParticipantType participant_type) { std::vector> results; auto list_prompt_sessions = [&results](std::shared_ptr const& prompt_session) { results.push_back(prompt_session); }; container.for_each_prompt_session_with_participant(session, participant_type, list_prompt_sessions); return results; } int count_prompt_sessions_for(std::shared_ptr const& session) { return list_prompt_sessions_for(session).size(); } }; } TEST_F(PromptSessionContainer, throws_exception_if_no_prompt_session) { EXPECT_THROW( container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider), std::runtime_error); } TEST_F(PromptSessionContainer, insert_true_if_prompt_session_exists) { container.insert_prompt_session(prompt_session1); EXPECT_TRUE(container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider)); } TEST_F(PromptSessionContainer, insert_true_if_not_duplicate) { container.insert_prompt_session(prompt_session1); container.insert_prompt_session(prompt_session2); container.insert_prompt_session(prompt_session3); EXPECT_TRUE(container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider)); EXPECT_TRUE(container.insert_participant(prompt_session1.get(), session2, ms::PromptSessionContainer::ParticipantType::prompt_provider)); EXPECT_TRUE(container.insert_participant(prompt_session1.get(), session3, ms::PromptSessionContainer::ParticipantType::prompt_provider)); EXPECT_FALSE(container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider)); EXPECT_TRUE(container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::helper)); EXPECT_TRUE(container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::application)); EXPECT_TRUE(container.insert_participant(prompt_session2.get(), session2, ms::PromptSessionContainer::ParticipantType::prompt_provider)); EXPECT_TRUE(container.insert_participant(prompt_session2.get(), session3, ms::PromptSessionContainer::ParticipantType::prompt_provider)); EXPECT_FALSE(container.insert_participant(prompt_session2.get(), session2, ms::PromptSessionContainer::ParticipantType::prompt_provider)); EXPECT_TRUE(container.insert_participant(prompt_session3.get(), session3, ms::PromptSessionContainer::ParticipantType::prompt_provider)); EXPECT_FALSE(container.insert_participant(prompt_session3.get(), session3, ms::PromptSessionContainer::ParticipantType::prompt_provider)); } TEST_F(PromptSessionContainer, lists_participants_in_a_prompt_session) { container.insert_prompt_session(prompt_session1); container.insert_prompt_session(prompt_session2); container.insert_prompt_session(prompt_session3); container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session1.get(), session2, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session2.get(), session3, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session2.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session3.get(), session2, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session2.get(), session4, ms::PromptSessionContainer::ParticipantType::prompt_provider); EXPECT_THAT(list_participants_for(prompt_session1), ElementsAre(session1, session2)); EXPECT_THAT(list_participants_for(prompt_session2), ElementsAre(session3, session1, session4)); EXPECT_THAT(list_participants_for(prompt_session3), ElementsAre(session2)); } TEST_F(PromptSessionContainer, lists_prompt_sessions_for_a_participant) { container.insert_prompt_session(prompt_session1); container.insert_prompt_session(prompt_session2); container.insert_prompt_session(prompt_session3); container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::helper); container.insert_participant(prompt_session1.get(), session2, ms::PromptSessionContainer::ParticipantType::application); container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session1.get(), session2, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session2.get(), session3, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session2.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session3.get(), session2, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session2.get(), session4, ms::PromptSessionContainer::ParticipantType::prompt_provider); EXPECT_THAT(list_prompt_sessions_for(session1), ElementsAre(prompt_session1, prompt_session1, prompt_session2)); EXPECT_THAT(list_prompt_sessions_for(session1, ms::PromptSessionContainer::ParticipantType::helper), ElementsAre(prompt_session1)); EXPECT_THAT(list_prompt_sessions_for(session2, ms::PromptSessionContainer::ParticipantType::application), ElementsAre(prompt_session1)); EXPECT_THAT(list_prompt_sessions_for(session1, ms::PromptSessionContainer::ParticipantType::prompt_provider), ElementsAre(prompt_session1, prompt_session2)); EXPECT_THAT(list_prompt_sessions_for(session2), ElementsAre(prompt_session1, prompt_session1, prompt_session3)); EXPECT_THAT(list_prompt_sessions_for(session3), ElementsAre(prompt_session2)); EXPECT_THAT(list_prompt_sessions_for(session4), ElementsAre(prompt_session2)); } TEST_F(PromptSessionContainer, associates_processes_with_a_prompt_session_until_it_is_removed) { container.insert_prompt_session(prompt_session1); container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session1.get(), session2, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session1.get(), session3, ms::PromptSessionContainer::ParticipantType::prompt_provider); EXPECT_THAT(count_participants_for(prompt_session1), Eq(3)); container.remove_prompt_session(prompt_session1); EXPECT_THAT(count_participants_for(prompt_session1), Eq(0)); } TEST_F(PromptSessionContainer, associates_prompt_sessions_with_a_participant_until_it_is_removed) { container.insert_prompt_session(prompt_session1); container.insert_prompt_session(prompt_session2); container.insert_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session1.get(), session2, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session2.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider); container.insert_participant(prompt_session2.get(), session2, ms::PromptSessionContainer::ParticipantType::prompt_provider); EXPECT_THAT(list_participants_for(prompt_session1), ElementsAre(session1, session2)); EXPECT_THAT(list_participants_for(prompt_session2), ElementsAre(session1, session2)); container.remove_participant(prompt_session1.get(), session1, ms::PromptSessionContainer::ParticipantType::prompt_provider); EXPECT_THAT(list_participants_for(prompt_session1), ElementsAre(session2)); EXPECT_THAT(list_participants_for(prompt_session2), ElementsAre(session1, session2)); } ./tests/unit-tests/scene/test_rendering_tracker.cpp0000644000015600001650000001072112676616125022723 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/scene/rendering_tracker.h" #include "mir/test/doubles/mock_surface.h" #include #include namespace mtd = mir::test::doubles; namespace mc = mir::compositor; namespace { struct RenderingTrackerTest : testing::Test { std::shared_ptr> const mock_surface{ std::make_shared>()}; mir::scene::RenderingTracker tracker{mock_surface}; mc::CompositorID const compositor_id1{&mock_surface}; mc::CompositorID const compositor_id2{&compositor_id1}; mc::CompositorID const compositor_id3{&compositor_id2}; }; } TEST_F(RenderingTrackerTest, occludes_surface_when_occluded_in_single_compositor) { using namespace testing; std::set const compositors{compositor_id1}; EXPECT_CALL( *mock_surface, configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded)); tracker.active_compositors(compositors); tracker.occluded_in(compositor_id1); } TEST_F(RenderingTrackerTest, exposes_surface_when_rendered_in_single_compositor) { using namespace testing; std::set const compositors{compositor_id1}; EXPECT_CALL( *mock_surface, configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed)); tracker.active_compositors(compositors); tracker.rendered_in(compositor_id1); } TEST_F(RenderingTrackerTest, exposes_surface_when_rendered_in_one_of_many_compositors) { using namespace testing; std::set const compositors{compositor_id1, compositor_id2, compositor_id3}; EXPECT_CALL( *mock_surface, configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed)); tracker.active_compositors(compositors); tracker.occluded_in(compositor_id1); tracker.rendered_in(compositor_id2); } TEST_F(RenderingTrackerTest, does_not_occlude_surface_when_not_occluded_in_all_compositors) { using namespace testing; std::set const compositors{compositor_id1, compositor_id2, compositor_id3}; EXPECT_CALL( *mock_surface, configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded)) .Times(0); tracker.active_compositors(compositors); tracker.occluded_in(compositor_id1); tracker.occluded_in(compositor_id2); } TEST_F(RenderingTrackerTest, occludes_surface_when_occluded_in_all_compositors) { using namespace testing; std::set const compositors{compositor_id1, compositor_id2, compositor_id3}; EXPECT_CALL( *mock_surface, configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded)); tracker.active_compositors(compositors); tracker.occluded_in(compositor_id1); tracker.occluded_in(compositor_id2); tracker.occluded_in(compositor_id3); } TEST_F(RenderingTrackerTest, occludes_surface_when_occluded_in_remaining_compositors_after_removing_compositor) { using namespace testing; std::set compositors{compositor_id1, compositor_id2, compositor_id3}; tracker.active_compositors(compositors); tracker.occluded_in(compositor_id1); tracker.occluded_in(compositor_id2); tracker.rendered_in(compositor_id3); Mock::VerifyAndClearExpectations(mock_surface.get()); EXPECT_CALL( *mock_surface, configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded)); compositors.erase(compositor_id3); tracker.active_compositors(compositors); } TEST_F(RenderingTrackerTest, throws_when_passed_inactive_compositor_id) { EXPECT_THROW({ tracker.occluded_in(compositor_id1); }, std::logic_error); EXPECT_THROW({ tracker.rendered_in(compositor_id2); }, std::logic_error); } ./tests/unit-tests/scene/test_application_session.cpp0000644000015600001650000011442612676616125023310 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #include "src/server/scene/application_session.h" #include "mir/events/event_private.h" #include "mir/graphics/buffer.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/scene/surface_factory.h" #include "mir/scene/null_session_listener.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_surface_stack.h" #include "mir/test/doubles/mock_surface.h" #include "mir/test/doubles/mock_session_listener.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/stub_surface_factory.h" #include "mir/test/doubles/stub_buffer_stream_factory.h" #include "mir/test/doubles/stub_buffer_stream.h" #include "mir/test/doubles/null_snapshot_strategy.h" #include "mir/test/doubles/null_event_sink.h" #include "mir/test/doubles/mock_event_sink.h" #include "mir/test/doubles/null_prompt_session.h" #include "mir/test/doubles/stub_display_configuration.h" #include #include namespace mc = mir::compositor; namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell; namespace mi = mir::input; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mt::doubles; namespace { static std::shared_ptr make_mock_surface() { using namespace testing; auto surface = std::make_shared>(); ON_CALL(*surface, size()).WillByDefault(Return(geom::Size { 100, 100 })); return surface; } struct MockBufferStreamFactory : public ms::BufferStreamFactory { MOCK_METHOD3(create_buffer_stream, std::shared_ptr( mf::BufferStreamId, std::shared_ptr const&, mg::BufferProperties const&)); MOCK_METHOD4(create_buffer_stream, std::shared_ptr( mf::BufferStreamId, std::shared_ptr const&, int, mg::BufferProperties const&)); }; class MockSnapshotStrategy : public ms::SnapshotStrategy { public: ~MockSnapshotStrategy() noexcept {} MOCK_METHOD2(take_snapshot_of, void(std::shared_ptr const&, ms::SnapshotCallback const&)); }; struct MockSnapshotCallback { void operator()(ms::Snapshot const& snapshot) { operator_call(snapshot); } MOCK_METHOD1(operator_call, void(ms::Snapshot const&)); }; MATCHER(IsNullSnapshot, "") { return arg.size == mir::geometry::Size{} && arg.stride == mir::geometry::Stride{} && arg.pixels == nullptr; } MATCHER_P(EqPromptSessionEventState, state, "") { return arg.type == mir_event_type_prompt_session_state_change && arg.prompt_session.new_state == state; } MATCHER_P(HasParent, parent, "") { return arg.parent.lock() == parent; } MATCHER(IsSurfaceOutputEvent, "") { return mir_event_get_type(&arg) == mir_event_type_surface_output; } struct StubSurfaceStack : public msh::SurfaceStack { void raise(std::weak_ptr const&) override { } void raise(SurfaceSet const&) override { } void add_surface(std::shared_ptr const&, mi::InputReceptionMode) override { } void remove_surface(std::weak_ptr const&) override { } auto surface_at(mir::geometry::Point) const -> std::shared_ptr { return std::shared_ptr{}; } }; struct ApplicationSession : public testing::Test { ApplicationSession() : event_sink(std::make_shared()), stub_session_listener(std::make_shared()), stub_surface_stack(std::make_shared()), null_snapshot_strategy(std::make_shared()), pid(0), name("test-session-name") { } std::shared_ptr make_application_session_with_stubs() { return std::make_shared( stub_surface_stack, stub_surface_factory, stub_buffer_stream_factory, pid, name, null_snapshot_strategy, stub_session_listener, mtd::StubDisplayConfig{}, event_sink); } std::shared_ptr make_application_session( std::shared_ptr const& bstream_factory, std::shared_ptr const& surface_factory) { return std::make_shared( stub_surface_stack, surface_factory, bstream_factory, pid, name, null_snapshot_strategy, stub_session_listener, mtd::StubDisplayConfig{}, event_sink); } std::shared_ptr make_application_session( std::shared_ptr const& surface_stack, std::shared_ptr const& surface_factory) { return std::make_shared( surface_stack, surface_factory, stub_buffer_stream_factory, pid, name, null_snapshot_strategy, stub_session_listener, mtd::StubDisplayConfig{}, event_sink); } std::shared_ptr make_application_session_with_coordinator( std::shared_ptr const& surface_stack) { return std::make_shared( surface_stack, stub_surface_factory, stub_buffer_stream_factory, pid, name, null_snapshot_strategy, stub_session_listener, mtd::StubDisplayConfig{}, event_sink); } std::shared_ptr make_application_session_with_listener( std::shared_ptr const& session_listener) { return std::make_shared( stub_surface_stack, stub_surface_factory, stub_buffer_stream_factory, pid, name, null_snapshot_strategy, session_listener, mtd::StubDisplayConfig{}, event_sink); } std::shared_ptr make_application_session_with_buffer_stream_factory( std::shared_ptr const& buffer_stream_factory) { return std::make_shared( stub_surface_stack, stub_surface_factory, buffer_stream_factory, pid, name, null_snapshot_strategy, stub_session_listener, mtd::StubDisplayConfig{}, event_sink); } std::shared_ptr const event_sink; std::shared_ptr const stub_session_listener; std::shared_ptr const stub_surface_stack; std::shared_ptr const null_snapshot_strategy; std::shared_ptr const stub_buffer_stream_factory = std::make_shared(); std::shared_ptr const stub_surface_factory{std::make_shared()}; std::shared_ptr const stub_buffer_stream{std::make_shared()}; pid_t pid; std::string name; }; struct MockSurfaceFactory : ms::SurfaceFactory { MOCK_METHOD2(create_surface, std::shared_ptr( std::shared_ptr const&, ms::SurfaceCreationParameters const& params)); }; } TEST_F(ApplicationSession, adds_created_surface_to_coordinator) { using namespace ::testing; NiceMock mock_surface_factory; NiceMock surface_stack; std::shared_ptr mock_surface = make_mock_surface(); EXPECT_CALL(mock_surface_factory, create_surface(_,_)) .WillOnce(Return(mock_surface)); EXPECT_CALL(surface_stack, add_surface(mock_surface,_)); auto session = make_application_session( mt::fake_shared(surface_stack), mt::fake_shared(mock_surface_factory)); ms::SurfaceCreationParameters params; auto surf = session->create_surface(params, event_sink); session->destroy_surface(surf); } TEST_F(ApplicationSession, notifies_listener_of_create_and_destroy_surface) { using namespace ::testing; mtd::MockSessionListener listener; EXPECT_CALL(listener, surface_created(_, _)) .Times(1); EXPECT_CALL(listener, destroying_surface(_, _)) .Times(1); auto session = make_application_session_with_listener(mt::fake_shared(listener)); ms::SurfaceCreationParameters params; auto surf = session->create_surface(params, event_sink); session->destroy_surface(surf); } TEST_F(ApplicationSession, notifies_listener_of_surface_destruction_via_session_destruction) { using namespace ::testing; auto mock_surface = make_mock_surface(); mtd::MockSessionListener listener; EXPECT_CALL(listener, surface_created(_, _)).Times(1); EXPECT_CALL(listener, destroying_surface(_, _)).Times(1); { auto session = make_application_session_with_listener(mt::fake_shared(listener)); ms::SurfaceCreationParameters params; session->create_surface(params, event_sink); } } TEST_F(ApplicationSession, throws_on_get_invalid_surface) { using namespace ::testing; auto app_session = make_application_session_with_stubs(); mf::SurfaceId invalid_surface_id(1); EXPECT_THROW({ app_session->get_surface(invalid_surface_id); }, std::runtime_error); } TEST_F(ApplicationSession, throws_on_destroy_invalid_surface) { using namespace ::testing; auto app_session = make_application_session_with_stubs(); mf::SurfaceId invalid_surface_id(1); EXPECT_THROW({ app_session->destroy_surface(invalid_surface_id); }, std::runtime_error); } TEST_F(ApplicationSession, default_surface_is_first_surface) { using namespace ::testing; auto app_session = make_application_session_with_stubs(); ms::SurfaceCreationParameters params; auto id1 = app_session->create_surface(params, nullptr); auto id2 = app_session->create_surface(params, nullptr); auto id3 = app_session->create_surface(params, nullptr); auto default_surf = app_session->default_surface(); EXPECT_EQ(app_session->get_surface(id1), default_surf); app_session->destroy_surface(id1); default_surf = app_session->default_surface(); EXPECT_EQ(app_session->get_surface(id2), default_surf); app_session->destroy_surface(id2); default_surf = app_session->default_surface(); EXPECT_EQ(app_session->get_surface(id3), default_surf); app_session->destroy_surface(id3); } TEST_F(ApplicationSession, foreign_surface_has_no_successor) { auto session1 = make_application_session_with_stubs(); ms::SurfaceCreationParameters params; auto id1 = session1->create_surface(params, nullptr); auto surf1 = session1->surface(id1); auto id2 = session1->create_surface(params, nullptr); auto session2 = make_application_session_with_stubs(); EXPECT_THROW({session2->surface_after(surf1);}, std::runtime_error); session1->destroy_surface(id1); session1->destroy_surface(id2); } TEST_F(ApplicationSession, surface_after_one_is_self) { auto session = make_application_session_with_stubs(); ms::SurfaceCreationParameters params; auto id = session->create_surface(params, nullptr); auto surf = session->surface(id); EXPECT_EQ(surf, session->surface_after(surf)); session->destroy_surface(id); } TEST_F(ApplicationSession, surface_after_cycles_through_all) { auto app_session = make_application_session_with_stubs(); ms::SurfaceCreationParameters params; int const N = 3; std::shared_ptr surf[N]; mf::SurfaceId id[N]; for (int i = 0; i < N; ++i) { id[i] = app_session->create_surface(params, nullptr); surf[i] = app_session->surface(id[i]); if (i > 0) ASSERT_NE(surf[i], surf[i-1]); } for (int i = 0; i < N-1; ++i) ASSERT_EQ(surf[i+1], app_session->surface_after(surf[i])); EXPECT_EQ(surf[0], app_session->surface_after(surf[N-1])); for (int i = 0; i < N; ++i) app_session->destroy_surface(id[i]); } TEST_F(ApplicationSession, session_visbility_propagates_to_surfaces) { using namespace ::testing; auto mock_surface = make_mock_surface(); NiceMock surface_factory; ON_CALL(surface_factory, create_surface(_,_)).WillByDefault(Return(mock_surface)); NiceMock surface_stack; auto app_session = make_application_session(mt::fake_shared(surface_stack), mt::fake_shared(surface_factory)); { InSequence seq; EXPECT_CALL(*mock_surface, hide()).Times(1); EXPECT_CALL(*mock_surface, show()).Times(1); } ms::SurfaceCreationParameters params; auto surf = app_session->create_surface(params, event_sink); app_session->hide(); app_session->show(); app_session->destroy_surface(surf); } TEST_F(ApplicationSession, takes_snapshot_of_default_surface) { using namespace ::testing; auto mock_surface = make_mock_surface(); NiceMock surface_factory; MockBufferStreamFactory mock_buffer_stream_factory; std::shared_ptr const mock_stream = std::make_shared(); ON_CALL(mock_buffer_stream_factory, create_buffer_stream(_,_,_)).WillByDefault(Return(mock_stream)); ON_CALL(surface_factory, create_surface(_,_)).WillByDefault(Return(mock_surface)); NiceMock surface_stack; auto const snapshot_strategy = std::make_shared(); EXPECT_CALL(*snapshot_strategy, take_snapshot_of(mock_stream, _)); ms::ApplicationSession app_session( mt::fake_shared(surface_stack), mt::fake_shared(surface_factory), mt::fake_shared(mock_buffer_stream_factory), pid, name, snapshot_strategy, std::make_shared(), mtd::StubDisplayConfig{}, event_sink); auto surface = app_session.create_surface(ms::SurfaceCreationParameters{}, event_sink); app_session.take_snapshot(ms::SnapshotCallback()); app_session.destroy_surface(surface); } TEST_F(ApplicationSession, returns_null_snapshot_if_no_default_surface) { using namespace ::testing; auto snapshot_strategy = std::make_shared(); MockSnapshotCallback mock_snapshot_callback; ms::ApplicationSession app_session( stub_surface_stack, stub_surface_factory, stub_buffer_stream_factory, pid, name, snapshot_strategy, std::make_shared(), mtd::StubDisplayConfig{}, event_sink); EXPECT_CALL(*snapshot_strategy, take_snapshot_of(_,_)).Times(0); EXPECT_CALL(mock_snapshot_callback, operator_call(IsNullSnapshot())); app_session.take_snapshot(std::ref(mock_snapshot_callback)); } TEST_F(ApplicationSession, process_id) { using namespace ::testing; pid_t const session_pid{__LINE__}; ms::ApplicationSession app_session( stub_surface_stack, stub_surface_factory, stub_buffer_stream_factory, session_pid, name, null_snapshot_strategy, std::make_shared(), mtd::StubDisplayConfig{}, event_sink); EXPECT_THAT(app_session.process_id(), Eq(session_pid)); } TEST_F(ApplicationSession, surface_ids_are_bufferstream_ids) { using namespace ::testing; NiceMock mock_surface_factory; NiceMock mock_bufferstream_factory; NiceMock surface_stack; std::shared_ptr mock_surface = make_mock_surface(); auto stub_bstream = std::make_shared(); EXPECT_CALL(mock_bufferstream_factory, create_buffer_stream(_,_,_)) .WillOnce(Return(stub_bstream)); EXPECT_CALL(mock_surface_factory, create_surface(std::shared_ptr(stub_bstream),_)) .WillOnce(Return(mock_surface)); auto session = make_application_session( mt::fake_shared(mock_bufferstream_factory), mt::fake_shared(mock_surface_factory)); ms::SurfaceCreationParameters params; auto id1 = session->create_surface(params, event_sink); EXPECT_THAT(session->get_buffer_stream(mf::BufferStreamId(id1.as_value())), Eq(stub_bstream)); EXPECT_THAT(session->get_surface(id1), Eq(mock_surface)); session->destroy_surface(id1); EXPECT_THROW({ session->get_buffer_stream(mf::BufferStreamId(id1.as_value())); }, std::runtime_error); } TEST_F(ApplicationSession, can_destroy_surface_bstream) { auto session = make_application_session_with_stubs(); ms::SurfaceCreationParameters params; auto id = session->create_surface(params, event_sink); mf::BufferStreamId stream_id(id.as_value()); session->destroy_buffer_stream(stream_id); EXPECT_THROW({ session->get_buffer_stream(stream_id); }, std::runtime_error); session->destroy_surface(id); } MATCHER(StreamEq, "") { return (std::get<0>(arg).stream == std::get<1>(arg).stream) && (std::get<0>(arg).displacement == std::get<1>(arg).displacement); } TEST_F(ApplicationSession, sets_and_looks_up_surface_streams) { using namespace testing; NiceMock mock_bufferstream_factory; NiceMock mock_surface_factory; auto mock_surface = make_mock_surface(); EXPECT_CALL(mock_surface_factory, create_surface(_,_)) .WillOnce(Return(mock_surface)); std::array,3> streams {{ std::make_shared(), std::make_shared(), std::make_shared() }}; EXPECT_CALL(mock_bufferstream_factory, create_buffer_stream(_,_,_)) .WillOnce(Return(streams[0])) .WillOnce(Return(streams[1])) .WillOnce(Return(streams[2])); auto stream_properties = mg::BufferProperties{{8,8}, mir_pixel_format_argb_8888, mg::BufferUsage::hardware}; auto session = make_application_session( mt::fake_shared(mock_bufferstream_factory), mt::fake_shared(mock_surface_factory)); auto stream_id0 = mf::BufferStreamId( session->create_surface( ms::a_surface().of_position({1,1}), event_sink).as_value()); auto stream_id1 = session->create_buffer_stream(stream_properties); auto stream_id2 = session->create_buffer_stream(stream_properties); std::list info { {streams[2], geom::Displacement{0,3}}, {streams[0], geom::Displacement{-1,1}}, {streams[1], geom::Displacement{0,2}} }; EXPECT_CALL(*mock_surface, set_streams(Pointwise(StreamEq(), info))); session->configure_streams(*mock_surface, { {stream_id2, geom::Displacement{0,3}}, {stream_id0, geom::Displacement{-1,1}}, {stream_id1, geom::Displacement{0,2}} }); } TEST_F(ApplicationSession, buffer_stream_constructed_with_requested_parameters) { using namespace ::testing; geom::Size const buffer_size{geom::Width{1}, geom::Height{1}}; mtd::StubBufferStream stream; MockBufferStreamFactory factory; mg::BufferProperties properties(buffer_size, mir_pixel_format_argb_8888, mg::BufferUsage::software); EXPECT_CALL(factory, create_buffer_stream(_,_,properties)).Times(1) .WillOnce(Return(mt::fake_shared(stream))); auto session = make_application_session_with_buffer_stream_factory(mt::fake_shared(factory)); auto id = session->create_buffer_stream(properties); EXPECT_TRUE(session->get_buffer_stream(id) != nullptr); session->destroy_buffer_stream(id); EXPECT_THROW({ session->get_buffer_stream(id); }, std::runtime_error); } TEST_F(ApplicationSession, buffer_stream_constructed_with_swapinterval_1) { using namespace ::testing; geom::Size const buffer_size{geom::Width{1}, geom::Height{1}}; mtd::MockBufferStream stream; MockBufferStreamFactory factory; mg::BufferProperties properties(buffer_size, mir_pixel_format_argb_8888, mg::BufferUsage::software); EXPECT_CALL(stream, allow_framedropping(true)) .Times(0); EXPECT_CALL(factory, create_buffer_stream(_,_,properties)).Times(1) .WillOnce(Return(mt::fake_shared(stream))); auto session = make_application_session_with_buffer_stream_factory(mt::fake_shared(factory)); auto id = session->create_buffer_stream(properties); session->destroy_buffer_stream(id); } TEST_F(ApplicationSession, surface_uses_prexisting_buffer_stream_if_set) { using namespace testing; mtd::StubBufferStreamFactory bufferstream_factory; NiceMock mock_surface_factory; geom::Size const buffer_size{geom::Width{1}, geom::Height{1}}; mg::BufferProperties properties(buffer_size, mir_pixel_format_argb_8888, mg::BufferUsage::software); auto session = make_application_session( mt::fake_shared(bufferstream_factory), mt::fake_shared(mock_surface_factory)); auto id = session->create_buffer_stream(properties); EXPECT_CALL(mock_surface_factory, create_surface(Eq(session->get_buffer_stream(id)),_)) .WillOnce(Invoke([&](auto bs, auto) { auto surface = make_mock_surface(); ON_CALL(*surface, primary_buffer_stream()) .WillByDefault(Return(bs)); return surface; })); ms::SurfaceCreationParameters params = ms::SurfaceCreationParameters{} .of_name("Aardavks") .of_type(mir_surface_type_normal) .with_buffer_stream(id); auto surface_id = session->create_surface(params, event_sink); auto surface = session->get_surface(surface_id); EXPECT_THAT(surface->primary_buffer_stream(), Eq(session->get_buffer_stream(id))); } namespace { struct ApplicationSessionSender : public ApplicationSession { ApplicationSessionSender() : app_session( stub_surface_stack, stub_surface_factory, stub_buffer_stream_factory, pid, name, null_snapshot_strategy, stub_session_listener, mtd::StubDisplayConfig{}, mt::fake_shared(sender)) { } testing::NiceMock sender; ms::ApplicationSession app_session; }; } TEST_F(ApplicationSessionSender, display_config_sender) { using namespace ::testing; mtd::StubDisplayConfig stub_config; EXPECT_CALL(sender, handle_display_config_change(testing::Ref(stub_config))) .Times(1); app_session.send_display_config(stub_config); } TEST_F(ApplicationSessionSender, lifecycle_event_sender) { using namespace ::testing; MirLifecycleState exp_state = mir_lifecycle_state_will_suspend; EXPECT_CALL(sender, handle_lifecycle_event(exp_state)).Times(1); app_session.set_lifecycle_state(mir_lifecycle_state_will_suspend); } TEST_F(ApplicationSessionSender, start_prompt_session) { using namespace ::testing; EXPECT_CALL(sender, handle_event(EqPromptSessionEventState(mir_prompt_session_state_started))).Times(1); app_session.start_prompt_session(); } TEST_F(ApplicationSessionSender, stop_prompt_session) { using namespace ::testing; EXPECT_CALL(sender, handle_event(EqPromptSessionEventState(mir_prompt_session_state_stopped))).Times(1); app_session.stop_prompt_session(); } namespace { class ObserverPreservingSurface : public mtd::MockSurface { public: void add_observer(std::shared_ptr const &observer) override { return BasicSurface::add_observer(observer); } void remove_observer(std::weak_ptr const &observer) override { return BasicSurface::remove_observer(observer); } }; class ObserverPreservingSurfaceFactory : public ms::SurfaceFactory { public: std::shared_ptr create_surface( std::shared_ptr const&, mir::scene::SurfaceCreationParameters const& params) override { using namespace testing; auto mock = std::make_shared>(); ON_CALL(*mock, size()).WillByDefault(Return(params.size)); return mock; }; }; int calculate_dpi(geom::Size const& resolution, geom::Size const& size) { float constexpr mm_per_inch = 25.4f; auto diagonal_mm = sqrt(size.height.as_int()*size.height.as_int() + size.width.as_int()*size.width.as_int()); auto diagonal_px = sqrt(resolution.height.as_int() * resolution.height.as_int() + resolution.width.as_int() * resolution.width.as_int()); return diagonal_px / diagonal_mm * mm_per_inch; } struct ApplicationSessionSurfaceOutput : public ApplicationSession { ApplicationSessionSurfaceOutput() : high_dpi(static_cast(5), {3840, 2160}, {509, 286}, 2.5f, mir_form_factor_monitor), projector(static_cast(2), {1280, 1024}, {800, 600}, 0.5f, mir_form_factor_projector), stub_surface_factory{std::make_shared()}, sender{std::make_shared>()}, app_session( stub_surface_stack, stub_surface_factory, stub_buffer_stream_factory, pid, name, null_snapshot_strategy, stub_session_listener, mtd::StubDisplayConfig{}, sender) { } struct TestOutput { TestOutput( mg::DisplayConfigurationOutputId id, geom::Size const& resolution, geom::Size const& physical_size, float scale, MirFormFactor form_factor) : output{id, resolution, physical_size, mir_pixel_format_argb_8888, 60.0, true}, form_factor{form_factor}, scale{scale}, dpi{calculate_dpi(resolution, physical_size)}, width{resolution.width.as_int()}, id{static_cast(output.id.as_value())} { output.scale = scale; output.form_factor = form_factor; } mtd::StubDisplayConfigurationOutput output; MirFormFactor form_factor; float scale; int dpi; int width; uint32_t id; }; TestOutput const high_dpi; TestOutput const projector; std::shared_ptr const stub_surface_factory; std::shared_ptr> sender; ms::ApplicationSession app_session; }; } namespace { MATCHER_P(SurfaceOutputEventFor, output, "") { using namespace testing; if (mir_event_get_type(arg) != mir_event_type_surface_output) { *result_listener << "Event is not a MirSurfaceOutputEvent"; return 0; } auto const event = mir_event_get_surface_output_event(arg); return ExplainMatchResult( Eq(output.dpi), mir_surface_output_event_get_dpi(event), result_listener) && ExplainMatchResult( Eq(output.form_factor), mir_surface_output_event_get_form_factor(event), result_listener) && ExplainMatchResult( Eq(output.scale), mir_surface_output_event_get_scale(event), result_listener) && ExplainMatchResult( Eq(output.id), mir_surface_output_event_get_output_id(event), result_listener); } } TEST_F(ApplicationSessionSurfaceOutput, sends_surface_output_events_to_surfaces) { using namespace ::testing; MirEvent event; bool event_received{false}; EXPECT_CALL(*sender, handle_event(IsSurfaceOutputEvent())) .WillOnce(Invoke([&event, &event_received](auto ev) { event = ev; event_received = true; })); std::vector outputs = { high_dpi.output }; mtd::StubDisplayConfig config(outputs); app_session.send_display_config(config); ms::SurfaceCreationParameters params = ms::SurfaceCreationParameters{} .of_size({100, 100}); auto surf_id = app_session.create_surface(params, sender); auto surface = app_session.surface(surf_id); ASSERT_TRUE(event_received); EXPECT_THAT(&event, SurfaceOutputEventFor(high_dpi)); } TEST_F(ApplicationSessionSurfaceOutput, sends_correct_surface_details_to_surface) { using namespace ::testing; std::array outputs{{ &high_dpi, &projector }}; std::array event; int events_received{0}; ON_CALL(*sender, handle_event(IsSurfaceOutputEvent())) .WillByDefault(Invoke([&event, &events_received](auto ev) { event[events_received] = ev; ++events_received; })); ms::SurfaceCreationParameters params = ms::SurfaceCreationParameters{} .of_size({100, 100}); mf::SurfaceId ids[2]; std::shared_ptr surfaces[2]; ids[0] = app_session.create_surface(params, sender); ids[1] = app_session.create_surface(params, sender); surfaces[0] = app_session.surface(ids[0]); surfaces[1] = app_session.surface(ids[1]); surfaces[0]->move_to({0 + 100, 100}); surfaces[1]->move_to({outputs[0]->width + 100, 100}); // Reset events recieved; we may have received events from the move. events_received = 0; std::vector configuration_outputs = { outputs[0]->output, outputs[1]->output }; configuration_outputs[0].top_left = {0, 0}; configuration_outputs[1].top_left = {outputs[0]->width, 0}; mtd::StubDisplayConfig config(configuration_outputs); app_session.send_display_config(config); ASSERT_THAT(events_received, Eq(2)); for (int i = 0; i < 2 ; ++i) { EXPECT_THAT(event[i].surface_output.surface_id, Eq(ids[i].as_value())); EXPECT_THAT(&event[i], SurfaceOutputEventFor(*outputs[i])); } } TEST_F(ApplicationSessionSurfaceOutput, sends_details_of_the_hightest_scale_factor_display_on_overlap) { using namespace ::testing; std::array outputs{{ &projector, &high_dpi }}; MirEvent event; bool event_received{false}; ON_CALL(*sender, handle_event(IsSurfaceOutputEvent())) .WillByDefault(Invoke([&event, &event_received](auto ev) { event = ev; event_received = true; })); ms::SurfaceCreationParameters params = ms::SurfaceCreationParameters{} .of_size({100, 100}); auto id = app_session.create_surface(params, sender); auto surface = app_session.surface(id); // This should overlap both outputs surface->move_to({outputs[0]->width - 50, 100}); std::vector configuration_outputs = { outputs[0]->output, outputs[1]->output }; // Put the higher-scale output on the right, so a surface's top_left coordinate // can be in the lower-scale output but overlap with the higher-scale output. configuration_outputs[0].top_left = {0, 0}; configuration_outputs[1].top_left = {outputs[0]->width, 0}; mtd::StubDisplayConfig config(configuration_outputs); app_session.send_display_config(config); ASSERT_TRUE(event_received); EXPECT_THAT(event.surface_output.surface_id, Eq(id.as_value())); EXPECT_THAT(&event, SurfaceOutputEventFor(high_dpi)); } TEST_F(ApplicationSessionSurfaceOutput, surfaces_on_edges_get_correct_values) { using namespace ::testing; std::array outputs{{ &projector, &high_dpi }}; MirEvent event; bool event_received{false}; ON_CALL(*sender, handle_event(IsSurfaceOutputEvent())) .WillByDefault(Invoke([&event, &event_received](auto ev) { event = ev; event_received = true; })); std::vector configuration_outputs = { outputs[0]->output, outputs[1]->output }; // Put the higher-scale output on the right, so a surface's top_left coordinate // can be in the lower-scale output but overlap with the higher-scale output. configuration_outputs[0].top_left = {0, 0}; configuration_outputs[1].top_left = {outputs[0]->width, 0}; mtd::StubDisplayConfig config(configuration_outputs); app_session.send_display_config(config); ms::SurfaceCreationParameters params = ms::SurfaceCreationParameters{} .of_size({640, 480}); auto id = app_session.create_surface(params, sender); auto surface = app_session.surface(id); // This should solidly overlap both outputs surface->move_to({outputs[0]->width - ((surface->size().width.as_uint32_t()) / 2), 100}); ASSERT_TRUE(event_received); EXPECT_THAT(&event, SurfaceOutputEventFor(high_dpi)); event_received = false; // This should be *just* entirely on the projector surface->move_to({outputs[0]->width - surface->size().width.as_uint32_t(), 100}); ASSERT_TRUE(event_received); EXPECT_THAT(&event, SurfaceOutputEventFor(projector)); event_received = false; // This should have a single pixel overlap on the high_dpi surface->move_to({outputs[0]->width - (surface->size().width.as_uint32_t() - 1), 100}); ASSERT_TRUE(event_received); EXPECT_THAT(&event, SurfaceOutputEventFor(high_dpi)); } TEST_F(ApplicationSessionSurfaceOutput, sends_surface_output_event_on_move) { using namespace ::testing; std::array outputs {{ &projector, &high_dpi }}; MirEvent event; int events_received{0}; ON_CALL(*sender, handle_event(IsSurfaceOutputEvent())) .WillByDefault(Invoke([&event, &events_received](auto ev) { event = ev; events_received++; })); std::vector configuration_outputs = { outputs[0]->output, outputs[1]->output }; // Put the higher-scale output on the right, so a surface's top_left coordinate // can be in the lower-scale output but overlap with the higher-scale output. configuration_outputs[0].top_left = {0, 0}; configuration_outputs[1].top_left = {outputs[0]->width, 0}; mtd::StubDisplayConfig config(configuration_outputs); app_session.send_display_config(config); ms::SurfaceCreationParameters params = ms::SurfaceCreationParameters{} .of_size({100, 100}); auto id = app_session.create_surface(params, sender); auto surface = app_session.surface(id); // This should overlap both outputs surface->move_to({outputs[0]->width - 50, 100}); ASSERT_THAT(events_received, Ge(1)); auto events_expected = events_received + 1; EXPECT_THAT(event.surface_output.surface_id, Eq(id.as_value())); EXPECT_THAT(&event, SurfaceOutputEventFor(high_dpi)); // Now solely on the left output surface->move_to({0, 0}); ASSERT_THAT(events_received, Eq(events_expected)); events_expected++; EXPECT_THAT(event.surface_output.surface_id, Eq(id.as_value())); EXPECT_THAT(&event, SurfaceOutputEventFor(projector)); // Now solely on the right output surface->move_to({outputs[0]->width + 100, 100}); ASSERT_THAT(events_received, Eq(events_expected)); events_expected++; EXPECT_THAT(event.surface_output.surface_id, Eq(id.as_value())); EXPECT_THAT(&event, SurfaceOutputEventFor(high_dpi)); } TEST_F(ApplicationSessionSurfaceOutput, sends_surface_output_event_on_move_only_if_changed) { using namespace ::testing; std::array outputs {{ &projector, &high_dpi }}; MirEvent event; int events_received{0}; ON_CALL(*sender, handle_event(IsSurfaceOutputEvent())) .WillByDefault(Invoke([&event, &events_received](auto ev) { event = ev; events_received++; })); std::vector configuration_outputs = { outputs[0]->output, outputs[1]->output }; // Put the higher-scale output on the right, so a surface's top_left coordinate // can be in the lower-scale output but overlap with the higher-scale output. configuration_outputs[0].top_left = {0, 0}; configuration_outputs[1].top_left = {outputs[0]->width, 0}; mtd::StubDisplayConfig config(configuration_outputs); app_session.send_display_config(config); ms::SurfaceCreationParameters params = ms::SurfaceCreationParameters{} .of_size({100, 100}); auto id = app_session.create_surface(params, sender); auto surface = app_session.surface(id); // We get an event on surface creation. EXPECT_THAT(events_received, Eq(1)); // This should move within the same output, so not generate any events surface->move_to({outputs[0]->width - (surface->size().width.as_int() + 1), 100}); EXPECT_THAT(events_received, Eq(1)); // This should move to *exactly* touching the edge of the output; still no events surface->move_to({outputs[0]->width - surface->size().width.as_int(), 100}); EXPECT_THAT(events_received, Eq(1)); // This should move to just overlaping the second output; should generate an event surface->move_to({outputs[0]->width - (surface->size().width.as_int() - 1), 100}); EXPECT_THAT(events_received, Eq(2)); // Back to exactly on the original display; another event. surface->move_to({outputs[0]->width - surface->size().width.as_int(), 100}); EXPECT_THAT(events_received, Eq(3)); } ./tests/unit-tests/scene/test_legacy_scene_change_notification.cpp0000644000015600001650000001115212676616125025726 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/scene/legacy_scene_change_notification.h" #include "mir/scene/surface_observer.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_surface.h" #include #include namespace ms = mir::scene; namespace mt = mir::test; namespace mtd = mt::doubles; namespace { struct MockSceneCallback { MOCK_METHOD0(invoke, void()); }; struct MockBufferCallback { MOCK_METHOD1(invoke, void(int)); }; struct LegacySceneChangeNotificationTest : public testing::Test { void SetUp() override { ON_CALL(surface,visible()).WillByDefault(testing::Return(true)); } testing::NiceMock scene_callback; testing::NiceMock buffer_callback; std::function buffer_change_callback{[this](int arg){buffer_callback.invoke(arg);}}; std::function scene_change_callback{[this](){scene_callback.invoke();}}; testing::NiceMock surface; }; } TEST_F(LegacySceneChangeNotificationTest, fowards_all_observations_to_callback) { EXPECT_CALL(scene_callback, invoke()).Times(2); ms::LegacySceneChangeNotification observer(scene_change_callback, buffer_change_callback); observer.surface_added(&surface); observer.surface_removed(&surface); observer.surfaces_reordered(); } TEST_F(LegacySceneChangeNotificationTest, registers_observer_with_surfaces) { EXPECT_CALL(surface, add_observer(testing::_)) .Times(1); ms::LegacySceneChangeNotification observer(scene_change_callback, buffer_change_callback); observer.surface_added(&surface); } TEST_F(LegacySceneChangeNotificationTest, registers_observer_with_existing_surfaces) { EXPECT_CALL(surface, add_observer(testing::_)) .Times(1); ms::LegacySceneChangeNotification observer(scene_change_callback, buffer_change_callback); observer.surface_exists(&surface); } TEST_F(LegacySceneChangeNotificationTest, observes_surface_changes) { using namespace ::testing; std::shared_ptr surface_observer; EXPECT_CALL(surface, add_observer(_)).Times(1) .WillOnce(SaveArg<0>(&surface_observer)); int buffer_num{3}; EXPECT_CALL(scene_callback, invoke()).Times(0); EXPECT_CALL(buffer_callback, invoke(buffer_num)).Times(1); ms::LegacySceneChangeNotification observer(scene_change_callback, buffer_change_callback); observer.surface_added(&surface); surface_observer->frame_posted(buffer_num, mir::geometry::Size{0,0}); } TEST_F(LegacySceneChangeNotificationTest, redraws_on_rename) { using namespace ::testing; std::shared_ptr surface_observer; EXPECT_CALL(surface, add_observer(_)).Times(1) .WillOnce(SaveArg<0>(&surface_observer)); EXPECT_CALL(scene_callback, invoke()).Times(1); ms::LegacySceneChangeNotification observer(scene_change_callback, buffer_change_callback); observer.surface_added(&surface); surface_observer->renamed("Something New"); } TEST_F(LegacySceneChangeNotificationTest, destroying_observer_unregisters_surface_observers) { using namespace ::testing; EXPECT_CALL(surface, add_observer(_)) .Times(1); EXPECT_CALL(surface, remove_observer(_)) .Times(1); { ms::LegacySceneChangeNotification observer(scene_change_callback, buffer_change_callback); observer.surface_added(&surface); } } TEST_F(LegacySceneChangeNotificationTest, ending_observation_unregisters_observers) { using namespace ::testing; EXPECT_CALL(surface, add_observer(_)) .Times(1); EXPECT_CALL(surface, remove_observer(_)) .Times(1); ms::LegacySceneChangeNotification observer(scene_change_callback, buffer_change_callback); observer.surface_added(&surface); observer.end_observation(); // Verify that its not simply the destruction removing the observer... ::testing::Mock::VerifyAndClearExpectations(&observer); } ./tests/unit-tests/scene/test_threaded_snapshot_strategy.cpp0000644000015600001650000000643212676616125024660 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/scene/threaded_snapshot_strategy.h" #include "src/server/scene/pixel_buffer.h" #include "mir/graphics/buffer.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/null_pixel_buffer.h" #include "mir/test/doubles/stub_buffer_stream.h" #include "mir/test/fake_shared.h" #include "mir/test/wait_condition.h" #include "mir/test/current_thread_name.h" #include #include #include #include #include namespace mg = mir::graphics; namespace ms = mir::scene; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace geom = mir::geometry; namespace { class MockPixelBuffer : public ms::PixelBuffer { public: ~MockPixelBuffer() noexcept {} MOCK_METHOD1(fill_from, void(mg::Buffer& buffer)); MOCK_METHOD0(as_argb_8888, void const*()); MOCK_CONST_METHOD0(size, geom::Size()); MOCK_CONST_METHOD0(stride, geom::Stride()); }; struct ThreadedSnapshotStrategyTest : testing::Test { mtd::StubBufferStream buffer_access; }; } TEST_F(ThreadedSnapshotStrategyTest, takes_snapshot) { using namespace testing; void const* pixels{reinterpret_cast(0xabcd)}; geom::Size size{10, 11}; geom::Stride stride{123}; MockPixelBuffer pixel_buffer; EXPECT_CALL(pixel_buffer, fill_from(Ref(*buffer_access.stub_compositor_buffer))); EXPECT_CALL(pixel_buffer, as_argb_8888()) .WillOnce(Return(pixels)); EXPECT_CALL(pixel_buffer, size()) .WillOnce(Return(size)); EXPECT_CALL(pixel_buffer, stride()) .WillOnce(Return(stride)); ms::ThreadedSnapshotStrategy strategy{mt::fake_shared(pixel_buffer)}; mt::WaitCondition snapshot_taken; ms::Snapshot snapshot; strategy.take_snapshot_of( mt::fake_shared(buffer_access), [&](ms::Snapshot const& s) { snapshot = s; snapshot_taken.wake_up_everyone(); }); snapshot_taken.wait_for_at_most_seconds(5); EXPECT_EQ(size, snapshot.size); EXPECT_EQ(stride, snapshot.stride); EXPECT_EQ(pixels, snapshot.pixels); } TEST_F(ThreadedSnapshotStrategyTest, names_snapshot_thread) { using namespace testing; mtd::NullPixelBuffer pixel_buffer; ms::ThreadedSnapshotStrategy strategy{mt::fake_shared(pixel_buffer)}; mt::WaitCondition snapshot_taken; strategy.take_snapshot_of( mt::fake_shared(buffer_access), [&](ms::Snapshot const&) { snapshot_taken.wake_up_everyone(); }); snapshot_taken.wait_for_at_most_seconds(5); EXPECT_THAT(buffer_access.thread_name, Eq("Mir/Snapshot")); } ./tests/unit-tests/scene/CMakeLists.txt0000644000015600001650000000235012676616125020227 0ustar jenkinsjenkinslist( APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_application_session.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_broadcasting_session_event_sink.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_gl_pixel_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_global_event_sender.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_session_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_the_session_container_implementation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_threaded_snapshot_strategy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mediating_display_changer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_prompt_session_container.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_prompt_session_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_prompt_session_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_abstract_shell.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_surface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_surface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_stack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_legacy_scene_change_notification.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_rendering_tracker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_timeout_application_not_responding_detector.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/scene/test_timeout_application_not_responding_detector.cpp0000644000015600001650000003525312676616125030314 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "src/server/scene/timeout_application_not_responding_detector.h" #include "mir/test/doubles/mock_scene_session.h" #include "mir/test/doubles/fake_alarm_factory.h" #include #include namespace mt = mir::time; namespace ms = mir::scene; namespace mtd = mir::test::doubles; namespace { class MockObserver : public ms::ApplicationNotRespondingDetector::Observer { public: MOCK_METHOD1(session_unresponsive, void(ms::Session const*)); MOCK_METHOD1(session_now_responsive, void(ms::Session const*)); }; } TEST(TimeoutApplicationNotRespondingDetector, pings_registered_sessions_on_schedule) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; bool first_session_pinged{false}, second_session_pinged{false}; NiceMock session_one, session_two; detector.register_session(&session_one, [&first_session_pinged]() { first_session_pinged = true; }); detector.register_session(&session_two, [&second_session_pinged]() { second_session_pinged = true; }); fake_alarms.advance_by(999ms); EXPECT_FALSE(first_session_pinged); EXPECT_FALSE(second_session_pinged); fake_alarms.advance_by(2ms); EXPECT_TRUE(first_session_pinged); EXPECT_TRUE(second_session_pinged); } TEST(TimeoutApplicationNotRespondingDetector, pings_repeatedly) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; int first_session_pinged{0}, second_session_pinged{0}; NiceMock session_one, session_two; detector.register_session(&session_one, [&first_session_pinged]() { first_session_pinged++; }); detector.register_session(&session_two, [&second_session_pinged]() { second_session_pinged++; }); int const expected_count{900}; for (int i = 0; i < expected_count ; ++i) { fake_alarms.advance_by(1001ms); detector.pong_received(&session_one); detector.pong_received(&session_two); } EXPECT_THAT(first_session_pinged, Eq(expected_count)); EXPECT_THAT(second_session_pinged, Eq(expected_count)); } TEST(TimeoutApplicationNotRespondingDetector, triggers_anr_signal_when_session_fails_to_pong) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; bool session_not_responding{false}; auto observer = std::make_shared>(); ON_CALL(*observer, session_unresponsive(_)) .WillByDefault(Invoke([&session_not_responding](auto /*session*/) { session_not_responding = true; })); detector.register_observer(observer); NiceMock session_one; detector.register_session(&session_one, [](){}); fake_alarms.advance_by(1001ms); // Should now have pung, but not marked as unresponsive EXPECT_FALSE(session_not_responding); // Now a full ping cycle has elapsed, should have marked as unresponsive fake_alarms.advance_by(1001ms); EXPECT_TRUE(session_not_responding); } TEST(TimeoutApplicationNotRespondingDetector, does_not_trigger_anr_when_session_pongs) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; bool session_not_responding{false}; auto observer = std::make_shared>(); ON_CALL(*observer, session_unresponsive(_)) .WillByDefault(Invoke([&session_not_responding](auto /*session*/) { session_not_responding = true; })); detector.register_observer(observer); NiceMock session_one; detector.register_session(&session_one, [](){}); fake_alarms.advance_by(1001ms); // Should now have pung, but not marked as unresponsive EXPECT_FALSE(session_not_responding); detector.pong_received(&session_one); // Now a full ping cycle has elapsed, but the session has pung and so isn't unresponsive fake_alarms.advance_by(1001ms); EXPECT_FALSE(session_not_responding); } TEST(TimeoutApplicationNotRespondingDetector, triggers_now_responsive_signal_when_ponged) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; bool session_not_responding{false}; auto observer = std::make_shared>(); ON_CALL(*observer, session_unresponsive(_)) .WillByDefault(Invoke([&session_not_responding](auto /*session*/) { session_not_responding = true; })); ON_CALL(*observer, session_now_responsive(_)) .WillByDefault(Invoke([&session_not_responding](auto /*session*/) { session_not_responding = false; })); detector.register_observer(observer); NiceMock session_one; detector.register_session(&session_one, [](){}); fake_alarms.advance_by(1001ms); // Should now have pung, but not marked as unresponsive EXPECT_FALSE(session_not_responding); // Now a full ping cycle has elapsed, should have marked as unresponsive fake_alarms.advance_by(1001ms); EXPECT_TRUE(session_not_responding); detector.pong_received(&session_one); EXPECT_FALSE(session_not_responding); } TEST(TimeoutApplicationNotRespondingDetector, fiddling_with_sessions_from_callbacks_doesnt_deadlock) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; NiceMock session_one, session_two, session_three; auto observer = std::make_shared>(); ON_CALL(*observer, session_unresponsive(_)) .WillByDefault(Invoke([&detector, &session_two](auto /*session*/) { detector.register_session(&session_two, [](){}); })); ON_CALL(*observer, session_now_responsive(_)) .WillByDefault(Invoke([&detector, &session_three](auto /*session*/) { detector.unregister_session(&session_three); })); detector.register_observer(observer); detector.register_session(&session_one, [](){}); detector.register_session(&session_three, [](){}); fake_alarms.advance_by(1001ms); // Should now have pung fake_alarms.advance_by(1001ms); // Should now have marked session three unresponsive, registering session two detector.pong_received(&session_three); // And now session three sholud be morked as responsive, unregistering itself } TEST(TimeoutApplicationNotRespondingDetector, does_not_generate_signals_after_unregister) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; bool session_not_responding{false}; auto observer = std::make_shared>(); ON_CALL(*observer, session_unresponsive(_)) .WillByDefault(Invoke([&session_not_responding](auto /*session*/) { session_not_responding = true; })); detector.register_observer(observer); NiceMock session_one; detector.register_session(&session_one, [](){}); fake_alarms.advance_by(1001ms); // Should now have pung, but not marked as unresponsive EXPECT_FALSE(session_not_responding); // Now a full ping cycle has elapsed, should have marked as unresponsive fake_alarms.advance_by(1001ms); EXPECT_TRUE(session_not_responding); // Mark session as responding detector.pong_received(&session_one); session_not_responding = false; detector.unregister_observer(observer); fake_alarms.advance_by(1001ms); fake_alarms.advance_by(1001ms); // Notification should not have been generated EXPECT_FALSE(session_not_responding); } TEST(TimeoutApplicationNotRespondingDetector, does_not_schedule_alarm_when_no_sessions) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; // Go through several ping cycles. fake_alarms.advance_smoothly_by(5000ms); EXPECT_THAT(fake_alarms.wakeup_count(), Eq(0)); NiceMock session; detector.register_session(&session, [](){}); fake_alarms.advance_smoothly_by(5000ms); EXPECT_THAT(fake_alarms.wakeup_count(), Gt(0)); int const previous_wakeup_count{fake_alarms.wakeup_count()}; detector.unregister_session(&session); fake_alarms.advance_smoothly_by(5000ms); // We allow one extra wakeup to notice there are no active sessions. EXPECT_THAT(fake_alarms.wakeup_count(), Le(previous_wakeup_count + 1)); } TEST(TimeoutApplicationNotRespondingDetector, does_not_schedule_alarm_when_all_sessions_are_unresponsive) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; NiceMock session; bool session_unresponsive{false}; auto observer = std::make_shared>(); ON_CALL(*observer, session_unresponsive(_)) .WillByDefault(Invoke([&session_unresponsive](auto /*session*/) { session_unresponsive = true; })); detector.register_observer(observer); detector.register_session(&session, [](){}); fake_alarms.advance_smoothly_by(5000ms); ASSERT_TRUE(session_unresponsive); EXPECT_THAT(fake_alarms.wakeup_count(), Gt(0)); int const previous_wakeup_count{fake_alarms.wakeup_count()}; // All the sessions are now unresponsive, so we shouldn't be triggering wakeups fake_alarms.advance_smoothly_by(5000ms); EXPECT_THAT(fake_alarms.wakeup_count(), Eq(previous_wakeup_count)); } TEST(TimeoutApplicationNotRespondingDetector, session_switches_between_responsive_and_unresponsive) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; NiceMock session; bool session_unresponsive{false}; auto observer = std::make_shared>(); ON_CALL(*observer, session_unresponsive(_)) .WillByDefault(Invoke([&session_unresponsive](auto /*session*/) { session_unresponsive = true; })); ON_CALL(*observer, session_now_responsive(_)) .WillByDefault(Invoke([&session_unresponsive](auto /*session*/) { session_unresponsive = false; })); detector.register_observer(observer); detector.register_session(&session, [](){}); fake_alarms.advance_smoothly_by(5000ms); EXPECT_TRUE(session_unresponsive); detector.pong_received(&session); EXPECT_FALSE(session_unresponsive); fake_alarms.advance_smoothly_by(5000ms); EXPECT_TRUE(session_unresponsive); detector.pong_received(&session); EXPECT_FALSE(session_unresponsive); } TEST(TimeoutApplicationNotRespondingDetector, sends_unresponsive_notification_only_once) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, 1s}; NiceMock session_one; NiceMock session_two; auto delayed_dispatch_pong = fake_alarms.create_alarm( [&detector, &session_two]() { detector.pong_received(&session_two); }); int unresponsive_notifications{0}; auto observer = std::make_shared>(); ON_CALL(*observer, session_unresponsive(_)) .WillByDefault(Invoke([&unresponsive_notifications](auto /*session*/) { ++unresponsive_notifications; })); detector.register_observer(observer); detector.register_session(&session_one, [](){}); // We need a responsive session to ensure the ANRDetector keeps rescheduling wakeups. // // We need the delayed_dispatch_pong so that we can do the pong outside of the // ping callback; otherwise this would deadlock. detector.register_session(&session_two, [&delayed_dispatch_pong]() { delayed_dispatch_pong->reschedule_in(1ms); }); fake_alarms.advance_smoothly_by(5000ms); EXPECT_THAT(unresponsive_notifications, Eq(1)); } TEST(TimeoutApplicationNotRespondingDetector, sends_pings_on_schedule_as_sessions_are_connecting) { using namespace testing; using namespace std::literals::chrono_literals; mtd::FakeAlarmFactory fake_alarms; auto const cycle_time = 1s; ms::TimeoutApplicationNotRespondingDetector detector{fake_alarms, cycle_time}; NiceMock session; std::atomic ping_count{0}; auto delayed_dispatch_pong = fake_alarms.create_alarm( [&detector, &session, &ping_count]() { detector.pong_received(&session); ++ping_count; }); detector.register_session(&session, [&delayed_dispatch_pong]() { delayed_dispatch_pong->reschedule_in(1ms); }); auto duration = 0ms; auto const step = 500ms; // <= ensures we go past the threshold for the cycle while (duration <= 5s) { NiceMock dummy_session; detector.register_session(&dummy_session, [](){}); fake_alarms.advance_smoothly_by(step); duration += step; detector.unregister_session(&dummy_session); } EXPECT_THAT(ping_count, Ge(duration / cycle_time)); } ./tests/unit-tests/scene/test_session_manager.cpp0000644000015600001650000002004712676616125022412 0ustar jenkinsjenkins/* * Copyright © 2012-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #include "src/server/scene/session_manager.h" #include "mir/scene/session.h" #include "mir/scene/session_listener.h" #include "mir/scene/null_session_listener.h" #include "src/server/scene/basic_surface.h" #include "src/server/scene/default_session_container.h" #include "src/server/scene/session_event_sink.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/mock_surface_stack.h" #include "mir/test/doubles/mock_session_listener.h" #include "mir/test/doubles/stub_buffer_stream.h" #include "mir/test/doubles/stub_buffer_stream_factory.h" #include "mir/test/doubles/null_snapshot_strategy.h" #include "mir/test/doubles/null_session_event_sink.h" #include "mir/test/doubles/null_event_sink.h" #include "mir/test/doubles/stub_surface_factory.h" #include "mir/test/doubles/null_application_not_responding_detector.h" #include "mir/test/doubles/stub_display.h" #include "mir/test/fake_shared.h" #include #include namespace mf = mir::frontend; namespace mi = mir::input; namespace ms = mir::scene; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace { struct MockSessionContainer : public ms::SessionContainer { MOCK_METHOD1(insert_session, void(std::shared_ptr const&)); MOCK_METHOD1(remove_session, void(std::shared_ptr const&)); MOCK_CONST_METHOD1(successor_of, std::shared_ptr(std::shared_ptr const&)); MOCK_CONST_METHOD1(for_each, void(std::function const&)>)); MOCK_METHOD0(lock, void()); MOCK_METHOD0(unlock, void()); ~MockSessionContainer() noexcept {} }; struct MockSessionEventSink : public ms::SessionEventSink { MOCK_METHOD1(handle_focus_change, void(std::shared_ptr const& session)); MOCK_METHOD0(handle_no_focus, void()); MOCK_METHOD1(handle_session_stopping, void(std::shared_ptr const& session)); }; struct SessionManagerSetup : public testing::Test { void SetUp() override { using namespace ::testing; ON_CALL(container, successor_of(_)).WillByDefault(Return((std::shared_ptr()))); } std::shared_ptr dummy_surface = std::make_shared( std::string("stub"), geom::Rectangle{{},{}}, false, std::make_shared(), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), mir::report::null_scene_report()); testing::NiceMock surface_stack; testing::NiceMock container; ms::NullSessionListener session_listener; mtd::StubBufferStreamFactory buffer_stream_factory; mtd::StubSurfaceFactory stub_surface_factory; mtd::StubDisplay display{2}; mtd::NullEventSink event_sink; ms::SessionManager session_manager{mt::fake_shared(surface_stack), mt::fake_shared(stub_surface_factory), mt::fake_shared(buffer_stream_factory), mt::fake_shared(container), std::make_shared(), std::make_shared(), mt::fake_shared(session_listener), mt::fake_shared(display), std::make_shared()}; }; } TEST_F(SessionManagerSetup, open_and_close_session) { using namespace ::testing; EXPECT_CALL(container, insert_session(_)).Times(1); EXPECT_CALL(container, remove_session(_)).Times(1); auto session = session_manager.open_session(__LINE__, "Visual Basic Studio", mt::fake_shared(event_sink)); session_manager.close_session(session); } TEST_F(SessionManagerSetup, closing_session_removes_surfaces) { using namespace ::testing; EXPECT_CALL(surface_stack, add_surface(_,_)).Times(1); EXPECT_CALL(container, insert_session(_)).Times(1); EXPECT_CALL(container, remove_session(_)).Times(1); auto session = session_manager.open_session(__LINE__, "Visual Basic Studio", mt::fake_shared(event_sink)); session->create_surface( ms::a_surface().of_size(geom::Size{geom::Width{1024}, geom::Height{768}}), mt::fake_shared(event_sink)); session_manager.close_session(session); } namespace { struct SessionManagerSessionListenerSetup : public testing::Test { void SetUp() override { using namespace ::testing; ON_CALL(container, successor_of(_)).WillByDefault(Return((std::shared_ptr()))); } mtd::MockSurfaceStack surface_stack; testing::NiceMock container; testing::NiceMock session_listener; mtd::StubSurfaceFactory stub_surface_factory; mtd::StubDisplay display{2}; ms::SessionManager session_manager{ mt::fake_shared(surface_stack), mt::fake_shared(stub_surface_factory), std::make_shared(), mt::fake_shared(container), std::make_shared(), std::make_shared(), mt::fake_shared(session_listener), mt::fake_shared(display), std::make_shared()}; }; } TEST_F(SessionManagerSessionListenerSetup, session_listener_is_notified_of_lifecycle) { using namespace ::testing; EXPECT_CALL(session_listener, starting(_)).Times(1); EXPECT_CALL(session_listener, stopping(_)).Times(1); auto session = session_manager.open_session(__LINE__, "XPlane", std::shared_ptr()); session_manager.close_session(session); } namespace { struct SessionManagerSessionEventsSetup : public testing::Test { void SetUp() override { using namespace ::testing; ON_CALL(container, successor_of(_)).WillByDefault(Return((std::shared_ptr()))); } mtd::MockSurfaceStack surface_stack; testing::NiceMock container; // Inelegant but some tests need a stub MockSessionEventSink session_event_sink; testing::NiceMock session_listener; mtd::StubSurfaceFactory stub_surface_factory; mtd::StubDisplay display{3}; ms::SessionManager session_manager{ mt::fake_shared(surface_stack), mt::fake_shared(stub_surface_factory), std::make_shared(), mt::fake_shared(container), std::make_shared(), mt::fake_shared(session_event_sink), mt::fake_shared(session_listener), mt::fake_shared(display), std::make_shared()}; }; } TEST_F(SessionManagerSessionEventsSetup, session_event_sink_is_notified_of_lifecycle) { using namespace ::testing; auto session = session_manager.open_session(__LINE__, "XPlane", std::shared_ptr()); auto session1 = session_manager.open_session(__LINE__, "Bla", std::shared_ptr()); Mock::VerifyAndClearExpectations(&session_event_sink); EXPECT_CALL(session_event_sink, handle_focus_change(_)).Times(AnyNumber()); EXPECT_CALL(session_event_sink, handle_no_focus()).Times(AnyNumber()); InSequence s; EXPECT_CALL(session_event_sink, handle_session_stopping(_)).Times(1); EXPECT_CALL(session_event_sink, handle_session_stopping(_)).Times(1); session_manager.close_session(session1); session_manager.close_session(session); } ./tests/unit-tests/scene/test_surface_impl.cpp0000644000015600001650000002116212676616125021705 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/events/event_private.h" #include "mir/events/event_builders.h" #include "src/server/scene/basic_surface.h" #include "mir/scene/surface_observer.h" #include "mir/scene/surface_event_source.h" #include "src/server/report/null_report_factory.h" #include "mir/frontend/event_sink.h" #include "mir/graphics/display_configuration.h" #include "src/server/scene/output_properties_cache.h" #include "mir/test/doubles/stub_buffer_stream.h" #include "mir/test/doubles/mock_buffer_stream.h" #include "mir/test/doubles/stub_input_sender.h" #include "mir/test/doubles/null_event_sink.h" #include "mir/test/doubles/mock_event_sink.h" #include "mir/test/fake_shared.h" #include "mir/test/event_matchers.h" #include #include #include #include namespace ms = mir::scene; namespace msh = mir::shell; namespace mf = mir::frontend; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace mi = mir::input; namespace mev = mir::events; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mt::doubles; namespace mr = mir::report; namespace { typedef testing::NiceMock StubBufferStream; struct Surface : testing::Test { std::shared_ptr const buffer_stream = std::make_shared(); void SetUp() { using namespace testing; ON_CALL(*buffer_stream, stream_size()).WillByDefault(Return(geom::Size())); ON_CALL(*buffer_stream, get_stream_pixel_format()).WillByDefault(Return(mir_pixel_format_abgr_8888)); ON_CALL(*buffer_stream, acquire_client_buffer(_)) .WillByDefault(InvokeArgument<0>(nullptr)); surface = std::make_shared(std::string("stub"), geom::Rectangle{{},{}}, false, buffer_stream, nullptr /* input_channel */, stub_input_sender, nullptr /* cursor_image */, report); } mf::SurfaceId stub_id; std::shared_ptr const report = mr::null_scene_report(); std::shared_ptr const stub_input_sender = std::make_shared(); std::shared_ptr surface; }; } TEST_F(Surface, attributes) { using namespace testing; EXPECT_THROW({ surface->configure(static_cast(111), 222); }, std::logic_error); } TEST_F(Surface, types) { using namespace testing; EXPECT_EQ(mir_surface_type_normal, surface->type()); EXPECT_EQ(mir_surface_type_utility, surface->configure(mir_surface_attrib_type, mir_surface_type_utility)); EXPECT_EQ(mir_surface_type_utility, surface->type()); EXPECT_THROW({ surface->configure(mir_surface_attrib_type, 999); }, std::logic_error); EXPECT_THROW({ surface->configure(mir_surface_attrib_type, -1); }, std::logic_error); EXPECT_EQ(mir_surface_type_utility, surface->type()); EXPECT_EQ(mir_surface_type_dialog, surface->configure(mir_surface_attrib_type, mir_surface_type_dialog)); EXPECT_EQ(mir_surface_type_dialog, surface->type()); EXPECT_EQ(mir_surface_type_freestyle, surface->configure(mir_surface_attrib_type, mir_surface_type_freestyle)); EXPECT_EQ(mir_surface_type_freestyle, surface->type()); } TEST_F(Surface, states) { using namespace testing; EXPECT_EQ(mir_surface_state_restored, surface->state()); EXPECT_EQ(mir_surface_state_vertmaximized, surface->configure(mir_surface_attrib_state, mir_surface_state_vertmaximized)); EXPECT_EQ(mir_surface_state_vertmaximized, surface->state()); EXPECT_THROW({ surface->configure(mir_surface_attrib_state, 999); }, std::logic_error); EXPECT_THROW({ surface->configure(mir_surface_attrib_state, -1); }, std::logic_error); EXPECT_EQ(mir_surface_state_vertmaximized, surface->state()); EXPECT_EQ(mir_surface_state_minimized, surface->configure(mir_surface_attrib_state, mir_surface_state_minimized)); EXPECT_EQ(mir_surface_state_minimized, surface->state()); EXPECT_EQ(mir_surface_state_fullscreen, surface->configure(mir_surface_attrib_state, mir_surface_state_fullscreen)); EXPECT_EQ(mir_surface_state_fullscreen, surface->state()); } bool operator==(MirEvent const& a, MirEvent const& b) { // We will always fill unused bytes with zero, so memcmp is accurate... return !memcmp(&a, &b, sizeof(MirEvent)); } TEST_F(Surface, clamps_undersized_resize) { using namespace testing; geom::Size const try_size{-123, -456}; geom::Size const expect_size{1, 1}; surface->resize(try_size); EXPECT_EQ(expect_size, surface->size()); } TEST_F(Surface, emits_resize_events) { using namespace testing; geom::Size const new_size{123, 456}; auto sink = std::make_shared(); ms::OutputPropertiesCache cache; auto const observer = std::make_shared(stub_id, *surface, cache, sink); surface->add_observer(observer); auto e = mev::make_event(stub_id, new_size); EXPECT_CALL(*sink, handle_event(*e)) .Times(1); surface->resize(new_size); EXPECT_EQ(new_size, surface->size()); } TEST_F(Surface, emits_resize_events_only_on_change) { using namespace testing; geom::Size const new_size{123, 456}; geom::Size const new_size2{789, 1011}; auto sink = std::make_shared(); ms::OutputPropertiesCache cache; auto const observer = std::make_shared(stub_id, *surface, cache, sink); surface->add_observer(observer); auto e = mev::make_event(stub_id, new_size); EXPECT_CALL(*sink, handle_event(*e)) .Times(1); auto e2 = mev::make_event(stub_id, new_size2); EXPECT_CALL(*sink, handle_event(*e2)) .Times(1); surface->resize(new_size); EXPECT_EQ(new_size, surface->size()); surface->resize(new_size); EXPECT_EQ(new_size, surface->size()); surface->resize(new_size2); EXPECT_EQ(new_size2, surface->size()); surface->resize(new_size2); EXPECT_EQ(new_size2, surface->size()); } TEST_F(Surface, sends_focus_notifications_when_focus_gained_and_lost) { using namespace testing; mtd::MockEventSink sink; { InSequence seq; EXPECT_CALL(sink, handle_event(mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_focused))) .Times(1); EXPECT_CALL(sink, handle_event(mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_unfocused))) .Times(1); } ms::OutputPropertiesCache cache; auto const observer = std::make_shared(stub_id, *surface, cache, mt::fake_shared(sink)); surface->add_observer(observer); surface->configure(mir_surface_attrib_focus, mir_surface_focused); surface->configure(mir_surface_attrib_focus, mir_surface_unfocused); } TEST_F(Surface, emits_client_close_events) { using namespace testing; auto sink = std::make_shared(); ms::OutputPropertiesCache cache; auto const observer = std::make_shared(stub_id, *surface, cache, sink); surface->add_observer(observer); MirEvent e; memset(&e, 0, sizeof e); e.type = mir_event_type_close_surface; e.close_surface.surface_id = stub_id.as_value(); EXPECT_CALL(*sink, handle_event(e)).Times(1); surface->request_client_surface_close(); } TEST_F(Surface, preferred_orientation_mode_defaults_to_any) { using namespace testing; ms::BasicSurface surf( std::string("stub"), geom::Rectangle{{},{}}, false, buffer_stream, std::shared_ptr(), stub_input_sender, std::shared_ptr(), report); EXPECT_EQ(mir_orientation_mode_any, surf.query(mir_surface_attrib_preferred_orientation)); } ./tests/unit-tests/shared_library_test.cpp0000644000015600001650000001152312676616125021131 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/shared_library.h" #include #include #include #include "mir_test_framework/executable_path.h" namespace mtf = mir_test_framework; namespace { class HasSubstring { public: HasSubstring(char const* substring) : substring(substring) {} HasSubstring(std::string const& substring) : substring{substring.c_str()} {} friend::testing::AssertionResult operator,(std::string const& target, HasSubstring const& match) { if (std::string::npos != target.find(match.substring)) return ::testing::AssertionSuccess(); else return ::testing::AssertionFailure() << "The target:\n\"" << target << "\"\n" "Does not contain:\n\"" << match.substring << "\""; } private: char const* const substring; HasSubstring(HasSubstring const&) = delete; HasSubstring& operator=(HasSubstring const&) = delete; }; #define MIR_EXPECT_THAT(target, condition) EXPECT_TRUE((target, condition)) class SharedLibrary : public testing::Test { public: SharedLibrary() : nonexistent_library{"imma_totally_not_a_library"}, existing_library{mtf::client_platform("dummy.so")}, nonexistent_function{"yo_dawg"}, existing_function{"create_client_platform"}, existent_version{MIR_CLIENT_PLATFORM_VERSION}, nonexistent_version{"GOATS_ON_THE_GREEN"} { } std::string const nonexistent_library; std::string const existing_library; std::string const nonexistent_function; std::string const existing_function; std::string const existent_version; std::string const nonexistent_version; }; } TEST_F(SharedLibrary, load_nonexistent_library_fails) { EXPECT_THROW({ mir::SharedLibrary nonexistent(nonexistent_library); }, std::runtime_error); } TEST_F(SharedLibrary, load_nonexistent_library_fails_with_useful_info) { try { mir::SharedLibrary nonexistent(nonexistent_library); } catch (std::exception const& error) { auto info = boost::diagnostic_information(error); MIR_EXPECT_THAT(info, HasSubstring("cannot open shared object")) << "What went wrong"; MIR_EXPECT_THAT(info, HasSubstring(nonexistent_library)) << "Name of library"; } } TEST_F(SharedLibrary, load_valid_library_works) { mir::SharedLibrary existing(existing_library); } TEST_F(SharedLibrary, load_nonexistent_function_fails) { mir::SharedLibrary existing(existing_library); EXPECT_THROW({ existing.load_function(nonexistent_function); }, std::runtime_error); } TEST_F(SharedLibrary, load_nonexistent_function_fails_with_useful_info) { mir::SharedLibrary existing(existing_library); try { existing.load_function(nonexistent_function); } catch (std::exception const& error) { auto info = boost::diagnostic_information(error); MIR_EXPECT_THAT(info, HasSubstring("undefined symbol")) << "What went wrong"; MIR_EXPECT_THAT(info, HasSubstring(existing_library)) << "Name of library"; MIR_EXPECT_THAT(info, HasSubstring(nonexistent_function)) << "Name of function"; } } TEST_F(SharedLibrary, load_valid_function_works) { mir::SharedLibrary existing(existing_library); existing.load_function(existing_function); } TEST_F(SharedLibrary, load_valid_versioned_function_works) { mir::SharedLibrary existing{existing_library}; existing.load_function(existing_function, existent_version); } TEST_F(SharedLibrary, load_invalid_versioned_function_fails_with_appropriate_error) { mir::SharedLibrary existing{existing_library}; try { existing.load_function(existing_function, nonexistent_version); } catch (std::exception const& error) { auto info = boost::diagnostic_information(error); MIR_EXPECT_THAT(info, HasSubstring("undefined symbol")) << "What went wrong"; MIR_EXPECT_THAT(info, HasSubstring(nonexistent_version)) << "Version info"; MIR_EXPECT_THAT(info, HasSubstring(existing_library)) << "Name of library"; MIR_EXPECT_THAT(info, HasSubstring(existing_function)) << "Name of function"; } } ./tests/unit-tests/test_fatal.cpp0000644000015600001650000000331512676616125017226 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Author: Daniel van Vugt * Alan Griffiths */ #include "mir/fatal.h" #include #include #include using namespace testing; TEST(FatalErrorDeathTest, abort_formats_message_to_stderr) { mir::FatalErrorStrategy on_error{mir::fatal_error_abort}; EXPECT_DEATH({mir::fatal_error("%s had %d %s %s", "Mary", 1, "little", "lamb");}, "Mary had 1 little lamb"); } TEST(FatalErrorDeathTest, abort_raises_sigabrt) { mir::FatalErrorStrategy on_error{mir::fatal_error_abort}; EXPECT_EXIT({mir::fatal_error("Hello world");}, KilledBySignal(SIGABRT), "Hello world"); } TEST(FatalErrorTest, throw_formats_message_to_what) { EXPECT_THROW( mir::fatal_error("%s had %d %s %s", "Mary", 1, "little", "lamb"), std::runtime_error); try { mir::fatal_error("%s had %d %s %s", "Mary", 1, "little", "lamb"); } catch (std::exception const& x) { EXPECT_THAT(x.what(), StrEq("Mary had 1 little lamb")); } } ./tests/unit-tests/test_glib_main_loop.cpp0000644000015600001650000010720412676616125021113 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis * Alberto Aguirre */ #include "mir/glib_main_loop.h" #include "mir/time/steady_clock.h" #include "mir/test/signal.h" #include "mir/test/pipe.h" #include "mir/test/fake_shared.h" #include "mir/test/auto_unblock_thread.h" #include "mir/test/barrier.h" #include "mir/test/doubles/advanceable_clock.h" #include "mir/test/doubles/mock_lockable_callback.h" #include "mir_test_framework/process.h" #include #include #include namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace { template std::vector values_from_to(T f, T t) { std::vector v; for (T i = f; i <= t; ++i) v.push_back(i); return v; } struct OnScopeExit { ~OnScopeExit() { f(); } std::function const f; }; void execute_in_forked_process( testing::Test* test, std::function const& f, std::function const& cleanup) { auto const pid = fork(); if (!pid) { { OnScopeExit on_scope_exit{cleanup}; f(); } exit(test->HasFailure() ? EXIT_FAILURE : EXIT_SUCCESS); } else { mir_test_framework::Process child{pid}; // Note: valgrind on armhf can very slow when dealing with forks, // so give it enough time. auto const result = child.wait_for_termination(std::chrono::seconds{30}); EXPECT_TRUE(result.succeeded()); } } struct GLibMainLoopTest : ::testing::Test { mir::GLibMainLoop ml{std::make_shared()}; std::function const destroy_glib_main_loop{[this]{ ml.~GLibMainLoop(); }}; }; } TEST_F(GLibMainLoopTest, stops_from_within_handler) { mt::Signal loop_finished; mt::AutoJoinThread t{ [&] { int const owner{0}; ml.enqueue(&owner, [&] { ml.stop(); }); ml.run(); loop_finished.raise(); }}; EXPECT_TRUE(loop_finished.wait_for(std::chrono::seconds{5})); } TEST_F(GLibMainLoopTest, stops_from_outside_handler) { mt::Signal loop_running; mt::Signal loop_finished; mt::AutoJoinThread t{ [&] { int const owner{0}; ml.enqueue(&owner, [&] { loop_running.raise(); }); ml.run(); loop_finished.raise(); }}; ASSERT_TRUE(loop_running.wait_for(std::chrono::seconds{5})); ml.stop(); EXPECT_TRUE(loop_finished.wait_for(std::chrono::seconds{30})); } TEST_F(GLibMainLoopTest, ignores_handler_added_after_stop) { int const owner{0}; bool handler_called{false}; mt::Signal loop_running; std::thread t{ [&] { loop_running.wait(); ml.stop(); int const owner1{0}; ml.enqueue(&owner1, [&] { handler_called = true; }); }}; ml.enqueue(&owner, [&] { loop_running.raise(); }); ml.run(); t.join(); EXPECT_FALSE(handler_called); } TEST_F(GLibMainLoopTest, handles_signal) { int const signum{SIGUSR1}; int handled_signum{0}; ml.register_signal_handler( {signum}, [&handled_signum, this](int sig) { handled_signum = sig; ml.stop(); }); kill(getpid(), signum); ml.run(); ASSERT_EQ(signum, handled_signum); } TEST_F(GLibMainLoopTest, handles_multiple_signals) { std::vector const signals{SIGUSR1, SIGUSR2}; size_t const num_signals_to_send{10}; std::vector handled_signals; std::atomic num_handled_signals{0}; ml.register_signal_handler( {signals[0], signals[1]}, [&handled_signals, &num_handled_signals](int sig) { handled_signals.push_back(sig); ++num_handled_signals; }); std::thread signal_sending_thread( [this, num_signals_to_send, &signals, &num_handled_signals] { for (size_t i = 0; i < num_signals_to_send; i++) { kill(getpid(), signals[i % signals.size()]); while (num_handled_signals <= i) std::this_thread::yield(); } ml.stop(); }); ml.run(); signal_sending_thread.join(); ASSERT_EQ(num_signals_to_send, handled_signals.size()); for (size_t i = 0; i < num_signals_to_send; i++) EXPECT_EQ(signals[i % signals.size()], handled_signals[i]) << " index " << i; } TEST_F(GLibMainLoopTest, invokes_all_registered_handlers_for_signal) { using namespace testing; int const signum{SIGUSR1}; std::vector handled_signum{0,0,0}; ml.register_signal_handler( {signum}, [&handled_signum, this](int sig) { handled_signum[0] = sig; if (handled_signum[0] != 0 && handled_signum[1] != 0 && handled_signum[2] != 0) { ml.stop(); } }); ml.register_signal_handler( {signum}, [&handled_signum, this](int sig) { handled_signum[1] = sig; if (handled_signum[0] != 0 && handled_signum[1] != 0 && handled_signum[2] != 0) { ml.stop(); } }); ml.register_signal_handler( {signum}, [&handled_signum, this](int sig) { handled_signum[2] = sig; if (handled_signum[0] != 0 && handled_signum[1] != 0 && handled_signum[2] != 0) { ml.stop(); } }); kill(getpid(), signum); ml.run(); ASSERT_THAT(handled_signum, Each(signum)); } TEST_F(GLibMainLoopTest, propagates_exception_from_signal_handler) { // Execute in forked process to work around // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61643 // causing subsequent tests to fail (e.g. MultiThreadedCompositor.*). execute_in_forked_process(this, [&] { int const signum{SIGUSR1}; ml.register_signal_handler( {signum}, [&] (int) { throw std::runtime_error("signal handler error"); }); kill(getpid(), signum); EXPECT_THROW({ ml.run(); }, std::runtime_error); }, // Since we terminate the forked process with an exit() call, objects on // the stack are not destroyed. We need to manually destroy the // GLibMainLoop object to avoid fd leaks. destroy_glib_main_loop); } TEST_F(GLibMainLoopTest, handles_signal_with_unique_module_ptr_handler) { int const signum{SIGUSR1}; int handled_signum{0}; ml.register_signal_handler( {signum}, mir::make_module_ptr>( [&handled_signum, this](int sig) { handled_signum = sig; ml.stop(); })); kill(getpid(), signum); ml.run(); ASSERT_EQ(signum, handled_signum); } TEST_F(GLibMainLoopTest, handles_fd) { mt::Pipe p; char const data_to_write{'a'}; int handled_fd{0}; char data_read{0}; ml.register_fd_handler( {p.read_fd()}, this, [&handled_fd, &data_read, this](int fd) { handled_fd = fd; EXPECT_EQ(1, read(fd, &data_read, 1)); ml.stop(); }); EXPECT_EQ(1, write(p.write_fd(), &data_to_write, 1)); ml.run(); EXPECT_EQ(data_to_write, data_read); } TEST_F(GLibMainLoopTest, multiple_fds_with_single_handler_handled) { using namespace testing; std::vector const pipes(2); size_t const num_elems_to_send{10}; std::vector handled_fds; std::vector elems_read; std::atomic num_handled_fds{0}; ml.register_fd_handler( {pipes[0].read_fd(), pipes[1].read_fd()}, this, [&handled_fds, &elems_read, &num_handled_fds](int fd) { handled_fds.push_back(fd); size_t i; EXPECT_EQ(static_cast(sizeof(i)), read(fd, &i, sizeof(i))); elems_read.push_back(i); ++num_handled_fds; }); std::thread fd_writing_thread{ [this, num_elems_to_send, &pipes, &num_handled_fds] { for (size_t i = 0; i < num_elems_to_send; i++) { EXPECT_EQ(static_cast(sizeof(i)), write(pipes[i % pipes.size()].write_fd(), &i, sizeof(i))); while (num_handled_fds <= i) std::this_thread::yield(); } ml.stop(); }}; ml.run(); fd_writing_thread.join(); ASSERT_EQ(num_elems_to_send, handled_fds.size()); for (size_t i = 0; i < num_elems_to_send; i++) EXPECT_EQ(pipes[i % pipes.size()].read_fd(), handled_fds[i]) << " index " << i; EXPECT_THAT(elems_read, ContainerEq(values_from_to(0, num_elems_to_send - 1))); } TEST_F(GLibMainLoopTest, multiple_fd_handlers_are_called) { using namespace testing; std::vector const pipes(3); std::vector const elems_to_send{10,11,12}; std::vector handled_fds{0,0,0}; std::vector elems_read{0,0,0}; ml.register_fd_handler( {pipes[0].read_fd()}, this, [&handled_fds, &elems_read, this](int fd) { EXPECT_EQ(static_cast(sizeof(elems_read[0])), read(fd, &elems_read[0], sizeof(elems_read[0]))); handled_fds[0] = fd; if (handled_fds[0] != 0 && handled_fds[1] != 0 && handled_fds[2] != 0) { ml.stop(); } }); ml.register_fd_handler( {pipes[1].read_fd()}, this, [&handled_fds, &elems_read, this](int fd) { EXPECT_EQ(static_cast(sizeof(elems_read[1])), read(fd, &elems_read[1], sizeof(elems_read[1]))); handled_fds[1] = fd; if (handled_fds[0] != 0 && handled_fds[1] != 0 && handled_fds[2] != 0) { ml.stop(); } }); ml.register_fd_handler( {pipes[2].read_fd()}, this, [&handled_fds, &elems_read, this](int fd) { EXPECT_EQ(static_cast(sizeof(elems_read[2])), read(fd, &elems_read[2], sizeof(elems_read[2]))); handled_fds[2] = fd; if (handled_fds[0] != 0 && handled_fds[1] != 0 && handled_fds[2] != 0) { ml.stop(); } }); EXPECT_EQ(static_cast(sizeof(elems_to_send[0])), write(pipes[0].write_fd(), &elems_to_send[0], sizeof(elems_to_send[0]))); EXPECT_EQ(static_cast(sizeof(elems_to_send[1])), write(pipes[1].write_fd(), &elems_to_send[1], sizeof(elems_to_send[1]))); EXPECT_EQ(static_cast(sizeof(elems_to_send[2])), write(pipes[2].write_fd(), &elems_to_send[2], sizeof(elems_to_send[2]))); ml.run(); EXPECT_THAT(handled_fds, ElementsAre( pipes[0].read_fd(), pipes[1].read_fd(), pipes[2].read_fd())); EXPECT_THAT(elems_read, ContainerEq(elems_to_send)); } TEST_F(GLibMainLoopTest, unregister_prevents_callback_and_does_not_harm_other_callbacks) { mt::Pipe p1, p2; char const data_to_write{'a'}; int p2_handler_executes{-1}; char data_read{0}; ml.register_fd_handler( {p1.read_fd()}, this, [this](int) { FAIL() << "unregistered handler called"; ml.stop(); }); ml.register_fd_handler( {p2.read_fd()}, this+2, [&p2_handler_executes,&data_read,this](int fd) { p2_handler_executes = fd; EXPECT_EQ(1, read(fd, &data_read, 1)); ml.stop(); }); ml.unregister_fd_handler(this); EXPECT_EQ(1, write(p1.write_fd(), &data_to_write, 1)); EXPECT_EQ(1, write(p2.write_fd(), &data_to_write, 1)); ml.run(); EXPECT_EQ(data_to_write, data_read); EXPECT_EQ(p2.read_fd(), p2_handler_executes); } TEST_F(GLibMainLoopTest, unregister_does_not_close_fds) { mt::Pipe p1, p2; char const data_to_write{'b'}; char data_read{0}; ml.register_fd_handler( {p1.read_fd()}, this, [this](int) { FAIL() << "unregistered handler called"; ml.stop(); }); ml.unregister_fd_handler(this); ml.register_fd_handler( {p1.read_fd()}, this, [this,&data_read](int fd) { EXPECT_EQ(1, read(fd, &data_read, 1)); ml.stop(); }); EXPECT_EQ(1, write(p1.write_fd(), &data_to_write, 1)); ml.run(); EXPECT_EQ(data_to_write, data_read); } TEST_F(GLibMainLoopTest, propagates_exception_from_fd_handler) { // Execute in forked process to work around // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61643 // causing subsequent tests to fail (e.g. MultiThreadedCompositor.*) execute_in_forked_process(this, [&] { mt::Pipe p; char const data_to_write{'a'}; ml.register_fd_handler( {p.read_fd()}, this, [] (int) { throw std::runtime_error("fd handler error"); }); EXPECT_EQ(1, write(p.write_fd(), &data_to_write, 1)); EXPECT_THROW({ ml.run(); }, std::runtime_error); }, // Since we terminate the forked process with an exit() call, objects on // the stack are not destroyed. We need to manually destroy the // GLibMainLoop object to avoid fd leaks. destroy_glib_main_loop); } TEST_F(GLibMainLoopTest, can_unregister_fd_from_within_fd_handler) { mt::Pipe p1; ml.register_fd_handler( {p1.read_fd()}, this, [this](int) { ml.unregister_fd_handler(this); ml.stop(); }); EXPECT_EQ(1, write(p1.write_fd(), "a", 1)); ml.run(); } TEST_F(GLibMainLoopTest, handles_fd_with_unique_module_ptr_handler) { mt::Pipe p; char const data_to_write{'a'}; int handled_fd{0}; char data_read{0}; ml.register_fd_handler( {p.read_fd()}, this, mir::make_module_ptr>( [&handled_fd, &data_read, this](int fd) { handled_fd = fd; EXPECT_EQ(1, read(fd, &data_read, 1)); ml.stop(); })); EXPECT_EQ(1, write(p.write_fd(), &data_to_write, 1)); ml.run(); EXPECT_EQ(data_to_write, data_read); } TEST_F(GLibMainLoopTest, dispatches_action) { using namespace testing; int num_actions{0}; int const owner{0}; ml.enqueue( &owner, [&] { ++num_actions; ml.stop(); }); ml.run(); EXPECT_THAT(num_actions, Eq(1)); } TEST_F(GLibMainLoopTest, dispatches_multiple_actions_in_order) { using namespace testing; int const num_actions{5}; std::vector actions; int const owner{0}; for (int i = 0; i < num_actions; ++i) { ml.enqueue( &owner, [&,i] { actions.push_back(i); if (i == num_actions - 1) ml.stop(); }); } ml.run(); EXPECT_THAT(actions, ContainerEq(values_from_to(0, num_actions - 1))); } TEST_F(GLibMainLoopTest, does_not_dispatch_paused_actions) { using namespace testing; std::vector actions; int const owner1{0}; int const owner2{0}; ml.enqueue( &owner1, [&] { int const id = 0; actions.push_back(id); }); ml.enqueue( &owner2, [&] { int const id = 1; actions.push_back(id); }); ml.enqueue( &owner1, [&] { int const id = 2; actions.push_back(id); }); ml.enqueue( &owner2, [&] { int const id = 3; actions.push_back(id); ml.stop(); }); ml.pause_processing_for(&owner1); ml.run(); EXPECT_THAT(actions, ElementsAre(1, 3)); } TEST_F(GLibMainLoopTest, dispatches_actions_resumed_from_within_another_action) { using namespace testing; std::vector actions; void const* const owner1_ptr{&actions}; int const owner2{0}; ml.enqueue( owner1_ptr, [&] { int const id = 0; actions.push_back(id); ml.stop(); }); ml.enqueue( &owner2, [&] { int const id = 1; actions.push_back(id); ml.resume_processing_for(owner1_ptr); }); ml.pause_processing_for(owner1_ptr); ml.run(); EXPECT_THAT(actions, ElementsAre(1, 0)); } TEST_F(GLibMainLoopTest, handles_enqueue_from_within_action) { using namespace testing; std::vector actions; int const num_actions{10}; void const* const owner{&num_actions}; ml.enqueue( owner, [&] { int const id = 0; actions.push_back(id); for (int i = 1; i < num_actions; ++i) { ml.enqueue( owner, [&,i] { actions.push_back(i); if (i == num_actions - 1) ml.stop(); }); } }); ml.run(); EXPECT_THAT(actions, ContainerEq(values_from_to(0, num_actions - 1))); } TEST_F(GLibMainLoopTest, dispatches_actions_resumed_externally) { using namespace testing; std::vector actions; void const* const owner1_ptr{&actions}; int const owner2{0}; mt::Signal action_with_id_1_done; ml.enqueue( owner1_ptr, [&] { int const id = 0; actions.push_back(id); ml.stop(); }); ml.enqueue( &owner2, [&] { int const id = 1; actions.push_back(id); action_with_id_1_done.raise(); }); ml.pause_processing_for(owner1_ptr); std::thread t{ [&] { action_with_id_1_done.wait_for(std::chrono::seconds{5}); ml.resume_processing_for(owner1_ptr); }}; ml.run(); t.join(); EXPECT_TRUE(action_with_id_1_done.raised()); EXPECT_THAT(actions, ElementsAre(1, 0)); } TEST_F(GLibMainLoopTest, propagates_exception_from_server_action) { // Execute in forked process to work around // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61643 // causing subsequent tests to fail (e.g. MultiThreadedCompositor.*) execute_in_forked_process(this, [&] { ml.enqueue(this, [] { throw std::runtime_error("server action error"); }); EXPECT_THROW({ ml.run(); }, std::runtime_error); }, // Since we terminate the forked process with an exit() call, objects on // the stack are not destroyed. We need to manually destroy the // GLibMainLoop object to avoid fd leaks. destroy_glib_main_loop); } TEST_F(GLibMainLoopTest, can_be_rerun_after_exception) { // Execute in forked process to work around // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61643 // causing subsequent tests to fail (e.g. MultiThreadedCompositor.*) execute_in_forked_process(this, [&] { ml.enqueue(this, [] { throw std::runtime_error("server action exception"); }); EXPECT_THROW({ ml.run(); }, std::runtime_error); ml.enqueue(this, [&] { ml.stop(); }); ml.run(); }, // Since we terminate the forked process with an exit() call, objects on // the stack are not destroyed. We need to manually destroy the // GLibMainLoop object to avoid fd leaks. destroy_glib_main_loop); } namespace { struct AdvanceableClock : mtd::AdvanceableClock { void advance_by(std::chrono::milliseconds const step, mir::GLibMainLoop& ml) { mtd::AdvanceableClock::advance_by(step); ml.reprocess_all_sources(); } }; class Counter { public: int operator++() { std::lock_guard lock(mutex); cv.notify_one(); return ++counter; } bool wait_for(std::chrono::milliseconds const& delay, int expected) { std::unique_lock lock(mutex); return cv.wait_for(lock, delay, [&]{ return counter == expected;}); } operator int() const { std::lock_guard lock(mutex); return counter; } private: std::mutex mutable mutex; std::condition_variable cv; int counter{0}; }; struct UnblockMainLoop : mt::AutoUnblockThread { UnblockMainLoop(mir::GLibMainLoop& loop) : mt::AutoUnblockThread([&loop]() {loop.stop();}, [&loop]() {loop.run();}) {} }; struct GLibMainLoopAlarmTest : ::testing::Test { std::shared_ptr clock = std::make_shared(); mir::GLibMainLoop ml{clock}; std::chrono::milliseconds delay{50}; std::function const destroy_glib_main_loop{[this]{ ml.~GLibMainLoop(); }}; }; } TEST_F(GLibMainLoopAlarmTest, main_loop_runs_until_stop_called) { auto mainloop_started = std::make_shared(); auto fire_on_mainloop_start = ml.create_alarm([mainloop_started]() { mainloop_started->raise(); }); fire_on_mainloop_start->reschedule_in(std::chrono::milliseconds{0}); UnblockMainLoop unblocker(ml); ASSERT_TRUE(mainloop_started->wait_for(std::chrono::milliseconds{100})); auto timer_fired = std::make_shared(); auto alarm = ml.create_alarm([timer_fired] { timer_fired->raise(); }); alarm->reschedule_in(std::chrono::milliseconds{10}); clock->advance_by(std::chrono::milliseconds{10}, ml); EXPECT_TRUE(timer_fired->wait_for(std::chrono::milliseconds{500})); ml.stop(); // Main loop should be stopped now timer_fired = std::make_shared(); auto should_not_fire = ml.create_alarm([timer_fired]() { timer_fired->raise(); }); should_not_fire->reschedule_in(std::chrono::milliseconds{0}); EXPECT_FALSE(timer_fired->wait_for(std::chrono::milliseconds{10})); } TEST_F(GLibMainLoopAlarmTest, alarm_starts_in_pending_state) { auto alarm = ml.create_alarm([]{}); UnblockMainLoop unblocker(ml); EXPECT_EQ(mir::time::Alarm::cancelled, alarm->state()); } TEST_F(GLibMainLoopAlarmTest, alarm_fires_with_correct_delay) { UnblockMainLoop unblocker(ml); auto alarm = ml.create_alarm([]{}); alarm->reschedule_in(delay); clock->advance_by(delay - std::chrono::milliseconds{1}, ml); EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); clock->advance_by(delay, ml); EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); } TEST_F(GLibMainLoopAlarmTest, multiple_alarms_fire) { using namespace testing; int const alarm_count{10}; Counter call_count; std::array, alarm_count> alarms; for (auto& alarm : alarms) { alarm = ml.create_alarm([&call_count]{ ++call_count;}); alarm->reschedule_in(delay); } UnblockMainLoop unblocker(ml); clock->advance_by(delay, ml); call_count.wait_for(delay, alarm_count); EXPECT_THAT(call_count, Eq(alarm_count)); for (auto const& alarm : alarms) EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); } TEST_F(GLibMainLoopAlarmTest, alarm_changes_to_triggered_state) { auto alarm_fired = std::make_shared(); auto alarm = ml.create_alarm([alarm_fired]() { alarm_fired->raise(); }); alarm->reschedule_in(std::chrono::milliseconds{5}); UnblockMainLoop unblocker(ml); clock->advance_by(delay, ml); ASSERT_TRUE(alarm_fired->wait_for(std::chrono::milliseconds{100})); EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); } TEST_F(GLibMainLoopAlarmTest, cancelled_alarm_doesnt_fire) { UnblockMainLoop unblocker(ml); auto alarm = ml.create_alarm([]{ FAIL() << "Alarm handler of canceld alarm called"; }); alarm->reschedule_in(std::chrono::milliseconds{100}); EXPECT_TRUE(alarm->cancel()); EXPECT_EQ(mir::time::Alarm::cancelled, alarm->state()); clock->advance_by(std::chrono::milliseconds{100}, ml); EXPECT_EQ(mir::time::Alarm::cancelled, alarm->state()); } TEST_F(GLibMainLoopAlarmTest, destroyed_alarm_doesnt_fire) { auto alarm = ml.create_alarm([]{ FAIL() << "Alarm handler of destroyed alarm called"; }); alarm->reschedule_in(std::chrono::milliseconds{200}); UnblockMainLoop unblocker(ml); alarm.reset(nullptr); clock->advance_by(std::chrono::milliseconds{200}, ml); } TEST_F(GLibMainLoopAlarmTest, rescheduled_alarm_fires_again) { std::atomic call_count{0}; auto alarm = ml.create_alarm([&call_count]() { if (call_count++ > 1) FAIL() << "Alarm called too many times"; }); alarm->reschedule_in(std::chrono::milliseconds{0}); UnblockMainLoop unblocker(ml); clock->advance_by(std::chrono::milliseconds{0}, ml); ASSERT_EQ(mir::time::Alarm::triggered, alarm->state()); alarm->reschedule_in(std::chrono::milliseconds{100}); EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); clock->advance_by(std::chrono::milliseconds{100}, ml); EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); } TEST_F(GLibMainLoopAlarmTest, rescheduled_alarm_cancels_previous_scheduling) { std::atomic call_count{0}; auto alarm = ml.create_alarm([&call_count]() { call_count++; }); alarm->reschedule_in(std::chrono::milliseconds{100}); UnblockMainLoop unblocker(ml); clock->advance_by(std::chrono::milliseconds{90}, ml); EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); EXPECT_EQ(0, call_count); EXPECT_TRUE(alarm->reschedule_in(std::chrono::milliseconds{100})); EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); clock->advance_by(std::chrono::milliseconds{110}, ml); EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); EXPECT_EQ(1, call_count); } TEST_F(GLibMainLoopAlarmTest, alarm_callback_preserves_lock_ordering) { using namespace testing; mtd::MockLockableCallback handler; { InSequence s; EXPECT_CALL(handler, lock()); EXPECT_CALL(handler, functor()); EXPECT_CALL(handler, unlock()); } auto alarm = ml.create_alarm(mt::fake_shared(handler)); UnblockMainLoop unblocker(ml); alarm->reschedule_in(std::chrono::milliseconds{10}); clock->advance_by(std::chrono::milliseconds{11}, ml); } TEST_F(GLibMainLoopAlarmTest, alarm_fires_at_correct_time_point) { mir::time::Timestamp real_soon = clock->now() + std::chrono::milliseconds{120}; auto alarm = ml.create_alarm([]{}); alarm->reschedule_for(real_soon); UnblockMainLoop unblocker(ml); clock->advance_by(std::chrono::milliseconds{119}, ml); EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); clock->advance_by(std::chrono::milliseconds{1}, ml); EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); } TEST_F(GLibMainLoopAlarmTest, propagates_exception_from_alarm) { // Execute in forked process to work around // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61643 // causing subsequent tests to fail (e.g. MultiThreadedCompositor.*). execute_in_forked_process(this, [&] { auto alarm = ml.create_alarm([] { throw std::runtime_error("alarm error"); }); alarm->reschedule_in(std::chrono::milliseconds{0}); EXPECT_THROW({ ml.run(); }, std::runtime_error); }, // Since we terminate the forked process with an exit() call, objects on // the stack are not destroyed. We need to manually destroy the // GLibMainLoop object to avoid fd leaks. destroy_glib_main_loop); } TEST_F(GLibMainLoopAlarmTest, can_reschedule_alarm_from_within_alarm_callback) { using namespace testing; int num_triggers = 0; int const expected_triggers = 3; std::shared_ptr alarm = ml.create_alarm( [&] { if (++num_triggers == expected_triggers) ml.stop(); else alarm->reschedule_in(std::chrono::milliseconds{0}); }); alarm->reschedule_in(std::chrono::milliseconds{0}); ml.run(); EXPECT_THAT(num_triggers, Eq(expected_triggers)); } TEST_F(GLibMainLoopAlarmTest, rescheduling_alarm_from_within_alarm_callback_doesnt_deadlock_with_external_reschedule) { using namespace testing; using namespace std::literals::chrono_literals; mt::Signal in_alarm; mt::Signal alarm_rescheduled; std::shared_ptr alarm = ml.create_alarm( [&] { // Ensure that the external thread reschedules us while we're // in the callback. in_alarm.raise(); ASSERT_TRUE(alarm_rescheduled.wait_for(5s)); alarm->reschedule_in(0ms); ml.stop(); }); alarm->reschedule_in(0ms); mt::AutoJoinThread rescheduler{ [alarm, &in_alarm, &alarm_rescheduled]() { ASSERT_TRUE(in_alarm.wait_for(5s)); alarm->reschedule_in(0ms); alarm_rescheduled.raise(); }}; ml.run(); } TEST_F(GLibMainLoopAlarmTest, cancel_blocks_until_definitely_cancelled) { using namespace testing; using namespace std::literals::chrono_literals; auto waiting_in_lock = std::make_shared(2); auto has_been_called = std::make_shared(); std::shared_ptr alarm = ml.create_alarm( [waiting_in_lock, has_been_called]() { waiting_in_lock->ready(); std::this_thread::sleep_for(500ms); has_been_called->raise(); }); alarm->reschedule_in(0ms); mt::AutoJoinThread canceller{ [waiting_in_lock, has_been_called, alarm, this]() { waiting_in_lock->ready(); alarm->cancel(); EXPECT_TRUE(has_been_called->raised()); ml.stop(); } }; ml.run(); } TEST_F(GLibMainLoopAlarmTest, can_cancel_from_callback) { using namespace testing; using namespace std::literals::chrono_literals; mir::time::Alarm* raw_alarm; auto cancel_didnt_deadlock = std::make_shared(); auto alarm = ml.create_alarm( [&raw_alarm, cancel_didnt_deadlock]() { raw_alarm->cancel(); cancel_didnt_deadlock->raise(); }); raw_alarm = alarm.get(); UnblockMainLoop unblocker{ml}; alarm->reschedule_in(0ms); EXPECT_TRUE(cancel_didnt_deadlock->wait_for(10s)); if (!cancel_didnt_deadlock->raised()) { // Deadlocking is no fun. There's nothing we can sensibly do, // so die rather than wait for the build to timeout. std::terminate(); } } TEST_F(GLibMainLoopAlarmTest, can_destroy_alarm_from_callback) { using namespace testing; using namespace std::literals::chrono_literals; mir::time::Alarm* raw_alarm; auto cancel_didnt_deadlock = std::make_shared(); auto alarm = ml.create_alarm( [&raw_alarm, cancel_didnt_deadlock]() { delete raw_alarm; cancel_didnt_deadlock->raise(); }); alarm->reschedule_in(0ms); raw_alarm = alarm.release(); UnblockMainLoop unblocker{ml}; EXPECT_TRUE(cancel_didnt_deadlock->wait_for(10s)); if (!cancel_didnt_deadlock->raised()) { // Deadlocking is no fun. There's nothing we can sensibly do, // so die rather than wait for the build to timeout. std::terminate(); } } TEST_F(GLibMainLoopAlarmTest, cancelling_a_triggered_alarm_has_no_effect) { using namespace testing; using namespace std::literals::chrono_literals; UnblockMainLoop unblocker{ml}; auto alarm_triggered = std::make_shared(); auto alarm = ml.create_alarm( [alarm_triggered]() { alarm_triggered->raise(); }); alarm->reschedule_in(0ms); EXPECT_TRUE(alarm_triggered->wait_for(10s)); EXPECT_THAT(alarm->state(), Eq(mir::time::Alarm::State::triggered)); EXPECT_FALSE(alarm->cancel()); EXPECT_THAT(alarm->state(), Eq(mir::time::Alarm::State::triggered)); } TEST_F(GLibMainLoopAlarmTest, reschedule_returns_true_when_it_resets_a_previous_schedule) { using namespace testing; using namespace std::literals::chrono_literals; UnblockMainLoop unblocker{ml}; auto alarm_triggered = std::make_shared(); auto alarm = ml.create_alarm([](){}); ASSERT_FALSE(alarm_triggered->raised()); alarm->reschedule_in(10min); EXPECT_TRUE(alarm->reschedule_in(5s)); } TEST_F(GLibMainLoopAlarmTest, reschedule_returns_false_when_it_didnt_reset_a_previous_schedule) { using namespace testing; using namespace std::literals::chrono_literals; UnblockMainLoop unblocker{ml}; auto alarm_triggered = std::make_shared(); auto alarm = ml.create_alarm( [alarm_triggered]() { alarm_triggered->raise(); }); alarm->reschedule_in(0ms); EXPECT_TRUE(alarm_triggered->wait_for(10s)); EXPECT_FALSE(alarm->reschedule_in(10s)); } // More targeted regression test for LP: #1381925 TEST_F(GLibMainLoopTest, stress_emits_alarm_notification_with_zero_timeout) { UnblockMainLoop unblocker{ml}; for (int i = 0; i < 1000; ++i) { mt::Signal notification_called; auto alarm = ml.create_alarm([&]{ notification_called.raise(); }); alarm->reschedule_in(std::chrono::milliseconds{0}); EXPECT_TRUE(notification_called.wait_for(std::chrono::seconds{15})); } } // This test recreates a scenario we get in our integration and acceptance test // runs, and which creates problems for the default glib signal source. The // scenario involves creating, running (with signal handling) and destroying // the main loop in the main process and then trying to do the same in a forked // process. This happens, for example, when we run some tests with an in-process // server setup followed by a test using an out-of-process server setup. TEST(GLibMainLoopForkTest, handles_signals_when_created_in_forked_process) { auto const check_mainloop_signal_handling = [] { int const signum = SIGUSR1; mir::GLibMainLoop ml{std::make_shared()}; ml.register_signal_handler( {signum}, [&](int) { ml.stop(); }); kill(getpid(), signum); ml.run(); }; check_mainloop_signal_handling(); // In problematic implementations the main loop in the forked process // doesn't handle signals, and thus hangs forever. auto const no_cleanup = []{}; execute_in_forked_process(this, [&] { check_mainloop_signal_handling(); }, no_cleanup); } ./tests/unit-tests/test_thread_name.cpp0000644000015600001650000000463512676616125020414 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/thread_name.h" #include "mir/test/wait_condition.h" #include #include #include #include namespace mt = mir::test; namespace { std::string name_of_thread(std::thread& t) { static size_t const max_thread_name_size = 16; char thread_name[max_thread_name_size]; pthread_getname_np(t.native_handle(), thread_name, sizeof thread_name); return {thread_name}; } struct MirThreadName : ::testing::Test { std::thread& start_thread_with_name(std::string const& name) { thread = std::thread{ [this,name] { mir::set_thread_name(name); thread_name_set.wake_up_everyone(); finish_thread.wait_for_at_most_seconds(5); }}; thread_name_set.wait_for_at_most_seconds(5); return thread; } ~MirThreadName() { finish_thread.wake_up_everyone(); if (thread.joinable()) thread.join(); } private: std::thread thread; mt::WaitCondition finish_thread; mt::WaitCondition thread_name_set; }; MATCHER_P(IsPrefixOf, value, "") { return value.find(arg) == 0; } } TEST_F(MirThreadName, sets_thread_name) { using namespace ::testing; std::string const thread_name{"Mir:thread"}; auto& thread = start_thread_with_name(thread_name); EXPECT_THAT(name_of_thread(thread), Eq(thread_name)); } TEST_F(MirThreadName, sets_part_of_long_thread_name) { using namespace ::testing; std::string const long_thread_name{"Mir:mylongthreadnameabcdefghijklmnopqrstuvwxyz"}; auto& thread = start_thread_with_name(long_thread_name); EXPECT_THAT(name_of_thread(thread), IsPrefixOf(long_thread_name)); } ./tests/unit-tests/shell/0000755000015600001650000000000012676616126015502 5ustar jenkinsjenkins./tests/unit-tests/shell/test_graphics_display_layout.cpp0000644000015600001650000001523712676616125024176 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/shell/graphics_display_layout.h" #include "mir/test/doubles/stub_display.h" #include #include #include #include namespace msh = mir::shell; namespace mg = mir::graphics; namespace mtd = mir::test::doubles; namespace geom = mir::geometry; namespace { struct StubDisplay : mtd::StubDisplay { StubDisplay() : mtd::StubDisplay{ std::vector{ {{0,0}, {800,600}}, {{0,600}, {100,100}}, {{800,0}, {100,100}} } } { } }; } TEST(GraphicsDisplayLayoutTest, clips_correctly) { auto stub_display = std::make_shared(); msh::GraphicsDisplayLayout display_layout{stub_display}; std::vector> rect_tuples{ std::make_tuple( geom::Rectangle{geom::Point{0,0}, geom::Size{800,600}}, geom::Rectangle{geom::Point{0,0}, geom::Size{800,600}}), std::make_tuple( geom::Rectangle{geom::Point{750,50}, geom::Size{100,100}}, geom::Rectangle{geom::Point{750,50}, geom::Size{50,100}}), std::make_tuple( geom::Rectangle{geom::Point{899,99}, geom::Size{100,100}}, geom::Rectangle{geom::Point{899,99}, geom::Size{1,1}}), std::make_tuple( geom::Rectangle{geom::Point{-1,-1}, geom::Size{100,100}}, geom::Rectangle{geom::Point{0,0}, geom::Size{99,99}}) }; for (auto const& t : rect_tuples) { auto clipped_rect = std::get<0>(t); auto const expected_rect = std::get<1>(t); display_layout.clip_to_output(clipped_rect); EXPECT_EQ(expected_rect, clipped_rect); } } TEST(GraphicsDisplayLayoutTest, makes_fullscreen_in_correct_screen) { auto stub_display = std::make_shared(); msh::GraphicsDisplayLayout display_layout{stub_display}; std::vector> rect_tuples{ std::make_tuple( geom::Rectangle{geom::Point{0,0}, geom::Size{800,600}}, geom::Rectangle{geom::Point{0,0}, geom::Size{800,600}}), std::make_tuple( geom::Rectangle{geom::Point{750,50}, geom::Size{150,130}}, geom::Rectangle{geom::Point{0,0}, geom::Size{800,600}}), std::make_tuple( geom::Rectangle{geom::Point{899,99}, geom::Size{30,40}}, geom::Rectangle{geom::Point{800,0}, geom::Size{100,100}}), std::make_tuple( geom::Rectangle{geom::Point{850,-10}, geom::Size{30,40}}, geom::Rectangle{geom::Point{800,0}, geom::Size{100,100}}), std::make_tuple( geom::Rectangle{geom::Point{749,20}, geom::Size{60,60}}, geom::Rectangle{geom::Point{0,0}, geom::Size{800,600}}), std::make_tuple( geom::Rectangle{geom::Point{790,20}, geom::Size{60,60}}, geom::Rectangle{geom::Point{800,0}, geom::Size{100,100}}), std::make_tuple( geom::Rectangle{geom::Point{-1000,-1000}, geom::Size{100,100}}, geom::Rectangle{geom::Point{0,0}, geom::Size{800,600}}), std::make_tuple( geom::Rectangle{geom::Point{-1,-1}, geom::Size{100,100}}, geom::Rectangle{geom::Point{0,0}, geom::Size{800,600}}) }; for (auto const& t : rect_tuples) { auto fullscreen_rect = std::get<0>(t); auto const expected_rect = std::get<1>(t); display_layout.size_to_output(fullscreen_rect); EXPECT_EQ(expected_rect, fullscreen_rect); } } TEST(GraphicsDisplayLayoutTest, place_in_output_places_in_correct_output) { auto stub_display = std::make_shared(); msh::GraphicsDisplayLayout display_layout{stub_display}; std::vector> rect_tuples { std::make_tuple( mg::DisplayConfigurationOutputId{1}, geom::Rectangle{{0,0}, {800,600}}, geom::Rectangle{{0,0}, {800,600}}), std::make_tuple( mg::DisplayConfigurationOutputId{1}, geom::Rectangle{{750,50}, {800,600}}, geom::Rectangle{{0,0}, {800,600}}), std::make_tuple( mg::DisplayConfigurationOutputId{2}, geom::Rectangle{{899,99}, {100,100}}, geom::Rectangle{{0,600}, {100,100}}), std::make_tuple( mg::DisplayConfigurationOutputId{3}, geom::Rectangle{{-1,-1}, {100,100}}, geom::Rectangle{{800,0}, {100,100}}) }; for (auto const& t : rect_tuples) { auto const output_id = std::get<0>(t); auto submitted_rect = std::get<1>(t); auto const expected_rect = std::get<2>(t); display_layout.place_in_output(output_id, submitted_rect); EXPECT_EQ(expected_rect, submitted_rect); } } TEST(GraphicsDisplayLayoutTest, place_in_output_updates_size_of_non_fullscreen_request) { using namespace testing; auto stub_display = std::make_shared(); msh::GraphicsDisplayLayout display_layout{stub_display}; std::vector> rect_tuples { std::make_tuple( mg::DisplayConfigurationOutputId{1}, geom::Rectangle{{0,0}, {801,600}}), std::make_tuple( mg::DisplayConfigurationOutputId{1}, geom::Rectangle{{750,50}, {800,599}}), std::make_tuple( mg::DisplayConfigurationOutputId{2}, geom::Rectangle{{899,99}, {1,1}}), std::make_tuple( mg::DisplayConfigurationOutputId{3}, geom::Rectangle{{-1,-1}, {0,0}}), }; for (auto const& t : rect_tuples) { auto const output_id = std::get<0>(t); auto submitted_rect = std::get<1>(t); display_layout.place_in_output(output_id, submitted_rect); EXPECT_THAT(submitted_rect.size, Eq(stub_display->output_rects[output_id.as_value() - 1].size)); } } ./tests/unit-tests/shell/test_default_persistent_surface_store.cpp0000644000015600001650000000662712676616125026107 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/shell/persistent_surface_store.h" #include "mir/scene/surface.h" #include "src/server/shell/default_persistent_surface_store.h" #include "mir/test/doubles/mock_surface.h" #include #include namespace ms = mir::scene; namespace msh = mir::shell; namespace mtd = mir::test::doubles; TEST(DefaultPersistentSurfaceStore, id_for_surface_is_idempotent) { using namespace testing; msh::DefaultPersistentSurfaceStore store; auto surface = std::make_shared>(); auto id_one = store.id_for_surface(surface); auto id_two = store.id_for_surface(surface); EXPECT_THAT(id_one, Eq(std::ref(id_two))); } TEST(DefaultPersistentSurfaceStore, id_is_stable_under_aliasing) { using namespace testing; msh::DefaultPersistentSurfaceStore store; auto surface = std::make_shared>(); auto surface_alias = surface; auto id_one = store.id_for_surface(surface); auto id_two = store.id_for_surface(surface_alias); EXPECT_THAT(id_one, Eq(std::ref(id_two))); } TEST(DefaultPersistentSurfaceStore, can_lookup_surface_by_id) { using namespace testing; msh::DefaultPersistentSurfaceStore store; auto surface = std::make_shared>(); auto id = store.id_for_surface(surface); auto looked_up_surface = store.surface_for_id(id); EXPECT_THAT(looked_up_surface, Eq(surface)); } TEST(DefaultPersistentSurfaceStore, retrieves_correct_surface) { using namespace testing; msh::DefaultPersistentSurfaceStore store; auto surface_one = std::make_shared>(); auto surface_two = std::make_shared>(); auto id_one = store.id_for_surface(surface_one); auto id_two = store.id_for_surface(surface_two); auto looked_up_surface_one = store.surface_for_id(id_one); auto looked_up_surface_two = store.surface_for_id(id_two); EXPECT_THAT(looked_up_surface_one, Eq(surface_one)); EXPECT_THAT(looked_up_surface_two, Eq(surface_two)); } TEST(DefaultPersistentSurfaceStore, looking_up_destroyed_surface_returns_nullptr) { using namespace testing; msh::DefaultPersistentSurfaceStore store; auto surface = std::make_shared>(); auto id = store.id_for_surface(surface); surface.reset(); auto looked_up_surface = store.surface_for_id(id); EXPECT_THAT(looked_up_surface, Eq(nullptr)); } TEST(DefaultPersistentSurfaceStore, looking_up_nonexistent_surface_throws) { using namespace testing; msh::DefaultPersistentSurfaceStore store; msh::PersistentSurfaceStore::Id id; EXPECT_THROW(store.surface_for_id(id), std::out_of_range); } ./tests/unit-tests/shell/test_persistent_surface_store_id.cpp0000644000015600001650000000460112676616125025045 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/shell/persistent_surface_store.h" #include #include namespace ms = mir::scene; namespace msh = mir::shell; //namespace mtd = mir::test::doubles; using Id = msh::PersistentSurfaceStore::Id; TEST(PersistentSurfaceStoreId, deserialising_wildly_incorrect_buffer_raises_exception) { EXPECT_THROW(Id{"bob"}, std::invalid_argument); } TEST(PersistentSurfaceStoreId, deserialising_invalid_buffer_raises_exception) { // This is the right size, but isn't a UUID because it lacks the XX-XX-XX structure EXPECT_THROW(Id{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, std::invalid_argument); } TEST(PersistentSurfaceStoreId, serialization_roundtrips_with_deserialization) { using namespace testing; Id first_id; auto const buf = first_id.serialize_to_string(); Id const second_id{buf}; EXPECT_THAT(second_id, Eq(first_id)); } TEST(PersistentSurfaceStoreId, ids_assigned_evaluate_equal) { using namespace testing; Id const first_id; auto const second_id = first_id; EXPECT_THAT(second_id, Eq(first_id)); } TEST(PersistentSurfaceStoreId, equal_ids_hash_equally) { using namespace testing; auto const uuid_string = "0744caf3-c8d9-4483-a005-3375c1954287"; Id const first_id{uuid_string}; Id const second_id{uuid_string}; EXPECT_THAT(std::hash()(second_id), Eq(std::hash()(first_id))); } TEST(PersistentSurfaceStoreId, can_assign_ids) { using namespace testing; Id first_id; Id second_id; // Technically, there's a roughly 1-in-2^128 chance of a false fail here. EXPECT_THAT(second_id, Not(Eq(first_id))); second_id = first_id; EXPECT_THAT(second_id, Eq(first_id)); } ./tests/unit-tests/shell/CMakeLists.txt0000644000015600001650000000045312676616125020243 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_graphics_display_layout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_persistent_surface_store_id.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_default_persistent_surface_store.cpp ) set( UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/test_module_deleter.cpp0000644000015600001650000000346512676616157021143 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "library_example.h" #include "mir/shared_library.h" #include "mir_test_framework/executable_path.h" #include namespace { mir::UniqueModulePtr function_in_executable() { return mir::make_module_ptr("foo"); } } TEST(ModuleDeleter, module_ptr_may_outlive_local_library) { mir::UniqueModulePtr module_object; { mir::SharedLibrary lib(mir_test_framework::test_data_path() + "/shared-libraries/example.so"); auto function = lib.load_function("module_create_instance"); module_object = function(); } module_object->can_be_executed(); } TEST(ModuleDeleter, shared_ptr_can_keep_library_alive) { std::shared_ptr module_object; { mir::SharedLibrary lib(mir_test_framework::test_data_path() + "/shared-libraries/example.so"); auto function = lib.load_function("module_create_instance"); module_object = function(); } module_object->can_be_executed(); } TEST(ModuleDeleter, module_ptr_can_work_in_executables) { EXPECT_NO_THROW(function_in_executable()); } ./tests/unit-tests/test_recursive_read_write_mutex.cpp0000644000015600001650000001121312676616125023571 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/recursive_read_write_mutex.h" #include "mir/test/barrier.h" #include #include namespace mt = mir::test; using namespace testing; namespace { /* These tests are not ideal as they may fail by hanging. But I don't know a good * way to kill a thread within the process (i.e. without leaking resources and * causing "undefined behavior"). That is having a "watchdog" timeout just leads * to other issues and running in a separate process seems OTT. */ struct RecursiveReadWriteMutex : public Test { int const recursion_depth{1729}; unsigned const reader_threads{42}; mt::Barrier readonly_barrier{reader_threads}; mt::Barrier read_and_write_barrier{reader_threads+1}; std::vector threads; mir::RecursiveReadWriteMutex mutex; void SetUp() { threads.reserve(reader_threads+1); } void TearDown() { for (auto& thread : threads) if (thread.joinable()) thread.join(); } MOCK_METHOD0(notify_read_locked, void()); MOCK_METHOD0(notify_read_unlocking, void()); MOCK_METHOD0(notify_write_locked, void()); MOCK_METHOD0(notify_write_unlocking, void()); }; } TEST_F(RecursiveReadWriteMutex, can_be_recursively_read_locked) { for (int i = 0; i != recursion_depth; ++i) mutex.read_lock(); } TEST_F(RecursiveReadWriteMutex, can_be_recursively_write_locked) { for (int i = 0; i != recursion_depth; ++i) mutex.write_lock(); } TEST_F(RecursiveReadWriteMutex, can_be_write_locked_on_thread_with_read_lock) { mutex.read_lock(); mutex.write_lock(); } TEST_F(RecursiveReadWriteMutex, can_be_read_locked_on_thread_with_write_lock) { mutex.write_lock(); mutex.read_lock(); } TEST_F(RecursiveReadWriteMutex, can_be_read_locked_on_multiple_threads) { auto const reader_function = [&]{ mutex.read_lock(); notify_read_locked(); readonly_barrier.ready(); notify_read_unlocking(); mutex.read_unlock(); }; InSequence seq; EXPECT_CALL(*this, notify_read_locked()).Times(reader_threads); EXPECT_CALL(*this, notify_read_unlocking()).Times(reader_threads); for (auto i = 0U; i != reader_threads; ++i) threads.push_back(std::thread{reader_function}); } TEST_F(RecursiveReadWriteMutex, write_lock_waits_for_read_locks_on_other_threads) { auto const reader_function = [&]{ mutex.read_lock(); notify_read_locked(); read_and_write_barrier.ready(); notify_read_unlocking(); mutex.read_unlock(); }; auto const writer_function = [&]{ read_and_write_barrier.ready(); mutex.write_lock(); notify_write_locked(); }; InSequence seq; EXPECT_CALL(*this, notify_read_locked()).Times(reader_threads); EXPECT_CALL(*this, notify_read_unlocking()).Times(reader_threads); EXPECT_CALL(*this, notify_write_locked()).Times(1); for (auto i = 0U; i != reader_threads; ++i) threads.push_back(std::thread{reader_function}); threads.push_back(std::thread{writer_function}); } TEST_F(RecursiveReadWriteMutex, read_lock_waits_for_write_locks_on_other_threads) { auto const reader_function = [&]{ read_and_write_barrier.ready(); mutex.read_lock(); notify_read_locked(); }; auto const writer_function = [&]{ mutex.write_lock(); notify_write_locked(); read_and_write_barrier.ready(); notify_write_unlocking(); mutex.write_unlock(); }; InSequence seq; EXPECT_CALL(*this, notify_write_locked()).Times(1); EXPECT_CALL(*this, notify_write_unlocking()).Times(1); EXPECT_CALL(*this, notify_read_locked()).Times(reader_threads); for (auto i = 0U; i != reader_threads; ++i) threads.push_back(std::thread{reader_function}); threads.push_back(std::thread{writer_function}); } ./tests/unit-tests/compositor/0000755000015600001650000000000012676616160016567 5ustar jenkinsjenkins./tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp0000644000015600001650000002665712676616125027333 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "src/server/compositor/default_display_buffer_compositor.h" #include "mir/compositor/display_buffer_compositor.h" #include "mir/compositor/decoration.h" #include "src/server/report/null_report_factory.h" #include "mir/compositor/scene.h" #include "mir/compositor/renderer.h" #include "mir/geometry/rectangle.h" #include "mir/test/doubles/mock_renderer.h" #include "mir/test/fake_shared.h" #include "mir/test/gmock_fixes.h" #include "mir/test/doubles/mock_display_buffer.h" #include "mir/test/doubles/mock_renderable.h" #include "mir/test/doubles/fake_renderable.h" #include "mir/test/doubles/stub_display_buffer.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/mock_compositor_report.h" #include "mir/test/doubles/mock_scene.h" #include "mir/test/doubles/stub_scene.h" #include "mir/test/doubles/stub_scene_element.h" #include #include namespace mg = mir::graphics; namespace mc = mir::compositor; namespace geom = mir::geometry; namespace ms = mir::scene; namespace mr = mir::report; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace { struct StubSceneElement : mc::SceneElement { StubSceneElement(std::shared_ptr const& renderable) : renderable_{renderable} { } std::shared_ptr renderable() const override { return renderable_; } void rendered() override { } void occluded() override { } std::unique_ptr decoration() const override { return nullptr; } private: std::shared_ptr const renderable_; }; auto make_scene_elements(std::initializer_list> list) -> mc::SceneElementSequence { mc::SceneElementSequence elements; for(auto& entry : list) elements.push_back(std::make_shared(entry)); return elements; } struct DefaultDisplayBufferCompositor : public testing::Test { DefaultDisplayBufferCompositor() : small(std::make_shared(geom::Rectangle{{10, 20},{30, 40}})), big(std::make_shared(geom::Rectangle{{5, 10},{100, 200}})), fullscreen(std::make_shared(screen)) { using namespace testing; ON_CALL(display_buffer, orientation()) .WillByDefault(Return(mir_orientation_normal)); ON_CALL(display_buffer, view_area()) .WillByDefault(Return(screen)); ON_CALL(display_buffer, post_renderables_if_optimizable(_)) .WillByDefault(Return(false)); } testing::NiceMock mock_renderer; geom::Rectangle screen{{0, 0}, {1366, 768}}; testing::NiceMock display_buffer; std::shared_ptr small; std::shared_ptr big; std::shared_ptr fullscreen; }; } TEST_F(DefaultDisplayBufferCompositor, render) { using namespace testing; EXPECT_CALL(mock_renderer, render(IsEmpty())) .Times(1); EXPECT_CALL(display_buffer, view_area()) .Times(AtLeast(1)); mc::DefaultDisplayBufferCompositor compositor( display_buffer, mt::fake_shared(mock_renderer), mr::null_compositor_report()); compositor.composite(make_scene_elements({})); } TEST_F(DefaultDisplayBufferCompositor, optimization_skips_composition) { using namespace testing; auto report = std::make_shared(); Sequence seq; EXPECT_CALL(*report, began_frame(_)) .InSequence(seq); EXPECT_CALL(display_buffer, post_renderables_if_optimizable(_)) .InSequence(seq) .WillOnce(Return(true)); EXPECT_CALL(*report, renderables_in_frame(_,_)) .InSequence(seq); EXPECT_CALL(mock_renderer, suspend()) .InSequence(seq); EXPECT_CALL(*report, rendered_frame(_)) .Times(0); EXPECT_CALL(*report, finished_frame(_)) .InSequence(seq); EXPECT_CALL(mock_renderer, render(_)) .Times(0); mc::DefaultDisplayBufferCompositor compositor( display_buffer, mt::fake_shared(mock_renderer), report); compositor.composite(make_scene_elements({})); } TEST_F(DefaultDisplayBufferCompositor, rendering_reports_everything) { using namespace testing; auto report = std::make_shared(); Sequence seq; EXPECT_CALL(*report, began_frame(_)) .InSequence(seq); EXPECT_CALL(display_buffer, post_renderables_if_optimizable(_)) .InSequence(seq) .WillOnce(Return(false)); EXPECT_CALL(*report, renderables_in_frame(_,_)) .InSequence(seq); EXPECT_CALL(*report, rendered_frame(_)) .InSequence(seq); EXPECT_CALL(*report, finished_frame(_)) .InSequence(seq); EXPECT_CALL(mock_renderer, render(_)) .Times(1); mc::DefaultDisplayBufferCompositor compositor( display_buffer, mt::fake_shared(mock_renderer), report); compositor.composite(make_scene_elements({})); } TEST_F(DefaultDisplayBufferCompositor, calls_renderer_in_sequence) { using namespace testing; Sequence render_seq; EXPECT_CALL(mock_renderer, suspend()) .Times(0); EXPECT_CALL(display_buffer, orientation()) .InSequence(render_seq) .WillOnce(Return(mir_orientation_normal)); EXPECT_CALL(mock_renderer, set_rotation(_)) .InSequence(render_seq); EXPECT_CALL(mock_renderer, render(ContainerEq(mg::RenderableList{big, small}))) .InSequence(render_seq); mc::DefaultDisplayBufferCompositor compositor( display_buffer, mt::fake_shared(mock_renderer), mr::null_compositor_report()); compositor.composite(make_scene_elements({ big, small })); } TEST_F(DefaultDisplayBufferCompositor, optimization_toggles_seamlessly) { using namespace testing; ON_CALL(display_buffer, view_area()) .WillByDefault(Return(screen)); ON_CALL(display_buffer, orientation()) .WillByDefault(Return(mir_orientation_normal)); Sequence seq; EXPECT_CALL(display_buffer, post_renderables_if_optimizable(_)) .InSequence(seq) .WillOnce(Return(false)); EXPECT_CALL(display_buffer, orientation()) .InSequence(seq); EXPECT_CALL(mock_renderer, set_rotation(mir_orientation_normal)) .InSequence(seq); EXPECT_CALL(mock_renderer, render(IsEmpty())) .InSequence(seq); EXPECT_CALL(display_buffer, post_renderables_if_optimizable(_)) .InSequence(seq) .WillOnce(Return(true)); //we should be testing that post_buffer is called, not just that //we check the bits on the compositor buffer EXPECT_CALL(display_buffer, post_renderables_if_optimizable(_)) .InSequence(seq) .WillOnce(Return(false)); EXPECT_CALL(display_buffer, orientation()) .InSequence(seq); EXPECT_CALL(mock_renderer, set_rotation(mir_orientation_normal)) .InSequence(seq); EXPECT_CALL(mock_renderer, render(IsEmpty())) .InSequence(seq); mc::DefaultDisplayBufferCompositor compositor( display_buffer, mt::fake_shared(mock_renderer), mr::null_compositor_report()); compositor.composite(make_scene_elements({})); compositor.composite(make_scene_elements({})); compositor.composite(make_scene_elements({})); fullscreen->set_buffer({}); // Avoid GMock complaining about false leaks } TEST_F(DefaultDisplayBufferCompositor, occluded_surfaces_are_not_rendered) { using namespace testing; EXPECT_CALL(display_buffer, view_area()) .WillRepeatedly(Return(screen)); EXPECT_CALL(display_buffer, orientation()) .WillOnce(Return(mir_orientation_normal)); EXPECT_CALL(display_buffer, post_renderables_if_optimizable(_)) .WillRepeatedly(Return(false)); auto window0 = std::make_shared(geom::Rectangle{{99,99},{2,2}}); auto window1 = std::make_shared(geom::Rectangle{{10,10},{20,20}}); auto window2 = std::make_shared(geom::Rectangle{{0,0},{100,100}}); auto window3 = std::make_shared(geom::Rectangle{{0,0},{100,100}}); mg::RenderableList const visible{window0, window3}; EXPECT_CALL(mock_renderer, render(ContainerEq(visible))); mc::DefaultDisplayBufferCompositor compositor( display_buffer, mt::fake_shared(mock_renderer), mr::null_compositor_report()); compositor.composite(make_scene_elements({ window0, //not occluded window1, //occluded window2, //occluded window3 //not occluded })); } namespace { struct MockSceneElement : mc::SceneElement { MockSceneElement(std::shared_ptr const& renderable) { ON_CALL(*this, renderable()) .WillByDefault(testing::Return(renderable)); } MOCK_CONST_METHOD0(renderable, std::shared_ptr()); MOCK_CONST_METHOD0(decoration, std::unique_ptr()); MOCK_METHOD0(rendered, void()); MOCK_METHOD0(occluded, void()); }; } TEST_F(DefaultDisplayBufferCompositor, marks_rendered_scene_elements) { using namespace testing; auto element0_rendered = std::make_shared>( std::make_shared(geom::Rectangle{{99,99},{2,2}})); auto element1_rendered = std::make_shared>( std::make_shared(geom::Rectangle{{0,0},{100,100}})); EXPECT_CALL(*element0_rendered, rendered()); EXPECT_CALL(*element1_rendered, rendered()); mc::DefaultDisplayBufferCompositor compositor( display_buffer, mt::fake_shared(mock_renderer), mr::null_compositor_report()); compositor.composite({element0_rendered, element1_rendered}); } TEST_F(DefaultDisplayBufferCompositor, marks_occluded_scene_elements) { using namespace testing; auto element0_occluded = std::make_shared>( std::make_shared(geom::Rectangle{{10,10},{20,20}})); auto element1_rendered = std::make_shared>( std::make_shared(geom::Rectangle{{0,0},{100,100}})); auto element2_occluded = std::make_shared>( std::make_shared(geom::Rectangle{{10000,10000},{20,20}})); EXPECT_CALL(*element0_occluded, occluded()); EXPECT_CALL(*element1_rendered, rendered()); EXPECT_CALL(*element2_occluded, occluded()); mc::DefaultDisplayBufferCompositor compositor( display_buffer, mt::fake_shared(mock_renderer), mr::null_compositor_report()); compositor.composite({element0_occluded, element1_rendered, element2_occluded}); } ./tests/unit-tests/compositor/test_dropping_schedule.cpp0000644000015600001650000000645212676616125024040 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/frontend/client_buffers.h" #include "src/server/compositor/dropping_schedule.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/fake_shared.h" #include #include using namespace testing; namespace mtd = mir::test::doubles; namespace mt = mir::test; namespace mg = mir::graphics; namespace mc = mir::compositor; namespace mf = mir::frontend; namespace { struct MockBufferMap : mf::ClientBuffers { MOCK_METHOD1(add_buffer, mg::BufferID(mg::BufferProperties const&)); MOCK_METHOD1(remove_buffer, void(mg::BufferID id)); MOCK_METHOD1(send_buffer, void(mg::BufferID id)); MOCK_METHOD1(receive_buffer, void(mg::BufferID id)); MOCK_METHOD1(at, std::shared_ptr&(mg::BufferID)); MOCK_CONST_METHOD0(client_owned_buffer_count, size_t()); std::shared_ptr& operator[](mg::BufferID id) { return at(id); } }; struct DroppingSchedule : Test { DroppingSchedule() { for(auto i = 0u; i < num_buffers; i++) buffers.emplace_back(std::make_shared()); } unsigned int const num_buffers{5}; std::vector> buffers; MockBufferMap mock_client_buffers; mc::DroppingSchedule schedule{mt::fake_shared(mock_client_buffers)}; std::vector> drain_queue() { std::vector> scheduled_buffers; while(schedule.num_scheduled()) scheduled_buffers.emplace_back(schedule.next_buffer()); return scheduled_buffers; } }; } TEST_F(DroppingSchedule, throws_if_no_buffers) { EXPECT_FALSE(schedule.num_scheduled()); EXPECT_THROW({ schedule.next_buffer(); }, std::logic_error); } TEST_F(DroppingSchedule, drops_excess_buffers) { InSequence seq; EXPECT_CALL(mock_client_buffers, send_buffer(buffers[0]->id())); EXPECT_CALL(mock_client_buffers, send_buffer(buffers[1]->id())); EXPECT_CALL(mock_client_buffers, send_buffer(buffers[2]->id())); EXPECT_CALL(mock_client_buffers, send_buffer(buffers[3]->id())); for(auto i = 0u; i < num_buffers; i++) schedule.schedule(buffers[i]); auto queue = drain_queue(); ASSERT_THAT(queue, SizeIs(1)); EXPECT_THAT(queue[0]->id(), Eq(buffers[4]->id())); } TEST_F(DroppingSchedule, queueing_same_buffer_many_times_doesnt_drop) { EXPECT_CALL(mock_client_buffers, send_buffer(_)).Times(0); schedule.schedule(buffers[2]); schedule.schedule(buffers[2]); schedule.schedule(buffers[2]); auto queue = drain_queue(); ASSERT_THAT(queue, SizeIs(1)); EXPECT_THAT(queue[0]->id(), Eq(buffers[2]->id())); } ./tests/unit-tests/compositor/test_buffer_stream.cpp0000644000015600001650000002155012676616125023162 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/server/compositor/buffer_stream_surfaces.h" #include "src/server/scene/legacy_surface_change_notification.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/mock_buffer_bundle.h" #include "mir/test/gmock_fixes.h" #include #include #include namespace mc = mir::compositor; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; class BufferStreamTest : public ::testing::Test { protected: BufferStreamTest() { mock_buffer = std::make_shared(); mock_bundle = std::make_shared>(); // Two of the tests care about this, the rest should not... EXPECT_CALL(*mock_bundle, force_requests_to_complete()) .Times(::testing::AnyNumber()); ON_CALL(*mock_bundle, properties()) .WillByDefault(testing::Return(properties)); } std::shared_ptr mock_buffer; std::shared_ptr mock_bundle; geom::Size size{4, 5}; MirPixelFormat format{mir_pixel_format_abgr_8888}; mg::BufferProperties properties {size, format, mg::BufferUsage::hardware}; }; TEST_F(BufferStreamTest, size_query) { EXPECT_CALL(*mock_bundle, properties()) .WillOnce(testing::Return(properties)); mc::BufferStreamSurfaces buffer_stream(mock_bundle); auto returned_size = buffer_stream.stream_size(); EXPECT_EQ(size, returned_size); } TEST_F(BufferStreamTest, pixel_format_query) { mc::BufferStreamSurfaces buffer_stream(mock_bundle); EXPECT_THAT(buffer_stream.pixel_format(), testing::Eq(format)); } TEST_F(BufferStreamTest, force_requests_to_complete) { EXPECT_CALL(*mock_bundle, force_requests_to_complete()) .Times(2); // Once explcit, once on destruction mc::BufferStreamSurfaces buffer_stream(mock_bundle); buffer_stream.force_requests_to_complete(); } TEST_F(BufferStreamTest, requests_are_completed_before_destruction) { EXPECT_CALL(*mock_bundle, force_requests_to_complete()) .Times(1); mc::BufferStreamSurfaces buffer_stream(mock_bundle); } TEST_F(BufferStreamTest, get_buffer_for_compositor_handles_resources) { using namespace testing; EXPECT_CALL(*mock_bundle, compositor_acquire(_)) .Times(1) .WillOnce(Return(mock_buffer)); EXPECT_CALL(*mock_bundle, compositor_release(_)) .Times(1); mc::BufferStreamSurfaces buffer_stream(mock_bundle); buffer_stream.lock_compositor_buffer(0); } TEST_F(BufferStreamTest, get_buffer_for_compositor_can_lock) { using namespace testing; EXPECT_CALL(*mock_bundle, compositor_acquire(_)) .Times(1) .WillOnce(Return(mock_buffer)); EXPECT_CALL(*mock_bundle, compositor_release(_)) .Times(1); mc::BufferStreamSurfaces buffer_stream(mock_bundle); buffer_stream.lock_compositor_buffer(0); } TEST_F(BufferStreamTest, get_buffer_for_client_releases_resources) { std::mutex mutex; std::condition_variable cv; mg::Buffer* buffer{nullptr}; bool done = false; auto const callback = [&](mg::Buffer* new_buffer) { std::unique_lock lock(mutex); buffer = new_buffer; done = true; cv.notify_one(); }; using namespace testing; mc::BufferStreamSurfaces buffer_stream(mock_bundle); InSequence seq; EXPECT_CALL(*mock_bundle, client_acquire(_)) .Times(1) .WillOnce(InvokeArgument<0>(mock_buffer.get())); EXPECT_CALL(*mock_bundle, client_release(_)) .Times(1); EXPECT_CALL(*mock_bundle, client_acquire(_)) .Times(1) .WillOnce(InvokeArgument<0>(mock_buffer.get())); buffer_stream.acquire_client_buffer(callback); { std::unique_lock lock(mutex); cv.wait(lock, [&]{ return done; }); } done = false; if (buffer) buffer_stream.release_client_buffer(buffer); buffer_stream.acquire_client_buffer(callback); { std::unique_lock lock(mutex); cv.wait(lock, [&]{ return done; }); } } TEST_F(BufferStreamTest, allow_framedropping_device) { EXPECT_CALL(*mock_bundle, allow_framedropping(true)) .Times(1); mc::BufferStreamSurfaces buffer_stream(mock_bundle); buffer_stream.allow_framedropping(true); } TEST_F(BufferStreamTest, resizes_bundle) { geom::Size const new_size{66, 77}; EXPECT_CALL(*mock_bundle, resize(new_size)) .Times(1); mc::BufferStreamSurfaces buffer_stream(mock_bundle); buffer_stream.resize(new_size); } TEST_F(BufferStreamTest, swap_buffer) { using namespace testing; EXPECT_CALL(*mock_bundle, client_acquire(_)) .Times(2) .WillRepeatedly(InvokeArgument<0>(mock_buffer.get())); EXPECT_CALL(*mock_bundle, client_release(_)) .Times(1); mc::BufferStreamSurfaces buffer_stream(mock_bundle); mir::graphics::Buffer* buffer = nullptr; auto const callback = [&](mir::graphics::Buffer* new_buffer) { buffer = new_buffer;}; // The first call is with a nullptr buffer, so no frame posted. buffer_stream.swap_buffers(buffer, callback); // The second call posts the buffer returned by first, and should notify buffer_stream.swap_buffers(buffer, callback); } TEST_F(BufferStreamTest, notifies_on_swap) { using namespace testing; ON_CALL(*mock_bundle, client_acquire(_)) .WillByDefault(InvokeArgument<0>(mock_buffer.get())); struct MockCallback { MOCK_METHOD0(call, void()); }; NiceMock mock_cb; EXPECT_CALL(mock_cb, call()).Times(3); auto observer = std::make_shared( []{ FAIL() << "buffer stream shouldnt notify of scene changes.";}, std::bind(&MockCallback::call, &mock_cb)); mc::BufferStreamSurfaces buffer_stream(mock_bundle); buffer_stream.add_observer(observer); mg::Buffer* buffer{nullptr}; auto const complete = [&buffer](mg::Buffer* new_buffer){ buffer = new_buffer; }; buffer_stream.swap_buffers(buffer, complete); buffer_stream.swap_buffers(buffer, complete); buffer_stream.swap_buffers(buffer, complete); buffer_stream.swap_buffers(buffer, complete); } TEST_F(BufferStreamTest, allocate_and_release_not_supported) { mg::BufferProperties properties; mc::BufferStreamSurfaces buffer_stream(mock_bundle); EXPECT_THROW({ buffer_stream.allocate_buffer(properties); }, std::logic_error); EXPECT_THROW({ buffer_stream.remove_buffer(mg::BufferID{3}); }, std::logic_error); } TEST_F(BufferStreamTest, scale_resizes_and_sets_size_appropriately) { using namespace testing; auto const scale = 2.0f; auto scaled_size = scale * size; mg::BufferProperties non_scaled{size, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware}; Sequence seq; EXPECT_CALL(*mock_bundle, properties()) .InSequence(seq) .WillOnce(testing::Return(non_scaled)); EXPECT_CALL(*mock_bundle, resize(scaled_size)) .InSequence(seq); mc::BufferStreamSurfaces buffer_stream(mock_bundle); buffer_stream.set_scale(scale); EXPECT_THAT(buffer_stream.stream_size(), Eq(size)); } TEST_F(BufferStreamTest, scaled_resizes_appropriately) { using namespace testing; auto const scale = 2.0f; auto scaled_size = scale * size; mg::BufferProperties non_scaled{size, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware}; geom::Size logical_resize_request{10, 20}; geom::Size physical_resize_request{20, 40}; InSequence seq; EXPECT_CALL(*mock_bundle, properties()) .WillOnce(testing::Return(non_scaled)); EXPECT_CALL(*mock_bundle, resize(scaled_size)); EXPECT_CALL(*mock_bundle, resize(physical_resize_request)); mc::BufferStreamSurfaces buffer_stream(mock_bundle); buffer_stream.set_scale(scale); buffer_stream.resize(logical_resize_request); } TEST_F(BufferStreamTest, cannot_set_silly_scale_values) { mc::BufferStreamSurfaces buffer_stream(mock_bundle); EXPECT_THROW( buffer_stream.set_scale(0.0f); , std::logic_error); EXPECT_THROW( buffer_stream.set_scale(-1.0f); , std::logic_error); } ./tests/unit-tests/compositor/test_multi_threaded_compositor.cpp0000644000015600001650000007673312676616125025623 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/compositor/multi_threaded_compositor.h" #include "src/server/report/null_report_factory.h" #include "mir/compositor/display_listener.h" #include "mir/compositor/display_buffer_compositor.h" #include "mir/compositor/scene.h" #include "mir/compositor/display_buffer_compositor_factory.h" #include "mir/scene/observer.h" #include "mir/raii.h" #include "mir/test/current_thread_name.h" #include "mir/test/doubles/null_display.h" #include "mir/test/doubles/null_display_buffer.h" #include "mir/test/doubles/mock_display_buffer.h" #include "mir/test/doubles/mock_compositor_report.h" #include "mir/test/doubles/mock_scene.h" #include "mir/test/doubles/stub_scene.h" #include "mir/test/doubles/stub_display.h" #include "mir/test/doubles/null_display_buffer_compositor_factory.h" #include #include #include #include #include #include #include #include #include using namespace std::literals::chrono_literals; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace ms = mir::scene; namespace mr = mir::report; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; namespace mt = mir::test; class StubDisplayWithMockBuffers : public mtd::NullDisplay { public: StubDisplayWithMockBuffers(unsigned int nbuffers) : buffers{nbuffers} {} void for_each_display_sync_group(std::function const& f) override { for (auto& db : buffers) f(db); } void for_each_mock_buffer(std::function const& f) { for (auto& db : buffers) f(db.buffer); } private: struct StubDisplaySyncGroup : mg::DisplaySyncGroup { void for_each_display_buffer(std::function const& f) override { f(buffer); } void post() override {} std::chrono::milliseconds recommended_sleep() const override { return std::chrono::milliseconds::zero(); } testing::NiceMock buffer; }; std::vector buffers; }; class StubScene : public mtd::StubScene { public: StubScene() : pending{0}, throw_on_add_observer_{false} { } void add_observer(std::shared_ptr const& observer_) { std::lock_guard lock{observer_mutex}; if (throw_on_add_observer_) BOOST_THROW_EXCEPTION(std::runtime_error("")); assert(!observer); observer = observer_; } void remove_observer(std::weak_ptr const& /* observer */) { std::lock_guard lock{observer_mutex}; observer.reset(); } void emit_change_event() { { std::lock_guard lock{observer_mutex}; // Any old event will do. if (observer) observer->surfaces_reordered(); } /* Reduce run-time under valgrind */ std::this_thread::yield(); } void throw_on_add_observer(bool flag) { throw_on_add_observer_ = flag; } int frames_pending(mc::CompositorID) const override { return pending; } void set_pending(int n) { pending = n; observer->scene_changed(); } private: std::atomic pending; std::mutex observer_mutex; std::shared_ptr observer; bool throw_on_add_observer_; }; class RecordingDisplayBufferCompositor : public mc::DisplayBufferCompositor { public: RecordingDisplayBufferCompositor(std::function const& mark_render_buffer) : mark_render_buffer{mark_render_buffer} { } void composite(mc::SceneElementSequence&&) { mark_render_buffer(); /* Reduce run-time under valgrind */ std::this_thread::yield(); } private: std::function const mark_render_buffer; }; class RecordingDisplayBufferCompositorFactory : public mc::DisplayBufferCompositorFactory { public: std::unique_ptr create_compositor_for(mg::DisplayBuffer& display_buffer) { auto raw = new RecordingDisplayBufferCompositor{ [&display_buffer,this]() { mark_render_buffer(display_buffer); }}; return std::unique_ptr(raw); } void mark_render_buffer(mg::DisplayBuffer& display_buffer) { std::lock_guard lk{m}; if (records.find(&display_buffer) == records.end()) records[&display_buffer] = Record(0, std::unordered_set()); ++records[&display_buffer].first; records[&display_buffer].second.insert(std::this_thread::get_id()); } bool enough_records_gathered(unsigned int nbuffers, unsigned int min_record_count = 1000) { std::lock_guard lk{m}; if (records.size() < nbuffers) return false; for (auto const& e : records) { Record const& r = e.second; if (r.first < min_record_count) return false; } return true; } bool each_buffer_rendered_in_single_thread() { for (auto const& e : records) { Record const& r = e.second; if (r.second.size() != 1) return false; } return true; } bool buffers_rendered_in_different_threads() { std::unordered_set seen; seen.insert(std::this_thread::get_id()); for (auto const& e : records) { Record const& r = e.second; auto iter = r.second.begin(); if (seen.find(*iter) != seen.end()) return false; seen.insert(*iter); } return true; } bool check_record_count_for_each_buffer( unsigned int nbuffers, unsigned int min, unsigned int max = ~0u) { std::lock_guard lk{m}; if (records.size() < nbuffers) return (min == 0 && max == 0); for (auto const& e : records) { Record const& r = e.second; if (r.first < min || r.first > max) return false; } return true; } private: std::mutex m; typedef std::pair> Record; std::unordered_map records; }; class SurfaceUpdatingDisplayBufferCompositor : public mc::DisplayBufferCompositor { public: SurfaceUpdatingDisplayBufferCompositor(std::function const& fake_surface_update) : fake_surface_update{fake_surface_update} { } void composite(mc::SceneElementSequence&&) override { fake_surface_update(); /* Reduce run-time under valgrind */ std::this_thread::yield(); } private: std::function const fake_surface_update; }; class SurfaceUpdatingDisplayBufferCompositorFactory : public mc::DisplayBufferCompositorFactory { public: SurfaceUpdatingDisplayBufferCompositorFactory(std::shared_ptr const& scene) : scene{scene}, render_count{0} { } std::unique_ptr create_compositor_for(mg::DisplayBuffer&) { auto raw = new SurfaceUpdatingDisplayBufferCompositor{[this]{fake_surface_update();}}; return std::unique_ptr(raw); } void fake_surface_update() { scene->emit_change_event(); ++render_count; } bool enough_renders_happened() { unsigned int const enough_renders{1000}; return render_count > enough_renders; } private: std::shared_ptr const scene; std::atomic render_count; }; class ThreadNameDisplayBufferCompositorFactory : public mc::DisplayBufferCompositorFactory { public: std::unique_ptr create_compositor_for(mg::DisplayBuffer&) { auto raw = new RecordingDisplayBufferCompositor{ [this]() { std::lock_guard lock{thread_names_mutex}; thread_names.emplace_back(mt::current_thread_name()); }}; return std::unique_ptr(raw); } size_t num_thread_names_gathered() { std::lock_guard lock{thread_names_mutex}; return thread_names.size(); } std::mutex thread_names_mutex; std::vector thread_names; }; namespace { struct StubDisplayListener : mc::DisplayListener { virtual void add_display(geom::Rectangle const& /*area*/) override {} virtual void remove_display(geom::Rectangle const& /*area*/) override {} }; struct MockDisplayListener : mc::DisplayListener { MOCK_METHOD1(add_display, void(geom::Rectangle const& /*area*/)); MOCK_METHOD1(remove_display, void(geom::Rectangle const& /*area*/)); }; auto const null_report = mr::null_compositor_report(); unsigned int const composites_per_update{1}; auto const null_display_listener = std::make_shared(); std::chrono::milliseconds const default_delay{-1}; } TEST(MultiThreadedCompositor, compositing_happens_in_different_threads) { using namespace testing; unsigned int const nbuffers{3}; auto display = std::make_shared(nbuffers); auto scene = std::make_shared(); auto db_compositor_factory = std::make_shared(); mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true}; compositor.start(); while (!db_compositor_factory->enough_records_gathered(nbuffers)) scene->emit_change_event(); compositor.stop(); EXPECT_TRUE(db_compositor_factory->each_buffer_rendered_in_single_thread()); EXPECT_TRUE(db_compositor_factory->buffers_rendered_in_different_threads()); } TEST(MultiThreadedCompositor, does_not_deadlock_itself) { // Regression test for LP: #1471909 auto scene = std::make_shared(); class ReentrantDisplayListener : public mc::DisplayListener { public: ReentrantDisplayListener(std::shared_ptr const& scene) : scene{scene} { } void add_display(geom::Rectangle const&) override { scene->emit_change_event(); } void remove_display(geom::Rectangle const&) override { } private: std::shared_ptr const scene; }; mc::MultiThreadedCompositor compositor{ std::make_shared(3), scene, std::make_shared(), std::make_shared(scene), null_report, default_delay, true }; for (int i = 0; i < 1000; ++i) { compositor.start(); compositor.stop(); std::this_thread::yield(); } } TEST(MultiThreadedCompositor, reports_in_the_right_places) { using namespace testing; auto display = std::make_shared(1); auto scene = std::make_shared(); auto db_compositor_factory = std::make_shared(); auto mock_report = std::make_shared(); mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, mock_report, default_delay, true}; EXPECT_CALL(*mock_report, started()) .Times(1); display->for_each_mock_buffer([](mtd::MockDisplayBuffer& mock_buf) { EXPECT_CALL(mock_buf, view_area()).Times(AtLeast(1)) .WillRepeatedly(Return(geom::Rectangle())); }); EXPECT_CALL(*mock_report, added_display(_,_,_,_,_)) .Times(1); EXPECT_CALL(*mock_report, scheduled()) .Times(2); EXPECT_CALL(*mock_report, stopped()) .Times(AtLeast(1)); compositor.start(); scene->emit_change_event(); while (!db_compositor_factory->check_record_count_for_each_buffer(1, composites_per_update)) std::this_thread::yield(); compositor.stop(); } /* * It's difficult to test that a render won't happen, without some further * introspective capabilities that would complicate the code. This test will * catch a problem if it occurs, but can't ensure that a problem, even if * present, will occur in a determinstic manner. * * Nonetheless, the test is useful since it's very likely to fail if a problem * is present (and don't forget that it's usually run multiple times per day). */ TEST(MultiThreadedCompositor, composites_only_on_demand) { using namespace testing; unsigned int const nbuffers = 3; auto display = std::make_shared(nbuffers); auto scene = std::make_shared(); auto db_compositor_factory = std::make_shared(); mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true}; // Verify we're actually starting at zero frames EXPECT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 0, 0)); compositor.start(); // Initial render: initial_composites frames should be composited at least while (!db_compositor_factory->check_record_count_for_each_buffer(nbuffers, composites_per_update)) std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Now we have initial_composites redraws, pause for a while std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // ... and make sure the number is still only 3 EXPECT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, composites_per_update, composites_per_update)); // Trigger more surface changes scene->emit_change_event(); // Display buffers should be forced to render another 3, so that's 6 while (!db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 2*composites_per_update)) std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Now pause without any further surface changes std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // Verify we never triggered more than 2*initial_composites compositions EXPECT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 2*composites_per_update, 2*composites_per_update)); compositor.stop(); // Pause the compositor so we don't race // Now trigger many surfaces changes close together for (int i = 0; i < 10; i++) scene->emit_change_event(); compositor.start(); // Display buffers should be forced to render another 3, so that's 9 while (!db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 3*composites_per_update)) std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Now pause without any further surface changes std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // Verify we never triggered more than 9 compositions EXPECT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 3*composites_per_update, 3*composites_per_update)); compositor.stop(); } TEST(MultiThreadedCompositor, schedules_enough_frames) { using namespace testing; unsigned int const nbuffers = 3; auto display = std::make_shared(nbuffers); auto scene = std::make_shared(); auto factory = std::make_shared(); mc::MultiThreadedCompositor compositor{display, scene, factory, null_display_listener, null_report, default_delay, true}; EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0)); compositor.start(); int const max_retries = 100; int retry = 0; while (retry < max_retries && !factory->check_record_count_for_each_buffer(nbuffers, 1)) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); ++retry; } ASSERT_LT(retry, max_retries); // Now we have the initial frame wait a bit std::this_thread::sleep_for(std::chrono::milliseconds(100)); // ... and make sure the number is still only 1 ASSERT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 1, 1)); // Trigger scene changes. Only need one frame to cover them all... scene->emit_change_event(); scene->emit_change_event(); scene->emit_change_event(); // Display buffers should be forced to render another 1, so that's 2 retry = 0; while (retry < max_retries && !factory->check_record_count_for_each_buffer(nbuffers, 2)) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); ++retry; } ASSERT_LT(retry, max_retries); // Scene unchanged. Expect no new frames: retry = 0; while (retry < max_retries && !factory->check_record_count_for_each_buffer(nbuffers, 2)) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); ++retry; } ASSERT_LT(retry, max_retries); // Some surface queues up multiple frames scene->set_pending(3); // Make sure they all get composited separately (2 + 3 more) retry = 0; while (retry < max_retries && !factory->check_record_count_for_each_buffer(nbuffers, 5)) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); ++retry; } ASSERT_LT(retry, max_retries); compositor.stop(); } TEST(MultiThreadedCompositor, recommended_sleep_throttles_compositor_loop) { using namespace testing; using namespace std::chrono; unsigned int const nbuffers = 3; milliseconds const recommendation(10); auto display = std::make_shared(nbuffers); auto scene = std::make_shared(); auto factory = std::make_shared(); mc::MultiThreadedCompositor compositor{display, scene, factory, null_display_listener, null_report, recommendation, false}; EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0)); compositor.start(); int const max_retries = 100; int const nframes = 10; auto start = system_clock::now(); for (int frame = 1; frame <= nframes; ++frame) { scene->emit_change_event(); int retry = 0; while (retry < max_retries && !factory->check_record_count_for_each_buffer(nbuffers, frame)) { std::this_thread::sleep_for(milliseconds(1)); ++retry; } ASSERT_LT(retry, max_retries); } /* * Detecting the throttling from outside the compositor thread is actually * trickier than you think. Because the display buffer counter won't be * delayed by the sleep; only the subsequent frame will be delayed. So * that's why we measure overall duration here... */ auto duration = system_clock::now() - start; // Minus 2 because the first won't be throttled, and the last not detected. int minimum = recommendation.count() * (nframes - 2); ASSERT_THAT(duration_cast(duration).count(), Ge(minimum)); compositor.stop(); } TEST(MultiThreadedCompositor, when_no_initial_composite_is_needed_there_is_none) { using namespace testing; unsigned int const nbuffers = 3; auto display = std::make_shared(nbuffers); auto scene = std::make_shared(); auto db_compositor_factory = std::make_shared(); mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, false}; // Verify we're actually starting at zero frames ASSERT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 0, 0)); compositor.start(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Verify we're still at zero frames EXPECT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 0, 0)); compositor.stop(); } TEST(MultiThreadedCompositor, when_no_initial_composite_is_needed_we_still_composite_on_restart) { using namespace testing; unsigned int const nbuffers = 3; auto display = std::make_shared(nbuffers); auto scene = std::make_shared(); auto db_compositor_factory = std::make_shared(); mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, false}; compositor.start(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Verify we're actually starting at zero frames ASSERT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, 0, 0)); compositor.stop(); compositor.start(); for (int countdown = 100; countdown != 0 && !db_compositor_factory->check_record_count_for_each_buffer(nbuffers, composites_per_update, composites_per_update); --countdown) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } // Verify we composited the expected frame EXPECT_TRUE(db_compositor_factory->check_record_count_for_each_buffer(nbuffers, composites_per_update, composites_per_update)); compositor.stop(); } TEST(MultiThreadedCompositor, surface_update_from_render_doesnt_deadlock) { using namespace testing; unsigned int const nbuffers{3}; auto display = std::make_shared(nbuffers); auto scene = std::make_shared(); auto db_compositor_factory = std::make_shared(scene); mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true}; compositor.start(); while (!db_compositor_factory->enough_renders_happened()) std::this_thread::sleep_for(std::chrono::milliseconds(10)); compositor.stop(); } TEST(MultiThreadedCompositor, double_start_or_stop_ignored) { using namespace testing; unsigned int const nbuffers{3}; auto display = std::make_shared(nbuffers); auto mock_scene = std::make_shared>(); auto db_compositor_factory = std::make_shared(); auto mock_report = std::make_shared>(); EXPECT_CALL(*mock_report, started()) .Times(1); EXPECT_CALL(*mock_report, stopped()) .Times(1); EXPECT_CALL(*mock_scene, add_observer(_)) .Times(1); EXPECT_CALL(*mock_scene, remove_observer(_)) .Times(1); EXPECT_CALL(*mock_scene, scene_elements_for(_)) .Times(AtLeast(0)) .WillRepeatedly(Return(mc::SceneElementSequence{})); mc::MultiThreadedCompositor compositor{display, mock_scene, db_compositor_factory, null_display_listener, mock_report, default_delay, true}; compositor.start(); compositor.start(); compositor.stop(); compositor.stop(); } TEST(MultiThreadedCompositor, cleans_up_after_throw_in_start) { unsigned int const nbuffers{3}; auto display = std::make_shared(nbuffers); auto scene = std::make_shared(); auto db_compositor_factory = std::make_shared(); mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true}; scene->throw_on_add_observer(true); EXPECT_THROW(compositor.start(), std::runtime_error); scene->throw_on_add_observer(false); /* No point in running the rest of the test if it throws again */ ASSERT_NO_THROW(compositor.start()); /* The minimum number of records here should be nbuffers *2, since we are checking for * presence of at least one additional rogue compositor thread per display buffer * However to avoid timing considerations like one good thread compositing the display buffer * twice before the rogue thread gets a chance to, an arbitrary number of records are gathered */ unsigned int min_number_of_records = 100; /* Timeout here in case the exception from setting the scene callback put the compositor * in a bad state that did not allow it to composite (hence no records gathered) */ auto time_out = std::chrono::steady_clock::now() + std::chrono::seconds(1); while (!db_compositor_factory->enough_records_gathered(nbuffers, min_number_of_records) && std::chrono::steady_clock::now() <= time_out) { scene->emit_change_event(); std::this_thread::yield(); } /* Check expectation in case a timeout happened */ EXPECT_TRUE(db_compositor_factory->enough_records_gathered(nbuffers, min_number_of_records)); compositor.stop(); /* Only one thread should be rendering each display buffer * If the compositor failed to cleanup correctly more than one thread could be * compositing the same display buffer */ EXPECT_TRUE(db_compositor_factory->each_buffer_rendered_in_single_thread()); } TEST(MultiThreadedCompositor, names_compositor_threads) { using namespace testing; unsigned int const nbuffers{3}; auto display = std::make_shared(nbuffers); auto scene = std::make_shared(); auto db_compositor_factory = std::make_shared(); mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_display_listener, null_report, default_delay, true}; compositor.start(); unsigned int const min_number_of_thread_names = 10; while (db_compositor_factory->num_thread_names_gathered() < min_number_of_thread_names) scene->emit_change_event(); compositor.stop(); auto const& thread_names = db_compositor_factory->thread_names; for (size_t i = 0; i < thread_names.size(); ++i) EXPECT_THAT(thread_names[i], Eq("Mir/Comp")) << "i=" << i; } TEST(MultiThreadedCompositor, registers_and_unregisters_with_scene) { using namespace testing; unsigned int const nbuffers{3}; auto display = std::make_shared(nbuffers); auto mock_scene = std::make_shared>(); auto db_compositor_factory = std::make_shared(); auto mock_report = std::make_shared>(); EXPECT_CALL(*mock_scene, register_compositor(_)) .Times(nbuffers); mc::MultiThreadedCompositor compositor{ display, mock_scene, db_compositor_factory, null_display_listener, mock_report, default_delay, true}; compositor.start(); Mock::VerifyAndClearExpectations(mock_scene.get()); EXPECT_CALL(*mock_scene, unregister_compositor(_)) .Times(nbuffers); compositor.stop(); } TEST(MultiThreadedCompositor, notifies_about_display_additions_and_removals) { using namespace testing; unsigned int const nbuffers{3}; auto display = std::make_shared(nbuffers); auto stub_scene = std::make_shared>(); auto mock_display_listener = std::make_shared(); auto db_compositor_factory = std::make_shared(); auto mock_report = std::make_shared>(); mc::MultiThreadedCompositor compositor{ display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, default_delay, true}; EXPECT_CALL(*mock_display_listener, add_display(_)).Times(nbuffers); compositor.start(); Mock::VerifyAndClearExpectations(mock_display_listener.get()); EXPECT_CALL(*mock_display_listener, remove_display(_)).Times(nbuffers); compositor.stop(); } TEST(MultiThreadedCompositor, when_compositor_thread_fails_start_reports_error) { using namespace testing; unsigned int const nbuffers{3}; auto display = std::make_shared(nbuffers); auto stub_scene = std::make_shared>(); auto mock_display_listener = std::make_shared(); auto db_compositor_factory = std::make_shared(); auto mock_report = std::make_shared>(); mc::MultiThreadedCompositor compositor{ display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, default_delay, true}; EXPECT_CALL(*mock_display_listener, add_display(_)) .WillRepeatedly(Throw(std::runtime_error("Failed to add display"))); EXPECT_THROW(compositor.start(), std::runtime_error); } //LP: 1481418 TEST(MultiThreadedCompositor, can_schedule_from_display_observer_when_adding_display) { using namespace testing; unsigned int const nbuffers{3}; auto display = std::make_shared(nbuffers); auto stub_scene = std::make_shared>(); auto mock_display_listener = std::make_shared>(); auto db_compositor_factory = std::make_shared(); auto mock_report = std::make_shared>(); ON_CALL(*mock_display_listener, add_display(_)) .WillByDefault(InvokeWithoutArgs([&]{ stub_scene->emit_change_event(); })); mc::MultiThreadedCompositor compositor{ display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, default_delay, true}; compositor.start(); } TEST(MultiThreadedCompositor, can_schedule_from_display_observer_when_removing_display) { using namespace testing; unsigned int const nbuffers{3}; auto display = std::make_shared(nbuffers); auto stub_scene = std::make_shared>(); auto mock_display_listener = std::make_shared>(); auto db_compositor_factory = std::make_shared(); auto mock_report = std::make_shared>(); ON_CALL(*mock_display_listener, remove_display(_)) .WillByDefault(InvokeWithoutArgs([&]{ stub_scene->emit_change_event(); })); mc::MultiThreadedCompositor compositor{ display, stub_scene, db_compositor_factory, mock_display_listener, mock_report, default_delay, true}; compositor.start(); } ./tests/unit-tests/compositor/test_occlusion.cpp0000644000015600001650000001662612676616125022344 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "mir/geometry/rectangle.h" #include "src/server/compositor/occlusion.h" #include "mir/test/doubles/fake_renderable.h" #include "mir/test/doubles/stub_scene_element.h" #include #include using namespace testing; using namespace mir::geometry; using namespace mir::compositor; namespace mg = mir::graphics; namespace mtd = mir::test::doubles; namespace { struct OcclusionFilterTest : public Test { OcclusionFilterTest() { monitor_rect.top_left = {0, 0}; monitor_rect.size = {1920, 1200}; } SceneElementSequence scene_elements_from( std::vector> renderables) { SceneElementSequence elements; for (auto const& renderable : renderables) elements.push_back(std::make_shared(renderable)); return elements; } mg::RenderableList renderables_from(SceneElementSequence const& elements) { mg::RenderableList renderables; for (auto const& element : elements) renderables.push_back(element->renderable()); return renderables; } Rectangle monitor_rect; }; } TEST_F(OcclusionFilterTest, single_window_not_occluded) { auto window = std::make_shared(12, 34, 56, 78); auto elements = scene_elements_from({window}); auto const& occlusions = filter_occlusions_from(elements, monitor_rect); EXPECT_THAT(renderables_from(occlusions), IsEmpty()); EXPECT_THAT(renderables_from(elements), ElementsAre(window)); } TEST_F(OcclusionFilterTest, partially_offscreen_still_visible) { // Regression test for LP: #1301115 auto left = std::make_shared(-10, 10, 100, 100); auto right = std::make_shared(1900, 10, 100, 100); auto top = std::make_shared(500, -1, 100, 100); auto bottom = std::make_shared(200, 1000, 100, 1000); auto elements = scene_elements_from({left, right, top, bottom}); auto const& occlusions = filter_occlusions_from(elements, monitor_rect); EXPECT_THAT(renderables_from(occlusions), IsEmpty()); EXPECT_THAT(renderables_from(elements), ElementsAre(left, right, top, bottom)); } TEST_F(OcclusionFilterTest, smaller_window_occluded) { auto top = std::make_shared(10, 10, 10, 10); auto bottom = std::make_shared(12, 12, 5, 5); auto elements = scene_elements_from({bottom, top}); auto const& occlusions = filter_occlusions_from(elements, monitor_rect); EXPECT_THAT(renderables_from(occlusions), ElementsAre(bottom)); EXPECT_THAT(renderables_from(elements), ElementsAre(top)); } TEST_F(OcclusionFilterTest, translucent_window_occludes_nothing) { auto top = std::make_shared(Rectangle{{10, 10}, {10, 10}}, 0.5f); auto bottom = std::make_shared(Rectangle{{12, 12}, {5, 5}}, 1.0f); auto elements = scene_elements_from({bottom, top}); auto const& occlusions = filter_occlusions_from(elements, monitor_rect); EXPECT_THAT(renderables_from(occlusions), IsEmpty()); EXPECT_THAT(renderables_from(elements), ElementsAre(bottom, top)); } TEST_F(OcclusionFilterTest, shaped_window_occludes_nothing) { auto top = std::make_shared(Rectangle{{10, 10}, {10, 10}}, 1.0f, false); auto bottom = std::make_shared(12, 12, 5, 5); auto elements = scene_elements_from({bottom, top}); auto const& occlusions = filter_occlusions_from(elements, monitor_rect); EXPECT_THAT(renderables_from(occlusions), IsEmpty()); EXPECT_THAT(renderables_from(elements), ElementsAre(bottom, top)); } TEST_F(OcclusionFilterTest, identical_window_occluded) { auto top = std::make_shared(10, 10, 10, 10); auto bottom = std::make_shared(10, 10, 10, 10); auto elements = scene_elements_from({bottom, top}); auto const& occlusions = filter_occlusions_from(elements, monitor_rect); EXPECT_THAT(renderables_from(occlusions), ElementsAre(bottom)); EXPECT_THAT(renderables_from(elements), ElementsAre(top)); } TEST_F(OcclusionFilterTest, larger_window_never_occluded) { auto top = std::make_shared(10, 10, 10, 10); auto bottom = std::make_shared(9, 9, 12, 12); auto elements = scene_elements_from({bottom, top}); auto const& occlusions = filter_occlusions_from(elements, monitor_rect); EXPECT_THAT(renderables_from(occlusions), IsEmpty()); EXPECT_THAT(renderables_from(elements), ElementsAre(bottom, top)); } TEST_F(OcclusionFilterTest, cascaded_windows_never_occluded) { std::vector> renderables; unsigned int const num_windows{10u}; for (auto x = 0u; x < num_windows; x++) renderables.push_back(std::make_shared(x, x, 200, 100)); auto elements = scene_elements_from(renderables); auto const& occlusions = filter_occlusions_from(elements, monitor_rect); EXPECT_THAT(renderables_from(occlusions), IsEmpty()); EXPECT_THAT(renderables_from(elements), ElementsAreArray(renderables)); } TEST_F(OcclusionFilterTest, some_occluded_and_some_not) { auto window0 = std::make_shared(10, 20, 400, 300); auto window1 = std::make_shared(10, 20, 5, 5); auto window2 = std::make_shared(100, 100, 20, 20); auto window3 = std::make_shared(200, 200, 50, 50); auto window4 = std::make_shared(500, 600, 34, 56); auto window5 = std::make_shared(200, 200, 1000, 1000); auto elements = scene_elements_from({ window5, //not occluded window4, //not occluded window3, //occluded window2, //occluded window1, //occluded window0 //not occluded }); auto const& occlusions = filter_occlusions_from(elements, monitor_rect); EXPECT_THAT(renderables_from(occlusions), ElementsAre(window3, window2, window1)); EXPECT_THAT(renderables_from(elements), ElementsAre(window5, window4, window0)); } TEST_F(OcclusionFilterTest, occludes_partially_onscreen_window_when_onscreen_part_is_covered_by_another_window) { auto const partially_onscreen = std::make_shared(-50, 100, 150, 100); auto const covering = std::make_shared(0, 100, 100, 100); auto elements = scene_elements_from({ partially_onscreen, covering }); auto const& occlusions = filter_occlusions_from(elements, monitor_rect); EXPECT_THAT(renderables_from(occlusions), ElementsAre(partially_onscreen)); EXPECT_THAT(renderables_from(elements), ElementsAre(covering)); } ./tests/unit-tests/compositor/test_temporary_buffers.cpp0000644000015600001650000001077012676616125024076 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/server/compositor/temporary_buffers.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/mock_buffer_bundle.h" #include #include namespace mtd=mir::test::doubles; namespace mg = mir::graphics; namespace mc=mir::compositor; namespace geom=mir::geometry; namespace { class TemporaryTestBuffer : public mc::TemporaryBuffer { public: TemporaryTestBuffer(const std::shared_ptr& buf) : TemporaryBuffer(buf) { } }; struct MockBufferAcquisition : mc::BufferAcquisition { MOCK_METHOD1(compositor_acquire, std::shared_ptr(void const*)); MOCK_METHOD1(compositor_release, void(std::shared_ptr const&)); MOCK_METHOD0(snapshot_acquire, std::shared_ptr()); MOCK_METHOD1(snapshot_release, void(std::shared_ptr const&)); }; class TemporaryBuffersTest : public ::testing::Test { public: TemporaryBuffersTest() : buffer_size{1024, 768}, buffer_stride{1024}, buffer_pixel_format{mir_pixel_format_abgr_8888}, mock_buffer{std::make_shared>( buffer_size, buffer_stride, buffer_pixel_format)}, mock_acquisition{std::make_shared>()} { using namespace testing; ON_CALL(*mock_acquisition, compositor_acquire(_)) .WillByDefault(Return(mock_buffer)); } geom::Size const buffer_size; geom::Stride const buffer_stride; MirPixelFormat const buffer_pixel_format; std::shared_ptr const mock_buffer; std::shared_ptr mock_acquisition; }; } TEST_F(TemporaryBuffersTest, compositor_buffer_acquires_and_releases) { using namespace testing; EXPECT_CALL(*mock_acquisition, compositor_acquire(_)) .WillOnce(Return(mock_buffer)); EXPECT_CALL(*mock_acquisition, compositor_release(_)) .Times(1); mc::TemporaryCompositorBuffer proxy_buffer(mock_acquisition, 0); } TEST_F(TemporaryBuffersTest, snapshot_buffer_acquires_and_releases) { using namespace testing; EXPECT_CALL(*mock_acquisition, snapshot_acquire()) .WillOnce(Return(mock_buffer)); EXPECT_CALL(*mock_acquisition, snapshot_release(_)) .Times(1); mc::TemporarySnapshotBuffer proxy_buffer(mock_acquisition); } TEST_F(TemporaryBuffersTest, base_test_size) { TemporaryTestBuffer proxy_buffer(mock_buffer); EXPECT_CALL(*mock_buffer, size()) .Times(1); geom::Size size; size = proxy_buffer.size(); EXPECT_EQ(buffer_size, size); } TEST_F(TemporaryBuffersTest, base_test_stride) { TemporaryTestBuffer proxy_buffer(mock_buffer); EXPECT_CALL(*mock_buffer, stride()) .Times(1); geom::Stride stride; stride = proxy_buffer.stride(); EXPECT_EQ(buffer_stride, stride); } TEST_F(TemporaryBuffersTest, base_test_pixel_format) { TemporaryTestBuffer proxy_buffer(mock_buffer); EXPECT_CALL(*mock_buffer, pixel_format()) .Times(1); MirPixelFormat pixel_format; pixel_format = proxy_buffer.pixel_format(); EXPECT_EQ(buffer_pixel_format, pixel_format); } TEST_F(TemporaryBuffersTest, base_test_id) { TemporaryTestBuffer proxy_buffer(mock_buffer); EXPECT_CALL(*mock_buffer, id()) .Times(1); proxy_buffer.id(); } TEST_F(TemporaryBuffersTest, base_test_native_buffer_handle) { TemporaryTestBuffer proxy_buffer(mock_buffer); EXPECT_CALL(*mock_buffer, native_buffer_handle()) .Times(1); proxy_buffer.native_buffer_handle(); } TEST_F(TemporaryBuffersTest, forwards_native_buffer_base_to_wrapped_buffer) { TemporaryTestBuffer proxy_buffer(mock_buffer); EXPECT_CALL(*mock_buffer, native_buffer_base()) .Times(1); proxy_buffer.native_buffer_base(); } ./tests/unit-tests/compositor/test_client_buffers.cpp0000644000015600001650000001355312676616125023334 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/mock_event_sink.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "src/server/compositor/buffer_map.h" #include "mir/graphics/display_configuration.h" #include using namespace testing; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace mf = mir::frontend; namespace geom = mir::geometry; namespace { struct MockBufferAllocator : public mg::GraphicBufferAllocator { MOCK_METHOD1(alloc_buffer, std::shared_ptr(mg::BufferProperties const&)); MOCK_METHOD0(supported_pixel_formats, std::vector()); }; struct StubBufferAllocator : public mg::GraphicBufferAllocator { std::shared_ptr alloc_buffer(mg::BufferProperties const&) { auto b = std::make_shared(); map[b->id()] = b; ids.push_back(b->id()); return b; } std::vector supported_pixel_formats() { return {}; } std::map> map; std::vector ids; }; } struct ClientBuffers : public Test { testing::NiceMock mock_sink; mf::BufferStreamId stream_id{44}; mg::BufferProperties properties{geom::Size{42,43}, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware}; MockBufferAllocator mock_allocator; StubBufferAllocator stub_allocator; mc::BufferMap map{stream_id, mt::fake_shared(mock_sink), mt::fake_shared(stub_allocator)}; }; TEST_F(ClientBuffers, sends_full_buffer_on_allocation) { auto stub_buffer = std::make_shared(); EXPECT_CALL(mock_allocator, alloc_buffer(Ref(properties))) .WillOnce(Return(stub_buffer)); EXPECT_CALL(mock_sink, send_buffer(stream_id, Ref(*stub_buffer), mg::BufferIpcMsgType::full_msg)); mc::BufferMap map{stream_id, mt::fake_shared(mock_sink), mt::fake_shared(mock_allocator)}; EXPECT_THAT(map.add_buffer(properties), Eq(stub_buffer->id())); } TEST_F(ClientBuffers, access_of_nonexistent_buffer_throws) { auto stub_buffer = std::make_unique(); EXPECT_THROW({ auto buffer = map[stub_buffer->id()]; }, std::logic_error); } TEST_F(ClientBuffers, removal_of_nonexistent_buffer_throws) { auto stub_buffer = std::make_unique(); EXPECT_THROW({ map.remove_buffer(stub_buffer->id()); }, std::logic_error); } TEST_F(ClientBuffers, can_access_once_added) { map.add_buffer(properties); ASSERT_THAT(stub_allocator.map, SizeIs(1)); ASSERT_THAT(stub_allocator.ids, SizeIs(1)); auto buffer = map[stub_allocator.ids[0]]; EXPECT_THAT(buffer, Eq(stub_allocator.map[stub_allocator.ids[0]])); } TEST_F(ClientBuffers, sends_update_msg_to_send_buffer) { map.add_buffer(properties); ASSERT_THAT(stub_allocator.map, SizeIs(1)); ASSERT_THAT(stub_allocator.ids, SizeIs(1)); auto buffer = map[stub_allocator.ids[0]]; EXPECT_CALL(mock_sink, send_buffer(stream_id, Ref(*buffer), mg::BufferIpcMsgType::update_msg)); map.send_buffer(stub_allocator.ids[0]); } TEST_F(ClientBuffers, sends_no_update_msg_if_buffer_is_not_around) { map.add_buffer(properties); ASSERT_THAT(stub_allocator.map, SizeIs(1)); ASSERT_THAT(stub_allocator.ids, SizeIs(1)); auto buffer = map[stub_allocator.ids[0]]; EXPECT_CALL(mock_sink, send_buffer(stream_id, Ref(*buffer), mg::BufferIpcMsgType::update_msg)) .Times(0); map.remove_buffer(stub_allocator.ids[0]); map.send_buffer(stub_allocator.ids[0]); } TEST_F(ClientBuffers, can_remove_buffer_from_send_callback) { map.add_buffer(properties); ON_CALL(mock_sink, send_buffer(_,_,_)) .WillByDefault(Invoke( [&] (mf::BufferStreamId, mg::Buffer& buffer, mg::BufferIpcMsgType) { map.remove_buffer(buffer.id()); })); map.send_buffer(stub_allocator.ids[0]); } TEST_F(ClientBuffers, removing_decreases_count) { map.add_buffer(properties); ASSERT_THAT(stub_allocator.map, SizeIs(1)); ASSERT_THAT(stub_allocator.ids, SizeIs(1)); EXPECT_THAT(map.client_owned_buffer_count(), Eq(1)); map.remove_buffer(stub_allocator.ids[0]); EXPECT_THAT(map.client_owned_buffer_count(), Eq(0)); } TEST_F(ClientBuffers, ignores_unknown_receive) { map.add_buffer(properties); map.remove_buffer(stub_allocator.ids[0]); EXPECT_THAT(map.client_owned_buffer_count(), Eq(0)); map.send_buffer(stub_allocator.ids[0]); EXPECT_THAT(map.client_owned_buffer_count(), Eq(0)); } TEST_F(ClientBuffers, can_track_how_many_buffers_client_owns) { map.add_buffer(properties); map.add_buffer(properties); map.add_buffer(properties); ASSERT_THAT(stub_allocator.map, SizeIs(3)); ASSERT_THAT(stub_allocator.ids, SizeIs(3)); EXPECT_THAT(map.client_owned_buffer_count(), Eq(3)); map.receive_buffer(stub_allocator.ids[0]); map.receive_buffer(stub_allocator.ids[1]); EXPECT_THAT(map.client_owned_buffer_count(), Eq(1)); map.send_buffer(stub_allocator.ids[0]); EXPECT_THAT(map.client_owned_buffer_count(), Eq(2)); map.send_buffer(stub_allocator.ids[1]); EXPECT_THAT(map.client_owned_buffer_count(), Eq(3)); } ./tests/unit-tests/compositor/test_buffer_queue.cpp0000644000015600001650000017260012676616157023023 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt * Alberto Aguirre */ #include "src/server/compositor/buffer_queue.h" #include "src/server/compositor/timeout_frame_dropping_policy_factory.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_frame_dropping_policy_factory.h" #include "mir/test/doubles/mock_frame_dropping_policy_factory.h" #include "mir/test/fake_clock.h" #include "mir/test/doubles/mock_timer.h" #include "mir/test/signal.h" #include "mir/test/auto_unblock_thread.h" #include #include #include #include #include #include #include #include #include namespace geom = mir::geometry; namespace mtd = mir::test::doubles; namespace mt = mir::test; namespace mc=mir::compositor; namespace mg = mir::graphics; namespace mt=mir::test; using namespace testing; namespace { int const max_nbuffers_to_test{5}; class BufferQueue : public ::testing::Test { public: BufferQueue() : allocator{std::make_shared()}, basic_properties{ geom::Size{3, 4}, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware } { } protected: std::shared_ptr allocator; mg::BufferProperties basic_properties; mtd::StubFrameDroppingPolicyFactory policy_factory; }; struct BufferQueueWithOneBuffer : BufferQueue { int nbuffers = 1; mc::BufferQueue q{nbuffers, allocator, basic_properties, policy_factory}; }; struct BufferQueueWithTwoBuffers : BufferQueue { int nbuffers = 2; mc::BufferQueue q{nbuffers, allocator, basic_properties, policy_factory}; }; struct BufferQueueWithThreeBuffers : BufferQueue { int nbuffers = 3; mc::BufferQueue q{nbuffers, allocator, basic_properties, policy_factory}; }; struct WithAnyNumberOfBuffers : BufferQueue, ::testing::WithParamInterface { int nbuffers = GetParam(); mc::BufferQueue q{nbuffers, allocator, basic_properties, policy_factory}; }; struct WithTwoOrMoreBuffers : BufferQueue, ::testing::WithParamInterface { int nbuffers = GetParam(); mc::BufferQueue q{nbuffers, allocator, basic_properties, policy_factory}; }; struct WithThreeOrMoreBuffers : BufferQueue, ::testing::WithParamInterface { int nbuffers = GetParam(); mc::BufferQueue q{nbuffers, allocator, basic_properties, policy_factory}; }; class AcquireWaitHandle { public: AcquireWaitHandle(mc::BufferQueue& q) : buffer_{nullptr}, q{&q}, received_buffer{false} {} void receive_buffer(mg::Buffer* new_buffer) { std::lock_guard lock(guard); buffer_ = new_buffer; received_buffer = true; cv.notify_one(); } void wait() { std::unique_lock lock(guard); cv.wait(lock, [&]{ return received_buffer; }); } template bool wait_for(std::chrono::duration const& duration) { std::unique_lock lock(guard); return cv.wait_for(lock, duration, [&]{ return received_buffer; }); } bool has_acquired_buffer() { std::lock_guard lock(guard); return received_buffer; } void release_buffer() { if (buffer_) { q->client_release(buffer_); received_buffer = false; } } mg::BufferID id() { return buffer_->id(); } mg::Buffer* buffer() { return buffer_; } private: mg::Buffer* buffer_; mc::BufferQueue* q; std::condition_variable cv; std::mutex guard; bool received_buffer; }; std::shared_ptr client_acquire_async(mc::BufferQueue& q) { std::shared_ptr wait_handle = std::make_shared(q); q.client_acquire( [wait_handle](mg::Buffer* buffer) { wait_handle->receive_buffer(buffer); }); return wait_handle; } mg::Buffer* client_acquire_sync(mc::BufferQueue& q) { auto handle = client_acquire_async(q); handle->wait(); return handle->buffer(); } void unthrottled_compositor_thread(mc::BufferQueue &bundle, std::atomic &done) { while (!done) { bundle.compositor_release(bundle.compositor_acquire(nullptr)); std::this_thread::yield(); } } void overlapping_compositor_thread(mc::BufferQueue &bundle, std::atomic &done) { std::shared_ptr b[2]; int i = 0; b[0] = bundle.compositor_acquire(nullptr); while (!done) { b[i^1] = bundle.compositor_acquire(nullptr); bundle.compositor_release(b[i]); std::this_thread::sleep_for(std::chrono::milliseconds(10)); i ^= 1; } if (b[i]) bundle.compositor_release(b[i]); } void snapshot_thread(mc::BufferQueue &bundle, std::atomic &done) { while (!done) { bundle.snapshot_release(bundle.snapshot_acquire()); std::this_thread::yield(); } } void client_thread(mc::BufferQueue &bundle, int nframes) { for (int i = 0; i < nframes; i++) { bundle.client_release(client_acquire_sync(bundle)); std::this_thread::yield(); } } void switching_client_thread(mc::BufferQueue &bundle, int nframes) { bool enable_frame_dropping{false}; int const nframes_to_test_before_switching{5}; for (int i = 0; i < nframes; ++i) { bundle.allow_framedropping(enable_frame_dropping); for (int j = 0; j < nframes_to_test_before_switching; j++) bundle.client_release(client_acquire_sync(bundle)); enable_frame_dropping = !enable_frame_dropping; std::this_thread::yield(); } } } TEST_F(BufferQueueWithOneBuffer, buffer_queue_of_one_is_supported) { auto handle = client_acquire_async(q); /* Client is allowed to get the only buffer in existence */ ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); /* Client blocks until the client releases * the buffer and compositor composites it*/ auto next_request = client_acquire_async(q); EXPECT_THAT(next_request->has_acquired_buffer(), Eq(false)); auto comp_buffer = q.compositor_acquire(this); auto client_id = handle->id(); /* Client and compositor always share the same buffer */ EXPECT_THAT(client_id, Eq(comp_buffer->id())); EXPECT_NO_THROW(handle->release_buffer()); EXPECT_NO_THROW(q.compositor_release(comp_buffer)); /* Simulate a composite pass */ comp_buffer = q.compositor_acquire(this); q.compositor_release(comp_buffer); /* The request should now be fullfilled after compositor * released the buffer */ EXPECT_THAT(next_request->has_acquired_buffer(), Eq(true)); EXPECT_NO_THROW(next_request->release_buffer()); } TEST_F(BufferQueueWithOneBuffer, buffer_queue_of_one_supports_resizing) { const geom::Size expect_size{10, 20}; q.resize(expect_size); auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); auto buffer = handle->buffer(); ASSERT_THAT(buffer->size(), Eq(expect_size)); /* Client and compositor share the same buffer so * expect the new size */ std::shared_ptr comp_buffer; ASSERT_NO_THROW(comp_buffer = q.compositor_acquire(this)); EXPECT_THAT(buffer->size(), Eq(expect_size)); EXPECT_NO_THROW(q.compositor_release(comp_buffer)); EXPECT_NO_THROW(handle->release_buffer()); EXPECT_NO_THROW(q.compositor_release(q.compositor_acquire(this))); } TEST_F(BufferQueueWithTwoBuffers, framedropping_is_disabled_by_default) { EXPECT_THAT(q.framedropping_allowed(), Eq(false)); } TEST_F(BufferQueue, throws_when_creating_with_invalid_num_buffers) { EXPECT_THROW(mc::BufferQueue a(0, allocator, basic_properties, policy_factory), std::logic_error); EXPECT_THROW(mc::BufferQueue a(-1, allocator, basic_properties, policy_factory), std::logic_error); EXPECT_THROW(mc::BufferQueue a(-10, allocator, basic_properties, policy_factory), std::logic_error); } TEST_P(WithAnyNumberOfBuffers, client_can_acquire_and_release_buffer) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); ASSERT_NO_THROW(handle->release_buffer()); } TEST_P(WithTwoOrMoreBuffers, client_can_acquire_buffers) { int const max_ownable_buffers = q.buffers_free_for_client(); ASSERT_THAT(max_ownable_buffers, Gt(0)); for (int acquires = 0; acquires < max_ownable_buffers; ++acquires) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); } } /* Regression test for LP: #1315302 */ TEST_F(BufferQueueWithThreeBuffers, clients_can_have_multiple_pending_completions) { int const prefill = q.buffers_free_for_client(); ASSERT_THAT(prefill, Gt(0)); for (int i = 0; i < prefill; ++i) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); } auto handle1 = client_acquire_async(q); ASSERT_THAT(handle1->has_acquired_buffer(), Eq(false)); auto handle2 = client_acquire_async(q); ASSERT_THAT(handle1->has_acquired_buffer(), Eq(false)); for (int i = 0; i < nbuffers + 1; ++i) q.compositor_release(q.compositor_acquire(this)); EXPECT_THAT(handle1->has_acquired_buffer(), Eq(true)); EXPECT_THAT(handle2->has_acquired_buffer(), Eq(true)); EXPECT_THAT(handle1->buffer(), Ne(handle2->buffer())); handle1->release_buffer(); handle2->release_buffer(); } TEST_P(WithTwoOrMoreBuffers, compositor_acquires_frames_in_order_for_synchronous_client) { ASSERT_THAT(q.framedropping_allowed(), Eq(false)); void const* main_compositor = reinterpret_cast(0); void const* second_compositor = reinterpret_cast(1); for (int i = 0; i < 20; i++) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); auto client_id = handle->id(); handle->release_buffer(); auto comp_buffer = q.compositor_acquire(main_compositor); auto composited_id = comp_buffer->id(); q.compositor_release(comp_buffer); EXPECT_THAT(composited_id, Eq(client_id)); comp_buffer = q.compositor_acquire(second_compositor); EXPECT_THAT(composited_id, Eq(comp_buffer->id())); q.compositor_release(comp_buffer); } } TEST_P(WithTwoOrMoreBuffers, framedropping_clients_never_block) { q.allow_framedropping(true); for (int i = 0; i < 1000; i++) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); } } /* Regression test for LP: #1210042 */ TEST_F(BufferQueueWithThreeBuffers, clients_dont_recycle_startup_buffer) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); auto client_id = handle->id(); handle->release_buffer(); handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); auto comp_buffer = q.compositor_acquire(this); EXPECT_THAT(client_id, Eq(comp_buffer->id())); q.compositor_release(comp_buffer); } TEST_P(WithThreeOrMoreBuffers, throws_on_out_of_order_client_release) { auto handle1 = client_acquire_async(q); ASSERT_THAT(handle1->has_acquired_buffer(), Eq(true)); auto handle2 = client_acquire_async(q); ASSERT_THAT(handle2->has_acquired_buffer(), Eq(true)); EXPECT_THROW(handle2->release_buffer(), std::logic_error); EXPECT_NO_THROW(handle1->release_buffer()); EXPECT_THROW(handle1->release_buffer(), std::logic_error); EXPECT_NO_THROW(handle2->release_buffer()); } TEST_P(WithTwoOrMoreBuffers, async_client_cycles_through_all_buffers) { // This test is technically not valid with dynamic queue scaling on q.set_scaling_delay(-1); std::atomic done(false); auto unblock = [&done] { done = true; }; mt::AutoUnblockThread compositor(unblock, unthrottled_compositor_thread, std::ref(q), std::ref(done)); std::unordered_set ids_acquired; int const max_ownable_buffers = nbuffers - 1; for (int i = 0; i < max_ownable_buffers*2; ++i) { std::vector client_buffers; for (int acquires = 0; acquires < max_ownable_buffers; ++acquires) { auto handle = client_acquire_async(q); handle->wait_for(std::chrono::seconds(1)); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); ids_acquired.insert(handle->id().as_value()); client_buffers.push_back(handle->buffer()); } for (auto const& buffer : client_buffers) { q.client_release(buffer); } } EXPECT_THAT(ids_acquired.size(), Eq(nbuffers)); } TEST_P(WithAnyNumberOfBuffers, compositor_can_acquire_and_release) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); auto client_id = handle->id(); ASSERT_NO_THROW(handle->release_buffer()); auto comp_buffer = q.compositor_acquire(this); EXPECT_THAT(client_id, Eq(comp_buffer->id())); EXPECT_NO_THROW(q.compositor_release(comp_buffer)); } TEST_P(WithTwoOrMoreBuffers, clients_get_new_buffers_on_compositor_release) { // Regression test for LP: #1480164 q.allow_framedropping(false); // Skip over the first frame. The early release optimization is too // conservative to allow it to happen right at the start (so as to // maintain correct multimonitor frame rates if required). auto handle = client_acquire_async(q); ASSERT_TRUE(handle->has_acquired_buffer()); handle->release_buffer(); q.compositor_release(q.compositor_acquire(this)); auto onscreen = q.compositor_acquire(this); // This is what tests should do instead of using buffers_free_for_client() bool blocking; do { handle = client_acquire_async(q); blocking = !handle->has_acquired_buffer(); if (!blocking) handle->release_buffer(); } while (!blocking); int throttled_count = 0; for (int f = 0; f < 100; ++f) { ASSERT_FALSE(handle->has_acquired_buffer()); q.compositor_release(onscreen); if (handle->has_acquired_buffer()) { // This should always happen if dynamic queue scaling is disabled... handle->release_buffer(); onscreen = q.compositor_acquire(this); handle = client_acquire_async(q); throttled_count = 0; } else { ASSERT_THAT(q.scaling_delay(), Ge(0)); ++throttled_count; ASSERT_THAT(throttled_count, Le(nbuffers)); onscreen = q.compositor_acquire(this); } } } TEST_P(WithTwoOrMoreBuffers, short_buffer_holds_dont_overclock_multimonitor) { // Regression test related to LP: #1480164 q.allow_framedropping(false); // Skip over the first frame. The early release optimization is too // conservative to allow it to happen right at the start (so as to // maintain correct multimonitor frame rates if required). auto handle = client_acquire_async(q); ASSERT_TRUE(handle->has_acquired_buffer()); handle->release_buffer(); const void* const leftid = "left"; const void* const rightid = "right"; auto left = q.compositor_acquire(leftid); q.compositor_release(left); left = q.compositor_acquire(leftid); auto right = q.compositor_acquire(rightid); // This is what tests should do instead of using buffers_free_for_client() bool blocking; do { handle = client_acquire_async(q); blocking = !handle->has_acquired_buffer(); if (!blocking) handle->release_buffer(); } while (!blocking); for (int f = 0; f < 100; ++f) { // These two assertions are the important ones: ASSERT_FALSE(handle->has_acquired_buffer()); q.compositor_release(left); q.compositor_release(right); ASSERT_FALSE(handle->has_acquired_buffer()); left = q.compositor_acquire(leftid); right = q.compositor_acquire(rightid); // Note: This is only reliably true when queue scaling is disabled: if (handle->has_acquired_buffer()) { handle->release_buffer(); handle = client_acquire_async(q); } // else dynamic queue scaling is throttling us. } } TEST_P(WithThreeOrMoreBuffers, greedy_clients_get_new_buffers_on_compositor_release) { // Regression test for LP: #1480164 q.allow_framedropping(false); // Skip over the first frame. The early release optimization is too // conservative to allow it to happen right at the start (so as to // maintain correct multimonitor frame rates if required). auto handle = client_acquire_async(q); ASSERT_TRUE(handle->has_acquired_buffer()); handle->release_buffer(); q.compositor_release(q.compositor_acquire(this)); auto onscreen = q.compositor_acquire(this); auto old_handle = handle; old_handle.reset(); bool blocking; do { handle = client_acquire_async(q); blocking = !handle->has_acquired_buffer(); if (!blocking) { if (old_handle) old_handle->release_buffer(); old_handle = handle; handle.reset(); } } while (!blocking); ASSERT_TRUE(old_handle->has_acquired_buffer()); ASSERT_FALSE(handle->has_acquired_buffer()); q.compositor_release(onscreen); ASSERT_TRUE(handle->has_acquired_buffer()); } TEST_P(WithAnyNumberOfBuffers, multiple_compositors_are_in_sync) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); auto client_id = handle->id(); ASSERT_NO_THROW(handle->release_buffer()); for (int monitor = 0; monitor < 10; monitor++) { void const* user_id = reinterpret_cast(monitor); auto comp_buffer = q.compositor_acquire(user_id); EXPECT_THAT(client_id, Eq(comp_buffer->id())); q.compositor_release(comp_buffer); } } TEST_P(WithThreeOrMoreBuffers, multiple_fast_compositors_are_in_sync) { // Regression test for LP: #1420678 // Client generates first frame auto handle1 = client_acquire_async(q); ASSERT_TRUE(handle1->has_acquired_buffer()); auto client_id1 = handle1->id(); handle1->release_buffer(); // Client generates second frame auto handle2 = client_acquire_async(q); ASSERT_TRUE(handle2->has_acquired_buffer()); auto client_id2 = handle2->id(); handle2->release_buffer(); // Many monitors... verify they all get the first frame. for (int monitor = 0; monitor < 10; monitor++) { void const* user_id = reinterpret_cast(monitor); auto comp_buffer = q.compositor_acquire(user_id); ASSERT_EQ(client_id1, comp_buffer->id()); q.compositor_release(comp_buffer); } // Still many monitors... verify they all get the second frame. for (int monitor = 0; monitor < 10; monitor++) { void const* user_id = reinterpret_cast(monitor); auto comp_buffer = q.compositor_acquire(user_id); ASSERT_EQ(client_id2, comp_buffer->id()); q.compositor_release(comp_buffer); } } TEST_P(WithTwoOrMoreBuffers, compositor_acquires_frames_in_order) { for (int i = 0; i < 10; ++i) { std::deque client_release_sequence; std::vector buffers; for (int i = 0; i < q.buffers_free_for_client(); ++i) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); buffers.push_back(handle->buffer()); } for (auto buffer : buffers) { client_release_sequence.push_back(buffer->id()); q.client_release(buffer); } for (auto const& client_id : client_release_sequence) { auto comp_buffer = q.compositor_acquire(this); EXPECT_THAT(client_id, Eq(comp_buffer->id())); q.compositor_release(comp_buffer); } } } TEST_P(WithAnyNumberOfBuffers, compositor_acquire_never_blocks_when_there_are_no_ready_buffers) { for (int i = 0; i < 100; i++) { auto buffer = q.compositor_acquire(this); q.compositor_release(buffer); } } TEST_P(WithTwoOrMoreBuffers, compositor_can_always_acquire_buffer) { q.allow_framedropping(false); std::atomic done(false); auto unblock = [&done] { done = true; }; mt::AutoJoinThread client([this] { for (int nframes = 0; nframes < 100; ++nframes) { auto handle = client_acquire_async(q); handle->wait_for(std::chrono::seconds(1)); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); std::this_thread::yield(); } }); mt::AutoUnblockThread compositor(unblock, [&] { while (!done) { std::shared_ptr buffer; EXPECT_NO_THROW(buffer = q.compositor_acquire(this)); EXPECT_THAT(buffer, Ne(nullptr)); EXPECT_NO_THROW(q.compositor_release(buffer)); std::this_thread::yield(); } }); client.stop(); compositor.stop(); } TEST_P(WithAnyNumberOfBuffers, compositor_acquire_recycles_latest_ready_buffer) { mg::Buffer* last_client_acquired_buffer{nullptr}; for (int i = 0; i < 20; i++) { if (i % 10 == 0) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); last_client_acquired_buffer = handle->buffer(); handle->release_buffer(); } for (int monitor_id = 0; monitor_id < 10; monitor_id++) { void const* user_id = reinterpret_cast(monitor_id); auto buffer = q.compositor_acquire(user_id); ASSERT_THAT(buffer.get(), Eq(last_client_acquired_buffer)); q.compositor_release(buffer); } } } TEST_P(WithAnyNumberOfBuffers, compositor_release_verifies_parameter) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); auto comp_buffer = q.compositor_acquire(this); q.compositor_release(comp_buffer); EXPECT_THROW(q.compositor_release(comp_buffer), std::logic_error); } /* Regression test for LP#1270964 */ TEST_F(BufferQueueWithThreeBuffers, compositor_client_interleaved) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); auto first_ready_buffer_id = handle->id(); handle->release_buffer(); handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); // in the original bug, compositor would be given the wrong buffer here auto compositor_buffer = q.compositor_acquire(this); EXPECT_THAT(compositor_buffer->id(), Eq(first_ready_buffer_id)); handle->release_buffer(); q.compositor_release(compositor_buffer); } TEST_P(WithTwoOrMoreBuffers, overlapping_compositors_get_different_frames) { // This test simulates bypass behaviour std::shared_ptr compositor[2]; auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); compositor[0] = q.compositor_acquire(this); handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); compositor[1] = q.compositor_acquire(this); for (int i = 0; i < 20; i++) { // Two compositors acquired, and they're always different... ASSERT_THAT(compositor[0]->id(), Ne(compositor[1]->id())); // One of the compositors (the oldest one) gets a new buffer... int oldest = i & 1; q.compositor_release(compositor[oldest]); auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); compositor[oldest] = q.compositor_acquire(this); } q.compositor_release(compositor[0]); q.compositor_release(compositor[1]); } TEST_P(WithAnyNumberOfBuffers, snapshot_acquire_basic) { auto comp_buffer = q.compositor_acquire(this); auto snapshot = q.snapshot_acquire(); EXPECT_THAT(snapshot->id(), Eq(comp_buffer->id())); q.compositor_release(comp_buffer); q.snapshot_release(snapshot); } TEST_F(BufferQueueWithOneBuffer, callbacks_cant_happen_after_shutdown) { q.drop_client_requests(); auto client = client_acquire_async(q); ASSERT_FALSE(client->has_acquired_buffer()); } TEST_F(BufferQueueWithOneBuffer, callbacks_cant_happen_after_shutdown_with_snapshots) { auto snapshot = q.snapshot_acquire(); q.drop_client_requests(); q.snapshot_release(snapshot); auto client = client_acquire_async(q); ASSERT_FALSE(client->has_acquired_buffer()); } TEST_P(WithAnyNumberOfBuffers, snapshot_acquire_never_blocks) { int const num_snapshots = 100; std::shared_ptr buf[num_snapshots]; for (int i = 0; i < num_snapshots; i++) buf[i] = q.snapshot_acquire(); for (int i = 0; i < num_snapshots; i++) q.snapshot_release(buf[i]); } TEST_P(WithTwoOrMoreBuffers, snapshot_release_verifies_parameter) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); auto comp_buffer = q.compositor_acquire(this); EXPECT_THROW(q.snapshot_release(comp_buffer), std::logic_error); handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); auto snapshot = q.snapshot_acquire(); EXPECT_THAT(snapshot->id(), Eq(comp_buffer->id())); EXPECT_THAT(snapshot->id(), Ne(handle->id())); EXPECT_NO_THROW(q.snapshot_release(snapshot)); EXPECT_THROW(q.snapshot_release(snapshot), std::logic_error); } TEST_P(WithTwoOrMoreBuffers, stress) { std::atomic done(false); auto unblock = [&done]{ done = true;}; mt::AutoUnblockThread compositor(unblock, unthrottled_compositor_thread, std::ref(q), std::ref(done)); mt::AutoUnblockThread snapshotter1(unblock, snapshot_thread, std::ref(q), std::ref(done)); mt::AutoUnblockThread snapshotter2(unblock, snapshot_thread, std::ref(q), std::ref(done)); q.allow_framedropping(false); mt::AutoJoinThread client1(client_thread, std::ref(q), 1000); client1.stop(); q.allow_framedropping(true); mt::AutoJoinThread client2(client_thread, std::ref(q), 1000); client2.stop(); mt::AutoJoinThread client3(switching_client_thread, std::ref(q), 1000); client3.stop(); } TEST_P(WithTwoOrMoreBuffers, framedropping_clients_get_all_buffers) { q.allow_framedropping(true); int const nframes = 100; std::unordered_set ids_acquired; for (int i = 0; i < nframes; ++i) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); ids_acquired.insert(handle->id().as_value()); handle->release_buffer(); } EXPECT_THAT(ids_acquired.size(), Ge(nbuffers)); } TEST_P(WithTwoOrMoreBuffers, waiting_clients_unblock_on_shutdown) { q.allow_framedropping(false); int const max_ownable_buffers = q.buffers_free_for_client(); ASSERT_THAT(max_ownable_buffers, Gt(0)); for (int b = 0; b < max_ownable_buffers; b++) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); } auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(false)); q.force_requests_to_complete(); EXPECT_THAT(handle->has_acquired_buffer(), Eq(true)); } TEST_P(WithTwoOrMoreBuffers, client_framerate_matches_compositor) { unsigned long client_frames = 0; const unsigned long compose_frames = 20; q.allow_framedropping(false); std::atomic done(false); mt::AutoJoinThread monitor1([&] { for (unsigned long frame = 0; frame != compose_frames+3; frame++) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); auto buf = q.compositor_acquire(this); q.compositor_release(buf); if (frame == compose_frames) { // Tell the "client" to stop after compose_frames, but // don't stop rendering immediately to avoid blocking // if we rendered any twice done.store(true); } } }); auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); while (!done.load()) { auto handle = client_acquire_async(q); handle->wait_for(std::chrono::seconds(1)); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); client_frames++; } monitor1.stop(); // Roughly compose_frames == client_frames within 50% ASSERT_THAT(client_frames, Gt(compose_frames / 2)); ASSERT_THAT(client_frames, Lt(compose_frames * 3 / 2)); } /* Regression test LP: #1241369 / LP: #1241371 */ TEST_P(WithThreeOrMoreBuffers, slow_client_framerate_matches_compositor) { /* BufferQueue can only satify this for nbuffers >= 3 * since a client can only own up to nbuffers - 1 at any one time */ unsigned long client_frames = 0; unsigned long const compose_frames = 100; auto const frame_time = std::chrono::milliseconds(16); q.allow_framedropping(false); std::atomic done(false); std::mutex sync; mt::AutoJoinThread monitor1([&] { for (unsigned long frame = 0; frame != compose_frames+3; frame++) { std::this_thread::sleep_for(frame_time); sync.lock(); auto buf = q.compositor_acquire(this); q.compositor_release(buf); sync.unlock(); if (frame == compose_frames) { // Tell the "client" to stop after compose_frames, but // don't stop rendering immediately to avoid blocking // if we rendered any twice done.store(true); } } }); auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); while (!done.load()) { sync.lock(); sync.unlock(); auto handle = client_acquire_async(q); handle->wait_for(std::chrono::seconds(1)); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); std::this_thread::sleep_for(frame_time); handle->release_buffer(); client_frames++; } monitor1.stop(); // Roughly compose_frames == client_frames within 20% ASSERT_THAT(client_frames, Gt(compose_frames * 0.80f)); ASSERT_THAT(client_frames, Lt(compose_frames * 1.2f)); } TEST_P(WithAnyNumberOfBuffers, resize_affects_client_acquires_immediately) { for (int width = 1; width < 100; ++width) { const geom::Size expect_size{width, width * 2}; for (int subframe = 0; subframe < 3; ++subframe) { q.resize(expect_size); auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); auto buffer = handle->buffer(); ASSERT_THAT(expect_size, Eq(buffer->size())); handle->release_buffer(); auto comp_buffer = q.compositor_acquire(this); ASSERT_THAT(expect_size, Eq(comp_buffer->size())); q.compositor_release(comp_buffer); } } } namespace { int max_ownable_buffers(int nbuffers) { return (nbuffers == 1) ? 1 : nbuffers - 1; } } TEST_P(WithAnyNumberOfBuffers, compositor_acquires_resized_frames) { mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory); std::vector history; const int width0 = 123; const int height0 = 456; const int dx = 2; const int dy = -3; int width = width0; int height = height0; int const nbuffers_to_use = q.buffers_free_for_client(); ASSERT_THAT(nbuffers_to_use, Gt(0)); int max_buffers{max_ownable_buffers(nbuffers)}; for (int produce = 0; produce < max_buffers; ++produce) { geom::Size new_size{width, height}; width += dx; height += dy; q.resize(new_size); auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); history.emplace_back(handle->id()); auto buffer = handle->buffer(); ASSERT_THAT(buffer->size(), Eq(new_size)); handle->release_buffer(); } width = width0; height = height0; ASSERT_THAT(history.size(), Eq(max_buffers)); for (int consume = 0; consume < max_buffers; ++consume) { geom::Size expect_size{width, height}; width += dx; height += dy; auto buffer = q.compositor_acquire(this); // Verify the compositor gets resized buffers, eventually ASSERT_THAT(buffer->size(), Eq(expect_size)); // Verify the compositor gets buffers with *contents*, ie. that // they have not been resized prematurely and are empty. ASSERT_THAT(history[consume], Eq(buffer->id())); q.compositor_release(buffer); } // Verify the final buffer size sticks const geom::Size final_size{width - dx, height - dy}; for (int unchanging = 0; unchanging < 100; ++unchanging) { auto buffer = q.compositor_acquire(this); ASSERT_THAT(buffer->size(), Eq(final_size)); q.compositor_release(buffer); } } TEST_P(WithTwoOrMoreBuffers, framedropping_policy_never_drops_newest_frame) { // Regression test for LP: #1396006 mtd::MockFrameDroppingPolicyFactory policy_factory; mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory); auto first = client_acquire_sync(q); q.client_release(first); // Start rendering one (don't finish) auto d = q.compositor_acquire(nullptr); ASSERT_EQ(first, d.get()); auto second = client_acquire_sync(q); q.client_release(second); // Client waits for a new frame auto end = client_acquire_async(q); // Surface goes offscreen or occluded; trigger a timeout policy_factory.trigger_policies(); // If the queue is still willing to drop under these difficult // circumstances (and we don't mind if it doesn't), then ensure // it's never the newest frame that's been discarded. // That could be catastrophic as you never know if a client ever // will produce another frame. if (end->has_acquired_buffer()) ASSERT_NE(second, end->buffer()); q.compositor_release(d); } TEST_P(WithTwoOrMoreBuffers, framedropping_surface_never_drops_newest_frame) { // Second regression test for LP: #1396006, LP: #1379685 mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory); q.allow_framedropping(true); // Fill 'er up std::vector order; for (int f = 0; f < nbuffers; ++f) { auto b = client_acquire_sync(q); order.push_back(b); q.client_release(b); } // Composite all but one std::vector> compositing; for (int n = 0; n < nbuffers-1; ++n) { auto c = q.compositor_acquire(nullptr); compositing.push_back(c); ASSERT_EQ(order[n], c.get()); } // Ensure it's not the newest frame that gets dropped to satisfy the // client. auto end = client_acquire_async(q); // The queue could solve this problem a few ways. It might choose to // defer framedropping till it's safe, or even allocate additional // buffers. We don't care which, just verify it's not losing the // latest frame. Because the screen could be indefinitely out of date // if that happens... ASSERT_TRUE(!end->has_acquired_buffer() || end->buffer() != order.back()); } TEST_P(WithTwoOrMoreBuffers, uncomposited_client_swaps_when_policy_triggered) { mtd::MockFrameDroppingPolicyFactory policy_factory; mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory); for (int i = 0; i < max_ownable_buffers(nbuffers); i++) { auto client = client_acquire_sync(q); q.client_release(client); } auto handle = client_acquire_async(q); EXPECT_FALSE(handle->has_acquired_buffer()); policy_factory.trigger_policies(); EXPECT_TRUE(handle->has_acquired_buffer()); } TEST_P(WithTwoOrMoreBuffers, scaled_queue_still_follows_dropping_policy) { // Regression test for LP: #1475120 using namespace std::literals::chrono_literals; auto clock = std::make_shared(); auto constexpr framedrop_timeout = 10ms; mc::TimeoutFrameDroppingPolicyFactory policy_factory{ std::make_shared(clock), framedrop_timeout}; mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory); int const nframes = 100; q.set_scaling_delay(0); for (int i = 0; i < nframes; i++) { auto handle = client_acquire_async(q); // Advance time past the framedrop timeout. clock->advance_time(framedrop_timeout); clock->advance_time(1ms); // If we fail once we don't need to run the rest of the 100 iterations... ASSERT_TRUE(handle->has_acquired_buffer()); handle->release_buffer(); } } TEST_P(WithTwoOrMoreBuffers, partially_composited_client_swaps_when_policy_triggered) { mtd::MockFrameDroppingPolicyFactory policy_factory; mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory); for (int i = 0; i < max_ownable_buffers(nbuffers); i++) { auto client = client_acquire_sync(q); q.client_release(client); } /* Queue up two pending swaps */ auto first_swap = client_acquire_async(q); auto second_swap = client_acquire_async(q); ASSERT_FALSE(first_swap->has_acquired_buffer()); ASSERT_FALSE(second_swap->has_acquired_buffer()); q.compositor_acquire(nullptr); EXPECT_TRUE(first_swap->has_acquired_buffer()); EXPECT_FALSE(second_swap->has_acquired_buffer()); /* We have to release a client buffer here; framedropping or not, * a client can't have 2 buffers outstanding in the nbuffers = 2 case. */ first_swap->release_buffer(); policy_factory.trigger_policies(); EXPECT_TRUE(second_swap->has_acquired_buffer()); } TEST_F(BufferQueueWithOneBuffer, with_single_buffer_compositor_acquires_resized_frames_eventually) { geom::Size const new_size{123,456}; q.client_release(client_acquire_sync(q)); q.resize(new_size); auto const handle = client_acquire_async(q); EXPECT_THAT(handle->has_acquired_buffer(), Eq(false)); auto buf = q.compositor_acquire(this); q.compositor_release(buf); buf = q.compositor_acquire(this); EXPECT_THAT(buf->size(), Eq(new_size)); q.compositor_release(buf); } TEST_F(BufferQueueWithTwoBuffers, double_buffered_client_is_not_blocked_prematurely) { // Regression test for LP: #1319765 q.client_release(client_acquire_sync(q)); auto a = q.compositor_acquire(this); q.client_release(client_acquire_sync(q)); auto b = q.compositor_acquire(this); ASSERT_NE(a.get(), b.get()); q.compositor_release(a); q.client_release(client_acquire_sync(q)); q.compositor_release(b); /* * Update to the original test case; This additional compositor acquire * represents the fixing of LP: #1395581 in the compositor logic. */ if (q.buffers_ready_for_compositor(this)) q.compositor_release(q.compositor_acquire(this)); auto handle = client_acquire_async(q); // With the fix, a buffer will be available instantaneously: ASSERT_TRUE(handle->has_acquired_buffer()); handle->release_buffer(); } TEST_F(BufferQueueWithTwoBuffers, composite_on_demand_never_deadlocks_with_2_buffers) { // Extended regression test for LP: #1319765 for (int i = 0; i < 100; ++i) { auto x = client_acquire_async(q); ASSERT_TRUE(x->has_acquired_buffer()); x->release_buffer(); auto a = q.compositor_acquire(this); auto y = client_acquire_async(q); ASSERT_TRUE(y->has_acquired_buffer()); y->release_buffer(); auto b = q.compositor_acquire(this); ASSERT_NE(a.get(), b.get()); q.compositor_release(a); auto w = client_acquire_async(q); ASSERT_TRUE(w->has_acquired_buffer()); w->release_buffer(); q.compositor_release(b); /* * Update to the original test case; This additional compositor acquire * represents the fixing of LP: #1395581 in the compositor logic. */ if (q.buffers_ready_for_compositor(this)) q.compositor_release(q.compositor_acquire(this)); auto z = client_acquire_async(q); ASSERT_TRUE(z->has_acquired_buffer()); z->release_buffer(); q.compositor_release(q.compositor_acquire(this)); q.compositor_release(q.compositor_acquire(this)); } } TEST_P(WithTwoOrMoreBuffers, buffers_ready_is_not_underestimated) { // Regression test for LP: #1395581 mc::BufferQueue q{nbuffers, allocator, basic_properties, policy_factory}; // Produce frame 1 q.client_release(client_acquire_sync(q)); // Acquire frame 1 auto a = q.compositor_acquire(this); // Produce frame 2 q.client_release(client_acquire_sync(q)); // Acquire frame 2 auto b = q.compositor_acquire(this); // Release frame 1 q.compositor_release(a); // Produce frame 3 q.client_release(client_acquire_sync(q)); // Release frame 2 q.compositor_release(b); // Verify frame 3 is ready for the first compositor ASSERT_THAT(q.buffers_ready_for_compositor(this), Ge(1)); auto c = q.compositor_acquire(this); // Verify frame 3 is ready for a second compositor int const that = 0; ASSERT_THAT(q.buffers_ready_for_compositor(&that), Ge(1)); q.compositor_release(c); } TEST_P(WithTwoOrMoreBuffers, buffers_ready_count_tapers_off) { // Another test related to QtMir's style of doing things (LP: #1476201) mc::BufferQueue q{nbuffers, allocator, basic_properties, policy_factory}; q.set_scaling_delay(3); ASSERT_THAT(q.buffers_ready_for_compositor(this), Eq(0)); // Produce one frame q.client_release(client_acquire_sync(q)); // Ensure we're told multiple frames are ready to facilitate the // compositor detecting a slow client that misses the second one. ASSERT_THAT(q.buffers_ready_for_compositor(this), Ge(2)); // Consume one frame q.compositor_release(q.compositor_acquire(this)); // Finally verify the count is tapering off instead of dropping off ASSERT_THAT(q.buffers_ready_for_compositor(this), Ge(1)); for (int flush = 0; flush < q.scaling_delay(); ++flush) q.compositor_release(q.compositor_acquire(this)); ASSERT_THAT(q.buffers_ready_for_compositor(this), Eq(0)); } TEST_P(WithTwoOrMoreBuffers, buffers_ready_eventually_reaches_zero) { mc::BufferQueue q{nbuffers, allocator, basic_properties, policy_factory}; const int nmonitors = 3; int monitor[nmonitors]; for (int m = 0; m < nmonitors; ++m) { ASSERT_EQ(0, q.buffers_ready_for_compositor(&monitor[m])); } // Produce a frame q.client_release(client_acquire_sync(q)); // Consume for (int m = 0; m < nmonitors; ++m) { ASSERT_NE(0, q.buffers_ready_for_compositor(&monitor[m])); // Extra consume to account for the additional frames that // buffers_ready_for_compositor adds to do dynamic performance // detection. int const nflush = (q.scaling_delay() > 0) ? q.scaling_delay() : 1; for (int flush = 0; flush < nflush; ++flush) q.compositor_release(q.compositor_acquire(&monitor[m])); ASSERT_EQ(0, q.buffers_ready_for_compositor(&monitor[m])); } } /* Regression test for LP: #1306464 */ TEST_F(BufferQueueWithThreeBuffers, framedropping_client_acquire_does_not_block_when_no_available_buffers) { q.allow_framedropping(true); std::vector> buffers; /* The client can never own this acquired buffer */ auto comp_buffer = q.compositor_acquire(this); buffers.push_back(comp_buffer); /* Let client release all possible buffers so they go into * the ready queue */ for (int i = 0; i < nbuffers; ++i) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); /* Check the client never got the compositor buffer acquired above */ ASSERT_THAT(handle->id(), Ne(comp_buffer->id())); handle->release_buffer(); } /* Let the compositor acquire all ready buffers */ for (int i = 0; i < nbuffers; ++i) { buffers.push_back(q.compositor_acquire(this)); } /* At this point the queue has 0 free buffers and 0 ready buffers * so the next client request should not be satisfied until * a compositor releases its buffers */ auto handle = client_acquire_async(q); /* ... unless the BufferQueue is overallocating. In that case it will * have succeeding in acquiring immediately. */ if (!handle->has_acquired_buffer()) { /* Release compositor buffers so that the client can get one */ for (auto const& buffer : buffers) { q.compositor_release(buffer); } EXPECT_THAT(handle->has_acquired_buffer(), Eq(true)); } } TEST_P(WithTwoOrMoreBuffers, compositor_never_owns_client_buffers) { static std::chrono::nanoseconds const time_for_client_to_acquire{1}; mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory); std::mutex client_buffer_guard; std::condition_variable client_buffer_cv; mg::Buffer* client_buffer = nullptr; std::atomic done(false); auto unblock = [&done]{ done = true; }; mt::AutoUnblockThread unthrottled_compositor_thread(unblock, [&] { while (!done) { auto buffer = q.compositor_acquire(this); { std::unique_lock lock(client_buffer_guard); if (client_buffer_cv.wait_for( lock, time_for_client_to_acquire, [&]()->bool{ return client_buffer; })) { ASSERT_THAT(buffer->id(), Ne(client_buffer->id())); } } std::this_thread::yield(); q.compositor_release(buffer); } }); for (int i = 0; i < 1000; ++i) { auto handle = client_acquire_async(q); handle->wait_for(std::chrono::seconds(1)); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); { std::lock_guard lock(client_buffer_guard); client_buffer = handle->buffer(); client_buffer_cv.notify_one(); } std::this_thread::yield(); std::lock_guard lock(client_buffer_guard); handle->release_buffer(); client_buffer = nullptr; } } TEST_P(WithTwoOrMoreBuffers, client_never_owns_compositor_buffers) { mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory); for (int i = 0; i < 100; ++i) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); auto client_id = handle->id(); std::vector> buffers; for (int j = 0; j < nbuffers; j++) { auto buffer = q.compositor_acquire(this); ASSERT_THAT(client_id, Ne(buffer->id())); buffers.push_back(buffer); } for (auto const& buffer: buffers) q.compositor_release(buffer); handle->release_buffer(); /* Flush out one ready buffer */ auto buffer = q.compositor_acquire(this); ASSERT_THAT(client_id, Eq(buffer->id())); q.compositor_release(buffer); } } /* Regression test for an issue brought up at: * http://code.launchpad.net/~albaguirre/mir/ * alternative-switching-bundle-implementation/+merge/216606/comments/517048 */ TEST_P(WithThreeOrMoreBuffers, buffers_are_not_lost) { // This test is technically not valid with dynamic queue scaling on q.set_scaling_delay(-1); void const* main_compositor = reinterpret_cast(0); void const* second_compositor = reinterpret_cast(1); /* Hold a reference to current compositor buffer*/ auto comp_buffer1 = q.compositor_acquire(main_compositor); int const prefill = q.buffers_free_for_client(); ASSERT_THAT(prefill, Gt(0)); for (int acquires = 0; acquires < prefill; ++acquires) { auto handle = client_acquire_async(q); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); handle->release_buffer(); } /* Have a second compositor advance the current compositor buffer at least twice */ for (int acquires = 0; acquires < nbuffers; ++acquires) { auto comp_buffer = q.compositor_acquire(second_compositor); q.compositor_release(comp_buffer); } q.compositor_release(comp_buffer1); /* An async client should still be able to cycle through all the available buffers */ std::atomic done(false); auto unblock = [&done] { done = true; }; mt::AutoUnblockThread compositor(unblock, unthrottled_compositor_thread, std::ref(q), std::ref(done)); std::unordered_set unique_buffers_acquired; int const max_ownable_buffers = nbuffers - 1; for (int frame = 0; frame < max_ownable_buffers*2; frame++) { std::vector client_buffers; for (int acquires = 0; acquires < max_ownable_buffers; ++acquires) { auto handle = client_acquire_async(q); handle->wait_for(std::chrono::seconds(1)); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); unique_buffers_acquired.insert(handle->buffer()); client_buffers.push_back(handle->buffer()); } for (auto const& buffer : client_buffers) { q.client_release(buffer); } } EXPECT_THAT(unique_buffers_acquired.size(), Eq(nbuffers)); } // Test that dynamic queue scaling/throttling actually works TEST_P(WithThreeOrMoreBuffers, queue_size_scales_with_client_performance) { q.allow_framedropping(false); std::unordered_set buffers_acquired; int const delay = 3; q.set_scaling_delay(delay); int const nframes = 100; std::shared_ptr client; for (int frame = 0; frame < nframes; ++frame) { do { if (!client) client = client_acquire_async(q); if (client->has_acquired_buffer()) { if (frame > delay) buffers_acquired.insert(client->buffer()); client->release_buffer(); client.reset(); } } while (!client); q.compositor_release(q.compositor_acquire(nullptr)); } // Expect double-buffers for fast clients EXPECT_THAT(buffers_acquired.size(), Eq(2)); // Now check what happens if the client becomes slow... buffers_acquired.clear(); for (int frame = 0; frame < nframes;) { do { if (!client) client = client_acquire_async(q); if (client->has_acquired_buffer()) { if (frame > delay) buffers_acquired.insert(client->buffer()); client->release_buffer(); client.reset(); } } while (!client); // Imbalance: Compositor is now requesting more than the client does: int nready = q.buffers_ready_for_compositor(nullptr); for (int r = 0; r <= nready; ++r) { q.compositor_release(q.compositor_acquire(nullptr)); ++frame; } } // Expect at least triple buffers for sluggish clients EXPECT_THAT(buffers_acquired.size(), Ge(3)); // And what happens if the client becomes fast again?... buffers_acquired.clear(); for (int frame = 0; frame < nframes; ++frame) { do { if (!client) client = client_acquire_async(q); if (client->has_acquired_buffer()) { if (frame > delay) buffers_acquired.insert(client->buffer()); client->release_buffer(); client.reset(); } } while (!client); q.compositor_release(q.compositor_acquire(nullptr)); } // Expect double-buffers for fast clients EXPECT_THAT(buffers_acquired.size(), Eq(2)); } TEST_P(WithThreeOrMoreBuffers, queue_size_scales_up_without_accumulator) { // A regression test similar to above but designed to mimic QtMir // for LP: #1476201. q.allow_framedropping(false); std::unordered_set buffers_acquired; int const delay = 3; q.set_scaling_delay(delay); int const nframes = 100; std::shared_ptr client; for (int frame = 0; frame < nframes; ++frame) { do { if (!client) client = client_acquire_async(q); if (client->has_acquired_buffer()) { if (frame > delay) buffers_acquired.insert(client->buffer()); client->release_buffer(); client.reset(); } } while (!client); q.compositor_release(q.compositor_acquire(nullptr)); } // Expect double-buffers for fast clients EXPECT_THAT(buffers_acquired.size(), Eq(2)); // Now check what happens if the client becomes slow... buffers_acquired.clear(); for (int frame = 0; frame < nframes;) { do { if (!client) client = client_acquire_async(q); if (client->has_acquired_buffer()) { if (frame > delay) buffers_acquired.insert(client->buffer()); client->release_buffer(); client.reset(); } } while (!client); // Mimic QtMir in that it tests buffers ready as a boolean and does // not keep its own accumulator in the compositor: while (q.buffers_ready_for_compositor(nullptr)) { q.compositor_release(q.compositor_acquire(nullptr)); ++frame; } } // Expect at least triple buffers for sluggish clients EXPECT_THAT(buffers_acquired.size(), Ge(3)); // And what happens if the client becomes fast again?... buffers_acquired.clear(); for (int frame = 0; frame < nframes; ++frame) { do { if (!client) client = client_acquire_async(q); if (client->has_acquired_buffer()) { if (frame > delay) buffers_acquired.insert(client->buffer()); client->release_buffer(); client.reset(); } } while (!client); q.compositor_release(q.compositor_acquire(nullptr)); } // Expect double-buffers for fast clients EXPECT_THAT(buffers_acquired.size(), Eq(2)); } TEST_P(WithThreeOrMoreBuffers, greedy_compositors_need_triple_buffers) { /* * "Greedy" compositors means those that can hold multiple buffers from * the same client simultaneously or a single buffer for a long time. * This usually means bypass/overlays, but can also mean multi-monitor. */ q.allow_framedropping(false); std::atomic done(false); auto unblock = [&done] { done = true; }; mt::AutoUnblockThread compositor(unblock, overlapping_compositor_thread, std::ref(q), std::ref(done)); std::unordered_set buffers_acquired; int const delay = q.scaling_delay(); for (int frame = 0; frame < delay+10; frame++) { auto handle = client_acquire_async(q); handle->wait_for(std::chrono::seconds(1)); ASSERT_THAT(handle->has_acquired_buffer(), Eq(true)); if (frame > delay) buffers_acquired.insert(handle->buffer()); handle->release_buffer(); } // Expect triple buffers for the whole time EXPECT_THAT(buffers_acquired.size(), Ge(3)); } TEST_P(WithTwoOrMoreBuffers, compositor_double_rate_of_slow_client) { mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory); q.allow_framedropping(false); q.set_scaling_delay(3); for (int frame = 0; frame < 10; frame++) { ASSERT_EQ(0, q.buffers_ready_for_compositor(this)); q.client_release(client_acquire_sync(q)); // Detecting a slow client requires scheduling extra frames int nready = q.buffers_ready_for_compositor(this); ASSERT_THAT(nready, Ge(2)); for (int i = 0; i < nready; ++i) q.compositor_release(q.compositor_acquire(this)); ASSERT_EQ(0, q.buffers_ready_for_compositor(this)); } } /* * This is a regression test for bug lp:1317801. This bug is a race and * very difficult to reproduce with pristine code. By carefully placing * a delay in the code, we can greatly increase the chances (100% for me) * that this test catches a regression. However these delays are not * acceptable for production use, so since the test and code in their * pristine state are highly unlikely to catch the issue, I have decided * to DISABLE the test to avoid giving a false sense of security. * * Apply the aforementioned delay, by adding * std::this_thread::sleep_for(std::chrono::milliseconds{20}) * just before returning the acquired_buffer at the end of * BufferQueue::compositor_acquire(). */ TEST_F(BufferQueueWithThreeBuffers, DISABLED_lp_1317801_regression_test) { q.client_release(client_acquire_sync(q)); mt::AutoJoinThread t{ [&] { /* Use in conjuction with a 20ms delay in compositor_acquire() */ std::this_thread::sleep_for(std::chrono::milliseconds{10}); q.client_release(client_acquire_sync(q)); q.client_release(client_acquire_sync(q)); }}; auto b = q.compositor_acquire(this); q.compositor_release(b); } TEST_P(WithAnyNumberOfBuffers, first_user_is_recorded) { mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory); auto comp = q.compositor_acquire(this); EXPECT_TRUE(q.is_a_current_buffer_user(this)); q.compositor_release(comp); } TEST_F(BufferQueueWithThreeBuffers, gives_compositor_a_valid_buffer_after_dropping_old_buffers_without_clients) { q.drop_old_buffers(); auto comp = q.compositor_acquire(this); ASSERT_THAT(comp, Ne(nullptr)); } TEST_F(BufferQueueWithThreeBuffers, gives_compositor_the_newest_buffer_after_dropping_old_buffers) { auto handle1 = client_acquire_async(q); ASSERT_THAT(handle1->has_acquired_buffer(), Eq(true)); handle1->release_buffer(); auto handle2 = client_acquire_async(q); ASSERT_THAT(handle2->has_acquired_buffer(), Eq(true)); handle2->release_buffer(); q.drop_old_buffers(); auto comp = q.compositor_acquire(this); ASSERT_THAT(comp->id(), Eq(handle2->id())); q.compositor_release(comp); comp = q.compositor_acquire(this); ASSERT_THAT(comp->id(), Eq(handle2->id())); } TEST_F(BufferQueueWithThreeBuffers, gives_new_compositor_the_newest_buffer_after_dropping_old_buffers) { void const* const new_compositor_id{&nbuffers}; auto handle1 = client_acquire_async(q); ASSERT_THAT(handle1->has_acquired_buffer(), Eq(true)); handle1->release_buffer(); auto comp = q.compositor_acquire(this); ASSERT_THAT(comp->id(), Eq(handle1->id())); q.compositor_release(comp); auto handle2 = client_acquire_async(q); ASSERT_THAT(handle2->has_acquired_buffer(), Eq(true)); handle2->release_buffer(); q.drop_old_buffers(); auto comp2 = q.compositor_acquire(new_compositor_id); ASSERT_THAT(comp2->id(), Eq(handle2->id())); } INSTANTIATE_TEST_CASE_P( BufferQueue, WithAnyNumberOfBuffers, Range(1, max_nbuffers_to_test)); INSTANTIATE_TEST_CASE_P( BufferQueue, WithTwoOrMoreBuffers, Range(2, max_nbuffers_to_test)); INSTANTIATE_TEST_CASE_P( BufferQueue, WithThreeOrMoreBuffers, Range(3, max_nbuffers_to_test)); ./tests/unit-tests/compositor/test_multi_monitor_arbiter.cpp0000644000015600001650000004320612676616157024756 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/mock_event_sink.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "src/server/compositor/multi_monitor_arbiter.h" #include "src/server/compositor/schedule.h" #include "mir/frontend/client_buffers.h" #include using namespace testing; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace mf = mir::frontend; namespace { struct MockBufferMap : mf::ClientBuffers { MOCK_METHOD1(add_buffer, mg::BufferID(mg::BufferProperties const&)); MOCK_METHOD1(remove_buffer, void(mg::BufferID id)); MOCK_METHOD1(receive_buffer, void(mg::BufferID id)); MOCK_METHOD1(send_buffer, void(mg::BufferID id)); MOCK_METHOD1(at, std::shared_ptr&(mg::BufferID)); MOCK_CONST_METHOD0(client_owned_buffer_count, size_t()); std::shared_ptr& operator[](mg::BufferID id) { return at(id); } }; struct FixedSchedule : mc::Schedule { void schedule(std::shared_ptr const&) { throw std::runtime_error("this stub doesnt support this"); } void cancel(std::shared_ptr const&) { throw std::runtime_error("this stub doesnt support this"); } unsigned int num_scheduled() { return sched.size() - current; } std::shared_ptr next_buffer() { if (sched.empty() || current == sched.size()) throw std::runtime_error("no buffer scheduled"); return sched[current++]; } void set_schedule(std::vector> s) { current = 0; sched = s; } private: unsigned int current{0}; std::vector> sched; }; struct MultiMonitorArbiterBase : Test { MultiMonitorArbiterBase() { for(auto i = 0u; i < num_buffers; i++) buffers.emplace_back(std::make_shared()); } unsigned int const num_buffers{6u}; std::vector> buffers; NiceMock mock_map; FixedSchedule schedule; }; struct MultiMonitorArbiter : MultiMonitorArbiterBase { mc::MultiMonitorMode guarantee{mc::MultiMonitorMode::multi_monitor_sync}; mc::MultiMonitorArbiter arbiter{guarantee, mt::fake_shared(mock_map), mt::fake_shared(schedule)}; }; struct MultiMonitorArbiterWithAnyFrameGuarantee : MultiMonitorArbiterBase { mc::MultiMonitorMode guarantee{mc::MultiMonitorMode::single_monitor_fast}; mc::MultiMonitorArbiter arbiter{guarantee, mt::fake_shared(mock_map), mt::fake_shared(schedule)}; }; } TEST_F(MultiMonitorArbiter, compositor_access_before_any_submission_throws) { //nothing owned EXPECT_THROW({ arbiter.compositor_acquire(this); }, std::logic_error); schedule.set_schedule({buffers[0]}); //something scheduled, should be ok arbiter.compositor_acquire(this); } TEST_F(MultiMonitorArbiter, compositor_access) { schedule.set_schedule({buffers[0]}); auto cbuffer = arbiter.compositor_acquire(this); EXPECT_THAT(cbuffer, Eq(buffers[0])); } TEST_F(MultiMonitorArbiterWithAnyFrameGuarantee, compositor_release_sends_buffer_back_with_any_monitor_guarantee) { EXPECT_CALL(mock_map, send_buffer(buffers[0]->id())); schedule.set_schedule({buffers[0]}); auto cbuffer = arbiter.compositor_acquire(this); schedule.set_schedule({buffers[1]}); arbiter.compositor_release(cbuffer); } TEST_F(MultiMonitorArbiterWithAnyFrameGuarantee, compositor_can_acquire_different_buffers) { EXPECT_CALL(mock_map, send_buffer(buffers[0]->id())); schedule.set_schedule({buffers[0]}); auto cbuffer1 = arbiter.compositor_acquire(this); schedule.set_schedule({buffers[1]}); auto cbuffer2 = arbiter.compositor_acquire(this); EXPECT_THAT(cbuffer1, Ne(cbuffer2)); arbiter.compositor_release(cbuffer2); arbiter.compositor_release(cbuffer1); } TEST_F(MultiMonitorArbiterWithAnyFrameGuarantee, compositor_buffer_syncs_to_fastest_compositor) { int comp_id1{0}; int comp_id2{0}; schedule.set_schedule({buffers[0]}); auto cbuffer1 = arbiter.compositor_acquire(&comp_id1); auto cbuffer2 = arbiter.compositor_acquire(&comp_id2); schedule.set_schedule({buffers[1]}); auto cbuffer3 = arbiter.compositor_acquire(&comp_id1); schedule.set_schedule({buffers[0]}); auto cbuffer4 = arbiter.compositor_acquire(&comp_id1); auto cbuffer5 = arbiter.compositor_acquire(&comp_id2); schedule.set_schedule({buffers[1]}); auto cbuffer6 = arbiter.compositor_acquire(&comp_id2); auto cbuffer7 = arbiter.compositor_acquire(&comp_id2); EXPECT_THAT(cbuffer1, Eq(buffers[0])); EXPECT_THAT(cbuffer2, Eq(buffers[0])); EXPECT_THAT(cbuffer3, Eq(buffers[1])); EXPECT_THAT(cbuffer4, Eq(buffers[0])); EXPECT_THAT(cbuffer5, Eq(buffers[0])); EXPECT_THAT(cbuffer6, Eq(buffers[1])); EXPECT_THAT(cbuffer7, Eq(buffers[1])); } TEST_F(MultiMonitorArbiter, compositor_consumes_all_buffers_when_operating_as_a_composited_scene_would) { schedule.set_schedule({buffers[0],buffers[1],buffers[2],buffers[3],buffers[4]}); auto cbuffer1 = arbiter.compositor_acquire(this); arbiter.compositor_release(cbuffer1); auto cbuffer2 = arbiter.compositor_acquire(this); arbiter.compositor_release(cbuffer2); auto cbuffer3 = arbiter.compositor_acquire(this); arbiter.compositor_release(cbuffer3); auto cbuffer4 = arbiter.compositor_acquire(this); arbiter.compositor_release(cbuffer4); auto cbuffer5 = arbiter.compositor_acquire(this); arbiter.compositor_release(cbuffer5); EXPECT_THAT(cbuffer1, Eq(buffers[0])); EXPECT_THAT(cbuffer2, Eq(buffers[1])); EXPECT_THAT(cbuffer3, Eq(buffers[2])); EXPECT_THAT(cbuffer4, Eq(buffers[3])); EXPECT_THAT(cbuffer5, Eq(buffers[4])); } TEST_F(MultiMonitorArbiter, compositor_consumes_all_buffers_when_operating_as_a_bypassed_buffer_would) { schedule.set_schedule({buffers[0],buffers[1],buffers[2],buffers[3],buffers[4]}); auto cbuffer1 = arbiter.compositor_acquire(this); auto cbuffer2 = arbiter.compositor_acquire(this); arbiter.compositor_release(cbuffer1); auto cbuffer3 = arbiter.compositor_acquire(this); arbiter.compositor_release(cbuffer2); auto cbuffer4 = arbiter.compositor_acquire(this); arbiter.compositor_release(cbuffer3); auto cbuffer5 = arbiter.compositor_acquire(this); arbiter.compositor_release(cbuffer4); arbiter.compositor_release(cbuffer5); EXPECT_THAT(cbuffer1, Eq(buffers[0])); EXPECT_THAT(cbuffer2, Eq(buffers[1])); EXPECT_THAT(cbuffer3, Eq(buffers[2])); EXPECT_THAT(cbuffer4, Eq(buffers[3])); EXPECT_THAT(cbuffer5, Eq(buffers[4])); } TEST_F(MultiMonitorArbiter, multimonitor_compositor_buffer_syncs_to_fastest_with_more_queueing) { int comp_id1{0}; int comp_id2{0}; schedule.set_schedule({buffers[0],buffers[1],buffers[2],buffers[3],buffers[4]}); auto cbuffer1 = arbiter.compositor_acquire(&comp_id1); //buffer[0] auto cbuffer2 = arbiter.compositor_acquire(&comp_id2); //buffer[0] auto cbuffer3 = arbiter.compositor_acquire(&comp_id1); //buffer[1] auto cbuffer4 = arbiter.compositor_acquire(&comp_id1); //buffer[2] auto cbuffer5 = arbiter.compositor_acquire(&comp_id2); //buffer[2] auto cbuffer6 = arbiter.compositor_acquire(&comp_id2); //buffer[3] auto cbuffer7 = arbiter.compositor_acquire(&comp_id2); //buffer[4] auto cbuffer8 = arbiter.compositor_acquire(&comp_id1); //buffer[4] EXPECT_THAT(cbuffer1, Eq(buffers[0])); EXPECT_THAT(cbuffer2, Eq(buffers[0])); EXPECT_THAT(cbuffer3, Eq(buffers[1])); EXPECT_THAT(cbuffer4, Eq(buffers[2])); EXPECT_THAT(cbuffer5, Eq(buffers[2])); EXPECT_THAT(cbuffer6, Eq(buffers[3])); EXPECT_THAT(cbuffer7, Eq(buffers[4])); EXPECT_THAT(cbuffer8, Eq(buffers[4])); } TEST_F(MultiMonitorArbiter, can_set_a_new_schedule) { FixedSchedule another_schedule; schedule.set_schedule({buffers[3],buffers[4]}); another_schedule.set_schedule({buffers[0],buffers[1]}); auto cbuffer1 = arbiter.compositor_acquire(this); arbiter.set_schedule(mt::fake_shared(another_schedule)); auto cbuffer2 = arbiter.compositor_acquire(this); EXPECT_THAT(cbuffer1, Eq(buffers[3])); EXPECT_THAT(cbuffer2, Eq(buffers[0])); } TEST_F(MultiMonitorArbiter, basic_snapshot_equals_compositor_buffer) { schedule.set_schedule({buffers[3],buffers[4]}); auto cbuffer1 = arbiter.compositor_acquire(this); auto sbuffer1 = arbiter.snapshot_acquire(); EXPECT_EQ(cbuffer1, sbuffer1); } TEST_F(MultiMonitorArbiter, basic_snapshot_equals_latest_compositor_buffer) { schedule.set_schedule({buffers[3],buffers[4]}); int that = 4; auto cbuffer1 = arbiter.compositor_acquire(this); auto cbuffer2 = arbiter.compositor_acquire(&that); auto sbuffer1 = arbiter.snapshot_acquire(); arbiter.snapshot_release(sbuffer1); arbiter.compositor_release(cbuffer2); cbuffer2 = arbiter.compositor_acquire(&that); auto sbuffer2 = arbiter.snapshot_acquire(); EXPECT_EQ(cbuffer1, sbuffer1); EXPECT_EQ(cbuffer2, sbuffer2); } TEST_F(MultiMonitorArbiter, snapshot_cycling_doesnt_advance_buffer_for_compositors) { schedule.set_schedule({buffers[3],buffers[4]}); auto that = 4; auto a_few_times = 5u; auto cbuffer1 = arbiter.compositor_acquire(this); std::vector> snapshot_buffers(a_few_times); for(auto i = 0u; i < a_few_times; i++) { auto b = arbiter.snapshot_acquire(); arbiter.snapshot_release(b); snapshot_buffers[i] = b; } auto cbuffer2 = arbiter.compositor_acquire(&that); EXPECT_THAT(cbuffer1, Eq(cbuffer2)); EXPECT_THAT(snapshot_buffers, Each(cbuffer1)); } TEST_F(MultiMonitorArbiter, no_buffers_available_throws_on_snapshot) { schedule.set_schedule({}); EXPECT_THROW({ arbiter.snapshot_acquire(); }, std::logic_error); } TEST_F(MultiMonitorArbiter, snapshotting_will_release_buffer_if_it_was_the_last_owner) { EXPECT_CALL(mock_map, send_buffer(_)).Times(0); schedule.set_schedule({buffers[3],buffers[4]}); auto cbuffer1 = arbiter.compositor_acquire(this); auto sbuffer1 = arbiter.snapshot_acquire(); arbiter.compositor_release(cbuffer1); Mock::VerifyAndClearExpectations(&mock_map); EXPECT_CALL(mock_map, send_buffer(sbuffer1->id())); arbiter.snapshot_release(sbuffer1); } TEST_F(MultiMonitorArbiterWithAnyFrameGuarantee, compositor_can_acquire_a_few_times_and_only_sends_on_the_last_release) { int comp_id1{0}; int comp_id2{0}; schedule.set_schedule({buffers[0], buffers[1]}); auto cbuffer1 = arbiter.compositor_acquire(&comp_id1); auto cbuffer2 = arbiter.compositor_acquire(&comp_id2); EXPECT_THAT(cbuffer1, Eq(cbuffer2)); EXPECT_CALL(mock_map, send_buffer(buffers[0]->id())).Times(Exactly(1)); auto cbuffer3 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(cbuffer2); arbiter.compositor_release(cbuffer1); } TEST_F(MultiMonitorArbiter, advance_on_fastest_has_same_buffer) { int comp_id1{0}; int comp_id2{0}; schedule.set_schedule({buffers[0],buffers[1]}); auto cbuffer1 = arbiter.compositor_acquire(&comp_id1); //buffer[0] arbiter.compositor_release(cbuffer1); auto cbuffer2 = arbiter.compositor_acquire(&comp_id2); //buffer[0] arbiter.compositor_release(cbuffer2); auto cbuffer3 = arbiter.compositor_acquire(&comp_id1); //buffer[1] EXPECT_THAT(cbuffer1, Eq(cbuffer2)); EXPECT_THAT(cbuffer1, Eq(buffers[0])); EXPECT_THAT(cbuffer3, Eq(buffers[1])); } TEST_F(MultiMonitorArbiter, compositor_acquire_sends_buffer_back_with_fastest_guarantee) { EXPECT_CALL(mock_map, send_buffer(buffers[0]->id())); schedule.set_schedule({buffers[0], buffers[1]}); auto cbuffer = arbiter.compositor_acquire(this); schedule.set_schedule({buffers[1]}); arbiter.compositor_release(cbuffer); cbuffer = arbiter.compositor_acquire(this); } TEST_F(MultiMonitorArbiter, buffers_are_sent_back) { EXPECT_CALL(mock_map, send_buffer(_)).Times(3); int comp_id1{0}; int comp_id2{0}; schedule.set_schedule({buffers[0], buffers[1], buffers[2], buffers[3]}); auto b1 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(b1); auto b2 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(b2); auto b3 = arbiter.compositor_acquire(&comp_id1); auto b5 = arbiter.compositor_acquire(&comp_id2); arbiter.compositor_release(b3); auto b4 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(b5); arbiter.compositor_release(b4); auto b6 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(b6); } TEST_F(MultiMonitorArbiter, can_check_if_buffers_are_ready) { int comp_id1{0}; int comp_id2{0}; schedule.set_schedule({buffers[3]}); EXPECT_TRUE(arbiter.buffer_ready_for(&comp_id1)); EXPECT_TRUE(arbiter.buffer_ready_for(&comp_id2)); auto b1 = arbiter.compositor_acquire(&comp_id1); EXPECT_FALSE(arbiter.buffer_ready_for(&comp_id1)); EXPECT_TRUE(arbiter.buffer_ready_for(&comp_id2)); arbiter.compositor_release(b1); auto b2 = arbiter.compositor_acquire(&comp_id2); EXPECT_FALSE(arbiter.buffer_ready_for(&comp_id1)); EXPECT_FALSE(arbiter.buffer_ready_for(&comp_id2)); arbiter.compositor_release(b2); } TEST_F(MultiMonitorArbiter, other_compositor_ready_status_advances_with_fastest_compositor) { int comp_id1{0}; int comp_id2{0}; schedule.set_schedule({buffers[0], buffers[1], buffers[2]}); EXPECT_TRUE(arbiter.buffer_ready_for(&comp_id1)); EXPECT_TRUE(arbiter.buffer_ready_for(&comp_id2)); auto b1 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(b1); EXPECT_TRUE(arbiter.buffer_ready_for(&comp_id1)); EXPECT_TRUE(arbiter.buffer_ready_for(&comp_id2)); b1 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(b1); EXPECT_TRUE(arbiter.buffer_ready_for(&comp_id1)); EXPECT_TRUE(arbiter.buffer_ready_for(&comp_id2)); b1 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(b1); EXPECT_FALSE(arbiter.buffer_ready_for(&comp_id1)); EXPECT_TRUE(arbiter.buffer_ready_for(&comp_id2)); b1 = arbiter.compositor_acquire(&comp_id2); arbiter.compositor_release(b1); EXPECT_FALSE(arbiter.buffer_ready_for(&comp_id1)); EXPECT_FALSE(arbiter.buffer_ready_for(&comp_id2)); } TEST_F(MultiMonitorArbiter, will_release_buffer_in_nbuffers_2_overlay_scenario) { int comp_id1{0}; schedule.set_schedule({buffers[0], buffers[1], buffers[0], buffers[1]}); EXPECT_CALL(mock_map, send_buffer(buffers[0]->id())); auto b1 = arbiter.compositor_acquire(&comp_id1); auto b2 = arbiter.compositor_acquire(&comp_id1); EXPECT_THAT(b1, Eq(buffers[0])); EXPECT_THAT(b2, Eq(buffers[1])); arbiter.compositor_release(b1); arbiter.compositor_release(b2); } TEST_F(MultiMonitorArbiter, will_release_buffer_in_nbuffers_2_starvation_scenario) { int comp_id1{0}; int comp_id2{0}; schedule.set_schedule({buffers[0], buffers[1], buffers[0], buffers[1]}); auto b1 = arbiter.compositor_acquire(&comp_id1); auto b2 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(b1); auto b3 = arbiter.compositor_acquire(&comp_id2); auto b4 = arbiter.compositor_acquire(&comp_id2); arbiter.compositor_release(b3); arbiter.compositor_release(b2); arbiter.compositor_release(b4); EXPECT_THAT(b1, Eq(buffers[0])); EXPECT_THAT(b2, Eq(buffers[1])); EXPECT_THAT(b3, Eq(buffers[1])); EXPECT_THAT(b4, Eq(buffers[0])); } TEST_F(MultiMonitorArbiter, will_ensure_smooth_monitor_production) { int comp_id1{0}; int comp_id2{0}; schedule.set_schedule({ buffers[0], buffers[1], buffers[2], buffers[0], buffers[1], buffers[2], buffers[0], buffers[1], buffers[2]}); EXPECT_CALL(mock_map, send_buffer(buffers[0]->id())); auto b1 = arbiter.compositor_acquire(&comp_id1); auto b2 = arbiter.compositor_acquire(&comp_id2); arbiter.compositor_release(b1); //send nothing auto b3 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(b3); //send nothing auto b4 = arbiter.compositor_acquire(&comp_id2); arbiter.compositor_release(b2); //send 0 auto b5 = arbiter.compositor_acquire(&comp_id1); arbiter.compositor_release(b5); //send nothing EXPECT_THAT(b1, Eq(buffers[0])); EXPECT_THAT(b2, Eq(buffers[0])); EXPECT_THAT(b3, Eq(buffers[1])); EXPECT_THAT(b4, Eq(buffers[1])); EXPECT_THAT(b5, Eq(buffers[2])); } TEST_F(MultiMonitorArbiter, can_advance_buffer_manually) { int comp_id1{0}; int comp_id2{0}; schedule.set_schedule({buffers[0], buffers[1], buffers[2]}); arbiter.advance_schedule(); arbiter.advance_schedule(); auto b1 = arbiter.compositor_acquire(&comp_id1); auto b2 = arbiter.compositor_acquire(&comp_id2); EXPECT_THAT(b1->id(), Eq(buffers[1]->id())); EXPECT_THAT(b2->id(), Eq(buffers[1]->id())); auto b3 = arbiter.compositor_acquire(&comp_id1); EXPECT_THAT(b3->id(), Eq(buffers[2]->id())); } ./tests/unit-tests/compositor/test_queueing_schedule.cpp0000644000015600001650000000473412676616125024041 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/server/compositor/queueing_schedule.h" #include "mir/test/doubles/stub_buffer.h" #include #include using namespace testing; namespace mtd = mir::test::doubles; namespace mg = mir::graphics; namespace mc = mir::compositor; namespace { struct QueueingSchedule : Test { QueueingSchedule() { for(auto i = 0u; i < num_buffers; i++) buffers.emplace_back(std::make_shared()); } unsigned int const num_buffers{5}; std::vector> buffers; mc::QueueingSchedule schedule; std::vector> drain_queue() { std::vector> scheduled_buffers; while(schedule.num_scheduled()) scheduled_buffers.emplace_back(schedule.next_buffer()); return scheduled_buffers; } }; } TEST_F(QueueingSchedule, throws_if_no_buffers) { EXPECT_FALSE(schedule.num_scheduled()); EXPECT_THROW({ schedule.next_buffer(); }, std::logic_error); } TEST_F(QueueingSchedule, queues_buffers_up) { EXPECT_FALSE(schedule.num_scheduled()); std::vector> scheduled_buffers { buffers[1], buffers[3], buffers[0], buffers[2], buffers[4] }; for (auto& buffer : scheduled_buffers) schedule.schedule(buffer); EXPECT_TRUE(schedule.num_scheduled()); EXPECT_THAT(drain_queue(), ContainerEq(scheduled_buffers)); EXPECT_FALSE(schedule.num_scheduled()); } TEST_F(QueueingSchedule, queuing_the_same_buffer_moves_it_to_front_of_queue) { for(auto i = 0u; i < num_buffers; i++) schedule.schedule(buffers[i]); schedule.schedule(buffers[0]); EXPECT_THAT(drain_queue(), ElementsAre(buffers[1], buffers[2], buffers[3], buffers[4], buffers[0])); } ./tests/unit-tests/compositor/test_stream.cpp0000644000015600001650000003026112676616125021630 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/mock_event_sink.h" #include "mir/test/doubles/mock_frame_dropping_policy_factory.h" #include "mir/test/fake_shared.h" #include "src/server/compositor/stream.h" #include "mir/scene/null_surface_observer.h" #include "mir/frontend/client_buffers.h" #include #include #include "mir/test/gmock_fixes.h" using namespace testing; namespace mf = mir::frontend; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { struct MockSurfaceObserver : mir::scene::NullSurfaceObserver { MOCK_METHOD2(frame_posted, void(int, geom::Size const&)); }; struct StubBufferMap : mf::ClientBuffers { StubBufferMap(mf::EventSink& sink, std::vector>& buffers) : client_count{static_cast(buffers.size())}, buffers{buffers}, sink{sink} { } mg::BufferID add_buffer(mg::BufferProperties const&) { return mg::BufferID{}; } void remove_buffer(mg::BufferID) { } void with_buffer(mg::BufferID, std::function const&) { } void receive_buffer(mg::BufferID) { client_count--; } void send_buffer(mg::BufferID id) { client_count++; sink.send_buffer(mf::BufferStreamId{33}, *operator[](id), mg::BufferIpcMsgType::update_msg); } std::shared_ptr& operator[](mg::BufferID id) { auto it = std::find_if(buffers.begin(), buffers.end(), [id](std::shared_ptr const& b) { return b->id() == id; }); if (it == buffers.end()) throw std::logic_error("cannot find buffer in map"); return *it; } size_t client_owned_buffer_count() const { return client_count; } int client_count{0}; std::vector>& buffers; mf::EventSink& sink; }; struct Stream : Test { Stream() : buffers{ std::make_shared(initial_size), std::make_shared(initial_size), std::make_shared(initial_size)} { } MOCK_METHOD1(called, void(mg::Buffer&)); geom::Size initial_size{44,2}; std::vector> buffers; NiceMock mock_sink; MirPixelFormat construction_format{mir_pixel_format_rgb_565}; mtd::MockFrameDroppingPolicyFactory framedrop_factory; mc::Stream stream{ framedrop_factory, std::make_unique(mock_sink, buffers), initial_size, construction_format}; }; } TEST_F(Stream, swapping_returns_null_via_callback) { stream.swap_buffers(buffers[0].get(), [](mg::Buffer* buffer) { EXPECT_THAT(buffer, IsNull()); }); } TEST_F(Stream, transitions_from_queuing_to_framedropping) { EXPECT_CALL(mock_sink, send_buffer(_,_,_)).Times(buffers.size() - 1); for(auto& buffer : buffers) stream.swap_buffers(buffer.get(), [](mg::Buffer*){}); stream.allow_framedropping(true); std::vector> cbuffers; while(stream.buffers_ready_for_compositor(this)) cbuffers.push_back(stream.lock_compositor_buffer(this)); ASSERT_THAT(cbuffers, SizeIs(1)); EXPECT_THAT(cbuffers[0]->id(), Eq(buffers.back()->id())); } TEST_F(Stream, transitions_from_framedropping_to_queuing) { stream.allow_framedropping(true); Mock::VerifyAndClearExpectations(&mock_sink); EXPECT_CALL(mock_sink, send_buffer(_,_,_)).Times(buffers.size() - 1); for(auto& buffer : buffers) stream.swap_buffers(buffer.get(), [](mg::Buffer*){}); stream.allow_framedropping(false); for(auto& buffer : buffers) stream.swap_buffers(buffer.get(), [](mg::Buffer*){}); Mock::VerifyAndClearExpectations(&mock_sink); std::vector> cbuffers; while(stream.buffers_ready_for_compositor(this)) cbuffers.push_back(stream.lock_compositor_buffer(this)); EXPECT_THAT(cbuffers, SizeIs(buffers.size())); } TEST_F(Stream, indicates_buffers_ready_when_queueing) { for(auto& buffer : buffers) stream.swap_buffers(buffer.get(), [](mg::Buffer*){}); for(auto i = 0u; i < buffers.size(); i++) { EXPECT_THAT(stream.buffers_ready_for_compositor(this), Eq(1)); stream.lock_compositor_buffer(this); } EXPECT_THAT(stream.buffers_ready_for_compositor(this), Eq(0)); } TEST_F(Stream, indicates_buffers_ready_when_dropping) { stream.allow_framedropping(true); for(auto& buffer : buffers) stream.swap_buffers(buffer.get(), [](mg::Buffer*){}); EXPECT_THAT(stream.buffers_ready_for_compositor(this), Eq(1)); stream.lock_compositor_buffer(this); EXPECT_THAT(stream.buffers_ready_for_compositor(this), Eq(0)); } TEST_F(Stream, tracks_has_buffer) { EXPECT_FALSE(stream.has_submitted_buffer()); stream.swap_buffers(buffers[0].get(), [](mg::Buffer*){}); EXPECT_TRUE(stream.has_submitted_buffer()); } TEST_F(Stream, calls_observers_after_scheduling_on_submissions) { auto observer = std::make_shared(); EXPECT_CALL(*observer, frame_posted(1, initial_size)); stream.add_observer(observer); stream.swap_buffers(buffers[0].get(), [](mg::Buffer*){}); stream.remove_observer(observer); stream.swap_buffers(buffers[0].get(), [](mg::Buffer*){}); } TEST_F(Stream, calls_observers_call_doesnt_hold_lock) { auto observer = std::make_shared(); EXPECT_CALL(*observer, frame_posted(1,_)) .WillOnce(InvokeWithoutArgs([&]{ EXPECT_THAT(stream.buffers_ready_for_compositor(this), Eq(1)); EXPECT_TRUE(stream.has_submitted_buffer()); })); stream.add_observer(observer); stream.swap_buffers(buffers[0].get(), [](mg::Buffer*){}); } TEST_F(Stream, flattens_queue_out_when_told_to_drop) { for(auto& buffer : buffers) stream.swap_buffers(buffer.get(), [](mg::Buffer*){}); EXPECT_THAT(stream.buffers_ready_for_compositor(this), Eq(1)); stream.drop_old_buffers(); stream.lock_compositor_buffer(this); EXPECT_THAT(stream.buffers_ready_for_compositor(this), Eq(0)); } TEST_F(Stream, forces_a_new_buffer_when_told_to_drop_buffers) { int that{0}; stream.swap_buffers(buffers[0].get(), [](mg::Buffer*){}); stream.swap_buffers(buffers[1].get(), [](mg::Buffer*){}); stream.swap_buffers(buffers[2].get(), [](mg::Buffer*){}); auto a = stream.lock_compositor_buffer(this); stream.drop_old_buffers(); auto b = stream.lock_compositor_buffer(&that); auto c = stream.lock_compositor_buffer(this); EXPECT_THAT(b->id(), Eq(c->id())); EXPECT_THAT(a->id(), Ne(b->id())); EXPECT_THAT(a->id(), Ne(c->id())); } TEST_F(Stream, ignores_nullptr_submissions) //legacy behavior { auto observer = std::make_shared(); EXPECT_CALL(*observer, frame_posted(_,_)).Times(0); stream.add_observer(observer); bool was_called = false; stream.swap_buffers(nullptr, [&](mg::Buffer*){ was_called = true; }); EXPECT_FALSE(stream.has_submitted_buffer()); EXPECT_TRUE(was_called); } //it doesnt quite make sense that the stream has a size, esp given that there could be different-sized buffers //in the stream, and the surface has the onscreen size info TEST_F(Stream, reports_size) { geom::Size new_size{333,139}; EXPECT_THAT(stream.stream_size(), Eq(initial_size)); stream.resize(new_size); EXPECT_THAT(stream.stream_size(), Eq(new_size)); } //Likewise, no reason buffers couldn't all be a different pixel format TEST_F(Stream, reports_format) { EXPECT_THAT(stream.pixel_format(), Eq(construction_format)); } TEST_F(Stream, can_access_buffer_after_allocation) { EXPECT_CALL(*this, called(testing::Ref(*buffers.front()))); stream.with_buffer(buffers.front()->id(), [this](mg::Buffer& b) { called(b); }); } //confusingly, we have two framedrops. One is swapinterval zero, where old buffers are dropped as quickly as possible. //In non-framedropping mode, we drop based on a timeout according to a policy, mostly for screen-off scenarios. // namespace { struct MockPolicy : mc::FrameDroppingPolicy { MOCK_METHOD0(swap_now_blocking, void(void)); MOCK_METHOD0(swap_unblocked, void(void)); }; } TEST_F(Stream, timer_starts_when_buffers_run_out_and_framedropping_disabled) { auto policy = std::make_unique(); mtd::FrameDroppingPolicyFactoryMock policy_factory; EXPECT_CALL(*policy, swap_now_blocking()); EXPECT_CALL(policy_factory, create_policy(_)) .WillOnce(InvokeWithoutArgs([&]{ return std::move(policy); })); mc::Stream stream{ policy_factory, std::make_unique(mock_sink, buffers), initial_size, construction_format}; for (auto& buffer : buffers) stream.swap_buffers(buffer.get(), [](mg::Buffer*){}); } TEST_F(Stream, timer_stops_if_a_buffer_is_available) { auto policy = std::make_unique(); mtd::FrameDroppingPolicyFactoryMock policy_factory; EXPECT_CALL(*policy, swap_now_blocking()); EXPECT_CALL(*policy, swap_unblocked()); EXPECT_CALL(policy_factory, create_policy(_)) .WillOnce(InvokeWithoutArgs([&]{ return std::move(policy); })); mc::Stream stream{ policy_factory, std::make_unique(mock_sink, buffers), initial_size, construction_format}; for (auto& buffer : buffers) stream.swap_buffers(buffer.get(), [](mg::Buffer*){}); stream.lock_compositor_buffer(this); } TEST_F(Stream, triggering_policy_gives_a_buffer_back) { for (auto& buffer : buffers) stream.swap_buffers(buffer.get(), [](mg::Buffer*){}); stream.lock_compositor_buffer(this); Mock::VerifyAndClearExpectations(&mock_sink); EXPECT_CALL(mock_sink, send_buffer(_,_,_)); framedrop_factory.trigger_policies(); } TEST_F(Stream, doesnt_drop_the_only_frame_when_arbiter_has_none) { stream.swap_buffers(buffers[0].get(), [](mg::Buffer*){}); Mock::VerifyAndClearExpectations(&mock_sink); EXPECT_CALL(mock_sink, send_buffer(_,_,_)) .Times(0); framedrop_factory.trigger_policies(); } TEST_F(Stream, doesnt_drop_the_latest_frame_with_a_longer_queue) { stream.swap_buffers(buffers[0].get(), [](mg::Buffer*){}); stream.lock_compositor_buffer(this); stream.swap_buffers(buffers[1].get(), [](mg::Buffer*){}); stream.swap_buffers(buffers[2].get(), [](mg::Buffer*){}); Mock::VerifyAndClearExpectations(&mock_sink); EXPECT_CALL(mock_sink, send_buffer(_,Ref(*buffers[1]),_)) .Times(1); framedrop_factory.trigger_policies(); } TEST_F(Stream, doesnt_drop_the_latest_frame_with_a_2_buffer_queue) { stream.swap_buffers(buffers[0].get(), [](mg::Buffer*){}); stream.lock_compositor_buffer(this); stream.swap_buffers(buffers[1].get(), [](mg::Buffer*){}); Mock::VerifyAndClearExpectations(&mock_sink); EXPECT_CALL(mock_sink, send_buffer(_,Ref(*buffers[1]),_)) .Times(0); framedrop_factory.trigger_policies(); } TEST_F(Stream, returns_buffers_to_client_when_told_to_bring_queue_up_to_date) { stream.swap_buffers(buffers[0].get(), [](mg::Buffer*){}); stream.swap_buffers(buffers[1].get(), [](mg::Buffer*){}); stream.swap_buffers(buffers[2].get(), [](mg::Buffer*){}); Mock::VerifyAndClearExpectations(&mock_sink); EXPECT_CALL(mock_sink, send_buffer(_,Ref(*buffers[0]),_)); EXPECT_CALL(mock_sink, send_buffer(_,Ref(*buffers[1]),_)); stream.drop_old_buffers(); } ./tests/unit-tests/compositor/test_timeout_frame_dropping_policy.cpp0000644000015600001650000001334112676616125026456 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Alberto Aguirre */ #include "src/server/compositor/timeout_frame_dropping_policy_factory.h" #include "mir/compositor/frame_dropping_policy.h" #include "mir/basic_callback.h" #include "mir/test/doubles/mock_timer.h" #include "mir/test/doubles/mock_lockable_callback.h" #include "mir/test/auto_unblock_thread.h" #include "mir/test/fake_shared.h" #include "mir/test/gmock_fixes.h" #include #include #include #include namespace mc = mir::compositor; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace { class TimeoutFrameDroppingPolicy : public ::testing::Test { public: void SetUp() override { clock = std::make_shared(); timer = std::make_shared(clock); handler = std::make_shared([this]{ frame_dropped = true; }); } auto create_default_policy() { mc::TimeoutFrameDroppingPolicyFactory factory{timer, timeout}; return factory.create_policy(handler); } protected: std::shared_ptr clock; std::shared_ptr timer; std::chrono::milliseconds const timeout = std::chrono::milliseconds(1000); std::shared_ptr handler; bool frame_dropped = false; }; } TEST_F(TimeoutFrameDroppingPolicy, does_not_fire_before_notified_of_block) { auto policy = create_default_policy(); clock->advance_time(timeout + std::chrono::milliseconds{1}); EXPECT_FALSE(frame_dropped); } TEST_F(TimeoutFrameDroppingPolicy, schedules_alarm_for_correct_timeout) { auto policy = create_default_policy(); policy->swap_now_blocking(); clock->advance_time(timeout - std::chrono::milliseconds{1}); EXPECT_FALSE(frame_dropped); clock->advance_time(std::chrono::milliseconds{2}); EXPECT_TRUE(frame_dropped); } TEST_F(TimeoutFrameDroppingPolicy, framedrop_callback_cancelled_by_unblock) { auto policy = create_default_policy(); policy->swap_now_blocking(); policy->swap_unblocked(); clock->advance_time(timeout * 10); EXPECT_FALSE(frame_dropped); } TEST_F(TimeoutFrameDroppingPolicy, policy_drops_one_frame_per_blocking_swap) { auto policy = create_default_policy(); policy->swap_now_blocking(); policy->swap_now_blocking(); policy->swap_now_blocking(); clock->advance_time(timeout + std::chrono::milliseconds{1}); EXPECT_TRUE(frame_dropped); frame_dropped = false; clock->advance_time(timeout + std::chrono::milliseconds{2}); EXPECT_TRUE(frame_dropped); frame_dropped = false; clock->advance_time(timeout + std::chrono::milliseconds{1}); EXPECT_TRUE(frame_dropped); frame_dropped = false; clock->advance_time(timeout + std::chrono::milliseconds{1}); EXPECT_FALSE(frame_dropped); } TEST_F(TimeoutFrameDroppingPolicy, policy_drops_frames_no_more_frequently_than_timeout) { auto policy = create_default_policy(); policy->swap_now_blocking(); policy->swap_now_blocking(); clock->advance_time(timeout + std::chrono::milliseconds{1}); EXPECT_TRUE(frame_dropped); frame_dropped = false; clock->advance_time(timeout - std::chrono::milliseconds{1}); EXPECT_FALSE(frame_dropped); clock->advance_time(std::chrono::milliseconds{2}); EXPECT_TRUE(frame_dropped); } TEST_F(TimeoutFrameDroppingPolicy, newly_blocking_frame_doesnt_reset_timeout) { auto policy = create_default_policy(); policy->swap_now_blocking(); clock->advance_time(timeout - std::chrono::milliseconds{1}); policy->swap_now_blocking(); clock->advance_time(std::chrono::milliseconds{2}); EXPECT_TRUE(frame_dropped); } TEST_F(TimeoutFrameDroppingPolicy, interspersed_timeouts_and_unblocks) { auto policy = create_default_policy(); policy->swap_now_blocking(); policy->swap_now_blocking(); policy->swap_now_blocking(); /* First frame gets dropped... */ clock->advance_time(timeout + std::chrono::milliseconds{1}); EXPECT_TRUE(frame_dropped); /* ...Compositor gets its act in order and consumes a frame... */ frame_dropped = false; policy->swap_unblocked(); clock->advance_time(timeout - std::chrono::milliseconds{1}); EXPECT_FALSE(frame_dropped); /* ...but not a second frame, so third swap should trigger a timeout */ clock->advance_time(std::chrono::milliseconds{2}); EXPECT_TRUE(frame_dropped); frame_dropped = false; clock->advance_time(timeout + std::chrono::milliseconds{1}); EXPECT_FALSE(frame_dropped); } TEST_F(TimeoutFrameDroppingPolicy, policy_calls_lock_unlock_functions) { using namespace testing; mc::TimeoutFrameDroppingPolicyFactory factory{timer, timeout}; mtd::MockLockableCallback handler; { InSequence s; EXPECT_CALL(handler, lock()); EXPECT_CALL(handler, functor()); EXPECT_CALL(handler, unlock()); } auto policy = factory.create_policy(mt::fake_shared(handler)); policy->swap_now_blocking(); clock->advance_time(timeout + std::chrono::milliseconds{1}); } ./tests/unit-tests/compositor/test_screencast_display_buffer.cpp0000644000015600001650000001225412676616125025547 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/compositor/screencast_display_buffer.h" #include "mir/test/doubles/mock_gl_buffer.h" #include "mir/test/doubles/stub_gl_buffer.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/stub_renderable.h" #include #include namespace mc = mir::compositor; namespace mtd = mir::test::doubles; namespace geom = mir::geometry; namespace mg = mir::graphics; namespace { struct ScreencastDisplayBufferTest : testing::Test { testing::NiceMock mock_gl; }; } TEST_F(ScreencastDisplayBufferTest, cleans_up_gl_resources) { using namespace testing; GLuint const texture{11}; GLuint const renderbuffer{12}; GLuint const framebuffer{13}; EXPECT_CALL(mock_gl, glGenTextures(1,_)) .WillOnce(SetArgPointee<1>(texture)); EXPECT_CALL(mock_gl, glGenRenderbuffers(1,_)) .WillOnce(SetArgPointee<1>(renderbuffer)); EXPECT_CALL(mock_gl, glGenFramebuffers(1,_)) .WillOnce(SetArgPointee<1>(framebuffer)); EXPECT_CALL(mock_gl, glDeleteTextures(1,Pointee(texture))); EXPECT_CALL(mock_gl, glDeleteRenderbuffers(1,Pointee(renderbuffer))); EXPECT_CALL(mock_gl, glDeleteFramebuffers(1,Pointee(framebuffer))); geom::Rectangle const rect{{100,100}, {800,600}}; mtd::StubGLBuffer stub_buffer; mc::ScreencastDisplayBuffer db{rect, stub_buffer}; } TEST_F(ScreencastDisplayBufferTest, cleans_up_gl_resources_on_construction_failure) { using namespace testing; GLuint const texture{11}; GLuint const renderbuffer{12}; GLuint const framebuffer{13}; EXPECT_CALL(mock_gl, glGenTextures(1,_)) .WillOnce(SetArgPointee<1>(texture)); EXPECT_CALL(mock_gl, glGenRenderbuffers(1,_)) .WillOnce(SetArgPointee<1>(renderbuffer)); EXPECT_CALL(mock_gl, glGenFramebuffers(1,_)) .WillOnce(SetArgPointee<1>(framebuffer)); EXPECT_CALL(mock_gl, glCheckFramebufferStatus(_)) .WillOnce(Return(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)); EXPECT_CALL(mock_gl, glDeleteTextures(1,Pointee(texture))); EXPECT_CALL(mock_gl, glDeleteRenderbuffers(1,Pointee(renderbuffer))); EXPECT_CALL(mock_gl, glDeleteFramebuffers(1,Pointee(framebuffer))); geom::Rectangle const rect{{100,100}, {800,600}}; mtd::StubGLBuffer stub_buffer; EXPECT_THROW({ mc::ScreencastDisplayBuffer db(rect, stub_buffer); }, std::runtime_error); } TEST_F(ScreencastDisplayBufferTest, sets_render_buffer_size_to_supplied_buffer_size) { using namespace testing; geom::Rectangle const rect{{100,100}, {800,600}}; testing::NiceMock mock_buffer{ rect.size, geom::Stride{100}, mir_pixel_format_xbgr_8888}; /* Set the buffer as rendering target */ EXPECT_CALL(mock_gl, glRenderbufferStorage(_, _, mock_buffer.size().width.as_int(), mock_buffer.size().height.as_int())); mc::ScreencastDisplayBuffer db{rect, mock_buffer}; } TEST_F(ScreencastDisplayBufferTest, renders_to_supplied_buffer) { using namespace testing; geom::Rectangle const rect{{100,100}, {800,600}}; testing::NiceMock mock_buffer{ rect.size, geom::Stride{100}, mir_pixel_format_xbgr_8888}; InSequence s; /* Set the buffer as rendering target */ EXPECT_CALL(mock_buffer, gl_bind_to_texture()); EXPECT_CALL(mock_gl, glViewport(0, 0, mock_buffer.size().width.as_int(), mock_buffer.size().height.as_int())); /* Restore previous viewport on exit */ EXPECT_CALL(mock_gl, glViewport(0, 0, 0, 0)); mc::ScreencastDisplayBuffer db{rect, mock_buffer}; db.make_current(); } TEST_F(ScreencastDisplayBufferTest, forces_rendering_to_complete_on_swap) { using namespace testing; geom::Rectangle const rect{{100,100}, {800,600}}; mtd::StubGLBuffer stub_buffer; mc::ScreencastDisplayBuffer db{rect, stub_buffer}; Mock::VerifyAndClearExpectations(&mock_gl); EXPECT_CALL(mock_gl, glFinish()); db.swap_buffers(); } TEST_F(ScreencastDisplayBufferTest, rejects_attempt_to_optimize) { geom::Rectangle const rect{{100,100}, {800,600}}; mtd::StubGLBuffer stub_buffer; mg::RenderableList renderables{ std::make_shared(), std::make_shared(), std::make_shared()}; mc::ScreencastDisplayBuffer db{rect, stub_buffer}; EXPECT_FALSE(db.post_renderables_if_optimizable(renderables)); } ./tests/unit-tests/compositor/CMakeLists.txt0000644000015600001650000000161612676616125021334 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_default_display_buffer_compositor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_stream.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_stream.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_temporary_buffers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_multi_threaded_compositor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_queue.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_occlusion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_screencast_display_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_compositing_screencast.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_timeout_frame_dropping_policy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_multi_monitor_arbiter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_client_buffers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_dropping_schedule.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_queueing_schedule.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/compositor/test_compositing_screencast.cpp0000644000015600001650000003074012676616125025104 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/compositor/compositing_screencast.h" #include "mir/compositor/display_buffer_compositor_factory.h" #include "mir/compositor/display_buffer_compositor.h" #include "mir/compositor/scene.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/geometry/rectangle.h" #include "mir/geometry/rectangles.h" #include "mir/test/doubles/null_display.h" #include "mir/test/doubles/null_display_buffer_compositor_factory.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/stub_gl_buffer_allocator.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/stub_scene.h" #include "mir/test/doubles/stub_scene_element.h" #include "mir/test/doubles/mock_scene.h" #include "mir/test/fake_shared.h" #include #include #include #include namespace mc = mir::compositor; namespace mg = mir::graphics; namespace mf = mir::frontend; namespace mtd = mir::test::doubles; namespace mt = mir::test; namespace geom = mir::geometry; namespace { class StubVirtualOutput : public mg::VirtualOutput { public: StubVirtualOutput(std::function notify_enable) : notify_enable{notify_enable} { } void enable() override { notify_enable(); } void disable() override { } private: std::function notify_enable; }; class StubDisplay : public mtd::NullDisplay { public: StubDisplay() : display_regions{{{0, 0}, {1920, 1080}}, {{1920, 0}, {640,480}}} { } std::unique_ptr configuration() const override { return {std::make_unique(display_regions)}; } std::unique_ptr create_virtual_output(int /*width*/, int /*height*/) override { virtual_output_created = true; return {std::make_unique([this] { virtual_output_enabled = true; })}; } geom::Rectangle extents() { geom::Rectangles disp_rects; configuration()->for_each_output([&disp_rects](mg::DisplayConfigurationOutput const& disp_conf) { if (disp_conf.connected) disp_rects.add(disp_conf.extents()); }); return disp_rects.bounding_rectangle(); } geom::Point adjacent_point() { return {extents().size.width.as_int(), 0}; } bool virtual_output_created{false}; bool virtual_output_enabled{false}; private: std::vector const display_regions; }; struct MockDisplayBufferCompositor : mc::DisplayBufferCompositor { void composite(mc::SceneElementSequence&& seq) { composite_(seq); } MOCK_METHOD1(composite_, void(mc::SceneElementSequence&)); }; class WrappingDisplayBufferCompositor : public mc::DisplayBufferCompositor { public: WrappingDisplayBufferCompositor(mc::DisplayBufferCompositor& comp) : comp(comp) { } void composite(mc::SceneElementSequence&& elements) { comp.composite(std::move(elements)); } private: mc::DisplayBufferCompositor& comp; }; struct MockBufferAllocator : mg::GraphicBufferAllocator { MOCK_METHOD1(alloc_buffer, std::shared_ptr(mg::BufferProperties const&)); MOCK_METHOD0(supported_pixel_formats, std::vector()); }; struct MockDisplayBufferCompositorFactory : mc::DisplayBufferCompositorFactory { std::unique_ptr create_compositor_for(mg::DisplayBuffer& db) { create_compositor_mock(db); return std::unique_ptr( new WrappingDisplayBufferCompositor{mock_db_compositor}); } MockDisplayBufferCompositor mock_db_compositor; MOCK_METHOD1(create_compositor_mock, void(mg::DisplayBuffer&)); }; MATCHER_P(DisplayBufferCoversArea, output_extents, "") { return arg.view_area() == output_extents; } MATCHER_P(BufferPropertiesMatchSize, size, "") { return arg.size == size; } struct CompositingScreencastTest : testing::Test { CompositingScreencastTest() : screencast{mt::fake_shared(stub_scene), mt::fake_shared(stub_display), mt::fake_shared(stub_buffer_allocator), mt::fake_shared(stub_db_compositor_factory)}, default_size{1, 1}, default_region{{0, 0}, {1, 1}}, default_pixel_format{mir_pixel_format_xbgr_8888} { } testing::NiceMock mock_gl; mtd::StubScene stub_scene; StubDisplay stub_display; mtd::StubGLBufferAllocator stub_buffer_allocator; mtd::NullDisplayBufferCompositorFactory stub_db_compositor_factory; mc::CompositingScreencast screencast; geom::Size const default_size; geom::Rectangle const default_region; MirPixelFormat default_pixel_format; }; } TEST_F(CompositingScreencastTest, produces_different_session_ids) { std::unordered_set session_ids; for (int i = 0; i != 10; ++i) { auto session_id = screencast.create_session(default_region, default_size, default_pixel_format); ASSERT_TRUE(session_ids.find(session_id) == session_ids.end()) << "session_id: " << session_id << " iter: " << i; session_ids.insert(session_id); } } TEST_F(CompositingScreencastTest, throws_on_creation_with_invalid_params) { std::unordered_set session_ids; geom::Size invalid_size{0, 0}; geom::Rectangle invalid_region{{0, 0}, {0, 0}}; EXPECT_THROW(screencast.create_session(invalid_region, default_size, default_pixel_format), std::runtime_error); EXPECT_THROW(screencast.create_session(default_region, invalid_size, default_pixel_format), std::runtime_error); EXPECT_THROW(screencast.create_session(default_region, default_size, mir_pixel_format_invalid), std::runtime_error); } TEST_F(CompositingScreencastTest, throws_on_capture_with_invalid_session_id) { mf::ScreencastSessionId const invalid_session_id{10}; EXPECT_THROW(screencast.capture(invalid_session_id), std::logic_error); } TEST_F(CompositingScreencastTest, throws_on_capture_with_destroyed_session_id) { auto session_id = screencast.create_session(default_region, default_size, default_pixel_format); screencast.destroy_session(session_id); EXPECT_THROW(screencast.capture(session_id), std::logic_error); } TEST_F(CompositingScreencastTest, captures_by_compositing_with_provided_region) { using namespace testing; mtd::StubSceneElement element1; mtd::StubSceneElement element2; mc::SceneElementSequence scene_elements{mt::fake_shared(element1), mt::fake_shared(element2)}; NiceMock mock_scene; MockDisplayBufferCompositorFactory mock_db_compositor_factory; InSequence s; EXPECT_CALL(mock_db_compositor_factory, create_compositor_mock(DisplayBufferCoversArea(default_region))); EXPECT_CALL(mock_scene, scene_elements_for(_)) .WillOnce(Return(scene_elements)); EXPECT_CALL(mock_db_compositor_factory.mock_db_compositor, composite_(Eq(scene_elements))); mc::CompositingScreencast screencast_local{ mt::fake_shared(mock_scene), mt::fake_shared(stub_display), mt::fake_shared(stub_buffer_allocator), mt::fake_shared(mock_db_compositor_factory)}; auto session_id = screencast_local.create_session(default_region, default_size, default_pixel_format); screencast_local.capture(session_id); } TEST_F(CompositingScreencastTest, allocates_and_uses_buffer_with_provided_size) { using namespace testing; MockBufferAllocator mock_buffer_allocator; mtd::StubGLBuffer stub_buffer; InSequence s; EXPECT_CALL(mock_buffer_allocator, alloc_buffer(BufferPropertiesMatchSize(default_size))) .WillOnce(Return(mt::fake_shared(stub_buffer))); mc::CompositingScreencast screencast_local{ mt::fake_shared(stub_scene), mt::fake_shared(stub_display), mt::fake_shared(mock_buffer_allocator), mt::fake_shared(stub_db_compositor_factory)}; auto session_id = screencast_local.create_session(default_region, default_size, default_pixel_format); auto buffer = screencast_local.capture(session_id); ASSERT_EQ(&stub_buffer, buffer.get()); } TEST_F(CompositingScreencastTest, uses_one_buffer_per_session) { using namespace testing; MockBufferAllocator mock_buffer_allocator; mtd::StubGLBuffer stub_buffer1; mtd::StubGLBuffer stub_buffer2; EXPECT_CALL(mock_buffer_allocator, alloc_buffer(_)) .WillOnce(Return(mt::fake_shared(stub_buffer1))) .WillOnce(Return(mt::fake_shared(stub_buffer2))); mc::CompositingScreencast screencast_local{ mt::fake_shared(stub_scene), mt::fake_shared(stub_display), mt::fake_shared(mock_buffer_allocator), mt::fake_shared(stub_db_compositor_factory)}; auto session_id1 = screencast_local.create_session(default_region, default_size, default_pixel_format); auto buffer1 = screencast_local.capture(session_id1); ASSERT_EQ(&stub_buffer1, buffer1.get()); buffer1 = screencast_local.capture(session_id1); ASSERT_EQ(&stub_buffer1, buffer1.get()); auto session_id2 = screencast_local.create_session(default_region, default_size, default_pixel_format); auto buffer2 = screencast_local.capture(session_id2); ASSERT_EQ(&stub_buffer2, buffer2.get()); buffer2 = screencast_local.capture(session_id2); ASSERT_EQ(&stub_buffer2, buffer2.get()); } TEST_F(CompositingScreencastTest, registers_and_unregisters_from_scene) { using namespace testing; NiceMock mock_scene; NiceMock mock_buffer_allocator; ON_CALL(mock_buffer_allocator, alloc_buffer(_)) .WillByDefault(Return(std::make_shared())); EXPECT_CALL(mock_scene, register_compositor(_)) .Times(1); EXPECT_CALL(mock_scene, unregister_compositor(_)) .Times(1); mc::CompositingScreencast screencast_local{ mt::fake_shared(mock_scene), mt::fake_shared(stub_display), mt::fake_shared(mock_buffer_allocator), mt::fake_shared(stub_db_compositor_factory)}; auto session_id = screencast_local.create_session(default_region, default_size, default_pixel_format); screencast_local.destroy_session(session_id); } TEST_F(CompositingScreencastTest, attempts_to_create_output_display_when_given_region_outside_any_display) { using namespace testing; geom::Rectangle region_outside_display{stub_display.adjacent_point(), default_size}; mc::CompositingScreencast screencast_local{ mt::fake_shared(stub_scene), mt::fake_shared(stub_display), mt::fake_shared(stub_buffer_allocator), mt::fake_shared(stub_db_compositor_factory)}; auto session_id = screencast_local.create_session(region_outside_display, default_size, default_pixel_format); screencast_local.destroy_session(session_id); EXPECT_TRUE(stub_display.virtual_output_created); EXPECT_TRUE(stub_display.virtual_output_enabled); } TEST_F(CompositingScreencastTest, does_not_create_virtual_output_when_given_region_that_covers_any_display) { using namespace testing; geom::Rectangle region_inside_display{stub_display.extents().top_left, default_size}; mc::CompositingScreencast screencast_local{ mt::fake_shared(stub_scene), mt::fake_shared(stub_display), mt::fake_shared(stub_buffer_allocator), mt::fake_shared(stub_db_compositor_factory)}; auto session_id = screencast_local.create_session(region_inside_display, default_size, default_pixel_format); screencast_local.destroy_session(session_id); EXPECT_FALSE(stub_display.virtual_output_created); EXPECT_FALSE(stub_display.virtual_output_enabled); } ./tests/unit-tests/dispatch/0000755000015600001650000000000012676616160016170 5ustar jenkinsjenkins./tests/unit-tests/dispatch/test_threaded_dispatcher.cpp0000644000015600001650000003331612676616157023735 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/dispatch/threaded_dispatcher.h" #include "mir/dispatch/dispatchable.h" #include "mir/fd.h" #include "mir/test/pipe.h" #include "mir/test/signal.h" #include "mir/test/test_dispatchable.h" #include "mir_test_framework/process.h" #include "mir/test/cross_process_action.h" #include #include #include #include #include namespace md = mir::dispatch; namespace mt = mir::test; namespace { class ThreadedDispatcherTest : public ::testing::Test { public: ThreadedDispatcherTest() { mt::Pipe pipe{O_NONBLOCK}; watch_fd = pipe.read_fd(); test_fd = pipe.write_fd(); } mir::Fd watch_fd; mir::Fd test_fd; }; class MockDispatchable : public md::Dispatchable { public: MOCK_CONST_METHOD0(watch_fd, mir::Fd()); MOCK_METHOD1(dispatch, bool(md::FdEvents)); MOCK_CONST_METHOD0(relevant_events, md::FdEvents()); }; } TEST_F(ThreadedDispatcherTest, calls_dispatch_when_fd_is_readable) { using namespace testing; auto dispatched = std::make_shared(); auto dispatchable = std::make_shared([dispatched]() { dispatched->raise(); }); md::ThreadedDispatcher dispatcher{"Ducks", dispatchable}; dispatchable->trigger(); EXPECT_TRUE(dispatched->wait_for(std::chrono::seconds{10})); } TEST_F(ThreadedDispatcherTest, stops_calling_dispatch_once_fd_is_not_readable) { using namespace testing; std::atomic dispatch_count{0}; auto dispatchable = std::make_shared([&dispatch_count]() { ++dispatch_count; }); md::ThreadedDispatcher dispatcher{"Dispatcher loop", dispatchable}; dispatchable->trigger(); // 1s is fine; if things are too slow we might get a false-pass, but that's OK. std::this_thread::sleep_for(std::chrono::seconds{1}); EXPECT_THAT(dispatch_count, Eq(1)); } TEST_F(ThreadedDispatcherTest, passes_dispatch_events_through) { using namespace testing; auto dispatched_with_only_readable = std::make_shared(); auto dispatched_with_hangup = std::make_shared(); auto delegate = [dispatched_with_only_readable, dispatched_with_hangup](md::FdEvents events) { if (events == md::FdEvent::readable) { dispatched_with_only_readable->raise(); } if (events & md::FdEvent::remote_closed) { dispatched_with_hangup->raise(); return false; } return true; }; auto dispatchable = std::make_shared(delegate, md::FdEvent::readable | md::FdEvent::remote_closed); md::ThreadedDispatcher dispatcher{"Avocado", dispatchable}; dispatchable->trigger(); EXPECT_TRUE(dispatched_with_only_readable->wait_for(std::chrono::seconds{10})); dispatchable->hangup(); EXPECT_TRUE(dispatched_with_hangup->wait_for(std::chrono::seconds{10})); } TEST_F(ThreadedDispatcherTest, doesnt_call_dispatch_after_first_false_return) { using namespace testing; int constexpr expected_count{10}; auto dispatched_more_than_enough = std::make_shared(); auto delegate = [dispatched_more_than_enough](md::FdEvents) { static std::atomic dispatch_count{0}; if (++dispatch_count == expected_count) { return false; } if (dispatch_count > expected_count) { dispatched_more_than_enough->raise(); } return true; }; auto dispatchable = std::make_shared(delegate); md::ThreadedDispatcher dispatcher{"Unexpected walrus", dispatchable}; for (int i = 0; i < expected_count + 1; ++i) { dispatchable->trigger(); } EXPECT_FALSE(dispatched_more_than_enough->wait_for(std::chrono::seconds{1})); } TEST_F(ThreadedDispatcherTest, only_calls_dispatch_with_remote_closed_when_relevant) { using namespace testing; auto dispatchable = std::make_shared>(); ON_CALL(*dispatchable, watch_fd()).WillByDefault(Return(test_fd)); ON_CALL(*dispatchable, relevant_events()).WillByDefault(Return(md::FdEvent::writable)); auto dispatched_writable = std::make_shared(); auto dispatched_closed = std::make_shared(); ON_CALL(*dispatchable, dispatch(_)).WillByDefault(Invoke([=](md::FdEvents events) { if (events & md::FdEvent::writable) { dispatched_writable->raise(); } if (events & md::FdEvent::remote_closed) { dispatched_closed->raise(); } return true; })); md::ThreadedDispatcher dispatcher{"Implacable iguana", dispatchable}; EXPECT_TRUE(dispatched_writable->wait_for(std::chrono::seconds{10})); // Make the fd remote-closed... watch_fd = mir::Fd{}; EXPECT_FALSE(dispatched_closed->wait_for(std::chrono::seconds{1})); } TEST_F(ThreadedDispatcherTest, dispatches_multiple_dispatchees_simultaneously) { using namespace testing; auto first_dispatched = std::make_shared(); auto second_dispatched = std::make_shared(); // Set up two dispatchables that can run given two threads of execution, // but will deadlock if run sequentially. auto first_dispatchable = std::make_shared([first_dispatched, second_dispatched]() { first_dispatched->raise(); EXPECT_TRUE(second_dispatched->wait_for(std::chrono::seconds{60})); }); auto second_dispatchable = std::make_shared([first_dispatched, second_dispatched]() { second_dispatched->raise(); EXPECT_TRUE(first_dispatched->wait_for(std::chrono::seconds{60})); }); auto combined_dispatchable = std::shared_ptr(new md::MultiplexingDispatchable{first_dispatchable, second_dispatchable}); md::ThreadedDispatcher dispatcher{"Barry", combined_dispatchable}; dispatcher.add_thread(); first_dispatchable->trigger(); second_dispatchable->trigger(); // Wait for dispatches to finish, so the dispatcher destructor doesn't race dispatching. EXPECT_TRUE(first_dispatched->wait_for(std::chrono::seconds{60})); EXPECT_TRUE(second_dispatched->wait_for(std::chrono::seconds{60})); } TEST_F(ThreadedDispatcherTest, remove_thread_decreases_concurrency) { using namespace testing; // Set up two dispatchables that will fail if run simultaneously auto second_dispatched = std::make_shared(); auto first_dispatchable = std::make_shared([second_dispatched]() { //1s is OK here; if things are slow, we might false-pass. EXPECT_FALSE(second_dispatched->wait_for(std::chrono::seconds{1})); }); auto second_dispatchable = std::make_shared([second_dispatched]() { second_dispatched->raise(); }); auto combined_dispatchable = std::make_shared(); combined_dispatchable->add_watch(first_dispatchable); combined_dispatchable->add_watch(second_dispatchable); md::ThreadedDispatcher dispatcher{"Questionable decision", combined_dispatchable}; dispatcher.add_thread(); first_dispatchable->trigger(); dispatcher.remove_thread(); second_dispatchable->trigger(); EXPECT_TRUE(second_dispatched->wait_for(std::chrono::seconds{10})); } using ThreadedDispatcherDeathTest = ThreadedDispatcherTest; TEST_F(ThreadedDispatcherDeathTest, exceptions_in_threadpool_trigger_termination) { using namespace testing; using namespace std::chrono_literals; FLAGS_gtest_death_test_style = "threadsafe"; constexpr char const* exception_msg = "Ducks! Ducks attack!"; auto dispatchable = std::make_shared([]() { throw std::runtime_error{exception_msg}; }); dispatchable->trigger(); // Ideally we'd use terminate_with_current_exception, but that's in server // and we're going to be called from a C context anyway, so just using the default // std::terminate behaviour should be acceptable. EXPECT_EXIT({ md::ThreadedDispatcher dispatcher("Die!", dispatchable); std::this_thread::sleep_for(10s); }, KilledBySignal(SIGABRT), (std::string{".*"} + exception_msg + ".*").c_str()); } TEST_F(ThreadedDispatcherTest, sets_thread_names_appropriately) { using namespace testing; using namespace std::chrono_literals; auto dispatched = std::make_shared(); constexpr int const threadcount{3}; constexpr char const* threadname_base{"Madness Thread"}; auto dispatchable = std::make_shared([dispatched]() { static std::atomic dispatch_count{0}; char buffer[80] = {0}; pthread_getname_np(pthread_self(), buffer, sizeof(buffer)); EXPECT_THAT(buffer, StartsWith(threadname_base)); if (++dispatch_count == threadcount) { dispatched->raise(); } else { dispatched->wait_for(10s); } }); md::ThreadedDispatcher dispatcher{threadname_base, dispatchable}; for (int i = 0; i < threadcount; ++i) { dispatcher.add_thread(); dispatchable->trigger(); } EXPECT_TRUE(dispatched->wait_for(10s)); } void sigcont_handler(int) { } // Regression test for: lp #1439719 TEST(ThreadedDispatcherSignalTest, keeps_dispatching_after_signal_interruption) { using namespace std::chrono_literals; mt::CrossProcessAction stop_and_restart_process; auto child = mir_test_framework::fork_and_run_in_a_different_process( [&] { { auto dispatched = std::make_shared(); auto dispatchable = std::make_shared( [dispatched]() { dispatched->raise(); }); /* When there's no SIGCONT handler installed a SIGSTOP/SIGCONT * pair POSIX mandates that a SIGSTOP/SIGCONT pair will *not* * result in blocked syscalls returning EINTR. * * Linux isn't POSIX compliant for some of its syscalls - * see “man 7 signal”, notably epoll - but for most syscalls * it does the right thing. * * When there's a signal handler for SIGCONT installed then * any blocked syscall will (correctly) return EINTR, so install one. */ signal(SIGCONT, &sigcont_handler); md::ThreadedDispatcher dispatcher{"Test thread", dispatchable}; // Ensure the dispatcher has started dispatchable->trigger(); EXPECT_TRUE(dispatched->wait_for(1s)); stop_and_restart_process(); dispatched->reset(); // The dispatcher shouldn't have been affected by the signal dispatchable->trigger(); EXPECT_TRUE(dispatched->wait_for(5s)); // Because we terminate this process with an explicit call to // exit(), objects on the stack are not destroyed. // We need to destroy the stop_and_restart_process object // manually to avoid fd leaks. stop_and_restart_process.~CrossProcessAction(); } exit(HasFailure() ? EXIT_FAILURE : EXIT_SUCCESS); }, []{ return 1; }); stop_and_restart_process.exec( [child] { // Increase chances of interrupting the dispatch mechanism for (int i = 0; i < 100; ++i) { child->stop(); child->cont(); } }); auto const result = child->wait_for_termination(30s); EXPECT_TRUE(result.succeeded()); } TEST_F(ThreadedDispatcherDeathTest, destroying_dispatcher_from_a_callback_is_an_error) { using namespace testing; using namespace std::literals::chrono_literals; EXPECT_EXIT( { md::ThreadedDispatcher* dispatcher; auto dispatchable = std::make_shared([&dispatcher]() { delete dispatcher; }); dispatchable->trigger(); dispatcher = new md::ThreadedDispatcher("Death thread", dispatchable); std::this_thread::sleep_for(10s); }, KilledBySignal(SIGABRT), ".*Destroying ThreadedDispatcher.*"); } TEST_F(ThreadedDispatcherTest, executes_exception_handler_with_current_exception) { using namespace testing; using namespace std::chrono_literals; auto dispatched = std::make_shared(); std::exception_ptr exception; auto dispatchable = std::make_shared( []() { throw std::runtime_error("thrown"); }); md::ThreadedDispatcher dispatcher{"Walruses", dispatchable, [&dispatched,&exception]() { exception = std::current_exception(); if (exception) dispatched->raise(); }}; dispatchable->trigger(); EXPECT_TRUE(dispatched->wait_for(10s)); EXPECT_THAT(exception, Not(Eq(nullptr))); } ./tests/unit-tests/dispatch/test_action_queue.cpp0000644000015600001650000000423612676616125022422 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreasd Pokorny */ #include "mir/dispatch/action_queue.h" #include "mir/test/fd_utils.h" #include #include namespace mt = mir::test; namespace md = mir::dispatch; using namespace ::testing; TEST(ActionQueue, watch_fd_becomes_readable_on_enqueue) { md::ActionQueue queue; ASSERT_FALSE(mt::fd_is_readable(queue.watch_fd())); queue.enqueue([]{}); ASSERT_TRUE(mt::fd_is_readable(queue.watch_fd())); } TEST(ActionQueue, executes_action_on_dispatch) { md::ActionQueue queue; auto action_exeuted = false; queue.enqueue([&](){action_exeuted = true;}); ASSERT_FALSE(action_exeuted); queue.dispatch(md::FdEvent::readable); ASSERT_TRUE(action_exeuted); } TEST(ActionQueue, calls_nothing_on_error) { md::ActionQueue queue; auto action_exeuted = false; queue.enqueue([&](){action_exeuted = true;}); queue.dispatch(md::FdEvent::error); ASSERT_FALSE(action_exeuted); } TEST(ActionQueue, executes_action_only_once) { md::ActionQueue queue; auto count_executed = 0; queue.enqueue([&](){++count_executed;}); queue.dispatch(md::FdEvent::readable); // The queue now doesn't need to be dispatched... EXPECT_FALSE(mt::fd_is_readable(queue.watch_fd())); // ...but might be anyway, in the case of multithreaded dispatching. queue.dispatch(md::FdEvent::readable); queue.dispatch(md::FdEvent::readable); // Even so, we expect our action to be executed only once. EXPECT_THAT(count_executed, Eq(1)); } ./tests/unit-tests/dispatch/test_dispatch_utils.cpp0000644000015600001650000000473112676616125022760 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "src/common/dispatch/utils.h" #include #include namespace md = mir::dispatch; TEST(DispatchUtilsTest, conversion_functions_roundtrip) { using namespace testing; epoll_event dummy; for (int event : {EPOLLIN, EPOLLOUT, EPOLLERR}) { dummy.events = event; EXPECT_THAT(md::fd_event_to_epoll(md::epoll_to_fd_event(dummy)), Eq(event)); } // HUP is special; FdEvent doesn't differentiate between remote hangup and local hangup dummy.events = EPOLLHUP; EXPECT_THAT(md::fd_event_to_epoll(md::epoll_to_fd_event(dummy)), Eq(EPOLLHUP | EPOLLRDHUP)); dummy.events = EPOLLRDHUP; EXPECT_THAT(md::fd_event_to_epoll(md::epoll_to_fd_event(dummy)), Eq(EPOLLHUP | EPOLLRDHUP)); } TEST(DispatchUtilsTest, epoll_to_fd_event_works_for_combinations) { using namespace testing; epoll_event dummy; dummy.events = EPOLLIN | EPOLLOUT; EXPECT_THAT(md::epoll_to_fd_event(dummy), Eq(md::FdEvent::readable | md::FdEvent::writable)); dummy.events = EPOLLERR | EPOLLOUT; EXPECT_THAT(md::epoll_to_fd_event(dummy), Eq(md::FdEvent::error | md::FdEvent::writable)); dummy.events = EPOLLIN | EPOLLRDHUP; EXPECT_THAT(md::epoll_to_fd_event(dummy), Eq(md::FdEvent::readable | md::FdEvent::remote_closed)); } TEST(DispatchUtilsTest, fd_event_to_epoll_works_for_combinations) { using namespace testing; EXPECT_THAT(md::fd_event_to_epoll(md::FdEvent::readable | md::FdEvent::writable), Eq(EPOLLIN | EPOLLOUT)); EXPECT_THAT(md::fd_event_to_epoll(md::FdEvent::error | md::FdEvent::writable), Eq(EPOLLERR | EPOLLOUT)); EXPECT_THAT(md::fd_event_to_epoll(md::FdEvent::readable | md::FdEvent::remote_closed), Eq(EPOLLIN | EPOLLHUP | EPOLLRDHUP)); } ./tests/unit-tests/dispatch/CMakeLists.txt0000644000015600001650000000056312676616125020735 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_action_queue.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_dispatch_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_multiplexing_dispatchable.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_readable_fd.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_threaded_dispatcher.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/dispatch/test_readable_fd.cpp0000644000015600001650000000222412676616125022144 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreasd Pokorny */ #include "mir/dispatch/readable_fd.h" #include #include namespace md = mir::dispatch; TEST(ReadableFd, executes_action_when_readable) { using namespace testing; bool action_triggered = false; md::ReadableFd dispatchable(mir::Fd{mir::IntOwnedFd{0}}, [&action_triggered](){ action_triggered = true;}); dispatchable.dispatch(md::FdEvent::readable); EXPECT_THAT(action_triggered, Eq(true)); } ./tests/unit-tests/dispatch/test_multiplexing_dispatchable.cpp0000644000015600001650000003512412676616125025165 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/dispatch/multiplexing_dispatchable.h" #include "mir/dispatch/threaded_dispatcher.h" #include "mir/fd.h" #include "mir/test/pipe.h" #include "mir/test/signal.h" #include "mir/test/fd_utils.h" #include "mir/test/test_dispatchable.h" #include "mir/test/auto_unblock_thread.h" #include #include #include #include #include namespace md = mir::dispatch; namespace mt = mir::test; TEST(MultiplexingDispatchableTest, dispatches_dispatchee_when_appropriate) { bool dispatched{false}; auto dispatchee = std::make_shared([&dispatched]() { dispatched = true; }); md::MultiplexingDispatchable dispatcher{dispatchee}; dispatchee->trigger(); ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd())); dispatcher.dispatch(md::FdEvent::readable); EXPECT_TRUE(dispatched); } TEST(MultiplexingDispatchableTest, calls_correct_dispatchee_when_fd_becomes_readable) { bool a_dispatched{false}; auto dispatchee_a = std::make_shared([&a_dispatched]() { a_dispatched = true; }); bool b_dispatched{false}; auto dispatchee_b = std::make_shared([&b_dispatched]() { b_dispatched = true; }); md::MultiplexingDispatchable dispatcher{dispatchee_a, dispatchee_b}; dispatchee_a->trigger(); ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd())); dispatcher.dispatch(md::FdEvent::readable); EXPECT_TRUE(a_dispatched); EXPECT_FALSE(b_dispatched); ASSERT_FALSE(mt::fd_is_readable(dispatcher.watch_fd())); a_dispatched = false; dispatchee_b->trigger(); ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd())); dispatcher.dispatch(md::FdEvent::readable); EXPECT_FALSE(a_dispatched); EXPECT_TRUE(b_dispatched); } TEST(MultiplexingDispatchableTest, keeps_dispatching_until_fd_is_unreadable) { bool dispatched{false}; auto dispatchee = std::make_shared([&dispatched]() { dispatched = true; }); md::MultiplexingDispatchable dispatcher{dispatchee}; int const trigger_count{10}; for (int i = 0; i < trigger_count; ++i) { dispatchee->trigger(); } for (int i = 0; i < trigger_count; ++i) { ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd())); dispatcher.dispatch(md::FdEvent::readable); EXPECT_TRUE(dispatched); dispatched = false; } EXPECT_FALSE(mt::fd_is_readable(dispatcher.watch_fd())); } TEST(MultiplexingDispatchableTest, dispatching_without_pending_event_is_harmless) { bool dispatched{false}; auto dispatchee = std::make_shared([&dispatched]() { dispatched = true; }); md::MultiplexingDispatchable dispatcher{dispatchee}; dispatcher.dispatch(md::FdEvent::readable); EXPECT_FALSE(dispatched); } TEST(MultiplexingDispatchableTest, keeps_dispatchees_alive) { bool dispatched{false}; auto dispatchee = std::make_shared([&dispatched]() { dispatched = true; }); dispatchee->trigger(); md::MultiplexingDispatchable dispatcher; dispatcher.add_watch(dispatchee); dispatchee.reset(); ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd())); dispatcher.dispatch(md::FdEvent::readable); EXPECT_TRUE(dispatched); } TEST(MultiplexingDispatchableTest, removed_dispatchables_are_no_longer_dispatched) { using namespace testing; mt::Pipe pipe; bool dispatched{false}; auto dispatchable = std::make_shared([&dispatched]() { dispatched = true; }); md::MultiplexingDispatchable dispatcher; dispatcher.add_watch(dispatchable); dispatcher.remove_watch(dispatchable); while (mt::fd_is_readable(dispatcher.watch_fd())) { dispatcher.dispatch(md::FdEvent::readable); } dispatchable->trigger(); EXPECT_FALSE(mt::fd_is_readable(dispatcher.watch_fd())); dispatcher.dispatch(md::FdEvent::readable); EXPECT_FALSE(dispatched); } TEST(MultiplexingDispatchableTest, adding_same_fd_twice_is_an_error) { using namespace testing; auto dispatchable = std::make_shared([](){}); md::MultiplexingDispatchable dispatcher; dispatcher.add_watch(dispatchable); EXPECT_THROW(dispatcher.add_watch(dispatchable), std::logic_error); } TEST(MultiplexingDispatchableTest, dispatcher_does_not_hold_reference_after_failing_to_add_dispatchee) { using namespace testing; auto dispatchable = std::make_shared([](){}); md::MultiplexingDispatchable dispatcher; dispatcher.add_watch(dispatchable); auto const dispatchable_refcount = dispatchable.use_count(); // This should not increase refcount EXPECT_THROW(dispatcher.add_watch(dispatchable), std::logic_error); EXPECT_THAT(dispatchable.use_count(), Eq(dispatchable_refcount)); } TEST(MultiplexingDispatchableTest, individual_dispatchee_is_not_concurrent) { using namespace testing; auto second_dispatch = std::make_shared(); auto dispatchee = std::make_shared([second_dispatch]() { static std::atomic canary{0}; static std::atomic total_count{0}; ++canary; EXPECT_THAT(canary, Eq(1)); if (++total_count == 2) { second_dispatch->raise(); } else { std::this_thread::sleep_for(std::chrono::seconds{1}); } --canary; }); dispatchee->trigger(); dispatchee->trigger(); auto dispatcher = std::make_shared(); dispatcher->add_watch(dispatchee); md::ThreadedDispatcher eventloop{"Gerry", dispatcher}; eventloop.add_thread(); EXPECT_TRUE(second_dispatch->wait_for(std::chrono::seconds{5})); } TEST(MultiplexingDispatchableTest, reentrant_dispatchee_is_dispatched_concurrently) { using namespace testing; std::atomic count{0}; auto dispatchee = std::make_shared([&count]() { ++count; std::this_thread::sleep_for(std::chrono::seconds{1}); EXPECT_THAT(count, Gt(1)); }); dispatchee->trigger(); dispatchee->trigger(); md::MultiplexingDispatchable dispatcher; dispatcher.add_watch(dispatchee, md::DispatchReentrancy::reentrant); std::thread first{[&dispatcher]() { dispatcher.dispatch(md::FdEvent::readable); }}; std::thread second{[&dispatcher]() { dispatcher.dispatch(md::FdEvent::readable); }}; first.join(); second.join(); } TEST(MultiplexingDispatchableTest, raw_callback_is_dispatched) { using namespace testing; bool dispatched{false}; auto dispatchee = [&dispatched]() { dispatched = true; }; mt::Pipe fd_source; md::MultiplexingDispatchable dispatcher; dispatcher.add_watch(fd_source.read_fd(), dispatchee); char buffer{0}; ASSERT_THAT(::write(fd_source.write_fd(), &buffer, sizeof(buffer)), Eq(sizeof(buffer))); EXPECT_TRUE(mt::fd_is_readable(dispatcher.watch_fd())); dispatcher.dispatch(md::FdEvent::readable); EXPECT_TRUE(dispatched); } TEST(MultiplexingDispatchableTest, raw_callback_can_be_removed) { using namespace testing; bool dispatched{false}; auto dispatchee = [&dispatched]() { dispatched = true; }; mt::Pipe fd_source; md::MultiplexingDispatchable dispatcher; dispatcher.add_watch(fd_source.read_fd(), dispatchee); dispatcher.remove_watch(fd_source.read_fd()); while (mt::fd_is_readable(dispatcher.watch_fd())) { dispatcher.dispatch(md::FdEvent::readable); } char buffer{0}; ASSERT_THAT(::write(fd_source.write_fd(), &buffer, sizeof(buffer)), Eq(sizeof(buffer))); EXPECT_FALSE(mt::fd_is_readable(dispatcher.watch_fd())); dispatcher.dispatch(md::FdEvent::readable); EXPECT_FALSE(dispatched); } TEST(MultiplexingDispatchableTest, removal_is_threadsafe) { using namespace testing; auto canary_killed = std::make_shared(); auto canary = std::shared_ptr(new int, [canary_killed](int* victim) { delete victim; canary_killed->raise(); }); auto in_dispatch = std::make_shared(); auto dispatcher = std::make_shared(); auto dispatchee = std::make_shared([canary, in_dispatch]() { in_dispatch->raise(); std::this_thread::sleep_for(std::chrono::seconds{1}); EXPECT_THAT(canary.use_count(), Gt(0)); }); dispatcher->add_watch(dispatchee); dispatchee->trigger(); md::ThreadedDispatcher eventloop{"Tempura", dispatcher}; EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1})); dispatcher->remove_watch(dispatchee); dispatchee.reset(); canary.reset(); EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2})); } TEST(MultiplexingDispatchableTest, destruction_is_threadsafe) { using namespace testing; auto canary_killed = std::make_shared(); auto canary = std::shared_ptr(new int, [canary_killed](int* victim) { delete victim; canary_killed->raise(); }); auto in_dispatch = std::make_shared(); auto dispatcher = std::make_shared(); auto dispatchee = std::make_shared([canary, in_dispatch]() { in_dispatch->raise(); std::this_thread::sleep_for(std::chrono::seconds{1}); EXPECT_THAT(canary.use_count(), Gt(0)); }); dispatcher->add_watch(dispatchee); dispatchee->trigger(); mt::AutoJoinThread dispatch_thread{[dispatcher]() { dispatcher->dispatch(md::FdEvent::readable); }}; EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1})); dispatcher->remove_watch(dispatchee); dispatcher.reset(); dispatchee.reset(); canary.reset(); EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2})); } TEST(MultiplexingDispatchableTest, stress_test_threading) { using namespace testing; int const dispatchee_count{20}; auto dispatcher = std::make_shared(); auto event_dispatcher = std::make_shared("Hello Kitty", dispatcher); for (int i = 0 ; i < dispatchee_count + 5 ; ++i) { event_dispatcher->add_thread(); } std::vector> canary_tomb; std::vector> dispatchees; for (int i = 0 ; i < dispatchee_count ; ++i) { canary_tomb.push_back(std::make_shared()); auto current_canary = canary_tomb.back(); auto canary = std::shared_ptr(new int, [current_canary](int* victim) { delete victim; current_canary->raise(); }); auto dispatchee = std::make_shared([canary]() { std::this_thread::sleep_for(std::chrono::seconds{1}); EXPECT_THAT(canary.use_count(), Gt(0)); }); dispatcher->add_watch(dispatchee, md::DispatchReentrancy::reentrant); dispatchee->trigger(); } for (auto& dispatchee : dispatchees) { dispatchee->trigger(); dispatcher->remove_watch(dispatchee); } dispatchees.clear(); dispatcher.reset(); event_dispatcher.reset(); for (auto headstone : canary_tomb) { // Use assert so as to not block for *ages* on failure ASSERT_TRUE(headstone->wait_for(std::chrono::seconds{2})); } } TEST(MultiplexingDispatchableTest, removes_dispatchable_that_returns_false_from_dispatch) { bool dispatched{false}; auto dispatchee = std::make_shared([&dispatched](md::FdEvents) { dispatched = true; return false; }); md::MultiplexingDispatchable dispatcher{dispatchee}; dispatchee->trigger(); dispatchee->trigger(); ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd())); dispatcher.dispatch(md::FdEvent::readable); EXPECT_TRUE(dispatched); dispatched = false; while (mt::fd_is_readable(dispatcher.watch_fd())) { dispatcher.dispatch(md::FdEvent::readable); } EXPECT_FALSE(dispatched); } TEST(MultiplexingDispatchableTest, multiple_removals_are_threadsafe) { using namespace testing; auto canary_killed = std::make_shared(); auto canary = std::shared_ptr(new int, [canary_killed](int* victim) { delete victim; canary_killed->raise(); }); auto in_dispatch = std::make_shared(); auto unblock_dispatchee = std::make_shared(); auto dispatcher = std::make_shared(); auto first_dispatchee = std::make_shared([canary, in_dispatch, unblock_dispatchee]() { in_dispatch->raise(); EXPECT_TRUE(unblock_dispatchee->wait_for(std::chrono::seconds{5})); EXPECT_THAT(canary.use_count(), Gt(0)); }); auto dummy_dispatchee = std::make_shared([](){}); dispatcher->add_watch(first_dispatchee); dispatcher->add_watch(dummy_dispatchee); first_dispatchee->trigger(); md::ThreadedDispatcher eventloop_one{"Bob", dispatcher}; md::ThreadedDispatcher eventloop_two{"Gerry", dispatcher}; EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1})); dispatcher->remove_watch(dummy_dispatchee); dispatcher->remove_watch(first_dispatchee); dispatcher.reset(); first_dispatchee.reset(); dummy_dispatchee.reset(); canary.reset(); unblock_dispatchee->raise(); EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2})); } TEST(MultiplexingDispatchableTest, automatic_removals_are_threadsafe) { auto dispatcher = std::make_shared(); auto dispatchee = std::make_shared([](md::FdEvents) { return false; }); dispatcher->add_watch(dispatchee, md::DispatchReentrancy::reentrant); md::ThreadedDispatcher eventloop{"Avocado", dispatcher}; eventloop.add_thread(); eventloop.add_thread(); eventloop.add_thread(); eventloop.add_thread(); dispatchee->trigger(); } ./tests/unit-tests/options/0000755000015600001650000000000012676616126016066 5ustar jenkinsjenkins./tests/unit-tests/options/test_program_option.cpp0000644000015600001650000001457212676616125022700 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/options/program_option.h" #include #include #include #include #include namespace bpo = boost::program_options; namespace { struct ProgramOption : testing::Test { ProgramOption() : desc("Options") { desc.add_options() ("file,f", bpo::value(), "") ("flag-yes", bpo::value(), "flag \"yes\"") ("flag-true", bpo::value(), "flag \"true\"") ("flag-no", bpo::value(), "flag \"no\"") ("flag-false", bpo::value(), "flag \"false\"") ("count,c", bpo::value(), "count") ("defaulted,d", bpo::value()->default_value(666), "defaulted") ("help,h", "this help text"); } bpo::options_description desc; }; } TEST_F(ProgramOption, parse_device_line_long) { mir::options::ProgramOption po; const int argc = 3; char const* argv[argc] = { __PRETTY_FUNCTION__, "--file", "test_file" }; po.parse_arguments(desc, argc, argv); EXPECT_EQ("test_file", po.get("file", "default")); EXPECT_EQ("default", po.get("garbage", "default")); EXPECT_TRUE(po.is_set("file")); EXPECT_FALSE(po.is_set("garbage")); } TEST_F(ProgramOption, parse_device_line_short) { mir::options::ProgramOption po; const int argc = 3; char const* argv[argc] = { __PRETTY_FUNCTION__, "-f", "test_file" }; po.parse_arguments(desc, argc, argv); EXPECT_EQ("test_file", po.get("file", "default")); EXPECT_EQ("default", po.get("garbage", "default")); EXPECT_TRUE(po.is_set("file")); EXPECT_FALSE(po.is_set("garbage")); } TEST_F(ProgramOption, parse_device_yes_no) { mir::options::ProgramOption po; const int argc = 9; char const* argv[argc] = { __PRETTY_FUNCTION__, "--flag-yes", "yes", "--flag-true", "true", "--flag-no", "no", "--flag-false", "false" }; po.parse_arguments(desc, argc, argv); EXPECT_TRUE(po.get("flag-yes", false)); EXPECT_TRUE(po.get("flag-true", false)); EXPECT_FALSE(po.get("flag-no", true)); EXPECT_FALSE(po.get("flag-false", true)); EXPECT_FALSE(po.get("flag-default", false)); EXPECT_TRUE(po.get("flag-default", true)); } TEST_F(ProgramOption, parse_device_line_help) { mir::options::ProgramOption po; const int argc = 2; char const* argv[argc] = { __PRETTY_FUNCTION__, "--help" }; po.parse_arguments(desc, argc, argv); EXPECT_TRUE(po.is_set("help")); } TEST_F(ProgramOption, matches_compound_name_lookup) { using testing::Eq; mir::options::ProgramOption po; const int argc = 8; char const* argv[argc] = { __PRETTY_FUNCTION__, "--help", "--flag-yes", "yes", "-f", "test_file", "-c", "27" }; po.parse_arguments(desc, argc, argv); EXPECT_THAT(po.is_set("help,h"), Eq(true)); EXPECT_THAT(po.get("flag-yes,y", false), Eq(true)); EXPECT_THAT(po.get("file,f", "default"), Eq("test_file")); EXPECT_THAT(po.get("count,c", 42), Eq(27)); } TEST_F(ProgramOption, defaulted_values_are_available) { using testing::Eq; mir::options::ProgramOption po; const int argc = 1; char const* argv[argc] = { __PRETTY_FUNCTION__ }; po.parse_arguments(desc, argc, argv); EXPECT_THAT(po.is_set("defaulted"), Eq(true)); EXPECT_THAT(po.get("defaulted", 3), Eq(666)); } TEST_F(ProgramOption, test_boost_any_overload) { using testing::Eq; mir::options::ProgramOption po; const int argc = 8; char const* argv[argc] = { __PRETTY_FUNCTION__, "--help", "--flag-yes", "yes", "-f", "test_file", "-c", "27" }; po.parse_arguments(desc, argc, argv); EXPECT_THAT(po.is_set("help,h"), Eq(true)); EXPECT_THAT(po.get("flag-yes,y"), Eq(true)); EXPECT_THAT(po.get("file,f"), Eq("test_file")); EXPECT_THAT(po.get("count,c"), Eq(27)); EXPECT_THAT(po.get("defaulted"), Eq(666)); auto const error_result = po.get("garbage"); EXPECT_THAT(error_result.empty(), Eq(true)); EXPECT_THROW(boost::any_cast(error_result), std::bad_cast); EXPECT_THROW(po.get("flag-yes,y"), std::bad_cast); } TEST_F(ProgramOption, unparsed_command_line_returns_unprocessed_tokens) { using namespace testing; mir::options::ProgramOption po; const int argc = 11; char const* argv[argc] = { __PRETTY_FUNCTION__, "--flag-yes", "yes", "--hello", "-f", "test_file", "world", "-c", "27", "--answer", "42" }; po.parse_arguments(desc, argc, argv); EXPECT_THAT(po.unparsed_command_line(), ElementsAre("--hello", "world", "--answer", "42")); } TEST(ProgramOptionEnv, parse_environment) { // Env variables should be uppercase and "_" delimited char const* name = "some-key"; char const* key = "SOME_KEY"; char const* value = "test_value"; auto const env = std::string(__PRETTY_FUNCTION__) + key; setenv(env.c_str(), value, true); bpo::options_description desc; desc.add_options() (name, bpo::value()); mir::options::ProgramOption po; po.parse_environment(desc, __PRETTY_FUNCTION__); EXPECT_EQ(value, po.get(name, "default")); EXPECT_EQ("default", po.get("garbage", "default")); EXPECT_TRUE(po.is_set(name)); EXPECT_FALSE(po.is_set("garbage")); } // TODO need to parse something TEST(ProgramOptionFile, parse_files) { bpo::options_description desc("Config file options"); mir::options::ProgramOption po; po.parse_file(desc, "test.config"); } ./tests/unit-tests/options/CMakeLists.txt0000644000015600001650000000022712676616125020626 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_program_option.cpp ) set( UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/test_udev_wrapper.cpp0000644000015600001650000004154212676616125020646 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir_test_framework/udev_environment.h" #include "mir/udev/wrapper.h" #include #include #include #include #include #include namespace mtf=mir_test_framework; namespace { bool KilledByInvalidMemoryAccess(int exit_status) { return testing::KilledBySignal(SIGSEGV)(exit_status) || testing::KilledBySignal(SIGBUS)(exit_status) || testing::KilledBySignal(SIGABRT)(exit_status) || // It seems that valgrind kills us with SIGKILL testing::KilledBySignal(SIGKILL)(exit_status); } class UdevWrapperTest : public ::testing::Test { public: mtf::UdevEnvironment udev_environment; }; } TEST_F(UdevWrapperTest, IteratesOverCorrectNumberOfDevices) { udev_environment.add_device("drm", "fakedev1", NULL, {}, {}); udev_environment.add_device("drm", "fakedev2", NULL, {}, {}); udev_environment.add_device("drm", "fakedev3", NULL, {}, {}); udev_environment.add_device("drm", "fakedev4", NULL, {}, {}); udev_environment.add_device("drm", "fakedev5", NULL, {}, {}); auto ctx = std::make_shared(); mir::udev::Enumerator enumerator(ctx); enumerator.scan_devices(); int device_count = 0; for (auto& device : enumerator) { // Silence unused variable warning static_cast(device); ++device_count; } ASSERT_EQ(device_count, 5); } TEST_F(UdevWrapperTest, EnumeratorMatchSubsystemIncludesCorrectDevices) { udev_environment.add_device("drm", "fakedrm1", NULL, {}, {}); udev_environment.add_device("scsi", "fakescsi1", NULL, {}, {}); udev_environment.add_device("drm", "fakedrm2", NULL, {}, {}); udev_environment.add_device("usb", "fakeusb1", NULL, {}, {}); udev_environment.add_device("usb", "fakeusb2", NULL, {}, {}); auto ctx = std::make_shared(); mir::udev::Enumerator devices(ctx); devices.match_subsystem("drm"); devices.scan_devices(); for (auto& device : devices) { ASSERT_STREQ("drm", device.subsystem()); } } TEST_F(UdevWrapperTest, UdevDeviceHasCorrectDevType) { auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {"DEVTYPE", "drm_minor"}); mir::udev::Context ctx; auto dev = ctx.device_from_syspath(sysfs_path); ASSERT_STREQ("drm_minor", dev->devtype()); } TEST_F(UdevWrapperTest, UdevDeviceHasCorrectDevPath) { auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {}); mir::udev::Context ctx; auto dev = ctx.device_from_syspath(sysfs_path); ASSERT_STREQ("/devices/card0", dev->devpath()); } TEST_F(UdevWrapperTest, UdevDeviceHasCorrectDevNode) { auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {"DEVNAME", "/dev/dri/card0"}); mir::udev::Context ctx; auto dev = ctx.device_from_syspath(sysfs_path); ASSERT_STREQ("/dev/dri/card0", dev->devnode()); } TEST_F(UdevWrapperTest, UdevDeviceHasCorrectProperties) { auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {"AWESOME", "yes", "REALLY", "absolutely"}); mir::udev::Context ctx; auto dev = ctx.device_from_syspath(sysfs_path); ASSERT_STREQ("yes", dev->property("AWESOME")); ASSERT_STREQ("absolutely", dev->property("REALLY")); ASSERT_EQ(NULL, dev->property("SOMETHING_ELSE")); } TEST_F(UdevWrapperTest, UdevDeviceComparisonIsReflexive) { auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {}); mir::udev::Context ctx; auto dev = ctx.device_from_syspath(sysfs_path); EXPECT_TRUE(*dev == *dev); } TEST_F(UdevWrapperTest, UdevDeviceComparisonIsSymmetric) { auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {}); mir::udev::Context ctx; std::shared_ptr same_one = ctx.device_from_syspath(sysfs_path); std::shared_ptr same_two = ctx.device_from_syspath(sysfs_path); EXPECT_TRUE(*same_one == *same_two); EXPECT_TRUE(*same_two == *same_one); } TEST_F(UdevWrapperTest, UdevDeviceDifferentDevicesCompareFalse) { auto path_one = udev_environment.add_device("drm", "card0", NULL, {}, {}); auto path_two = udev_environment.add_device("drm", "card1", NULL, {}, {}); mir::udev::Context ctx; auto dev_one = ctx.device_from_syspath(path_one); auto dev_two = ctx.device_from_syspath(path_two); EXPECT_FALSE(*dev_one == *dev_two); EXPECT_FALSE(*dev_two == *dev_one); } TEST_F(UdevWrapperTest, UdevDeviceDifferentDevicesAreNotEqual) { auto path_one = udev_environment.add_device("drm", "card0", NULL, {}, {}); auto path_two = udev_environment.add_device("drm", "card1", NULL, {}, {}); mir::udev::Context ctx; auto dev_one = ctx.device_from_syspath(path_one); auto dev_two = ctx.device_from_syspath(path_two); EXPECT_TRUE(*dev_one != *dev_two); EXPECT_TRUE(*dev_two != *dev_one); } TEST_F(UdevWrapperTest, UdevDeviceSameDeviceIsNotNotEqual) { auto sysfs_path = udev_environment.add_device("drm", "card0", NULL, {}, {}); mir::udev::Context ctx; auto same_one = ctx.device_from_syspath(sysfs_path); auto same_two = ctx.device_from_syspath(sysfs_path); EXPECT_FALSE(*same_one != *same_two); EXPECT_FALSE(*same_two != *same_one); } TEST_F(UdevWrapperTest, EnumeratorMatchParentMatchesOnlyChildren) { auto card0_syspath = udev_environment.add_device("drm", "card0", NULL, {}, {}); udev_environment.add_device("usb", "fakeusb", NULL, {}, {}); udev_environment.add_device("drm", "card0-HDMI1", "/sys/devices/card0", {}, {}); udev_environment.add_device("drm", "card0-VGA1", "/sys/devices/card0", {}, {}); udev_environment.add_device("drm", "card0-LVDS1", "/sys/devices/card0", {}, {}); auto ctx = std::make_shared(); mir::udev::Enumerator devices(ctx); auto drm_device = ctx->device_from_syspath(card0_syspath); devices.match_parent(*drm_device); devices.scan_devices(); int child_count = 0; for (auto& device : devices) { EXPECT_STREQ("drm", device.subsystem()); ++child_count; } EXPECT_EQ(4, child_count); } TEST_F(UdevWrapperTest, EnumeratorThrowsLogicErrorIfIteratedBeforeScanned) { auto ctx = std::make_shared(); mir::udev::Enumerator devices(ctx); EXPECT_THROW({ devices.begin(); }, std::logic_error); } TEST_F(UdevWrapperTest, EnumeratorLogicErrorHasSensibleMessage) { auto ctx = std::make_shared(); mir::udev::Enumerator devices(ctx); std::string error_msg; try { devices.begin(); } catch (std::logic_error& e) { error_msg = e.what(); } EXPECT_STREQ("Attempted to iterate over udev devices without first scanning", error_msg.c_str()); } TEST_F(UdevWrapperTest, EnumeratorEnumeratesEmptyList) { auto ctx = std::make_shared(); mir::udev::Enumerator devices(ctx); devices.scan_devices(); for (auto& device : devices) ADD_FAILURE() << "Unexpected udev device: " << device.devpath(); } TEST_F(UdevWrapperTest, EnumeratorAddMatchSysnameIncludesCorrectDevices) { auto drm_sysfspath = udev_environment.add_device("drm", "card0", NULL, {}, {}); udev_environment.add_device("drm", "control64D", NULL, {}, {}); udev_environment.add_device("drm", "card0-LVDS1", drm_sysfspath.c_str(), {}, {}); udev_environment.add_device("drm", "card1", NULL, {}, {}); auto ctx = std::make_shared(); mir::udev::Enumerator devices(ctx); devices.match_sysname("card[0-9]"); devices.scan_devices(); for (auto& device : devices) { const char *start = strstr(device.devpath(), "card"); ASSERT_TRUE(start); int num; EXPECT_EQ(1, sscanf(start, "card%d", &num)) << "Unexpected device with devpath:" << device.devpath(); } } typedef UdevWrapperTest UdevWrapperDeathTest; TEST_F(UdevWrapperDeathTest, DereferencingEndReturnsInvalidObject) { ::testing::FLAGS_gtest_death_test_style = "threadsafe"; udev_environment.add_device("drm", "control64D", NULL, {}, {}); udev_environment.add_device("drm", "card1", NULL, {}, {}); mir::udev::Enumerator devices(std::make_shared()); devices.scan_devices(); EXPECT_EXIT((*devices.end()).subsystem(), KilledByInvalidMemoryAccess, ""); auto iter = devices.begin(); while(iter != devices.end()) { iter++; } EXPECT_EXIT((*iter).subsystem(), KilledByInvalidMemoryAccess, ""); } TEST_F(UdevWrapperTest, MemberDereferenceWorks) { udev_environment.add_device("drm", "control64D", NULL, {}, {}); udev_environment.add_device("drm", "card1", NULL, {}, {}); mir::udev::Enumerator devices(std::make_shared()); devices.scan_devices(); auto iter = devices.begin(); EXPECT_STREQ("drm", iter->subsystem()); EXPECT_STREQ("drm", iter->subsystem()); } TEST_F(UdevWrapperDeathTest, MemberDereferenceOfEndDies) { ::testing::FLAGS_gtest_death_test_style = "threadsafe"; udev_environment.add_device("drm", "control64D", NULL, {}, {}); udev_environment.add_device("drm", "card1", NULL, {}, {}); mir::udev::Enumerator devices(std::make_shared()); devices.scan_devices(); EXPECT_EXIT(devices.end()->subsystem(), KilledByInvalidMemoryAccess, ""); auto iter = devices.begin(); while(iter != devices.end()) { iter++; } EXPECT_EXIT(iter->subsystem(), KilledByInvalidMemoryAccess, ""); } TEST_F(UdevWrapperTest, UdevMonitorDoesNotTriggerBeforeEnabling) { mir::udev::Monitor monitor{mir::udev::Context()}; bool event_handler_called = false; udev_environment.add_device("drm", "control64D", NULL, {}, {}); monitor.process_events([&event_handler_called](mir::udev::Monitor::EventType, mir::udev::Device const&) {event_handler_called = true;}); EXPECT_FALSE(event_handler_called); } TEST_F(UdevWrapperTest, UdevMonitorTriggersAfterEnabling) { mir::udev::Monitor monitor{mir::udev::Context()}; bool event_handler_called = false; monitor.enable(); udev_environment.add_device("drm", "control64D", NULL, {}, {}); monitor.process_events([&event_handler_called](mir::udev::Monitor::EventType, mir::udev::Device const&) {event_handler_called = true;}); EXPECT_TRUE(event_handler_called); } TEST_F(UdevWrapperTest, UdevMonitorSendsRemoveEvent) { mir::udev::Monitor monitor{mir::udev::Context()}; bool remove_event_received = false; monitor.enable(); auto test_sysfspath = udev_environment.add_device("drm", "control64D", NULL, {}, {}); udev_environment.remove_device(test_sysfspath); monitor.process_events([&remove_event_received] (mir::udev::Monitor::EventType action, mir::udev::Device const&) { if (action == mir::udev::Monitor::EventType::REMOVED) remove_event_received = true; }); EXPECT_TRUE(remove_event_received); } TEST_F(UdevWrapperTest, UdevMonitorSendsChangedEvent) { mir::udev::Monitor monitor{mir::udev::Context()}; bool changed_event_received = false; monitor.enable(); auto test_sysfspath = udev_environment.add_device("drm", "control64D", NULL, {}, {}); udev_environment.emit_device_changed(test_sysfspath); monitor.process_events([&changed_event_received] (mir::udev::Monitor::EventType action, mir::udev::Device const&) { if (action == mir::udev::Monitor::EventType::CHANGED) changed_event_received = true; }); EXPECT_TRUE(changed_event_received); } TEST_F(UdevWrapperTest, UdevMonitorEventHasCorrectDeviceDetails) { mir::udev::Context ctx; mir::udev::Monitor monitor{mir::udev::Context()}; bool event_handler_called = false; monitor.enable(); auto sysfs_path = udev_environment.add_device("drm", "control64D", NULL, {}, {}); auto device = ctx.device_from_syspath(sysfs_path); monitor.process_events( [&event_handler_called, device](mir::udev::Monitor::EventType, mir::udev::Device const& dev) { event_handler_called = true; EXPECT_EQ(*device, dev); }); ASSERT_TRUE(event_handler_called); } TEST_F(UdevWrapperTest, UdevMonitorFdIsReadableWhenEventsAvailable) { mir::udev::Monitor monitor{mir::udev::Context()}; monitor.enable(); udev_environment.add_device("drm", "card0", NULL, {}, {}); struct pollfd fds; fds.fd = monitor.fd(); fds.events = POLLIN; ASSERT_GT(poll(&fds, 1, 0), 0); EXPECT_TRUE(fds.revents & POLLIN); } TEST_F(UdevWrapperTest, UdevMonitorFdIsUnreadableAfterProcessingEvents) { mir::udev::Monitor monitor{mir::udev::Context()}; monitor.enable(); udev_environment.add_device("drm", "card0", NULL, {}, {}); udev_environment.add_device("drm", "card1", NULL, {}, {}); udev_environment.add_device("usb", "mightymouse", NULL, {}, {}); struct pollfd fds; fds.fd = monitor.fd(); fds.events = POLLIN; ASSERT_GT(poll(&fds, 1, 0), 0); ASSERT_TRUE(fds.revents & POLLIN); monitor.process_events([](mir::udev::Monitor::EventType, mir::udev::Device const&){}); EXPECT_EQ(poll(&fds, 1, 0), 0); } TEST_F(UdevWrapperTest, UdevMonitorFiltersByPathAndType) { mir::udev::Context ctx; mir::udev::Monitor monitor{mir::udev::Context()}; bool event_received = false; monitor.filter_by_subsystem_and_type("drm", "drm_minor"); monitor.enable(); auto test_sysfspath = udev_environment.add_device("drm", "control64D", NULL, {}, {"DEVTYPE", "drm_minor"}); auto minor_device = ctx.device_from_syspath(test_sysfspath); udev_environment.add_device("drm", "card0-LVDS1", test_sysfspath.c_str(), {}, {}); udev_environment.add_device("usb", "mightymouse", NULL, {}, {}); monitor.process_events([&event_received, minor_device] (mir::udev::Monitor::EventType, mir::udev::Device const& dev) { EXPECT_EQ(dev, *minor_device); event_received = true; }); ASSERT_TRUE(event_received); } TEST_F(UdevWrapperTest, UdevMonitorFiltersAreAdditive) { mir::udev::Context ctx; mir::udev::Monitor monitor{mir::udev::Context()}; bool usb_event_received = false, drm_event_recieved = false; monitor.filter_by_subsystem_and_type("drm", "drm_minor"); monitor.filter_by_subsystem_and_type("usb", "hid"); monitor.enable(); auto drm_sysfspath = udev_environment.add_device("drm", "control64D", NULL, {}, {"DEVTYPE", "drm_minor"}); auto drm_device = ctx.device_from_syspath(drm_sysfspath); udev_environment.add_device("drm", "card0-LVDS1", drm_sysfspath.c_str(), {}, {}); auto usb_sysfspath = udev_environment.add_device("usb", "mightymouse", NULL, {}, {"DEVTYPE", "hid"}); auto usb_device = ctx.device_from_syspath(usb_sysfspath); monitor.process_events([&drm_event_recieved, drm_device, &usb_event_received, usb_device] (mir::udev::Monitor::EventType, mir::udev::Device const& dev) { if (dev == *drm_device) drm_event_recieved = true; if (dev == *usb_device) usb_event_received = true; }); EXPECT_TRUE(drm_event_recieved); EXPECT_TRUE(usb_event_received); } TEST_F(UdevWrapperTest, UdevMonitorFiltersApplyAfterEnable) { mir::udev::Context ctx; mir::udev::Monitor monitor{mir::udev::Context()}; bool event_received = false; monitor.enable(); monitor.filter_by_subsystem_and_type("drm", "drm_minor"); auto test_sysfspath = udev_environment.add_device("drm", "control64D", NULL, {}, {"DEVTYPE", "drm_minor"}); auto minor_device = ctx.device_from_syspath(test_sysfspath); udev_environment.add_device("drm", "card0-LVDS1", test_sysfspath.c_str(), {}, {}); udev_environment.add_device("usb", "mightymouse", NULL, {}, {}); monitor.process_events([&event_received, minor_device] (mir::udev::Monitor::EventType, mir::udev::Device const& dev) { EXPECT_EQ(dev, *minor_device); event_received = true; }); ASSERT_TRUE(event_received); } ./tests/unit-tests/frontend/0000755000015600001650000000000012676616160016210 5ustar jenkinsjenkins./tests/unit-tests/frontend/test_protobuf_message_processor.cpp0000644000015600001650000001526312676616125025426 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/frontend/protobuf_message_sender.h" #include "mir/frontend/message_processor_report.h" #include "src/server/frontend/display_server.h" #include "src/server/frontend/protobuf_message_processor.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/stub_display_server.h" #include "mir_protobuf_wire.pb.h" #include #include namespace mf = mir::frontend; namespace mfd = mir::frontend::detail; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace gp = google::protobuf; namespace mp = mir::protobuf; namespace mpw = mir::protobuf::wire; namespace { struct StubProtobufMessageSender : mfd::ProtobufMessageSender { void send_response(gp::uint32, gp::MessageLite*, mf::FdSets const&) override { } }; struct StubMessageProcessorReport : mf::MessageProcessorReport { void received_invocation(void const*, int, std::string const&) override { } void completed_invocation(void const*, int, bool) override { } void unknown_method(void const*, int, std::string const&) override { } void exception_handled(void const*, int, std::exception const&) override { } void exception_handled(void const*, std::exception const&) override { } }; struct StubDisplayServer : mtd::StubDisplayServer { void exchange_buffer( mp::BufferRequest const*, mp::Buffer* response, gp::Closure* closure) override { exchange_buffer_response = response; exchange_closure = closure; } void create_surface( mp::SurfaceParameters const*, mp::Surface* response, google::protobuf::Closure* closure) { response->mutable_buffer_stream(); auto before = response->buffer_stream().has_buffer(); closure->Run(); auto after = response->buffer_stream().has_buffer(); changed_during_create_surface_closure = before != after; } void create_buffer_stream( mp::BufferStreamParameters const*, mp::BufferStream* response, google::protobuf::Closure* closure) { auto before = response->has_buffer(); closure->Run(); auto after = response->has_buffer(); changed_during_create_bstream_closure = before != after; } mp::Buffer* exchange_buffer_response; gp::Closure* exchange_closure; bool changed_during_create_surface_closure; bool changed_during_create_bstream_closure; }; } TEST(ProtobufMessageProcessor, preserves_response_resource_for_exchange_buffer) { using namespace testing; StubProtobufMessageSender stub_msg_sender; StubMessageProcessorReport stub_report; StubDisplayServer stub_display_server; mfd::ProtobufMessageProcessor pb_message_processor( mt::fake_shared(stub_msg_sender), mt::fake_shared(stub_display_server), mt::fake_shared(stub_report)); std::shared_ptr mp = mt::fake_shared(pb_message_processor); mpw::Invocation raw_invocation; mp::BufferRequest buffer_request; std::string str_parameters; buffer_request.SerializeToString(&str_parameters); raw_invocation.set_parameters(str_parameters.c_str()); raw_invocation.set_method_name("exchange_buffer"); mfd::Invocation invocation(raw_invocation); std::vector fds; mp->dispatch(invocation, fds); ASSERT_THAT(stub_display_server.exchange_buffer_response, testing::Ne(nullptr)); ASSERT_THAT(stub_display_server.exchange_closure, testing::Ne(nullptr)); int num_data{5}; stub_display_server.exchange_buffer_response->clear_data(); for(auto i = 0; i < num_data; i++) stub_display_server.exchange_buffer_response->add_data(i); EXPECT_THAT(stub_display_server.exchange_buffer_response->data().size(), Eq(num_data)); stub_display_server.exchange_closure->Run(); } TEST(ProtobufMessageProcessor, doesnt_inject_buffers_when_creating_surface) { using namespace testing; StubProtobufMessageSender stub_msg_sender; StubMessageProcessorReport stub_report; StubDisplayServer stub_display_server; mfd::ProtobufMessageProcessor pb_message_processor( mt::fake_shared(stub_msg_sender), mt::fake_shared(stub_display_server), mt::fake_shared(stub_report)); std::shared_ptr mp = mt::fake_shared(pb_message_processor); mpw::Invocation raw_invocation; mp::SurfaceParameters request; request.set_width(1); request.set_height(1); request.set_pixel_format(1); request.set_buffer_usage(1); std::string str_parameters; request.SerializeToString(&str_parameters); str_parameters.shrink_to_fit(); raw_invocation.set_parameters(str_parameters.c_str()); raw_invocation.set_method_name("create_surface"); mfd::Invocation invocation(raw_invocation); std::vector fds; mp->dispatch(invocation, fds); EXPECT_FALSE(stub_display_server.changed_during_create_surface_closure); } TEST(ProtobufMessageProcessor, doesnt_inject_buffers_when_creating_bstream) { using namespace testing; StubProtobufMessageSender stub_msg_sender; StubMessageProcessorReport stub_report; StubDisplayServer stub_display_server; mfd::ProtobufMessageProcessor pb_message_processor( mt::fake_shared(stub_msg_sender), mt::fake_shared(stub_display_server), mt::fake_shared(stub_report)); std::shared_ptr mp = mt::fake_shared(pb_message_processor); mpw::Invocation raw_invocation; mp::BufferStreamParameters request; request.set_width(1); request.set_height(1); request.set_pixel_format(1); request.set_buffer_usage(1); std::string str_parameters; request.SerializeToString(&str_parameters); str_parameters.shrink_to_fit(); raw_invocation.set_parameters(str_parameters.c_str()); raw_invocation.set_method_name("create_buffer_stream"); mfd::Invocation invocation(raw_invocation); std::vector fds; mp->dispatch(invocation, fds); EXPECT_FALSE(stub_display_server.changed_during_create_bstream_closure); } ./tests/unit-tests/frontend/test_buffering_message_sender.cpp0000644000015600001650000001440612676616125024774 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "src/server/frontend/message_sender.h" #include "src/server/frontend/reordering_message_sender.h" #include #include #include namespace mf = mir::frontend; class MockMessageSender : public mf::MessageSender { public: MOCK_METHOD3(send, void(char const*, size_t, mf::FdSets const&)); }; TEST(ReorderingMessageSender, sends_no_message_before_being_uncorked) { using namespace testing; auto mock_sender = std::make_shared>(); mf::ReorderingMessageSender sender{mock_sender}; bool messages_sent{false}; ON_CALL(*mock_sender, send(_,_,_)) .WillByDefault(InvokeWithoutArgs([&messages_sent] { messages_sent = true;})); std::array data; sender.send(data.data(), data.size(), {}); EXPECT_FALSE(messages_sent); } TEST(ReorderingMessageSender, sends_messages_in_order_when_uncorked) { using namespace testing; auto mock_sender = std::make_shared>(); std::vector> messages_sent; ON_CALL(*mock_sender, send(_,_,_)) .WillByDefault(Invoke( [&messages_sent](char const* data, size_t length, auto) { messages_sent.emplace_back(std::vector(data, data + length)); })); mf::ReorderingMessageSender sender{mock_sender}; std::array, 9> messages; for (auto& message : messages) { auto content = std::to_string(reinterpret_cast(&message)); message = std::vector(content.begin(), content.end()); } for (auto const& message : messages) { sender.send(message.data(), message.size(), {}); } ASSERT_THAT(messages_sent, IsEmpty()); sender.uncork(); EXPECT_THAT(messages_sent, ElementsAreArray(messages.data(), messages.size())); } TEST(ReorderingMessageSender, messages_sent_after_uncork_proceed_normally) { using namespace testing; auto mock_sender = std::make_shared>(); std::vector> messages_sent; ON_CALL(*mock_sender, send(_,_,_)) .WillByDefault(Invoke( [&messages_sent](char const* data, size_t length, auto) { messages_sent.emplace_back(std::vector(data, data + length)); })); mf::ReorderingMessageSender sender{mock_sender}; std::array, 9> messages; for (auto& message : messages) { auto content = std::to_string(reinterpret_cast(&message)); message = std::vector(content.begin(), content.end()); } sender.uncork(); for (auto const& message : messages) { sender.send(message.data(), message.size(), {}); EXPECT_THAT(messages_sent.back(), Eq(message)); } } TEST(ReorderingMessageSender, calling_uncork_twice_is_harmless) { using namespace testing; auto mock_sender = std::make_shared>(); std::vector> messages_sent; ON_CALL(*mock_sender, send(_,_,_)) .WillByDefault(Invoke( [&messages_sent](char const* data, size_t length, auto) { messages_sent.emplace_back(std::vector(data, data + length)); })); mf::ReorderingMessageSender sender{mock_sender}; std::array, 9> messages; for (auto& message : messages) { auto content = std::to_string(reinterpret_cast(&message)); message = std::vector(content.begin(), content.end()); } for (auto const& message : messages) { sender.send(message.data(), message.size(), {}); } ASSERT_THAT(messages_sent, IsEmpty()); sender.uncork(); sender.uncork(); EXPECT_THAT(messages_sent, ElementsAreArray(messages.data(), messages.size())); } TEST(ReorderingMessageSender, messages_with_fds_are_sent) { using namespace testing; auto mock_sender = std::make_shared>(); struct Message { std::vector data; mf::FdSets fds; }; std::vector messages_sent; ON_CALL(*mock_sender, send(_,_,_)) .WillByDefault(Invoke( [&messages_sent](char const* data, size_t length, auto fds) { messages_sent.emplace_back( Message { std::vector(data, data + length), mf::FdSets{fds} }); })); std::array fdsets; for (auto& fds : fdsets) { fds = { {mir::Fd{::open("/dev/null", O_RDONLY)}, mir::Fd{::open("/dev/null", O_RDONLY)}}, {mir::Fd{::open("/dev/null", O_RDONLY)}} }; } std::array, 3> datas; for (auto& data : datas) { data = { 'a', 'b', 'c' }; } mf::ReorderingMessageSender sender{mock_sender}; for (size_t i = 0; i < datas.size(); ++i) { sender.send(datas[i].data(), datas[i].size(), fdsets[i]); } EXPECT_THAT(messages_sent, IsEmpty()); sender.uncork(); for (size_t i = 0; i < datas.size(); ++i) { sender.send(datas[i].data(), datas[i].size(), fdsets[i]); } for (size_t i = 0; i < datas.size(); ++i) { EXPECT_THAT(messages_sent[i].data, Eq(datas[i])); EXPECT_THAT(messages_sent[i].fds, Eq(fdsets[i])); } for (size_t i = 0; i < datas.size(); ++i) { EXPECT_THAT(messages_sent[i + datas.size()].data, Eq(datas[i])); EXPECT_THAT(messages_sent[i + datas.size()].fds, Eq(fdsets[i])); } } ./tests/unit-tests/frontend/test_protobuf_surface_apis.cpp0000644000015600001650000001755612676616125024356 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Alan Griffiths */ #include "mir/frontend/connector.h" #include "src/server/frontend/resource_cache.h" #include "mir_protobuf.pb.h" #include "mir/test/doubles/stub_ipc_factory.h" #include "mir/test/stub_server_tool.h" #include "mir/test/test_protobuf_client.h" #include "mir/test/test_protobuf_server.h" #include "mir_test_framework/testing_server_configuration.h" #include #include #include #include #include namespace mf = mir::frontend; namespace mt = mir::test; namespace mtf = mir_test_framework; namespace mir { namespace test { struct StubServerSurfaceCounter : public StubServerTool { int surface_count; StubServerSurfaceCounter() : surface_count(0) { } void create_surface( const mir::protobuf::SurfaceParameters* request, mir::protobuf::Surface* response, google::protobuf::Closure* done) override { { std::unique_lock lock(guard); ++surface_count; } StubServerTool::create_surface(request, response, done); } void expect_surface_count(int expected_count) { std::unique_lock ul(guard); while (surface_count != expected_count) wait_condition.wait(ul); EXPECT_EQ(expected_count, surface_count); } }; } struct ProtobufSurfaceCounter : public ::testing::Test { void SetUp() { stub_server_tool = std::make_shared(); stub_server = std::make_shared(mtf::test_socket_file(), stub_server_tool); stub_server->comm->start(); stub_client = std::make_shared(mtf::test_socket_file(), 10000); stub_client->connect_parameters.set_application_name(__PRETTY_FUNCTION__); } void TearDown() { stub_server.reset(); } std::shared_ptr stub_client; std::shared_ptr stub_server_tool; std::shared_ptr stub_server; }; TEST_F(ProtobufSurfaceCounter, server_creates_surface_on_create_surface_call) { EXPECT_CALL(*stub_client, create_surface_done()).Times(testing::AtLeast(1)); stub_client->display_server.create_surface( &stub_client->surface_parameters, &stub_client->surface, google::protobuf::NewCallback(stub_client.get(), &mt::TestProtobufClient::create_surface_done)); stub_client->wait_for_create_surface(); stub_server_tool->expect_surface_count(1); } TEST_F(ProtobufSurfaceCounter, surface_count_is_zero_after_connection) { using namespace testing; EXPECT_CALL(*stub_client, connect_done()).Times(AtLeast(0)); stub_client->display_server.connect( &stub_client->connect_parameters, &stub_client->connection, google::protobuf::NewCallback(stub_client.get(), &mt::TestProtobufClient::connect_done)); stub_client->wait_for_connect_done(); stub_server_tool->expect_surface_count(0); } TEST_F(ProtobufSurfaceCounter, each_create_surface_results_in_a_new_surface_being_created) { int const surface_count{5}; EXPECT_CALL(*stub_client, create_surface_done()).Times(surface_count); for (int i = 0; i != surface_count; ++i) { stub_client->create_surface(); stub_client->wait_for_create_surface(); } stub_server_tool->expect_surface_count(surface_count); } TEST_F(ProtobufSurfaceCounter, each_create_surface_results_in_a_new_surface_being_created_asynchronously) { int const surface_count{5}; EXPECT_CALL(*stub_client, create_surface_done()).Times(surface_count); for (int i = 0; i != surface_count; ++i) { stub_client->display_server.create_surface( &stub_client->surface_parameters, &stub_client->surface, google::protobuf::NewCallback(stub_client.get(), &mt::TestProtobufClient::create_surface_done)); } stub_server_tool->expect_surface_count(surface_count); stub_client->wait_for_surface_count(surface_count); } /* start Multi test cases */ struct ProtobufSocketMultiClientCommunicator : public ::testing::Test { static int const number_of_clients = 10; void SetUp() { using namespace testing; stub_server_tool = std::make_shared(); stub_server = std::make_shared(mtf::test_socket_file(), stub_server_tool); stub_server->comm->start(); for(int i=0; i(mtf::test_socket_file(), 10000); clients.push_back(client_tmp); } } void TearDown() { clients.clear(); stub_server.reset(); stub_server_tool.reset(); } std::vector> clients; std::shared_ptr stub_server_tool; std::shared_ptr stub_server; }; TEST_F(ProtobufSocketMultiClientCommunicator, multiple_clients_can_connect_create_surface_and_disconnect) { for (int i = 0; i != number_of_clients; ++i) { EXPECT_CALL(*clients[i], create_surface_done()).Times(1); clients[i]->display_server.create_surface( &clients[i]->surface_parameters, &clients[i]->surface, google::protobuf::NewCallback(clients[i].get(), &mt::TestProtobufClient::create_surface_done)); clients[i]->wait_for_create_surface(); } stub_server_tool->expect_surface_count(number_of_clients); for (int i = 0; i != number_of_clients; ++i) { EXPECT_CALL(*clients[i], disconnect_done()).Times(1); clients[i]->display_server.disconnect( &clients[i]->ignored, &clients[i]->ignored, google::protobuf::NewCallback(clients[i].get(), &mt::TestProtobufClient::disconnect_done)); clients[i]->wait_for_disconnect_done(); } stub_server_tool->expect_surface_count(number_of_clients); } TEST_F(ProtobufSocketMultiClientCommunicator, multiple_clients_can_connect_and_disconnect_asynchronously) { for (int i = 0; i != number_of_clients; ++i) { EXPECT_CALL(*clients[i], create_surface_done()).Times(1); clients[i]->display_server.create_surface( &clients[i]->surface_parameters, &clients[i]->surface, google::protobuf::NewCallback(clients[i].get(), &mt::TestProtobufClient::create_surface_done)); } for (int i = 0; i != number_of_clients; ++i) { clients[i]->wait_for_create_surface(); } stub_server_tool->expect_surface_count(number_of_clients); for (int i = 0; i != number_of_clients; ++i) { EXPECT_CALL(*clients[i], disconnect_done()).Times(1); clients[i]->display_server.disconnect( &clients[i]->ignored, &clients[i]->ignored, google::protobuf::NewCallback(clients[i].get(), &mt::TestProtobufClient::disconnect_done)); } for (int i = 0; i != number_of_clients; ++i) { clients[i]->wait_for_disconnect_done(); } stub_server_tool->expect_surface_count(number_of_clients); } } ./tests/unit-tests/frontend/test_authorizing_display_changer.cpp0000644000015600001650000000672112676616157025546 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/server/frontend/authorizing_display_changer.h" #include "mir/test/doubles/mock_display_changer.h" #include "mir/test/doubles/null_display_configuration.h" #include "mir/test/fake_shared.h" #include #include namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mf = mir::frontend; struct AuthorizingDisplayChangerTest : public ::testing::TestWithParam> { testing::NiceMock underlying_changer; std::shared_ptr const conf{ std::make_shared()}; mf::AuthorizingDisplayChanger changer{ mt::fake_shared(underlying_changer), std::get<0>(GetParam()), std::get<1>(GetParam())}; }; TEST_P(AuthorizingDisplayChangerTest, configure_throws_only_when_configure_is_disallowed) { bool const configure_allowed = std::get<0>(GetParam()); if (configure_allowed) { EXPECT_NO_THROW({ changer.configure(std::shared_ptr(), conf); }); } else { EXPECT_THROW( { changer.configure(std::shared_ptr(), conf); }, std::runtime_error); } } TEST_P(AuthorizingDisplayChangerTest, set_base_configuration_throws_only_when_disallowed) { bool const base_configure_allowed = std::get<1>(GetParam()); if (base_configure_allowed) { EXPECT_NO_THROW({ changer.set_base_configuration(conf); }); } else { EXPECT_THROW( { changer.set_base_configuration(conf); }, std::runtime_error); } } TEST_P(AuthorizingDisplayChangerTest, only_calls_configure_if_authorized) { using namespace testing; bool const configure_allowed = std::get<0>(GetParam()); EXPECT_CALL(underlying_changer, configure(_, _)).Times(configure_allowed); try { changer.configure(std::shared_ptr(), conf); } catch (...) { } } TEST_P(AuthorizingDisplayChangerTest, only_calls_set_base_configuration_if_authorized) { using namespace testing; bool const base_configure_allowed = std::get<1>(GetParam()); EXPECT_CALL(underlying_changer, mock_set_base_configuration(_)).Times(base_configure_allowed); try { changer.set_base_configuration(conf); } catch (...) { } } TEST_P(AuthorizingDisplayChangerTest, can_always_access_config) { using namespace testing; EXPECT_CALL(underlying_changer, base_configuration()) .WillOnce(Return(conf)); auto returned_conf = changer.base_configuration(); EXPECT_EQ(conf, returned_conf); } INSTANTIATE_TEST_CASE_P(Authorization, AuthorizingDisplayChangerTest, testing::Combine(testing::Bool(), testing::Bool())); ./tests/unit-tests/frontend/test_session_mediator.cpp0000644000015600001650000014624112676616157023340 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/compositor/buffer_stream.h" #include "src/server/frontend/session_mediator.h" #include "src/server/report/null_report_factory.h" #include "src/server/frontend/resource_cache.h" #include "src/server/scene/application_session.h" #include "src/server/frontend/event_sender.h" #include "src/server/frontend/protobuf_buffer_packer.h" #include "mir/graphics/display.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/platform.h" #include "mir/graphics/platform_ipc_package.h" #include "mir/graphics/buffer_ipc_message.h" #include "mir/graphics/platform_operation_message.h" #include "mir/input/cursor_images.h" #include "mir/graphics/platform_ipc_operations.h" #include "src/server/scene/basic_surface.h" #include "mir/test/doubles/mock_display.h" #include "mir/test/doubles/mock_display_changer.h" #include "mir/test/doubles/null_display.h" #include "mir/test/doubles/null_event_sink.h" #include "mir/test/doubles/null_event_sink_factory.h" #include "mir/test/doubles/null_display_changer.h" #include "mir/test/doubles/mock_buffer_stream.h" #include "mir/test/doubles/mock_display.h" #include "mir/test/doubles/mock_shell.h" #include "mir/test/doubles/mock_frontend_surface.h" #include "mir/test/doubles/mock_event_sink.h" #include "mir/test/doubles/mock_event_sink_factory.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/stub_session.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/null_screencast.h" #include "mir/test/doubles/null_application_not_responding_detector.h" #include "mir/test/doubles/mock_platform_ipc_operations.h" #include "mir/test/doubles/null_message_sender.h" #include "mir/test/doubles/mock_message_sender.h" #include "mir/test/doubles/mock_input_device_hub.h" #include "mir/test/doubles/stub_input_device.h" #include "mir/test/display_config_matchers.h" #include "mir/test/input_devices_matcher.h" #include "mir/test/fake_shared.h" #include "mir/test/signal.h" #include "mir/frontend/connector.h" #include "mir/frontend/event_sink.h" #include "mir/cookie/authority.h" #include "mir_protobuf.pb.h" #include "mir_protobuf_wire.pb.h" #include "gmock_set_arg.h" #include #include #include #include #include #include namespace mf = mir::frontend; namespace mg = mir::graphics; namespace mi = mir::input; namespace mc = mir::compositor; namespace ms = mir::scene; namespace geom = mir::geometry; namespace mp = mir::protobuf; namespace msh = mir::shell; namespace mt = mir::test; namespace mtd = mt::doubles; namespace mr = mir::report; namespace { struct MockResourceCache : public mf::MessageResourceCache { MOCK_METHOD2(save_resource, void(google::protobuf::MessageLite*, std::shared_ptr const&)); MOCK_METHOD2(save_fd, void(google::protobuf::MessageLite*, mir::Fd const&)); MOCK_METHOD1(free_resource, void(google::protobuf::MessageLite*)); }; struct MockConnector : public mf::Connector { public: void start() override {} void stop() override {} int client_socket_fd() const override { return 0; } MOCK_CONST_METHOD1(client_socket_fd, int (std::function const&)> const&)); }; class StubbedSession : public mtd::StubSession { public: StubbedSession() : last_surface_id{0} { } std::shared_ptr get_surface(mf::SurfaceId surface) const { if (mock_surfaces.find(surface) == mock_surfaces.end()) BOOST_THROW_EXCEPTION(std::logic_error("Invalid SurfaceId")); return mock_surfaces.at(surface); } std::shared_ptr get_buffer_stream(mf::BufferStreamId stream) const override { if (mock_streams.find(stream) == mock_streams.end()) BOOST_THROW_EXCEPTION(std::logic_error("Invalid SurfaceId")); return mock_streams.at(stream); } std::shared_ptr mock_surface_at(mf::SurfaceId id) { if (mock_surfaces.end() == mock_surfaces.find(id)) return create_mock_surface(id); return mock_surfaces.at(id); } std::shared_ptr mock_primary_stream_at(mf::SurfaceId id) { if (mock_surfaces.end() == mock_surfaces.find(id)) create_mock_surface(id); return mock_streams.at(mf::BufferStreamId(id.as_value())); } std::shared_ptr create_mock_surface(mf::SurfaceId id) { using namespace testing; auto surface = std::make_shared>(testing_client_input_fd); auto stream = std::make_shared>(); auto buffer1 = std::make_shared(); auto buffer2 = std::make_shared(); ON_CALL(*stream, swap_buffers(testing::_,testing::_)) .WillByDefault(testing::Invoke( [buffer1, buffer2](mg::Buffer* b, std::function complete) { if ((!b) || (b == buffer1.get())) complete(buffer2.get()); if (b == buffer2.get()) complete(buffer1.get()); })); ON_CALL(*surface, primary_buffer_stream()) .WillByDefault(Return(stream)); mock_surfaces[id] = surface; mock_streams[mf::BufferStreamId(id.as_value())] = stream; return surface; } std::shared_ptr create_mock_stream(mf::BufferStreamId id) { mock_streams[id] = std::make_shared>(); return mock_streams[id]; } mf::SurfaceId create_surface( ms::SurfaceCreationParameters const&, std::shared_ptr const&) { mf::SurfaceId id{last_surface_id}; if (mock_surfaces.end() == mock_surfaces.find(id)) create_mock_surface(id); last_surface_id++; return id; } mf::BufferStreamId create_buffer_stream(mg::BufferProperties const&) { mf::BufferStreamId id{last_surface_id}; if (mock_streams.end() == mock_streams.find(id)) create_mock_stream(id); last_surface_id++; return id; } void destroy_surface(mf::SurfaceId surface) { mock_surfaces.erase(surface); } std::map> mock_streams; std::map> mock_surfaces; static int const testing_client_input_fd; int last_surface_id; }; int const StubbedSession::testing_client_input_fd{11}; struct StubScreencast : mtd::NullScreencast { std::shared_ptr capture(mf::ScreencastSessionId) { return mt::fake_shared(stub_buffer); } mtd::StubBuffer stub_buffer; }; struct SessionMediator : public ::testing::Test { SessionMediator() : shell{std::make_shared>()}, graphics_changer{std::make_shared()}, surface_pixel_formats{mir_pixel_format_argb_8888, mir_pixel_format_xrgb_8888}, report{mr::null_session_mediator_report()}, resource_cache{std::make_shared()}, stub_screencast{std::make_shared()}, stubbed_session{std::make_shared()}, null_callback{google::protobuf::NewPermanentCallback(google::protobuf::DoNothing)}, mediator{ shell, mt::fake_shared(mock_ipc_operations), graphics_changer, surface_pixel_formats, report, std::make_shared(), std::make_shared(), resource_cache, stub_screencast, &connector, nullptr, nullptr, std::make_shared(), mir::cookie::Authority::create(), mt::fake_shared(mock_hub)} { using namespace ::testing; ON_CALL(*shell, open_session(_, _, _)).WillByDefault(Return(stubbed_session)); ON_CALL(*shell, create_surface( _, _, _)).WillByDefault( WithArgs<1, 2>(Invoke(stubbed_session.get(), &StubbedSession::create_surface))); ON_CALL(*shell, destroy_surface( _, _)).WillByDefault( WithArg<1>(Invoke(stubbed_session.get(), &StubbedSession::destroy_surface))); } std::shared_ptr create_session_mediator_with_display_changer( std::shared_ptr const& display_changer) { return std::make_shared( shell, mt::fake_shared(mock_ipc_operations), display_changer, surface_pixel_formats, report, std::make_shared(), std::make_shared(), resource_cache, std::make_shared(), nullptr, nullptr, nullptr, std::make_shared(), mir::cookie::Authority::create(), mt::fake_shared(mock_hub)); } MockConnector connector; testing::NiceMock mock_ipc_operations; testing::NiceMock mock_hub; std::shared_ptr> const shell; std::shared_ptr const graphics_changer; std::vector const surface_pixel_formats; std::shared_ptr const report; std::shared_ptr const resource_cache; std::shared_ptr const stub_screencast; std::shared_ptr const stubbed_session; std::unique_ptr null_callback; mf::SessionMediator mediator; mp::ConnectParameters connect_parameters; mp::Connection connection; mp::SurfaceParameters surface_parameters; mp::Surface surface_response; mp::Void void_response; mp::SurfaceId surface_id_request; mp::Buffer buffer_response; mp::BufferRequest buffer_request; }; } TEST_F(SessionMediator, disconnect_releases_session) { using namespace ::testing; EXPECT_CALL(*shell, close_session(_)) .Times(1); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, connect_calls_connect_handler) { using namespace ::testing; int connects_handled_count = 0; mf::ConnectionContext const context { [&](std::shared_ptr const&) { ++connects_handled_count; }, nullptr }; mf::SessionMediator mediator{ shell, mt::fake_shared(mock_ipc_operations), graphics_changer, surface_pixel_formats, report, std::make_shared(), std::make_shared(), resource_cache, stub_screencast, context, nullptr, nullptr, std::make_shared(), mir::cookie::Authority::create(), mt::fake_shared(mock_hub)}; EXPECT_THAT(connects_handled_count, Eq(0)); mediator.connect(&connect_parameters, &connection, null_callback.get()); EXPECT_THAT(connects_handled_count, Eq(1)); mediator.disconnect(nullptr, nullptr, null_callback.get()); EXPECT_THAT(connects_handled_count, Eq(1)); } TEST_F(SessionMediator, calling_methods_before_connect_throws) { EXPECT_THROW({ mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); }, std::logic_error); EXPECT_THROW({ mediator.next_buffer(&surface_id_request, &buffer_response, null_callback.get()); }, std::logic_error); EXPECT_THROW({ mediator.exchange_buffer(&buffer_request, &buffer_response, null_callback.get()); }, std::logic_error); EXPECT_THROW({ mediator.release_surface(&surface_id_request, nullptr, null_callback.get()); }, std::logic_error); EXPECT_THROW({ mediator.disconnect(nullptr, nullptr, null_callback.get()); }, std::logic_error); } TEST_F(SessionMediator, calling_methods_after_connect_works) { mediator.connect(&connect_parameters, &connection, null_callback.get()); EXPECT_NO_THROW({ mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); *buffer_request.mutable_buffer() = surface_response.buffer_stream().buffer(); buffer_request.mutable_id()->set_value(surface_response.id().value()); mediator.next_buffer(&surface_id_request, &buffer_response, null_callback.get()); mediator.exchange_buffer(&buffer_request, &buffer_response, null_callback.get()); mediator.release_surface(&surface_id_request, nullptr, null_callback.get()); }); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, calling_methods_after_disconnect_throws) { mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.disconnect(nullptr, nullptr, null_callback.get()); EXPECT_THROW({ mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); }, std::logic_error); EXPECT_THROW({ mediator.next_buffer(&surface_id_request, &buffer_response, null_callback.get()); }, std::logic_error); EXPECT_THROW({ mediator.exchange_buffer(&buffer_request, &buffer_response, null_callback.get()); }, std::logic_error); EXPECT_THROW({ mediator.release_surface(&surface_id_request, nullptr, null_callback.get()); }, std::logic_error); EXPECT_THROW({ mediator.disconnect(nullptr, nullptr, null_callback.get()); }, std::logic_error); } //How does this test fail? consider removal TEST_F(SessionMediator, can_reconnect_after_disconnect) { mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.disconnect(nullptr, nullptr, null_callback.get()); mediator.connect(&connect_parameters, &connection, null_callback.get()); } TEST_F(SessionMediator, connect_packs_display_configuration) { using namespace testing; mtd::StubDisplayConfig config; auto mock_display_changer = std::make_shared>(); ON_CALL(*mock_display_changer, base_configuration()) .WillByDefault(Return(mt::fake_shared(config))); auto const mediator = create_session_mediator_with_display_changer(mock_display_changer); mediator->connect(&connect_parameters, &connection, null_callback.get()); EXPECT_THAT(connection.display_configuration(), mt::DisplayConfigMatches(std::cref(config))); } TEST_F(SessionMediator, creating_surface_packs_response_with_input_fds) { mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); ASSERT_THAT(surface_response.fd().size(), testing::Ge(1)); EXPECT_EQ(StubbedSession::testing_client_input_fd, surface_response.fd(0)); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, no_input_channel_returns_no_fds) { using namespace testing; auto surface = stubbed_session->mock_surface_at(mf::SurfaceId{0}); EXPECT_CALL(*surface, supports_input()) .WillOnce(Return(false)); EXPECT_CALL(*surface, client_input_fd()) .Times(0); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); EXPECT_THAT(surface_response.fd().size(), Eq(0)); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, session_only_sends_mininum_information_for_buffers) { using namespace testing; mf::SurfaceId surf_id{0}; mtd::StubBuffer buffer1; mtd::StubBuffer buffer2; auto stream = stubbed_session->mock_primary_stream_at(surf_id); ON_CALL(*stream, swap_buffers(nullptr,_)) .WillByDefault(InvokeArgument<1>(&buffer2)); ON_CALL(*stream, swap_buffers(&buffer1,_)) .WillByDefault(InvokeArgument<1>(&buffer2)); ON_CALL(*stream, swap_buffers(&buffer2,_)) .WillByDefault(InvokeArgument<1>(&buffer1)); //create Sequence seq; EXPECT_CALL(*stream, swap_buffers(_, _)) .InSequence(seq) .WillOnce(InvokeArgument<1>(&buffer2)); EXPECT_CALL(mock_ipc_operations, pack_buffer(_, Ref(buffer2), mg::BufferIpcMsgType::full_msg)) .InSequence(seq); //swap1 EXPECT_CALL(*stream, swap_buffers(&buffer2, _)) .InSequence(seq) .WillOnce(InvokeArgument<1>(&buffer1)); EXPECT_CALL(mock_ipc_operations, pack_buffer(_, Ref(buffer1), mg::BufferIpcMsgType::full_msg)) .InSequence(seq); //swap2 EXPECT_CALL(*stream, swap_buffers(&buffer1, _)) .InSequence(seq) .WillOnce(InvokeArgument<1>(&buffer2)); EXPECT_CALL(mock_ipc_operations, pack_buffer(_, Ref(buffer2), mg::BufferIpcMsgType::update_msg)) .InSequence(seq); //swap3 EXPECT_CALL(*stream, swap_buffers(&buffer2, _)) .InSequence(seq) .WillOnce(InvokeArgument<1>(&buffer1)); EXPECT_CALL(mock_ipc_operations, pack_buffer(_, Ref(buffer1), mg::BufferIpcMsgType::update_msg)) .InSequence(seq); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); surface_id_request = surface_response.id(); mediator.next_buffer(&surface_id_request, &buffer_response, null_callback.get()); mediator.next_buffer(&surface_id_request, &buffer_response, null_callback.get()); mediator.next_buffer(&surface_id_request, &buffer_response, null_callback.get()); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, session_with_multiple_surfaces_only_sends_needed_buffers) { using namespace testing; EXPECT_CALL(mock_ipc_operations, pack_buffer(_,_,mg::BufferIpcMsgType::full_msg)) .Times(4); EXPECT_CALL(mock_ipc_operations, pack_buffer(_,_,mg::BufferIpcMsgType::update_msg)) .Times(4); mediator.connect(&connect_parameters, &connection, null_callback.get()); mp::Surface surface_response[2]; mp::SurfaceId buffer_request[2]; mediator.create_surface(&surface_parameters, &surface_response[0], null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response[1], null_callback.get()); buffer_request[0] = surface_response[0].id(); buffer_request[1] = surface_response[1].id(); mediator.next_buffer(&buffer_request[0], &buffer_response, null_callback.get()); mediator.next_buffer(&buffer_request[1], &buffer_response, null_callback.get()); mediator.next_buffer(&buffer_request[0], &buffer_response, null_callback.get()); mediator.next_buffer(&buffer_request[1], &buffer_response, null_callback.get()); mediator.next_buffer(&buffer_request[0], &buffer_response, null_callback.get()); mediator.next_buffer(&buffer_request[1], &buffer_response, null_callback.get()); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, destroys_tracker_associated_with_destroyed_surface) { using namespace testing; mf::SurfaceId first_id{0}; mp::Surface surface_response; EXPECT_CALL(mock_ipc_operations, pack_buffer(_,_,mg::BufferIpcMsgType::full_msg)) .Times(2); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); surface_id_request.set_value(first_id.as_value()); mediator.release_surface(&surface_id_request, nullptr, null_callback.get()); stubbed_session->last_surface_id = first_id.as_value(); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); surface_id_request.set_value(first_id.as_value()); mediator.release_surface(&surface_id_request, nullptr, null_callback.get()); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, buffer_resource_for_surface_unaffected_by_other_surfaces) { using namespace testing; mtd::StubBuffer buffer; mediator.connect(&connect_parameters, &connection, null_callback.get()); mp::SurfaceParameters surface_request; mp::Surface surface_response; auto stream1 = stubbed_session->mock_primary_stream_at(mf::SurfaceId{0}); ON_CALL(*stream1, swap_buffers(_,_)) .WillByDefault(InvokeArgument<1>(&buffer)); mediator.create_surface(&surface_request, &surface_response, null_callback.get()); mp::SurfaceId our_surface{surface_response.id()}; /* Creating a new surface should not affect our surfaces' buffers */ EXPECT_CALL(*stream1, swap_buffers(_, _)).Times(0); mediator.create_surface(&surface_request, &surface_response, null_callback.get()); Mock::VerifyAndClearExpectations(stream1.get()); mp::SurfaceId new_surface{surface_response.id()}; mp::Buffer buffer_response; /* Getting the next buffer of new surface should not affect our surfaces' buffers */ mediator.next_buffer(&new_surface, &buffer_response, null_callback.get()); /* Getting the next buffer of our surface should post the original */ EXPECT_CALL(*stream1, swap_buffers(Eq(&buffer),_)).Times(1); mediator.next_buffer(&our_surface, &buffer_response, null_callback.get()); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, display_config_request) { using namespace testing; mp::ConnectParameters connect_parameters; mp::Connection connection; bool used0 = false, used1 = true; geom::Point pt0{44,22}, pt1{3,2}; size_t mode_index0 = 1, mode_index1 = 3; MirPixelFormat format0{mir_pixel_format_invalid}; MirPixelFormat format1{mir_pixel_format_argb_8888}; mg::DisplayConfigurationOutputId id0{6}, id1{3}; mtd::StubDisplayConfig stub_display_config; auto mock_display_changer = std::make_shared(); Sequence seq; EXPECT_CALL(*mock_display_changer, base_configuration()) .InSequence(seq) .WillOnce(Return(mt::fake_shared(stub_display_config))); EXPECT_CALL(*mock_display_changer, base_configuration()) .InSequence(seq) .WillOnce(Return(mt::fake_shared(stub_display_config))); EXPECT_CALL(*mock_display_changer, configure(_,_)) .InSequence(seq); EXPECT_CALL(*mock_display_changer, base_configuration()) .InSequence(seq) .WillOnce(Return(mt::fake_shared(stub_display_config))); auto const mediator = create_session_mediator_with_display_changer(mock_display_changer); mediator->connect(&connect_parameters, &connection, null_callback.get()); mp::DisplayConfiguration configuration_response; mp::DisplayConfiguration configuration; auto disp0 = configuration.add_display_output(); disp0->set_output_id(id0.as_value()); disp0->set_used(used0); disp0->set_position_x(pt0.x.as_uint32_t()); disp0->set_position_y(pt0.y.as_uint32_t()); disp0->set_current_mode(mode_index0); disp0->set_current_format(format0); disp0->set_power_mode(static_cast(mir_power_mode_on)); disp0->set_orientation(mir_orientation_left); auto disp1 = configuration.add_display_output(); disp1->set_output_id(id1.as_value()); disp1->set_used(used1); disp1->set_position_x(pt1.x.as_uint32_t()); disp1->set_position_y(pt1.y.as_uint32_t()); disp1->set_current_mode(mode_index1); disp1->set_current_format(format1); disp1->set_power_mode(static_cast(mir_power_mode_off)); disp1->set_orientation(mir_orientation_inverted); mediator->configure_display(&configuration, &configuration_response, null_callback.get()); EXPECT_THAT(configuration_response, mt::DisplayConfigMatches(std::cref(stub_display_config))); mediator->disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, fully_packs_buffer_for_create_screencast) { using namespace testing; mp::ScreencastParameters screencast_parameters; mp::Screencast screencast; auto const& stub_buffer = stub_screencast->stub_buffer; EXPECT_CALL(mock_ipc_operations, pack_buffer(_, Ref(stub_buffer), _)); mediator.create_screencast(&screencast_parameters, &screencast, null_callback.get()); EXPECT_EQ(stub_buffer.id().as_value(), screencast.buffer_stream().buffer().buffer_id()); } TEST_F(SessionMediator, partially_packs_buffer_for_screencast_buffer) { using namespace testing; mp::ScreencastId screencast_id; mp::Buffer protobuf_buffer; auto const& stub_buffer = stub_screencast->stub_buffer; EXPECT_CALL(mock_ipc_operations, pack_buffer(_, Ref(stub_buffer), mg::BufferIpcMsgType::update_msg)) .Times(1); mediator.screencast_buffer(&screencast_id, &protobuf_buffer, null_callback.get()); EXPECT_EQ(stub_buffer.id().as_value(), protobuf_buffer.buffer_id()); } TEST_F(SessionMediator, prompt_provider_fds_allocated_by_connector) { using namespace ::testing; int const fd_count{11}; int const dummy_fd{__LINE__}; mp::SocketFDRequest request; mp::SocketFD response; request.set_number(fd_count); EXPECT_CALL(connector, client_socket_fd(_)) .Times(fd_count) .WillRepeatedly(Return(dummy_fd)); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.new_fds_for_prompt_providers(&request, &response, null_callback.get()); EXPECT_THAT(response.fd_size(), Eq(fd_count)); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, exchange_buffer) { using namespace testing; auto const& mock_stream = stubbed_session->mock_primary_stream_at(mf::SurfaceId{0}); mp::Buffer exchanged_buffer; mtd::StubBuffer stub_buffer1; mtd::StubBuffer stub_buffer2; //create Sequence seq; EXPECT_CALL(*mock_stream, swap_buffers(_, _)) .InSequence(seq) .WillOnce(InvokeArgument<1>(&stub_buffer1)); //exchange EXPECT_CALL(*mock_stream, swap_buffers(&stub_buffer1,_)) .InSequence(seq) .WillOnce(InvokeArgument<1>(&stub_buffer2)); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); EXPECT_THAT(surface_response.buffer_stream().buffer().buffer_id(), Eq(stub_buffer1.id().as_value())); buffer_request.mutable_id()->set_value(surface_response.id().value()); buffer_request.mutable_buffer()->set_buffer_id(surface_response.buffer_stream().buffer().buffer_id()); mediator.exchange_buffer(&buffer_request, &exchanged_buffer, null_callback.get()); EXPECT_THAT(exchanged_buffer.buffer_id(), Eq(stub_buffer2.id().as_value())); } TEST_F(SessionMediator, session_exchange_buffer_sends_minimum_information) { using namespace testing; mp::Buffer exchanged_buffer; mf::SurfaceId surf_id{0}; mtd::StubBuffer buffer1; mtd::StubBuffer buffer2; auto stream = stubbed_session->mock_primary_stream_at(surf_id); ON_CALL(*stream, swap_buffers(nullptr,_)) .WillByDefault(InvokeArgument<1>(&buffer2)); ON_CALL(*stream, swap_buffers(&buffer1,_)) .WillByDefault(InvokeArgument<1>(&buffer2)); ON_CALL(*stream, swap_buffers(&buffer2,_)) .WillByDefault(InvokeArgument<1>(&buffer1)); Sequence seq; //create EXPECT_CALL(mock_ipc_operations, pack_buffer(_, Ref(buffer2), mg::BufferIpcMsgType::full_msg)) .InSequence(seq); //swap1 EXPECT_CALL(mock_ipc_operations, unpack_buffer(_, Ref(buffer2))) .InSequence(seq); EXPECT_CALL(mock_ipc_operations, pack_buffer(_, Ref(buffer1), mg::BufferIpcMsgType::full_msg)) .InSequence(seq); //swap2 EXPECT_CALL(mock_ipc_operations, unpack_buffer(_, Ref(buffer1))) .InSequence(seq); EXPECT_CALL(mock_ipc_operations, pack_buffer(_, Ref(buffer2), mg::BufferIpcMsgType::update_msg)) .InSequence(seq); //swap3 EXPECT_CALL(mock_ipc_operations, unpack_buffer(_, Ref(buffer2))) .InSequence(seq); EXPECT_CALL(mock_ipc_operations, pack_buffer(_, Ref(buffer1), mg::BufferIpcMsgType::update_msg)) .InSequence(seq); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); buffer_request.mutable_id()->set_value(surface_response.id().value()); buffer_request.mutable_buffer()->set_buffer_id(surface_response.buffer_stream().buffer().buffer_id()); mediator.exchange_buffer(&buffer_request, &exchanged_buffer, null_callback.get()); buffer_request.mutable_buffer()->set_buffer_id(exchanged_buffer.buffer_id()); mediator.exchange_buffer(&buffer_request, &exchanged_buffer, null_callback.get()); buffer_request.mutable_buffer()->set_buffer_id(exchanged_buffer.buffer_id()); mediator.exchange_buffer(&buffer_request, &exchanged_buffer, null_callback.get()); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, exchange_buffer_throws_if_client_submits_bad_request) { using namespace testing; auto const& mock_stream = stubbed_session->mock_primary_stream_at(mf::SurfaceId{0}); mp::Buffer exchanged_buffer; mtd::StubBuffer stub_buffer1; mtd::StubBuffer stub_buffer2; EXPECT_CALL(*mock_stream, swap_buffers(_, _)) .WillOnce(InvokeArgument<1>(&stub_buffer1)); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); EXPECT_THAT(surface_response.buffer_stream().buffer().buffer_id(), Eq(stub_buffer1.id().as_value())); buffer_request.mutable_id()->set_value(surface_response.id().value()); //client doesnt own stub_buffer2 buffer_request.mutable_buffer()->set_buffer_id(stub_buffer2.id().as_value()); EXPECT_THROW({ mediator.exchange_buffer(&buffer_request, &exchanged_buffer, null_callback.get()); }, std::logic_error); //client made up its own surface id. buffer_request.mutable_id()->set_value(surface_response.id().value() + 2); buffer_request.mutable_buffer()->set_buffer_id(stub_buffer1.id().as_value()); EXPECT_THROW({ mediator.exchange_buffer(&buffer_request, &exchanged_buffer, null_callback.get()); }, std::logic_error); } TEST_F(SessionMediator, exchange_buffer_different_for_different_surfaces) { using namespace testing; mp::SurfaceParameters surface_request; mp::BufferRequest req1; mp::BufferRequest req2; auto const& mock_stream1 = stubbed_session->mock_primary_stream_at(mf::SurfaceId{0}); auto const& mock_stream2 = stubbed_session->mock_primary_stream_at(mf::SurfaceId{1}); Sequence seq; EXPECT_CALL(*mock_stream1, swap_buffers(_,_)) .InSequence(seq); EXPECT_CALL(*mock_stream2, swap_buffers(_,_)) .InSequence(seq); EXPECT_CALL(*mock_stream2, swap_buffers(_,_)) .InSequence(seq); EXPECT_CALL(*mock_stream1, swap_buffers(_,_)) .InSequence(seq); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_request, &surface_response, null_callback.get()); req1.mutable_id()->set_value(surface_response.id().value()); *req1.mutable_buffer() = surface_response.buffer_stream().buffer(); mediator.create_surface(&surface_request, &surface_response, null_callback.get()); req2.mutable_id()->set_value(surface_response.id().value()); *req2.mutable_buffer() = surface_response.buffer_stream().buffer(); mediator.exchange_buffer(&req2, &buffer_response, null_callback.get()); mediator.exchange_buffer(&req1, &buffer_response, null_callback.get()); mediator.disconnect(nullptr, nullptr, null_callback.get()); } TEST_F(SessionMediator, buffer_fd_resources_are_put_in_resource_cache) { using namespace testing; NiceMock mock_cache; mp::Buffer exchanged_buffer; mir::Fd fake_fd0(mir::IntOwnedFd{99}); mir::Fd fake_fd1(mir::IntOwnedFd{100}); mir::Fd fake_fd2(mir::IntOwnedFd{101}); EXPECT_CALL(mock_ipc_operations, pack_buffer(_,_,_)) .WillOnce(Invoke([&](mg::BufferIpcMessage& msg, mg::Buffer const&, mg::BufferIpcMsgType) { msg.pack_fd(fake_fd0); })) .WillOnce(Invoke([&](mg::BufferIpcMessage& msg, mg::Buffer const&, mg::BufferIpcMsgType) { msg.pack_fd(fake_fd1); })) .WillOnce(Invoke([&](mg::BufferIpcMessage& msg, mg::Buffer const&, mg::BufferIpcMsgType) { msg.pack_fd(fake_fd2); })); EXPECT_CALL(mock_cache, save_fd(_,fake_fd0)); EXPECT_CALL(mock_cache, save_fd(_,fake_fd1)); EXPECT_CALL(mock_cache, save_fd(_,fake_fd2)); mf::SessionMediator mediator{ shell, mt::fake_shared(mock_ipc_operations), graphics_changer, surface_pixel_formats, report, std::make_shared(), std::make_shared(), mt::fake_shared(mock_cache), stub_screencast, &connector, nullptr, nullptr, std::make_shared(), mir::cookie::Authority::create(), mt::fake_shared(mock_hub)}; mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); buffer_request.mutable_id()->set_value(surface_response.id().value()); buffer_request.mutable_buffer()->set_buffer_id(surface_response.buffer_stream().buffer().buffer_id()); mediator.exchange_buffer(&buffer_request, &exchanged_buffer, null_callback.get()); buffer_request.mutable_buffer()->set_buffer_id(exchanged_buffer.buffer_id()); exchanged_buffer.clear_fd(); mediator.exchange_buffer(&buffer_request, &exchanged_buffer, null_callback.get()); buffer_request.mutable_buffer()->set_buffer_id(exchanged_buffer.buffer_id()); buffer_request.mutable_buffer()->clear_fd(); } // Regression test for LP: #1441759 TEST_F(SessionMediator, completes_exchange_buffer_when_completion_is_invoked_asynchronously_from_thread_that_initiated_exchange) { using namespace testing; auto const& mock_stream = stubbed_session->mock_primary_stream_at(mf::SurfaceId{0}); mtd::StubBuffer stub_buffer1; mtd::StubBuffer stub_buffer2; std::function completion_func; // create InSequence seq; EXPECT_CALL(*mock_stream, swap_buffers(_, _)) .WillOnce(InvokeArgument<1>(&stub_buffer1)); // exchange, steal completion function EXPECT_CALL(*mock_stream, swap_buffers(_,_)) .WillOnce(SaveArg<1>(&completion_func)); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); buffer_request.mutable_id()->set_value(surface_response.id().value()); *buffer_request.mutable_buffer() = surface_response.buffer_stream().buffer(); mediator.exchange_buffer(&buffer_request, &buffer_response, null_callback.get()); // Execute completion function asynchronously (i.e. not as part of the exchange_buffer // call), but from the same thread that initiated the exchange_buffer operation completion_func(&stub_buffer2); } MATCHER(ConfigEq, "stream configurations are equivalent") { return (std::get<0>(arg).stream_id == std::get<1>(arg).stream_id) && (std::get<0>(arg).displacement == std::get<1>(arg).displacement); } namespace mir { namespace shell { void PrintTo(msh::StreamSpecification const& s, std::ostream* os) { *os << "streams with id: " << s.stream_id.as_value(); } } } MATCHER_P(StreamsAre, value, "configuration streams match") { if(!arg.streams.is_set()) return false; EXPECT_THAT(arg.streams.value(), testing::Pointwise(ConfigEq(), value)); return !(::testing::Test::HasFailure()); } TEST_F(SessionMediator, arranges_bufferstreams_via_shell) { using namespace testing; mp::Void null; mp::SurfaceModifications mods; mp::BufferStreamParameters stream_request; std::array streams; std::array displacement = { { geom::Displacement{-12,11}, geom::Displacement{4,-3} } }; mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); for (auto &stream : streams) mediator.create_buffer_stream(&stream_request, &stream, null_callback.get()); mods.mutable_surface_id()->set_value(surface_response.id().value()); for (auto i = 0u; i < streams.size(); i++) { auto stream = mods.mutable_surface_specification()->add_stream(); stream->mutable_id()->set_value(streams[i].id().value()); stream->set_displacement_x(displacement[i].dx.as_int()); stream->set_displacement_y(displacement[i].dy.as_int()); } EXPECT_CALL(*shell, modify_surface(_, mf::SurfaceId{surface_response.id().value()}, StreamsAre(std::vector{ {mf::BufferStreamId(streams[0].id().value()), displacement[0]}, {mf::BufferStreamId(streams[1].id().value()), displacement[1]}, }))); mediator.modify_surface(&mods, &null, null_callback.get()); } TEST_F(SessionMediator, sends_a_buffer_when_submit_buffer_is_called) { using namespace testing; auto sink_factory = std::make_shared(); auto mock_sink = sink_factory->the_mock_sink(); auto buffer1 = std::make_shared(); mf::SessionMediator mediator{ shell, mt::fake_shared(mock_ipc_operations), graphics_changer, surface_pixel_formats, report, sink_factory, std::make_shared(), resource_cache, stub_screencast, nullptr, nullptr, nullptr, std::make_shared(), mir::cookie::Authority::create(), mt::fake_shared(mock_hub)}; mp::Void null; mp::BufferRequest request; mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); auto mock_stream = stubbed_session->mock_primary_stream_at(mf::SurfaceId{0}); request.mutable_id()->set_value(surface_response.id().value()); request.mutable_buffer()->set_buffer_id(buffer1->id().as_value()); InSequence seq; EXPECT_CALL(mock_ipc_operations, unpack_buffer(_,_)); EXPECT_CALL(*mock_stream, swap_buffers(_,_)) .WillOnce(InvokeArgument<1>(buffer1.get())); EXPECT_CALL(*mock_sink, send_buffer(_, Ref(*buffer1), mg::BufferIpcMsgType::full_msg)); mediator.submit_buffer(&request, &null, null_callback.get()); } TEST_F(SessionMediator, allocates_from_the_correct_stream) { using namespace testing; auto num_requests = 3u; mp::Void null; mp::BufferStreamId id; id.set_value(0); mp::BufferAllocation request; *request.mutable_id() = id; mg::BufferProperties properties(geom::Size{34, 84}, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware); for(auto i = 0u; i < num_requests; i++) { auto buffer_request = request.add_buffer_requests(); buffer_request->set_width(properties.size.width.as_int()); buffer_request->set_height(properties.size.height.as_int()); buffer_request->set_pixel_format(properties.format); buffer_request->set_buffer_usage((int)properties.usage); } mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); auto mock_stream = stubbed_session->mock_primary_stream_at(mf::SurfaceId{0}); EXPECT_CALL(*mock_stream, allocate_buffer(properties)) .Times(num_requests) .WillRepeatedly(Return(mg::BufferID{})); mediator.allocate_buffers(&request, &null, null_callback.get()); } TEST_F(SessionMediator, removes_buffer_from_the_correct_stream) { using namespace testing; auto num_requests = 3u; mp::Void null; mp::BufferStreamId id; id.set_value(0); mp::BufferRelease request; *request.mutable_id() = id; auto buffer_id = 442u; for(auto i = 0u; i < num_requests; i++) { auto buffer_request = request.add_buffers(); buffer_request->set_buffer_id(buffer_id); } mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); auto mock_stream = stubbed_session->mock_primary_stream_at(mf::SurfaceId{0}); EXPECT_CALL(*mock_stream, remove_buffer(mg::BufferID{buffer_id})) .Times(num_requests); mediator.release_buffers(&request, &null, null_callback.get()); } TEST_F(SessionMediator, doesnt_mind_swap_buffers_returning_nullptr_in_submit) { using namespace testing; auto mock_stream = stubbed_session->mock_primary_stream_at(mf::SurfaceId{0}); ON_CALL(*mock_stream, swap_buffers(_,_)) .WillByDefault(InvokeArgument<1>(nullptr)); auto buffer1 = std::make_shared(); mf::SessionMediator mediator{ shell, mt::fake_shared(mock_ipc_operations), graphics_changer, surface_pixel_formats, report, std::make_shared(), std::make_shared(), resource_cache, stub_screencast, nullptr, nullptr, nullptr, std::make_shared(), mir::cookie::Authority::create(), mt::fake_shared(mock_hub)}; mp::Void null; mp::BufferRequest request; mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); request.mutable_id()->set_value(surface_response.id().value()); request.mutable_buffer()->set_buffer_id(buffer1->id().as_value()); InSequence seq; EXPECT_CALL(*mock_stream, with_buffer(buffer1->id(),_)) .WillOnce(InvokeArgument<1>(*buffer1)); EXPECT_CALL(mock_ipc_operations, unpack_buffer(_,_)); EXPECT_CALL(*mock_stream, swap_buffers(_,_)) .WillOnce(InvokeArgument<1>(nullptr)); mediator.submit_buffer(&request, &null, null_callback.get()); } TEST_F(SessionMediator, doesnt_mind_swap_buffers_returning_nullptr_in_create) { using namespace testing; mf::SurfaceId surf_id{0}; mtd::StubBuffer buffer; auto stream = stubbed_session->mock_primary_stream_at(surf_id); ON_CALL(*stream, swap_buffers(_,_)) .WillByDefault(InvokeArgument<1>(nullptr)); Sequence seq; EXPECT_CALL(*stream, swap_buffers(_,_)) .InSequence(seq) .WillOnce(InvokeArgument<1>(nullptr)); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); } TEST_F(SessionMediator, doesnt_mind_swap_buffers_returning_nullptr_in_bstream_create) { using namespace testing; mf::SurfaceId surf_id{0}; mtd::StubBuffer buffer; auto stream = stubbed_session->mock_primary_stream_at(surf_id); ON_CALL(*stream, swap_buffers(_,_)) .WillByDefault(InvokeArgument<1>(nullptr)); Sequence seq; EXPECT_CALL(*stream, swap_buffers(_,_)) .InSequence(seq) .WillOnce(InvokeArgument<1>(nullptr)); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); } TEST_F(SessionMediator, configures_swap_intervals_on_streams) { using namespace testing; mf::SurfaceId surf_id{0}; mp::StreamConfiguration request; mp::Void response; auto interval = 0u; mtd::StubBuffer buffer; auto stream = stubbed_session->mock_primary_stream_at(surf_id); EXPECT_CALL(*stream, allow_framedropping(true)); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface(&surface_parameters, &surface_response, null_callback.get()); request.mutable_id()->set_value(surf_id.as_value()); request.set_swapinterval(interval); mediator.configure_buffer_stream(&request, &response, null_callback.get()); } namespace { MATCHER(IsReplyWithEvents, "") { auto buffer = std::get<0>(arg); auto buffer_len = std::get<1>(arg); mir::protobuf::wire::Result result; if (!result.ParseFromArray(buffer, buffer_len)) { *result_listener << "is not a protobuf Result"; return false; } auto num_events = result.events_size(); if (num_events > 0) { *result_listener << "has " << num_events << " events"; } else { *result_listener << "has no events"; } return num_events > 0; } void send_non_event(std::shared_ptr sender) { sender->send("hello", 5, {}); } } TEST_F(SessionMediator, events_sent_before_surface_creation_reply_are_buffered) { using namespace testing; auto mock_sender = std::make_shared(); class EventSinkFactory : public mf::EventSinkFactory { public: EventSinkFactory(std::shared_ptr const& ops) : ops{ops} { } std::unique_ptr create_sink( std::shared_ptr const& sender) { return std::make_unique(sender, ops); } private: std::shared_ptr const ops; }; auto sink_factory = std::make_shared(mt::fake_shared(mock_ipc_operations)); mf::SessionMediator mediator{ shell, mt::fake_shared(mock_ipc_operations), graphics_changer, surface_pixel_formats, report, sink_factory, mock_sender, resource_cache, stub_screencast, nullptr, nullptr, nullptr, std::make_shared(), mir::cookie::Authority::create(), mt::fake_shared(mock_hub)}; ON_CALL(*shell, create_surface( _, _, _)) .WillByDefault( Invoke([session = stubbed_session.get()](auto, auto params, auto sink) { sink->send_ping(0xdeadbeef); return session->create_surface(params, sink); })); InSequence seq; EXPECT_CALL(*mock_sender, send(_,_,_)) .With(Args<0, 1>(Not(IsReplyWithEvents()))); EXPECT_CALL(*mock_sender, send(_,_,_)) .With(Args<0, 1>(IsReplyWithEvents())); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_surface( &surface_parameters, &surface_response, google::protobuf::NewCallback(&send_non_event, mock_sender)); } TEST_F(SessionMediator, doesnt_inadventently_set_buffer_field_when_theres_no_buffer) { mp::Void null; mf::SurfaceId surf_id{0}; mp::BufferStreamParameters stream_request; mp::BufferStream stream_response; auto stream = stubbed_session->mock_primary_stream_at(surf_id); ON_CALL(*stream, swap_buffers(nullptr,testing::_)) .WillByDefault(testing::InvokeArgument<1>(nullptr)); mediator.connect(&connect_parameters, &connection, null_callback.get()); mediator.create_buffer_stream(&stream_request, &stream_response, null_callback.get()); EXPECT_FALSE(stream_response.has_buffer()); } TEST_F(SessionMediator, sets_base_display_configuration) { using namespace testing; mtd::StubDisplayConfig display_config{2}; mp::DisplayConfiguration request; mp::Void response; mf::detail::pack_protobuf_display_configuration(request, display_config); auto mock_display_changer = std::make_shared>(); ON_CALL(*mock_display_changer, base_configuration()) .WillByDefault(Return(mt::fake_shared(display_config))); EXPECT_CALL(*mock_display_changer, mock_set_base_configuration( mt::DisplayConfigMatches(std::cref(display_config)))); auto const mediator = create_session_mediator_with_display_changer(mock_display_changer); mediator->connect(&connect_parameters, &connection, null_callback.get()); mediator->set_base_display_configuration(&request, &response, null_callback.get()); } TEST_F(SessionMediator, sanitizes_base_display_configuration_before_setting) { using namespace testing; auto const set_second_mode = [] (mg::UserDisplayConfigurationOutput& output) { output.current_mode_index = 1; }; mtd::StubDisplayConfig single_output_base_config{1}; mtd::StubDisplayConfig dual_output_requested_config{2}; mtd::StubDisplayConfig single_output_sanitized_config{1}; dual_output_requested_config.for_each_output(set_second_mode); single_output_sanitized_config.for_each_output(set_second_mode); mp::DisplayConfiguration request; mp::Void response; mf::detail::pack_protobuf_display_configuration(request, dual_output_requested_config); auto mock_display_changer = std::make_shared>(); ON_CALL(*mock_display_changer, base_configuration()) .WillByDefault(Return(mt::fake_shared(single_output_base_config))); EXPECT_CALL(*mock_display_changer, mock_set_base_configuration( mt::DisplayConfigMatches(std::cref(single_output_sanitized_config)))); auto const mediator = create_session_mediator_with_display_changer(mock_display_changer); mediator->connect(&connect_parameters, &connection, null_callback.get()); mediator->set_base_display_configuration(&request, &response, null_callback.get()); } TEST_F(SessionMediator, raise_with_invalid_cookie_throws) { mp::RaiseRequest raise_request; mediator.connect(&connect_parameters, &connection, null_callback.get()); EXPECT_THROW({ mediator.raise_surface(&raise_request, &void_response, null_callback.get()); }, mir::cookie::SecurityCheckError); } TEST_F(SessionMediator, connect_sends_input_devices_at_seat) { using namespace testing; mtd::StubDevice dev1{MirInputDeviceId{3}, mi::DeviceCapability::keyboard, "kbd", "kbd-aaf474"}; mtd::StubDevice dev2{MirInputDeviceId{7}, mi::DeviceCapability::touchscreen, "ts", "ts-ewrkw2"}; std::vector> devices{mt::fake_shared(dev1), mt::fake_shared(dev2)}; ON_CALL(mock_hub, for_each_input_device(_)) .WillByDefault(Invoke( [&](std::function const& callback) { for(auto const& dev : devices) callback(*dev); })); mediator.connect(&connect_parameters, &connection, null_callback.get()); EXPECT_THAT(connection.input_devices(), mt::InputDevicesMatch(devices)); } ./tests/unit-tests/frontend/test_protobuf_buffer_packer.cpp0000644000015600001650000000654312676616125024502 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/fd_matcher.h" #include "src/server/frontend/protobuf_buffer_packer.h" #include "src/server/frontend/resource_cache.h" #include "mir_protobuf.pb.h" #include #include #include namespace mf=mir::frontend; namespace mfd=mir::frontend::detail; namespace mp=mir::protobuf; namespace geom=mir::geometry; namespace mtd=mir::test::doubles; TEST(ProtobufBufferPacker, packing) { geom::Stride dummy_stride(4); mp::Buffer response; mfd::ProtobufBufferPacker packer(&response); int num_fd = 33, num_int = 44; std::vector raw_fds; for(auto i=0; i < num_fd; i++) { raw_fds.push_back(mir::Fd(fileno(tmpfile()))); packer.pack_fd(mir::Fd(mir::IntOwnedFd{raw_fds.back()})); } for(auto i=0; i < num_int; i++) packer.pack_data(i); packer.pack_stride(dummy_stride); packer.pack_flags(123); packer.pack_size(geom::Size{456, 789}); EXPECT_EQ(num_fd, response.fd_size()); EXPECT_EQ(num_int, response.data_size()); auto i = 0u; for (auto raw_fd : raw_fds) EXPECT_EQ(raw_fd, response.fd(i++)); for (int i = 0; i < response.data_size(); ++i) EXPECT_EQ(i, response.data(i)); EXPECT_EQ(dummy_stride.as_uint32_t(), static_cast(response.stride())); EXPECT_EQ(123U, response.flags()); EXPECT_EQ(456, response.width()); EXPECT_EQ(789, response.height()); } TEST(ProtobufBufferPacker, data_and_fds_are_the_same_as_packed) { using namespace testing; mp::Buffer response; unsigned int const num_fds{3}; unsigned int const num_data{9}; for(auto i = 0u; i < num_fds; i++) response.add_fd(mir::Fd{fileno(tmpfile())}); for(auto i = 0u; i < num_data; i++) response.add_data(i*3); mfd::ProtobufBufferPacker packer(&response); mir::Fd additional_fd{fileno(tmpfile())}; packer.pack_fd(additional_fd); auto fds = packer.fds(); EXPECT_THAT(fds.size(), Eq(num_fds + 1)); auto data = packer.data(); EXPECT_THAT(data, ElementsAreArray(response.data().data(), num_data)); } TEST(ProtobufBufferPacker, message_takes_ownership_of_fds) { using namespace testing; mp::Buffer response; unsigned int const num_fds{3}; for(auto i = 0u; i < num_fds; i++) response.add_fd(fileno(tmpfile())); mir::Fd additional_fd{fileno(tmpfile())}; { mfd::ProtobufBufferPacker packer(&response); packer.pack_fd(additional_fd); } EXPECT_THAT(response.fd().size(), Eq(num_fds+1)); auto i = 0u; for(; i < num_fds; i++) EXPECT_THAT(response.fd().Get(i), Not(mtd::RawFdIsValid())); EXPECT_THAT(response.fd().Get(i), mtd::RawFdIsValid()); } ./tests/unit-tests/frontend/test_event_sender.cpp0000644000015600001650000001516112676616157022446 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_protobuf.pb.h" #include "mir_protobuf_wire.pb.h" #include "src/server/frontend/message_sender.h" #include "src/server/frontend/event_sender.h" #include "mir/events/event_builders.h" #include "mir/test/display_config_matchers.h" #include "mir/test/input_devices_matcher.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_input_device.h" #include "mir/test/doubles/mock_platform_ipc_operations.h" #include "mir/input/device.h" #include "mir/input/device_capability.h" #include "mir/input/pointer_configuration.h" #include "mir/input/touchpad_configuration.h" #include #include namespace mt = mir::test; namespace mi = mir::input; namespace mtd = mir::test::doubles; namespace mf = mir::frontend; namespace mfd = mf::detail; namespace mev = mir::events; namespace geom = mir::geometry; namespace { struct StubDevice : mi::Device { StubDevice(MirInputDeviceId id, mi::DeviceCapabilities caps, std::string const& name, std::string const& unique_id) : device_id(id), device_capabilities(caps), device_name(name), device_unique_id(unique_id) {} MirInputDeviceId id() const override { return device_id; } mi::DeviceCapabilities capabilities() const override { return device_capabilities; } std::string name() const override { return device_name; } std::string unique_id() const override { return device_unique_id; } mir::optional_value pointer_configuration() const override { return {}; } void apply_pointer_configuration(mi::PointerConfiguration const&) override { } mir::optional_value touchpad_configuration() const override { return {}; } void apply_touchpad_configuration(mi::TouchpadConfiguration const&) override { } MirInputDeviceId device_id; mi::DeviceCapabilities device_capabilities; std::string device_name; std::string device_unique_id; }; struct MockMsgSender : public mf::MessageSender { MOCK_METHOD3(send, void(char const*, size_t, mf::FdSets const&)); }; struct EventSender : public testing::Test { EventSender() : event_sender(mt::fake_shared(mock_msg_sender), mt::fake_shared(mock_buffer_packer)) { } MockMsgSender mock_msg_sender; mtd::MockPlatformIpcOperations mock_buffer_packer; mfd::EventSender event_sender; }; } TEST_F(EventSender, display_send) { using namespace testing; mtd::StubDisplayConfig config; auto msg_validator = [&config](char const* data, size_t len){ mir::protobuf::wire::Result wire; wire.ParseFromArray(data, len); std::string str = wire.events(0); mir::protobuf::EventSequence seq; seq.ParseFromString(str); EXPECT_THAT(seq.display_configuration(), mt::DisplayConfigMatches(std::cref(config))); }; EXPECT_CALL(mock_msg_sender, send(_, _, _)) .Times(1) .WillOnce(WithArgs<0,1>(Invoke(msg_validator))); event_sender.handle_display_config_change(config); } TEST_F(EventSender, sends_noninput_events) { using namespace testing; auto surface_ev = mev::make_event(mf::SurfaceId{1}, mir_surface_attrib_focus, mir_surface_focused); auto resize_ev = mev::make_event(mf::SurfaceId{1}, {10, 10}); EXPECT_CALL(mock_msg_sender, send(_, _, _)) .Times(2); event_sender.handle_event(*surface_ev); event_sender.handle_event(*resize_ev); } TEST_F(EventSender, never_sends_input_events) { using namespace testing; auto ev = mev::make_event(MirInputDeviceId(), std::chrono::nanoseconds(0), std::vector{}, MirKeyboardAction(), 0, 0, MirInputEventModifiers()); EXPECT_CALL(mock_msg_sender, send(_, _, _)) .Times(0); event_sender.handle_event(*ev); } TEST_F(EventSender, packs_buffer_with_platform_packer) { using namespace testing; mf::BufferStreamId id{8}; auto msg_type = mir::graphics::BufferIpcMsgType::update_msg; mtd::StubBuffer buffer; InSequence seq; EXPECT_CALL(mock_buffer_packer, pack_buffer(_, Ref(buffer), msg_type)); EXPECT_CALL(mock_msg_sender, send(_,_,_)); event_sender.send_buffer(mf::BufferStreamId{}, buffer, msg_type); } TEST_F(EventSender, sends_input_devices) { using namespace testing; std::vector> devices{ std::make_shared(3, mi::DeviceCapability::pointer | mi::DeviceCapability::touchpad, "touchpad", "5352"), std::make_shared(23, mi::DeviceCapability::keyboard | mi::DeviceCapability::alpha_numeric, "keybaord", "7853")}; auto msg_validator = [&devices](char const* data, size_t len){ mir::protobuf::wire::Result wire; wire.ParseFromArray(data, len); std::string str = wire.events(0); mir::protobuf::EventSequence seq; seq.ParseFromString(str); EXPECT_THAT(seq.input_devices(), mt::InputDevicesMatch(devices)); }; EXPECT_CALL(mock_msg_sender, send(_, _, _)) .Times(1) .WillOnce(WithArgs<0,1>(Invoke(msg_validator))); event_sender.handle_input_device_change(devices); } TEST_F(EventSender, sends_empty_sequence_of_devices) { using namespace testing; std::vector> devices; auto msg_validator = [&devices](char const* data, size_t len){ mir::protobuf::wire::Result wire; wire.ParseFromArray(data, len); std::string str = wire.events(0); mir::protobuf::EventSequence seq; seq.ParseFromString(str); EXPECT_THAT(seq.input_devices(), mt::InputDevicesMatch(devices)); }; EXPECT_CALL(mock_msg_sender, send(_, _, _)) .Times(1) .WillOnce(WithArgs<0,1>(Invoke(msg_validator))); event_sender.handle_input_device_change(devices); } ./tests/unit-tests/frontend/test_published_socket_connector.cpp0000644000015600001650000002313512676616125025361 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Alan Griffiths */ #include "mir/frontend/connector.h" #include "mir/events/event_private.h" #include "src/server/report/null_report_factory.h" #include "src/server/frontend/resource_cache.h" #include "src/server/frontend/published_socket_connector.h" #include "mir/frontend/protobuf_connection_creator.h" #include "mir/frontend/connector_report.h" #include "mir_protobuf.pb.h" #include "mir/test/stub_server_tool.h" #include "mir/test/test_protobuf_client.h" #include "mir/test/test_protobuf_server.h" #include "mir/test/wait_condition.h" #include "mir/test/doubles/stub_ipc_factory.h" #include "mir/test/doubles/stub_session_authorizer.h" #include "mir/test/doubles/mock_rpc_report.h" #include "mir/test/doubles/stub_ipc_factory.h" #include "mir_test_framework/testing_server_configuration.h" #include #include #include #include #include namespace mf = mir::frontend; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mr = mir::report; namespace mtf = mir_test_framework; namespace { const int timeout_ms = 60 * 1000; class MockConnectorReport : public mf::ConnectorReport { public: MockConnectorReport() { using namespace testing; EXPECT_CALL(*this, thread_start()).Times(AnyNumber()); EXPECT_CALL(*this, thread_end()).Times(AnyNumber()); EXPECT_CALL(*this, creating_session_for(_)).Times(AnyNumber()); EXPECT_CALL(*this, creating_socket_pair(_, _)).Times(AnyNumber()); EXPECT_CALL(*this, listening_on(_)).Times(AnyNumber()); EXPECT_CALL(*this, error(_)).Times(AnyNumber()); } ~MockConnectorReport() noexcept {} MOCK_METHOD0(thread_start, void ()); MOCK_METHOD0(thread_end, void()); MOCK_METHOD1(creating_session_for, void(int socket_handle)); MOCK_METHOD2(creating_socket_pair, void(int server_handle, int client_handle)); MOCK_METHOD1(listening_on, void(std::string const& endpoint)); MOCK_METHOD1(error, void (std::exception const& error)); }; } struct PublishedSocketConnector : public ::testing::Test { static std::string const test_socket; static void SetUpTestCase() { remove(test_socket.c_str()); } void SetUp() { communicator_report = std::make_shared(); stub_server_tool = std::make_shared(); stub_server = std::make_shared( test_socket, stub_server_tool, communicator_report); stub_server->comm->start(); client = std::make_shared(test_socket, timeout_ms); client->connect_parameters.set_application_name(__PRETTY_FUNCTION__); } void TearDown() { client.reset(); stub_server->comm->stop(); stub_server.reset(); stub_server_tool.reset(); communicator_report.reset(); } static void TearDownTestCase() { } std::shared_ptr client; static std::shared_ptr communicator_report; static std::shared_ptr stub_server_tool; static std::shared_ptr stub_server; }; std::string const PublishedSocketConnector::test_socket = mtf::test_socket_file(); std::shared_ptr PublishedSocketConnector::stub_server_tool; std::shared_ptr PublishedSocketConnector::communicator_report; std::shared_ptr PublishedSocketConnector::stub_server; TEST_F(PublishedSocketConnector, create_surface_results_in_a_callback) { EXPECT_CALL(*client, create_surface_done()).Times(1); client->create_surface(); client->wait_for_create_surface(); } TEST_F(PublishedSocketConnector, connection_sets_app_name) { EXPECT_CALL(*client, connect_done()).Times(1); client->connect_parameters.set_application_name(__PRETTY_FUNCTION__); client->connect(); client->wait_for_connect_done(); EXPECT_EQ(__PRETTY_FUNCTION__, stub_server_tool->application_name()); } TEST_F(PublishedSocketConnector, create_surface_sets_surface_name) { EXPECT_CALL(*client, connect_done()).Times(1); EXPECT_CALL(*client, create_surface_done()).Times(1); client->connect_parameters.set_application_name(__PRETTY_FUNCTION__); client->connect(); client->wait_for_connect_done(); client->surface_parameters.set_surface_name(__PRETTY_FUNCTION__); client->create_surface(); client->wait_for_create_surface(); EXPECT_EQ(__PRETTY_FUNCTION__, stub_server_tool->surface_name()); } TEST_F(PublishedSocketConnector, create_surface_results_in_a_surface_being_created) { EXPECT_CALL(*client, create_surface_done()).Times(1); client->create_surface(); client->wait_for_create_surface(); } namespace { MATCHER_P(InvocationMethodEq, name, "") { return arg.method_name() == name; } } TEST_F(PublishedSocketConnector, double_disconnection_does_not_break) { using namespace testing; EXPECT_CALL(*client, create_surface_done()).Times(1); client->create_surface(); client->wait_for_create_surface(); EXPECT_CALL(*client, disconnect_done()).Times(1); client->disconnect(); client->wait_for_disconnect_done(); Mock::VerifyAndClearExpectations(client.get()); // There is a race between the server closing the socket and // the client sending the second "disconnect". // Usually[*] the server wins and the "disconnect" fails resulting // in an invocation_failed() report and an exception. // Occasionally, the client wins and the message is sent to dying socket. // [*] ON my desktop under valgrind "Usually" is ~49 times out of 50 EXPECT_CALL(*client->rpc_report, invocation_failed(InvocationMethodEq("disconnect"),_)).Times(AtMost(1)); EXPECT_CALL(*client, disconnect_done()).Times(1); try { client->disconnect(); // the write beat the socket closing } catch (std::runtime_error const& x) { // the socket closing beat the write EXPECT_THAT(x.what(), HasSubstr("Failed to send message to server:")); } client->wait_for_disconnect_done(); } TEST_F(PublishedSocketConnector, getting_and_advancing_buffers) { EXPECT_CALL(*client, create_surface_done()).Times(testing::AtLeast(0)); EXPECT_CALL(*client, disconnect_done()).Times(testing::AtLeast(0)); client->create_surface(); client->wait_for_create_surface(); EXPECT_TRUE(client->surface.has_buffer()); EXPECT_CALL(*client, next_buffer_done()).Times(8); for (int i = 0; i != 8; ++i) { client->next_buffer(); client->wait_for_next_buffer(); EXPECT_TRUE(client->surface.has_buffer()); } client->disconnect(); client->wait_for_disconnect_done(); } TEST_F(PublishedSocketConnector, connect_create_surface_then_disconnect_a_session) { EXPECT_CALL(*client, create_surface_done()).Times(1); client->create_surface(); client->wait_for_create_surface(); EXPECT_CALL(*client, disconnect_done()).Times(1); client->disconnect(); client->wait_for_disconnect_done(); } TEST_F(PublishedSocketConnector, disorderly_disconnection_handled) { using namespace testing; EXPECT_CALL(*client, create_surface_done()).Times(AnyNumber()); client->create_surface(); client->wait_for_create_surface(); mt::WaitCondition wc; ON_CALL(*communicator_report, error(_)).WillByDefault(Invoke([&wc] (std::exception const&) { wc.wake_up_everyone(); })); EXPECT_CALL(*communicator_report, error(_)).Times(1); client.reset(); wc.wait_for_at_most_seconds(1); EXPECT_TRUE(wc.woken()); } TEST_F(PublishedSocketConnector, configure_display) { EXPECT_CALL(*client, display_configure_done()) .Times(1); client->configure_display(); client->wait_for_configure_display_done(); } TEST_F(PublishedSocketConnector, connection_using_socket_fd) { int const next_buffer_calls{8}; char buffer[128] = {0}; sprintf(buffer, "fd://%d", stub_server->comm->client_socket_fd()); auto client = std::make_shared(buffer, timeout_ms); client->connect_parameters.set_application_name(__PRETTY_FUNCTION__); EXPECT_CALL(*client, connect_done()).Times(1); client->connect(); client->wait_for_connect_done(); EXPECT_CALL(*client, create_surface_done()).Times(testing::AtLeast(0)); EXPECT_CALL(*client, disconnect_done()).Times(testing::AtLeast(0)); client->create_surface(); client->wait_for_create_surface(); EXPECT_TRUE(client->surface.has_buffer()); EXPECT_CALL(*client, next_buffer_done()).Times(next_buffer_calls); for (int i = 0; i != next_buffer_calls; ++i) { client->next_buffer(); client->wait_for_next_buffer(); EXPECT_TRUE(client->surface.has_buffer()); } client->disconnect(); client->wait_for_disconnect_done(); EXPECT_EQ(__PRETTY_FUNCTION__, stub_server_tool->app_name); } ./tests/unit-tests/frontend/CMakeLists.txt0000644000015600001650000000166012676616157020761 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_client_buffer_tracker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/stress_protobuf_communicator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_published_socket_connector.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_protobuf_surface_apis.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_protobuf_reports_errors.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_protobuf_buffer_packer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_resource_cache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_session_mediator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_socket_connection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_event_sender.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_authorizing_display_changer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_connector.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_protobuf_message_processor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_buffering_message_sender.cpp ) set( UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/frontend/test_basic_connector.cpp0000644000015600001650000000265412676616125023116 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/frontend/published_socket_connector.h" #include "src/server/report/null/connector_report.h" #include "mir/test/current_thread_name.h" #include "mir/test/fake_shared.h" #include #include namespace mt = mir::test; namespace { struct StubConnectorReport : mir::report::null::ConnectorReport { void thread_start() { thread_name = mt::current_thread_name(); } std::string thread_name; }; } TEST(BasicConnector, names_ipc_threads) { using namespace testing; StubConnectorReport report; mir::frontend::BasicConnector connector{{}, mt::fake_shared(report)}; connector.start(); connector.stop(); EXPECT_THAT(report.thread_name, Eq("Mir/IPC")); } ./tests/unit-tests/frontend/test_client_buffer_tracker.cpp0000644000015600001650000002134712676616125024305 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "src/server/frontend/client_buffer_tracker.h" #include "src/server/frontend/buffer_stream_tracker.h" #include "mir/graphics/buffer_id.h" #include "mir/test/doubles/stub_buffer.h" #include #include namespace mtd = mir::test::doubles; namespace mf = mir::frontend; namespace mg = mir::graphics; struct ClientBufferTracker : public testing::Test { mtd::StubBuffer stub_buffer0; mtd::StubBuffer stub_buffer1; mtd::StubBuffer stub_buffer2; mtd::StubBuffer stub_buffer3; mtd::StubBuffer stub_buffer4; }; TEST_F(ClientBufferTracker, just_added_buffer_is_known_by_client) { mf::ClientBufferTracker tracker(3); mg::BufferID const id{stub_buffer0.id()}; tracker.add(&stub_buffer0); EXPECT_TRUE(tracker.client_has(id)); EXPECT_THAT(tracker.buffer_from(id), testing::Eq(&stub_buffer0)); } TEST_F(ClientBufferTracker, nullptrs_dont_affect_owned_buffers) { mf::ClientBufferTracker tracker(3); mg::BufferID const id{stub_buffer0.id()}; tracker.add(&stub_buffer0); tracker.add(nullptr); tracker.add(nullptr); tracker.add(nullptr); EXPECT_TRUE(tracker.client_has(id)); EXPECT_THAT(tracker.buffer_from(id), testing::Eq(&stub_buffer0)); } TEST_F(ClientBufferTracker, dead_buffers_are_still_tracked) { mf::ClientBufferTracker tracker(3); mg::BufferID id{stub_buffer0.id()}; mg::Buffer* dead_buffer_ptr{nullptr}; { mtd::StubBuffer dead_buffer; tracker.add(&dead_buffer); id = dead_buffer.id(); dead_buffer_ptr = &dead_buffer; } EXPECT_TRUE(tracker.client_has(id)); EXPECT_THAT(tracker.buffer_from(id), testing::Eq(dead_buffer_ptr)); } TEST_F(ClientBufferTracker, unadded_buffer_is_unknown_by_client) { mf::ClientBufferTracker tracker(3); tracker.add(&stub_buffer0); EXPECT_FALSE(tracker.client_has(stub_buffer1.id())); } TEST_F(ClientBufferTracker, tracks_sequence_of_buffers) { mf::ClientBufferTracker tracker(3); tracker.add(&stub_buffer0); tracker.add(&stub_buffer1); tracker.add(&stub_buffer2); EXPECT_TRUE(tracker.client_has(stub_buffer0.id())); EXPECT_TRUE(tracker.client_has(stub_buffer1.id())); EXPECT_TRUE(tracker.client_has(stub_buffer2.id())); EXPECT_FALSE(tracker.client_has(stub_buffer3.id())); } TEST_F(ClientBufferTracker, old_buffers_expire_from_tracker) { mf::ClientBufferTracker tracker(3); tracker.add(&stub_buffer0); tracker.add(&stub_buffer1); tracker.add(&stub_buffer2); ASSERT_TRUE(tracker.client_has(stub_buffer0.id())); ASSERT_TRUE(tracker.client_has(stub_buffer1.id())); ASSERT_TRUE(tracker.client_has(stub_buffer2.id())); tracker.add(&stub_buffer1); tracker.add(&stub_buffer2); tracker.add(&stub_buffer3); EXPECT_FALSE(tracker.client_has(stub_buffer0.id())); EXPECT_TRUE(tracker.client_has(stub_buffer1.id())); EXPECT_TRUE(tracker.client_has(stub_buffer2.id())); EXPECT_TRUE(tracker.client_has(stub_buffer3.id())); } TEST_F(ClientBufferTracker, tracks_correct_number_of_buffers) { std::vector buffers(10); for (unsigned int tracker_size = 2; tracker_size < 10; ++tracker_size) { mf::ClientBufferTracker tracker{tracker_size}; for (unsigned int i = 0; i <= tracker_size; ++i) tracker.add(&buffers[i]); EXPECT_FALSE(tracker.client_has(buffers[0].id())); for (unsigned int i = 1; i <= tracker_size; ++i) EXPECT_TRUE(tracker.client_has(buffers[i].id())); } } struct BufferStreamTracker : public testing::Test { mtd::StubBuffer stub_buffer0; mtd::StubBuffer stub_buffer1; mtd::StubBuffer stub_buffer2; mtd::StubBuffer stub_buffer3; mtd::StubBuffer stub_buffer4; mf::BufferStreamId stream_id0{0}; mf::BufferStreamId stream_id1{1}; size_t const client_cache_size{3}; }; TEST_F(BufferStreamTracker, only_returns_true_if_buffer_already_tracked) { mf::BufferStreamTracker tracker{client_cache_size}; EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer0)); EXPECT_TRUE(tracker.track_buffer(stream_id0, &stub_buffer0)); EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer1)); EXPECT_FALSE(tracker.track_buffer(stream_id1, &stub_buffer2)); EXPECT_FALSE(tracker.track_buffer(stream_id1, &stub_buffer3)); } TEST_F(BufferStreamTracker, removals_remove_buffer_instances) { mf::BufferStreamTracker tracker{client_cache_size}; EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer0)); EXPECT_FALSE(tracker.track_buffer(stream_id1, &stub_buffer1)); EXPECT_TRUE(tracker.track_buffer(stream_id0, &stub_buffer0)); EXPECT_TRUE(tracker.track_buffer(stream_id1, &stub_buffer1)); EXPECT_EQ(&stub_buffer0, tracker.last_buffer(stream_id0)); tracker.remove_buffer_stream(stream_id0); EXPECT_EQ(nullptr, tracker.last_buffer(stream_id0)); EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer0)); EXPECT_TRUE(tracker.track_buffer(stream_id1, &stub_buffer1)); tracker.remove_buffer_stream(mf::BufferStreamId{33}); } TEST_F(BufferStreamTracker, last_client_buffer) { mf::BufferStreamTracker tracker{client_cache_size}; tracker.track_buffer(stream_id0, &stub_buffer0); EXPECT_EQ(&stub_buffer0, tracker.last_buffer(stream_id0)); tracker.track_buffer(stream_id0, &stub_buffer1); EXPECT_EQ(&stub_buffer1, tracker.last_buffer(stream_id0)); EXPECT_EQ(nullptr, tracker.last_buffer(stream_id1)); tracker.track_buffer(stream_id1, &stub_buffer2); EXPECT_EQ(&stub_buffer2, tracker.last_buffer(stream_id1)); } TEST_F(BufferStreamTracker, buffers_expire_if_they_overrun_cache_size) { mf::BufferStreamTracker tracker{client_cache_size}; EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer0)); EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer1)); EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer2)); //stub_buffer0 forced out EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer3)); //tracker reports its never seen stub_buffer0 EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer0)); EXPECT_TRUE(tracker.track_buffer(stream_id0, &stub_buffer3)); EXPECT_EQ(&stub_buffer3, tracker.last_buffer(stream_id0)); EXPECT_TRUE(tracker.track_buffer(stream_id0, &stub_buffer3)); EXPECT_TRUE(tracker.track_buffer(stream_id0, &stub_buffer0)); EXPECT_TRUE(tracker.track_buffer(stream_id0, &stub_buffer2)); EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer1)); } TEST_F(BufferStreamTracker, buffers_only_affect_associated_surfaces) { mf::BufferStreamTracker tracker{client_cache_size}; EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer0)); EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer1)); EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer2)); EXPECT_EQ(&stub_buffer2, tracker.last_buffer(stream_id0)); EXPECT_FALSE(tracker.track_buffer(stream_id1, &stub_buffer3)); EXPECT_EQ(&stub_buffer2, tracker.last_buffer(stream_id0)); EXPECT_EQ(&stub_buffer3, tracker.last_buffer(stream_id1)); EXPECT_FALSE(tracker.track_buffer(stream_id0, &stub_buffer4)); EXPECT_EQ(&stub_buffer4, tracker.last_buffer(stream_id0)); EXPECT_EQ(&stub_buffer3, tracker.last_buffer(stream_id1)); } TEST_F(BufferStreamTracker, can_lookup_a_buffer_from_a_buffer_id) { using namespace testing; mf::BufferStreamTracker tracker{client_cache_size}; tracker.track_buffer(stream_id0, &stub_buffer0); tracker.track_buffer(stream_id0, &stub_buffer1); tracker.track_buffer(stream_id0, &stub_buffer2); EXPECT_THAT(tracker.buffer_from(stub_buffer0.id()), Eq(&stub_buffer0)); EXPECT_THAT(tracker.buffer_from(stub_buffer1.id()), Eq(&stub_buffer1)); EXPECT_THAT(tracker.buffer_from(stub_buffer2.id()), Eq(&stub_buffer2)); EXPECT_THROW({ tracker.buffer_from(stub_buffer3.id()); }, std::logic_error); tracker.track_buffer(stream_id0, &stub_buffer3); EXPECT_THAT(tracker.buffer_from(stub_buffer3.id()), Eq(&stub_buffer3)); EXPECT_THROW({ tracker.buffer_from(stub_buffer0.id()); }, std::logic_error); } ./tests/unit-tests/frontend/stress_protobuf_communicator.cpp0000644000015600001650000001651112676616157024751 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/frontend/connector.h" #include "src/server/frontend/resource_cache.h" #include "mir_protobuf.pb.h" #include "mir/test/doubles/stub_ipc_factory.h" #include "mir/test/stub_server_tool.h" #include "mir/test/test_protobuf_server.h" #include "mir/test/doubles/null_client_event_sink.h" #include "mir_test_framework/testing_server_configuration.h" #include "src/client/connection_surface_map.h" #include "src/client/display_configuration.h" #include "src/client/lifecycle_control.h" #include "src/client/ping_handler.h" #include "src/client/rpc/null_rpc_report.h" #include "src/client/rpc/make_rpc_channel.h" #include "src/client/rpc/mir_basic_rpc_channel.h" #include "src/client/rpc/mir_display_server.h" #include "src/client/buffer_factory.h" #include "mir/input/input_devices.h" #include #include #include #include #include #include #include namespace mf = mir::frontend; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { struct StubProtobufClient { StubProtobufClient(std::string socket_file, int timeout_ms); std::shared_ptr rpc_report; std::shared_ptr channel; mir::client::rpc::DisplayServer display_server; mir::protobuf::ConnectParameters connect_parameters; mir::protobuf::SurfaceParameters surface_parameters; mir::protobuf::Surface surface; mir::protobuf::Void ignored; mir::protobuf::Connection connection; void create_surface_done(); void exchange_buffer_done(); void disconnect_done(); void wait_for_create_surface(); void wait_for_exchange_buffer(); void wait_for_disconnect_done(); const int maxwait; std::atomic connect_done_called; std::atomic create_surface_called; std::atomic exchange_buffer_called; std::atomic release_surface_called; std::atomic disconnect_done_called; std::atomic tfd_done_called; std::atomic connect_done_count; std::atomic create_surface_done_count; std::atomic disconnect_done_count; }; } struct StressProtobufCommunicator : public ::testing::Test { static void SetUpTestCase() { stub_server_tool = std::make_shared(); stub_server = std::make_shared(mtf::test_socket_file(), stub_server_tool); stub_server->comm->start(); } void SetUp() { client = std::make_shared(mtf::test_socket_file(), 100); client->connect_parameters.set_application_name(__PRETTY_FUNCTION__); } void TearDown() { client.reset(); } static void TearDownTestCase() { stub_server.reset(); stub_server_tool.reset(); } std::shared_ptr client; static std::shared_ptr stub_server_tool; private: static std::shared_ptr stub_server; }; std::shared_ptr StressProtobufCommunicator::stub_server_tool; std::shared_ptr StressProtobufCommunicator::stub_server; TEST_F(StressProtobufCommunicator, DISABLED_stress_exchange_buffer) { client->display_server.create_surface( &client->surface_parameters, &client->surface, google::protobuf::NewCallback(client.get(), &StubProtobufClient::create_surface_done)); client->wait_for_create_surface(); for (int i = 0; i != 100000; ++i) { mir::protobuf::BufferRequest request; request.mutable_id()->set_value(client->surface.id().value()); *request.mutable_buffer() = client->surface.buffer(); client->display_server.exchange_buffer( &request, client->surface.mutable_buffer(), google::protobuf::NewCallback(client.get(), &StubProtobufClient::exchange_buffer_done)); client->wait_for_exchange_buffer(); } client->display_server.disconnect( &client->ignored, &client->ignored, google::protobuf::NewCallback(client.get(), &StubProtobufClient::disconnect_done)); client->wait_for_disconnect_done(); } StubProtobufClient::StubProtobufClient( std::string socket_file, int timeout_ms) : rpc_report(std::make_shared()), channel(mir::client::rpc::make_rpc_channel( socket_file, std::make_shared(), std::make_shared(), std::make_shared(), std::make_shared(), rpc_report, std::make_shared(), std::make_shared(), std::make_shared())), display_server(channel), maxwait(timeout_ms), connect_done_called(false), create_surface_called(false), exchange_buffer_called(false), release_surface_called(false), disconnect_done_called(false), tfd_done_called(false), connect_done_count(0), create_surface_done_count(0), disconnect_done_count(0) { surface_parameters.set_width(640); surface_parameters.set_height(480); surface_parameters.set_pixel_format(0); surface_parameters.set_buffer_usage(0); } void StubProtobufClient::create_surface_done() { create_surface_called.store(true); auto old = create_surface_done_count.load(); while (!create_surface_done_count.compare_exchange_weak(old, old+1)); } void StubProtobufClient::exchange_buffer_done() { exchange_buffer_called.store(true); } void StubProtobufClient::disconnect_done() { disconnect_done_called.store(true); auto old = disconnect_done_count.load(); while (!disconnect_done_count.compare_exchange_weak(old, old+1)); } void StubProtobufClient::wait_for_create_surface() { for (int i = 0; !create_surface_called.load() && i < maxwait; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::yield(); } create_surface_called.store(false); } void StubProtobufClient::wait_for_exchange_buffer() { for (int i = 0; !exchange_buffer_called.load() && i < maxwait; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::yield(); } exchange_buffer_called.store(false); } void StubProtobufClient::wait_for_disconnect_done() { for (int i = 0; !disconnect_done_called.load() && i < maxwait; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::yield(); } disconnect_done_called.store(false); } ./tests/unit-tests/frontend/test_resource_cache.cpp0000644000015600001650000000572012676616125022732 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "src/server/frontend/resource_cache.h" #include "mir/test/doubles/fd_matcher.h" #include "mir_protobuf.pb.h" #include #include #include #include namespace mir { namespace { struct TestResource { TestResource() { int count = instances.load(); while (!instances.compare_exchange_weak(count, count + 1)) std::this_thread::yield(); } ~TestResource() { int count = instances.load(); while (!instances.compare_exchange_weak(count, count - 1)) std::this_thread::yield(); } TestResource(TestResource const&) = delete; TestResource& operator=(TestResource const&) = delete; static std::atomic instances; }; std::atomic TestResource::instances; } } using mir::TestResource; namespace { struct ResourceCache : ::testing::Test { mir::frontend::ResourceCache cache; }; } TEST_F(ResourceCache, resources_are_saved) { int const a_few = 13; mir::protobuf::Void keys[a_few]; for (auto& p : keys) { auto sp = std::make_shared(); cache.save_resource(&p, sp); } EXPECT_EQ(a_few, TestResource::instances.load()); } TEST_F(ResourceCache, resources_are_freed) { int const a_few = 13; mir::protobuf::Void keys[a_few]; for (auto& p : keys) { auto sp = std::make_shared(); cache.save_resource(&p, sp); } for (auto& p : keys) { cache.free_resource(&p); } EXPECT_EQ(0, TestResource::instances.load()); } TEST_F(ResourceCache, fds_are_saved) { using namespace mir::test::doubles; using namespace testing; int const a_few = 3; int const fds_per_key = 3; mir::protobuf::Void keys[a_few]; std::vector raw_fds; for (auto& p : keys) { for(auto fake_fd = 0; fake_fd < fds_per_key; fake_fd++) { auto raw_fd = fileno(tmpfile()); raw_fds.push_back(raw_fd); cache.save_fd(&p, mir::Fd(raw_fd)); } } //resource_cache should be the only owner for(auto raw_fd : raw_fds) EXPECT_THAT(raw_fd, RawFdIsValid()); for (auto& p : keys) cache.free_resource(&p); for(auto raw_fd : raw_fds) EXPECT_THAT(raw_fd, Not(RawFdIsValid())); } ./tests/unit-tests/frontend/test_protobuf_reports_errors.cpp0000644000015600001650000000722112676616125024770 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Alan Griffiths */ #include "mir/frontend/connector.h" #include "src/server/frontend/resource_cache.h" #include "mir/test/stub_server_tool.h" #include "mir/test/test_protobuf_server.h" #include "mir/test/doubles/stub_ipc_factory.h" #include "mir/test/test_protobuf_client.h" namespace mt = mir::test; namespace mir { namespace test { struct ErrorServer : StubServerTool { static std::string const test_exception_text; void create_surface( const protobuf::SurfaceParameters*, protobuf::Surface*, google::protobuf::Closure*) override { throw std::runtime_error(test_exception_text); } void release_surface( const protobuf::SurfaceId*, protobuf::Void*, google::protobuf::Closure*) override { throw std::runtime_error(test_exception_text); } void connect( const ::mir::protobuf::ConnectParameters*, ::mir::protobuf::Connection*, ::google::protobuf::Closure*) override { throw std::runtime_error(test_exception_text); } void disconnect( const protobuf::Void*, protobuf::Void*, google::protobuf::Closure*) override { throw std::runtime_error(test_exception_text); } }; std::string const ErrorServer::test_exception_text{"test exception text"}; } } struct ProtobufErrorTestFixture : public ::testing::Test { void SetUp() { stub_services = std::make_shared(); server = std::make_shared("./test_error_fixture", stub_services); client = std::make_shared("./test_error_fixture", 10000); server->comm->start(); } void TearDown() { server.reset(); } std::shared_ptr stub_services; std::shared_ptr server; std::shared_ptr client; }; TEST_F(ProtobufErrorTestFixture, connect_exception) { client->connect_parameters.set_application_name(__PRETTY_FUNCTION__); EXPECT_CALL(*client, connect_done()).Times(1); mir::protobuf::Connection result; client->display_server.connect( &client->connect_parameters, &result, google::protobuf::NewCallback(client.get(), &mt::TestProtobufClient::connect_done)); client->wait_for_connect_done(); ASSERT_TRUE(result.has_error()); EXPECT_NE(std::string::npos, result.error().find(stub_services->test_exception_text)); } TEST_F(ProtobufErrorTestFixture, create_surface_exception) { EXPECT_CALL(*client, create_surface_done()).Times(1); client->display_server.create_surface( &client->surface_parameters, &client->surface, google::protobuf::NewCallback(client.get(), &mt::TestProtobufClient::create_surface_done)); client->wait_for_create_surface(); ASSERT_TRUE(client->surface.has_error()); EXPECT_NE(std::string::npos, client->surface.error().find(stub_services->test_exception_text)); } ./tests/unit-tests/frontend/test_socket_connection.cpp0000644000015600001650000001422412676616125023466 0ustar jenkinsjenkins/* * Copyright © 2013, 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois * Alan Griffiths */ #include "src/server/frontend/socket_connection.h" #include "src/server/frontend/message_receiver.h" #include "mir/frontend/message_processor.h" #include "mir/frontend/session_credentials.h" #include "mir/fd.h" #include "mir/protobuf/protocol_version.h" #include "mir/test/fake_shared.h" #include "mir_protobuf_wire.pb.h" #include #include #include namespace mf = mir::frontend; namespace mfd = mir::frontend::detail; namespace ba = boost::asio; namespace mt = mir::test; using namespace testing; namespace { mir::Fd temp_fd() { return mir::Fd(fileno(tmpfile())); } struct StubReceiver : mfd::MessageReceiver { StubReceiver() : async_buffer{nullptr, 0}, some_fds{temp_fd(), temp_fd(), temp_fd()} { } void async_receive_msg( std::function const& callback, boost::asio::mutable_buffers_1 const& buffer) override { async_buffer = buffer; callback_function = callback; } boost::system::error_code receive_msg(boost::asio::mutable_buffers_1 const& buffer) override { if (ba::buffer_cast(buffer) == nullptr) throw std::runtime_error("StubReceiver::receive_msg got null buffer"); if (ba::buffer_size(buffer) != message.size()) throw std::runtime_error("StubReceiver::receive_msg buffer size not equal to message size"); memcpy(ba::buffer_cast(buffer), message.data(), ba::buffer_size(buffer)); return boost::system::error_code(); } void receive_fds(std::vector& fds) override { int i = 0; for(auto& fd : fds) fd = some_fds[i++ % some_fds.size()]; } size_t available_bytes() override { return message.size(); } void fake_receive_msg(char* buffer, size_t size) { message.assign(buffer, buffer + size); ASSERT_NE(nullptr, callback_function); ASSERT_THAT(ba::buffer_cast(async_buffer), NotNull()); ASSERT_THAT(message.size(), Ge(ba::buffer_size(async_buffer))); memcpy(ba::buffer_cast(async_buffer), buffer, ba::buffer_size(async_buffer)); message.erase( message.begin(), message.begin() + ba::buffer_size(async_buffer)); boost::system::error_code code; callback_function(code, size); } std::function callback_function; boost::asio::mutable_buffers_1 async_buffer; std::vector message; std::vector some_fds; MOCK_METHOD0(client_creds, mf::SessionCredentials()); }; struct MockProcessor : public mfd::MessageProcessor { MOCK_METHOD2(dispatch, bool(mfd::Invocation const& invocation, std::vector const&)); MOCK_METHOD1(client_pid, void(int pid)); }; } struct SocketConnection : public Test { NiceMock mock_processor; NiceMock stub_receiver; std::shared_ptr> null_sessions; mf::SessionCredentials client_creds{1, 1, 1}; mfd::SocketConnection connection{mt::fake_shared(stub_receiver), 0, null_sessions, mt::fake_shared(mock_processor)}; void SetUp() { ON_CALL(mock_processor, dispatch(_,_)).WillByDefault(Return(true)); ON_CALL(stub_receiver, client_creds()).WillByDefault(Return(client_creds)); connection.read_next_message(); } void fake_receiving_message() { int const header_size = 2; char buffer[512]; mir::protobuf::wire::Invocation invocation; invocation.set_id(1); invocation.set_method_name(""); invocation.set_parameters(buffer, 0); invocation.set_protocol_version(mir::protobuf::current_protocol_version()); invocation.set_side_channel_fds(2); auto const body_size = invocation.ByteSize(); buffer[0] = body_size / 0x100; buffer[1] = body_size % 0x100; invocation.SerializeToArray(buffer + header_size, sizeof buffer - header_size); stub_receiver.fake_receive_msg(buffer, header_size + body_size); } }; TEST_F(SocketConnection, dispatches_message_on_receipt) { EXPECT_CALL(mock_processor, dispatch(_,_)).Times(1); fake_receiving_message(); } TEST_F(SocketConnection, dispatches_messages_on_receipt) { auto const arbitary_no_of_messages = 5; EXPECT_CALL(mock_processor, dispatch(_,_)).Times(arbitary_no_of_messages); for (int i = 0; i != arbitary_no_of_messages; ++i) fake_receiving_message(); } TEST_F(SocketConnection, checks_client_pid_when_message_received) { EXPECT_CALL(stub_receiver, client_creds()).Times(1); fake_receiving_message(); } TEST_F(SocketConnection, notifies_client_pid_before_message_dispatched) { InSequence seq; EXPECT_CALL(mock_processor, client_pid(_)).Times(1); EXPECT_CALL(mock_processor, dispatch(_,_)).Times(1); fake_receiving_message(); } TEST_F(SocketConnection, notifies_client_pid_once_only) { auto const arbitary_no_of_messages = 5; EXPECT_CALL(mock_processor, client_pid(_)).Times(1); for (int i = 0; i != arbitary_no_of_messages; ++i) fake_receiving_message(); } TEST_F(SocketConnection, receives_and_dispatches_fds) { std::vector fds{stub_receiver.some_fds[0], stub_receiver.some_fds[1]}; EXPECT_CALL(mock_processor, dispatch(_, ContainerEq(fds))); fake_receiving_message(); } ./tests/unit-tests/precompiled.hpp0000644000015600001650000000162212676616125017407 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_UNIT_TESTS_PRECOMPILED_H_ #define MIR_UNIT_TESTS_PRECOMPILED_H_ #include #include #include #include #include #include #endif ./tests/unit-tests/test_fd.cpp0000644000015600001650000000365512676616125016537 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/fd.h" #include #include #include #include #include namespace { bool fd_is_open(int fd) { return fcntl(fd, F_GETFD) != -1; } struct Fd : public testing::Test { Fd() : raw_fd(::open("/dev/null", 0)) { } ~Fd() { if (fd_is_open(raw_fd)) ::close(raw_fd); } int const raw_fd; }; } TEST_F(Fd, does_not_close_if_construction_doesnt_intend_to_transfer_ownership) { EXPECT_TRUE(fd_is_open(raw_fd)); { mir::Fd fd(mir::IntOwnedFd{raw_fd}); } EXPECT_TRUE(fd_is_open(raw_fd)); } TEST_F(Fd, closes_when_refcount_is_zero) { EXPECT_TRUE(fd_is_open(raw_fd)); mir::Fd fd2(-1); { mir::Fd fd(raw_fd); { mir::Fd fd1(fd); fd2 = fd1; } } EXPECT_TRUE(fd_is_open(raw_fd)); fd2 = mir::Fd(-1); EXPECT_FALSE(fd_is_open(raw_fd)); } TEST_F(Fd, moves_around) { EXPECT_TRUE(fd_is_open(raw_fd)); mir::Fd fd0(-1); fd0 = mir::Fd(raw_fd); mir::Fd fd1(std::move(fd0)); mir::Fd fd2(fd1); EXPECT_TRUE(fd_is_open(raw_fd)); fd1 = mir::Fd(-1); EXPECT_TRUE(fd_is_open(raw_fd)); fd2 = mir::Fd(-1); EXPECT_FALSE(fd_is_open(raw_fd)); } ./tests/unit-tests/input/0000755000015600001650000000000012676616160015530 5ustar jenkinsjenkins./tests/unit-tests/input/test_touchspot_controller.cpp0000644000015600001650000001643512676616125023600 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/server/input/touchspot_controller.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/renderable.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_scene.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/stub_input_scene.h" #include #include #include #include #include namespace mi = mir::input; namespace mg = mir::graphics; namespace mt = mir::test; namespace mtd = mt::doubles; namespace geom = mir::geometry; namespace { struct MockBufferAllocator : public mg::GraphicBufferAllocator { MOCK_METHOD1(alloc_buffer, std::shared_ptr(mg::BufferProperties const&)); MOCK_METHOD0(supported_pixel_formats, std::vector(void)); }; struct StubScene : public mtd::StubInputScene { void add_input_visualization(std::shared_ptr const& overlay) override { overlays.push_back(overlay); } void remove_input_visualization(std::weak_ptr const& overlay) override { auto l = overlay.lock(); assert(l); auto it = std::find(overlays.begin(), overlays.end(), l); assert(it != overlays.end()); overlays.erase(it); } void expect_spots_centered_at(std::vector spots) { int const touchspot_side_in_pixels = 64; for (auto overlay : overlays) { auto top_left_pos = overlay->screen_position().top_left; auto center_pos = geom::Point{top_left_pos.x.as_int() + touchspot_side_in_pixels/2, top_left_pos.y.as_int() + touchspot_side_in_pixels/2}; auto it = std::find(spots.begin(), spots.end(), center_pos); EXPECT_FALSE(it == spots.end()); spots.erase(it); } // If there are left over spots then we didn't have an overlay corresponding to one EXPECT_EQ(0, spots.size()); } std::vector> overlays; }; struct TestTouchspotController : public ::testing::Test { TestTouchspotController() : allocator(std::make_shared()), scene(std::make_shared()) { } std::shared_ptr const allocator; std::shared_ptr const scene; }; MATCHER(SoftwareBuffer, "") { auto properties = static_cast(arg); if (properties.usage != mg::BufferUsage::software) return false; return true; } } TEST_F(TestTouchspotController, allocates_software_buffer_for_touchspots) { using namespace ::testing; EXPECT_CALL(*allocator, alloc_buffer(SoftwareBuffer())).Times(1) .WillOnce(Return(std::make_shared())); mi::TouchspotController controller(allocator, scene); } TEST_F(TestTouchspotController, touches_result_in_renderables_in_stack) { using namespace ::testing; EXPECT_CALL(*allocator, alloc_buffer(SoftwareBuffer())).Times(1) .WillOnce(Return(std::make_shared())); mi::TouchspotController controller(allocator, scene); controller.enable(); controller.visualize_touches({ {{0,0}, 1} }); scene->expect_spots_centered_at({{0, 0}}); } TEST_F(TestTouchspotController, spots_move) { using namespace ::testing; EXPECT_CALL(*allocator, alloc_buffer(SoftwareBuffer())).Times(1) .WillOnce(Return(std::make_shared())); mi::TouchspotController controller(allocator, scene); controller.enable(); controller.visualize_touches({ {{0,0}, 1} }); scene->expect_spots_centered_at({{0, 0}}); controller.visualize_touches({ {{1,1}, 1} }); scene->expect_spots_centered_at({{1, 1}}); } TEST_F(TestTouchspotController, multiple_spots) { using namespace ::testing; EXPECT_CALL(*allocator, alloc_buffer(SoftwareBuffer())).Times(1) .WillOnce(Return(std::make_shared())); mi::TouchspotController controller(allocator, scene); controller.enable(); controller.visualize_touches({ {{0,0}, 1}, {{1, 1}, 1}, {{3, 3}, 1} }); scene->expect_spots_centered_at({{0, 0}, {1, 1}, {3, 3}}); controller.visualize_touches({ {{0,0}, 1}, {{1, 1}, 1}, {{3, 3}, 1}, {{5, 5}, 1} }); scene->expect_spots_centered_at({{0, 0}, {1, 1}, {3, 3}, {5, 5}}); controller.visualize_touches({ {{1,1}, 1} }); scene->expect_spots_centered_at({{1, 1}}); controller.visualize_touches({}); scene->expect_spots_centered_at({}); } // This leaves some semantics undefined, i,e. if the touchspot controller is enabled/disabled // during a gesture do the spots appear/dissapear? I've been unable to develop a strong opinion // on this semantic, so I am leaving it unspecified ~racarr TEST_F(TestTouchspotController, touches_do_not_result_in_renderables_in_stack_when_disabled) { using namespace ::testing; EXPECT_CALL(*allocator, alloc_buffer(SoftwareBuffer())).Times(1) .WillOnce(Return(std::make_shared())); mi::TouchspotController controller(allocator, scene); controller.enable(); controller.disable(); controller.visualize_touches({ {{0,0}, 1} }); scene->expect_spots_centered_at({}); controller.enable(); controller.visualize_touches({ {{0,0}, 1} }); scene->expect_spots_centered_at({{0, 0}}); } namespace { struct StubSceneWithMockEmission : public StubScene { MOCK_METHOD0(emit_scene_changed, void()); }; struct TestTouchspotControllerSceneUpdates : public TestTouchspotController { TestTouchspotControllerSceneUpdates() : scene(std::make_shared()) { EXPECT_CALL(*allocator, alloc_buffer(SoftwareBuffer())).Times(1) .WillOnce(testing::Return(std::make_shared())); } std::shared_ptr const scene; }; } TEST_F(TestTouchspotControllerSceneUpdates, does_not_emit_damage_if_nothing_happens) { EXPECT_CALL(*scene, emit_scene_changed()).Times(0); mi::TouchspotController controller(allocator, scene); // We are disabled so no damage should occur. controller.visualize_touches({ {{0,0}, 1} }); controller.enable(); // Now we are enabled but do nothing so still no damage should occur. controller.visualize_touches({}); } TEST_F(TestTouchspotControllerSceneUpdates, emits_scene_damage) { EXPECT_CALL(*scene, emit_scene_changed()).Times(2); mi::TouchspotController controller(allocator, scene); controller.enable(); controller.visualize_touches({ {{0,0}, 1} }); controller.visualize_touches({ {{1,1}, 1}}); } ./tests/unit-tests/input/test_event_filter_chain_dispatcher.cpp0000644000015600001650000000754712676616125025347 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/server/input/event_filter_chain_dispatcher.h" #include "src/server/input/null_input_dispatcher.h" #include "mir/test/doubles/mock_event_filter.h" #include "mir/test/doubles/mock_input_dispatcher.h" #include "mir/events/event_builders.h" #include "mir/events/event_private.h" #include #include #include namespace mi = mir::input; namespace mtd = mir::test::doubles; using namespace ::testing; namespace { std::shared_ptr mock_filter() { return std::make_shared(); } struct EventFilterChainDispatcher : public ::testing::Test { mir::EventUPtr const event = mir::events::make_event(MirInputDeviceId(), std::chrono::nanoseconds(0), std::vector{}, MirKeyboardAction(), xkb_keysym_t(), 0, MirInputEventModifiers()); }; } TEST_F(EventFilterChainDispatcher, offers_events_to_filters) { auto filter = mock_filter(); mi::EventFilterChainDispatcher filter_chain({filter, filter}, std::make_shared()); // Filter will pass the event on twice EXPECT_CALL(*filter, handle(_)).Times(2).WillRepeatedly(Return(false)); // So the filter chain should also reject the event EXPECT_FALSE(filter_chain.handle(*event)); } TEST_F(EventFilterChainDispatcher, prepends_appends_filters) { auto filter1 = mock_filter(); auto filter2 = mock_filter(); auto filter3 = mock_filter(); mi::EventFilterChainDispatcher filter_chain({filter2}, std::make_shared()); filter_chain.append(filter3); filter_chain.prepend(filter1); { InSequence s; EXPECT_CALL(*filter1, handle(_)).WillOnce(Return(false)); EXPECT_CALL(*filter2, handle(_)).WillOnce(Return(false)); EXPECT_CALL(*filter3, handle(_)).WillOnce(Return(false)); } filter_chain.handle(*event); } TEST_F(EventFilterChainDispatcher, accepting_event_halts_emission) { auto filter = mock_filter(); mi::EventFilterChainDispatcher filter_chain({filter, filter, filter}, std::make_shared()); // First filter will reject, second will accept, third one should not be asked. { InSequence seq; EXPECT_CALL(*filter, handle(_)).Times(1).WillOnce(Return(false)); EXPECT_CALL(*filter, handle(_)).Times(1).WillOnce(Return(true)); } filter_chain.handle(*event); } TEST_F(EventFilterChainDispatcher, does_not_own_event_filters) { auto filter = mock_filter(); mi::EventFilterChainDispatcher filter_chain({filter}, std::make_shared()); EXPECT_CALL(*filter, handle(_)).Times(1).WillOnce(Return(true)); EXPECT_TRUE(filter_chain.handle(*event)); filter.reset(); EXPECT_FALSE(filter_chain.handle(*event)); } TEST_F(EventFilterChainDispatcher, forwards_start_and_stop) { auto mock_next_dispatcher = std::make_shared(); mi::EventFilterChainDispatcher filter_chain({}, mock_next_dispatcher); InSequence seq; EXPECT_CALL(*mock_next_dispatcher, start()).Times(1); EXPECT_CALL(*mock_next_dispatcher, stop()).Times(1); filter_chain.start(); filter_chain.stop(); } ./tests/unit-tests/input/test_surface_input_dispatcher.cpp0000644000015600001650000005503212676616125024356 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/server/input/surface_input_dispatcher.h" #include "mir/events/event_builders.h" #include "mir/events/event_private.h" #include "mir/scene/observer.h" #include "mir/thread_safe_list.h" #include "mir/test/event_matchers.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/stub_input_scene.h" #include "mir/test/doubles/mock_surface.h" #include #include #include #include namespace ms = mir::scene; namespace mi = mir::input; namespace mev = mir::events; namespace mt = mir::test; namespace mtd = mt::doubles; namespace geom = mir::geometry; using namespace ::testing; namespace { struct MockSurfaceWithGeometry : public mtd::MockSurface { MockSurfaceWithGeometry(geom::Rectangle const& geom) : geom(geom) { } bool input_area_contains(geom::Point const& p) const override { return geom.contains(p); } geom::Rectangle input_bounds() const override { return geom; } geom::Rectangle const geom; }; struct StubInputScene : public mtd::StubInputScene { std::shared_ptr add_surface(geom::Rectangle const& geometry) { auto surface = std::make_shared(geometry); surfaces.add(surface); observer->surface_added(surface.get()); return surface; } void remove_surface(std::shared_ptr const& surface) { surfaces.remove(surface); observer->surface_removed(surface.get()); } std::shared_ptr add_surface() { return add_surface({{0, 0}, {1, 1}}); } void for_each(std::function const&)> const& exec) override { surfaces.for_each([&exec](std::shared_ptr const& surface) { exec(surface); }); } void add_observer(std::shared_ptr const& new_observer) override { assert(observer == nullptr); observer = new_observer; surfaces.for_each([this](std::shared_ptr const& surface) { observer->surface_exists(surface.get()); }); } void remove_observer(std::weak_ptr const& /* remove_observer */) override { assert(observer != nullptr); observer->end_observation(); observer.reset(); } mir::ThreadSafeList> surfaces; std::shared_ptr observer; }; struct SurfaceInputDispatcher : public testing::Test { SurfaceInputDispatcher() : dispatcher(mt::fake_shared(scene)) { } void TearDown() override { dispatcher.stop(); } StubInputScene scene; mi::SurfaceInputDispatcher dispatcher; }; struct FakeKeyboard { FakeKeyboard(MirInputDeviceId id = 0) : id(id) { } mir::EventUPtr press(int scan_code = 7) { return mev::make_event(id, std::chrono::nanoseconds(0), std::vector{}, mir_keyboard_action_down, 0, scan_code, mir_input_event_modifier_alt); } mir::EventUPtr release(int scan_code = 7) { return mev::make_event(id, std::chrono::nanoseconds(0), std::vector{}, mir_keyboard_action_up, 0, scan_code, mir_input_event_modifier_alt); } MirInputDeviceId const id; }; struct FakePointer { FakePointer(MirInputDeviceId id = 0) : id(id), buttons(0) { } mir::EventUPtr move_to(geom::Point const& location) { return mev::make_event(id, std::chrono::nanoseconds(0), std::vector{}, 0, mir_pointer_action_motion, buttons, location.x.as_int(), location.y.as_int(), 0, 0, 0, 0); } mir::EventUPtr release_button(geom::Point const& location, MirPointerButton button = mir_pointer_button_primary) { buttons &= ~button; return mev::make_event(id, std::chrono::nanoseconds(0), std::vector{}, 0, mir_pointer_action_button_up, buttons, location.x.as_int(), location.y.as_int(), 0, 0, 0, 0); } mir::EventUPtr press_button(geom::Point const& location, MirPointerButton button = mir_pointer_button_primary) { buttons |= button; return mev::make_event(id, std::chrono::nanoseconds(0), std::vector{}, 0, mir_pointer_action_button_down, buttons, location.x.as_int(), location.y.as_int(), 0, 0, 0, 0); } MirInputDeviceId const id; MirPointerButtons buttons; }; struct FakeToucher { FakeToucher(MirInputDeviceId id = 0) : id(id) { } mir::EventUPtr move_to(geom::Point const& point) { auto ev = mev::make_event(id, std::chrono::nanoseconds(0), std::vector{}, 0); mev::add_touch(*ev, 0, mir_touch_action_change, mir_touch_tooltype_finger, point.x.as_int(), point.y.as_int(), touched ? 1.0 : 0.0, touched ? 1.0 : 0.0, touched ? 1.0 : 0.0, touched ? 1.0 : 0.0); return ev; } mir::EventUPtr touch_at(geom::Point const& point) { touched = true; auto ev = mev::make_event(id, std::chrono::nanoseconds(0), std::vector{}, 0); mev::add_touch(*ev, 0, mir_touch_action_down, mir_touch_tooltype_finger, point.x.as_int(), point.y.as_int(), 1.0, 1.0, 1.0, 1.0); return ev; } mir::EventUPtr touches_at(geom::Point const& point1, geom::Point const& point2) { touched = true; auto ev = mev::make_event(id, std::chrono::nanoseconds(0), std::vector{}, 0); mev::add_touch(*ev, 0, mir_touch_action_down, mir_touch_tooltype_finger, point1.x.as_int(), point1.y.as_int(), 1.0, 1.0, 1.0, 1.0); mev::add_touch(*ev, 0, mir_touch_action_down, mir_touch_tooltype_finger, point2.x.as_int(), point2.y.as_int(), 1.0, 1.0, 1.0, 1.0); return ev; } mir::EventUPtr release_at(geom::Point const& point) { touched = false; auto ev = mev::make_event(id, std::chrono::nanoseconds(0), std::vector{}, 0); mev::add_touch(*ev, 0, mir_touch_action_up, mir_touch_tooltype_finger, point.x.as_int(), point.y.as_int(), 0.0, 0.0, 0.0, 0.0); return ev; } mir::EventUPtr releases_at(geom::Point const& point1, geom::Point const& point2) { touched = false; auto ev = mev::make_event(id, std::chrono::nanoseconds(0), std::vector{}, 0); mev::add_touch(*ev, 0, mir_touch_action_up, mir_touch_tooltype_finger, point1.x.as_int(), point1.y.as_int(), 1.0, 1.0, 1.0, 1.0); mev::add_touch(*ev, 0, mir_touch_action_up, mir_touch_tooltype_finger, point2.x.as_int(), point2.y.as_int(), 1.0, 1.0, 1.0, 1.0); return ev; } bool touched = false; MirInputDeviceId const id; }; } TEST_F(SurfaceInputDispatcher, key_event_delivered_to_focused_surface) { auto surface = scene.add_surface(); FakeKeyboard keyboard; auto event = keyboard.press(); EXPECT_CALL(*surface, consume(mt::MirKeyboardEventMatches(*event))).Times(1); dispatcher.start(); dispatcher.set_focus(surface); EXPECT_TRUE(dispatcher.dispatch(*event)); } TEST_F(SurfaceInputDispatcher, key_event_dropped_if_no_surface_focused) { auto surface = scene.add_surface(); EXPECT_CALL(*surface, consume(_)).Times(0); dispatcher.start(); FakeKeyboard keyboard; EXPECT_FALSE(dispatcher.dispatch(*keyboard.press())); } TEST_F(SurfaceInputDispatcher, inconsistent_key_events_dropped) { auto surface = scene.add_surface(); EXPECT_CALL(*surface, consume(_)).Times(0); dispatcher.start(); dispatcher.set_focus(surface); FakeKeyboard keyboard; EXPECT_FALSE(dispatcher.dispatch(*keyboard.release())); } TEST_F(SurfaceInputDispatcher, key_state_is_consistent_per_client) { auto surface_1 = scene.add_surface(); auto surface_2 = scene.add_surface(); FakeKeyboard keyboard; auto down_event = keyboard.press(); auto up_event = keyboard.release(); EXPECT_CALL(*surface_1, consume(mt::MirKeyboardEventMatches(*down_event))).Times(1); EXPECT_CALL(*surface_2, consume(_)).Times(0); dispatcher.start(); dispatcher.set_focus(surface_1); EXPECT_TRUE(dispatcher.dispatch(*down_event)); dispatcher.set_focus(surface_2); EXPECT_FALSE(dispatcher.dispatch(*up_event)); } TEST_F(SurfaceInputDispatcher, inconsistent_key_down_dropped) { auto surface = scene.add_surface(); FakeKeyboard keyboard; auto event = keyboard.press(); InSequence seq; EXPECT_CALL(*surface, consume(mt::MirKeyboardEventMatches(*event))).Times(1); dispatcher.start(); dispatcher.set_focus(surface); EXPECT_TRUE(dispatcher.dispatch(*event)); EXPECT_FALSE(dispatcher.dispatch(*event)); EXPECT_FALSE(dispatcher.dispatch(*event)); } TEST_F(SurfaceInputDispatcher, device_reset_resets_key_state_consistency) { auto surface = scene.add_surface(); auto device_id = MirInputDeviceId{1}; FakeKeyboard keyboard(device_id); auto down_event = keyboard.press(11); auto release_event = keyboard.release(11); dispatcher.start(); dispatcher.set_focus(surface); EXPECT_TRUE(dispatcher.dispatch(*down_event)); EXPECT_TRUE(dispatcher.dispatch( *mev::make_event(mir_input_configuration_action_device_reset, device_id, std::chrono::nanoseconds{1}))); EXPECT_FALSE(dispatcher.dispatch(*release_event)); } TEST_F(SurfaceInputDispatcher, key_input_target_may_disappear_and_things_remain_quote_a_unquote_ok) { auto surface_2 = scene.add_surface(); auto surface_1 = scene.add_surface(); EXPECT_CALL(*surface_1, consume(_)).Times(AnyNumber()); EXPECT_CALL(*surface_2, consume(_)).Times(AnyNumber()); FakeKeyboard k; auto an_ev = k.press(11); auto another_ev = k.press(12); dispatcher.start(); dispatcher.set_focus(surface_1); EXPECT_TRUE(dispatcher.dispatch(*an_ev)); scene.remove_surface(surface_1); EXPECT_FALSE(dispatcher.dispatch(*another_ev)); dispatcher.set_focus(surface_2); EXPECT_TRUE(dispatcher.dispatch(*another_ev)); } TEST_F(SurfaceInputDispatcher, pointer_motion_delivered_to_client_under_pointer) { auto surface = scene.add_surface({{0, 0}, {5, 5}}); FakePointer pointer; auto ev_1 = pointer.move_to({1, 0}); auto ev_2 = pointer.move_to({5, 0}); InSequence seq; EXPECT_CALL(*surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*surface, consume(mt::PointerEventWithPosition(1, 0))).Times(1); EXPECT_CALL(*surface, consume(mt::PointerLeaveEvent())).Times(1); dispatcher.start(); EXPECT_TRUE(dispatcher.dispatch(*pointer.move_to({1, 0}))); EXPECT_TRUE(dispatcher.dispatch(*pointer.move_to({5, 0}))); } TEST_F(SurfaceInputDispatcher, pointer_delivered_only_to_top_surface) { auto surface = scene.add_surface({{0, 0}, {5, 5}}); auto top_surface = scene.add_surface({{0, 0}, {5, 5}}); FakePointer pointer; InSequence seq; EXPECT_CALL(*top_surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*top_surface, consume(mt::PointerEventWithPosition(1, 0))).Times(1); EXPECT_CALL(*surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*surface, consume(mt::PointerEventWithPosition(1, 0))).Times(1); dispatcher.start(); EXPECT_TRUE(dispatcher.dispatch(*pointer.move_to({1, 0}))); scene.remove_surface(top_surface); EXPECT_TRUE(dispatcher.dispatch(*pointer.move_to({1, 0}))); } TEST_F(SurfaceInputDispatcher, pointer_may_move_between_adjacent_surfaces) { auto surface = scene.add_surface({{0, 0}, {5, 5}}); auto another_surface = scene.add_surface({{5, 5}, {5, 5}}); FakePointer pointer; auto ev_1 = pointer.move_to({6, 6}); auto ev_2 = pointer.move_to({7, 7}); InSequence seq; EXPECT_CALL(*surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*surface, consume(mt::PointerEventWithPosition(1, 1))).Times(1); EXPECT_CALL(*surface, consume(mt::PointerLeaveEvent())).Times(1); EXPECT_CALL(*another_surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*another_surface, consume(mt::PointerEventWithPosition(1, 1))).Times(1); EXPECT_CALL(*another_surface, consume(mt::PointerLeaveEvent())).Times(1); dispatcher.start(); EXPECT_TRUE(dispatcher.dispatch(*pointer.move_to({1, 1}))); EXPECT_TRUE(dispatcher.dispatch(*pointer.move_to({6, 6}))); EXPECT_TRUE(dispatcher.dispatch(*pointer.move_to({11, 11}))); } // We test that a client will receive pointer events following a button down // until the pointer comes up. TEST_F(SurfaceInputDispatcher, gestures_persist_over_button_down) { auto surface = scene.add_surface({{0, 0}, {5, 5}}); auto another_surface = scene.add_surface({{5, 5}, {5, 5}}); FakePointer pointer; auto ev_1 = pointer.press_button({0, 0}); auto ev_2 = pointer.move_to({6, 6}); auto ev_3 = pointer.release_button({6, 6}); InSequence seq; EXPECT_CALL(*surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*surface, consume(mt::ButtonDownEvent(0,0))).Times(1); EXPECT_CALL(*surface, consume(mt::PointerEventWithPosition(6, 6))).Times(1); EXPECT_CALL(*surface, consume(mt::ButtonUpEvent(6,6))).Times(1); EXPECT_CALL(*surface, consume(mt::PointerLeaveEvent())).Times(1); EXPECT_CALL(*another_surface, consume(mt::PointerEnterEvent())).Times(1); dispatcher.start(); EXPECT_TRUE(dispatcher.dispatch(*ev_1)); EXPECT_TRUE(dispatcher.dispatch(*ev_2)); EXPECT_TRUE(dispatcher.dispatch(*ev_3)); } TEST_F(SurfaceInputDispatcher, gestures_terminated_by_device_reset) { auto surface = scene.add_surface({{0, 0}, {5, 5}}); auto another_surface = scene.add_surface({{5, 5}, {5, 5}}); MirInputDeviceId device_id{1}; FakePointer pointer(device_id); auto ev_1 = pointer.press_button({0, 0}); auto ev_2 = pointer.move_to({6, 6}); InSequence seq; EXPECT_CALL(*surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*surface, consume(mt::ButtonDownEvent(0,0))).Times(1); EXPECT_CALL(*another_surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*another_surface, consume(mt::PointerEventWithPosition(1, 1))).Times(1); dispatcher.start(); EXPECT_TRUE(dispatcher.dispatch(*ev_1)); EXPECT_TRUE(dispatcher.dispatch( *mev::make_event(mir_input_configuration_action_device_reset, device_id, std::chrono::nanoseconds{1}))); EXPECT_TRUE(dispatcher.dispatch(*ev_2)); } TEST_F(SurfaceInputDispatcher, pointer_gestures_may_transfer_over_buttons) { auto surface = scene.add_surface({{0, 0}, {5, 5}}); auto another_surface = scene.add_surface({{5, 5}, {5, 5}}); FakePointer pointer; auto ev_1 = pointer.press_button({0, 0}, mir_pointer_button_primary); auto ev_2 = pointer.press_button({0, 0}, mir_pointer_button_secondary); auto ev_3 = pointer.release_button({0, 0}, mir_pointer_button_primary); auto ev_4 = pointer.move_to({6, 6}); auto ev_5 = pointer.release_button({6, 6}, mir_pointer_button_secondary); InSequence seq; EXPECT_CALL(*surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*surface, consume(mt::ButtonDownEvent(0,0))).Times(1); EXPECT_CALL(*surface, consume(mt::ButtonDownEvent(0,0))).Times(1); EXPECT_CALL(*surface, consume(mt::ButtonUpEvent(0,0))).Times(1); EXPECT_CALL(*surface, consume(mt::PointerEventWithPosition(6, 6))).Times(1); EXPECT_CALL(*surface, consume(mt::ButtonUpEvent(6,6))).Times(1); EXPECT_CALL(*surface, consume(mt::PointerLeaveEvent())).Times(1); EXPECT_CALL(*another_surface, consume(mt::PointerEnterEvent())).Times(1); dispatcher.start(); EXPECT_TRUE(dispatcher.dispatch(*ev_1)); EXPECT_TRUE(dispatcher.dispatch(*ev_2)); EXPECT_TRUE(dispatcher.dispatch(*ev_3)); EXPECT_TRUE(dispatcher.dispatch(*ev_4)); EXPECT_TRUE(dispatcher.dispatch(*ev_5)); } TEST_F(SurfaceInputDispatcher, pointer_gesture_target_may_vanish_and_the_situation_remains_hunky_dorey) { auto surface = scene.add_surface({{0, 0}, {5, 5}}); auto another_surface = scene.add_surface({{5, 5}, {5, 5}}); FakePointer pointer; auto ev_1 = pointer.press_button({0, 0}); auto ev_2 = pointer.release_button({0, 0}); auto ev_3 = pointer.move_to({6, 6}); InSequence seq; EXPECT_CALL(*surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*surface, consume(mt::ButtonDownEvent(0,0))).Times(1); EXPECT_CALL(*another_surface, consume(mt::PointerEnterEvent())).Times(1); EXPECT_CALL(*another_surface, consume(mt::PointerEventWithPosition(1,1))).Times(1); dispatcher.start(); EXPECT_TRUE(dispatcher.dispatch(*ev_1)); scene.remove_surface(surface); EXPECT_FALSE(dispatcher.dispatch(*ev_2)); EXPECT_TRUE(dispatcher.dispatch(*ev_3)); } TEST_F(SurfaceInputDispatcher, touch_delivered_to_surface) { auto surface = scene.add_surface({{1, 1}, {1, 1}}); InSequence seq; EXPECT_CALL(*surface, consume(mt::TouchEvent(0,0))).Times(1); EXPECT_CALL(*surface, consume(mt::TouchUpEvent(0,0))).Times(1); dispatcher.start(); FakeToucher toucher; EXPECT_TRUE(dispatcher.dispatch(*toucher.touch_at({1,1}))); EXPECT_TRUE(dispatcher.dispatch(*toucher.release_at({1,1}))); } TEST_F(SurfaceInputDispatcher, touch_delivered_only_to_top_surface) { auto bottom_surface = scene.add_surface({{1, 1}, {3, 3}}); auto surface = scene.add_surface({{1, 1}, {3, 3}}); InSequence seq; EXPECT_CALL(*surface, consume(mt::TouchEvent(0,0))).Times(1); EXPECT_CALL(*surface, consume(mt::TouchUpEvent(1,1))).Times(1); EXPECT_CALL(*bottom_surface, consume(mt::TouchEvent(0,0))).Times(0); EXPECT_CALL(*bottom_surface, consume(mt::TouchUpEvent(0,0))).Times(0); dispatcher.start(); FakeToucher toucher; EXPECT_TRUE(dispatcher.dispatch(*toucher.touch_at({1,1}))); EXPECT_TRUE(dispatcher.dispatch(*toucher.release_at({2,2}))); } TEST_F(SurfaceInputDispatcher, gestures_persist_over_touch_down) { auto left_surface = scene.add_surface({{0, 0}, {1, 1}}); auto right_surface = scene.add_surface({{1, 1}, {1, 1}}); InSequence seq; EXPECT_CALL(*left_surface, consume(mt::TouchEvent(0, 0))).Times(1); EXPECT_CALL(*left_surface, consume(mt::TouchMovementEvent())).Times(1); EXPECT_CALL(*left_surface, consume(mt::TouchUpEvent(2, 2))).Times(1); EXPECT_CALL(*right_surface, consume(_)).Times(0); dispatcher.start(); FakeToucher toucher; EXPECT_TRUE(dispatcher.dispatch(*toucher.touch_at({0, 0}))); EXPECT_TRUE(dispatcher.dispatch(*toucher.move_to({2, 2}))); EXPECT_TRUE(dispatcher.dispatch(*toucher.release_at({2, 2}))); } TEST_F(SurfaceInputDispatcher, touch_target_switches_on_finger_down) { // Regression test for LP: #1480654 auto left_surface = scene.add_surface({{0, 0}, {1, 1}}); auto right_surface = scene.add_surface({{5, 5}, {1, 1}}); InSequence seq; EXPECT_CALL(*left_surface, consume(_)).Times(1); // Note: No TouchUpEvent expected EXPECT_CALL(*right_surface, consume(_)).Times(1); dispatcher.start(); FakeToucher toucher; EXPECT_TRUE(dispatcher.dispatch(*toucher.touch_at({0, 0}))); // Note: No touch release event produced EXPECT_TRUE(dispatcher.dispatch(*toucher.touch_at({5, 5}))); } TEST_F(SurfaceInputDispatcher, touch_target_switches_on_fingers_down) { auto left_surface = scene.add_surface({{0, 0}, {1, 1}}); auto right_surface = scene.add_surface({{5, 5}, {2, 2}}); InSequence seq; EXPECT_CALL(*left_surface, consume(_)).Times(1); // Note: No TouchUpEvent expected EXPECT_CALL(*right_surface, consume(_)).Times(1); dispatcher.start(); FakeToucher toucher; EXPECT_TRUE(dispatcher.dispatch(*toucher.touch_at({0, 0}))); // Note: No touch release event produced EXPECT_TRUE(dispatcher.dispatch(*toucher.touches_at({5, 5}, {6, 6}))); } TEST_F(SurfaceInputDispatcher, touch_gestures_terminated_by_release_all_touches) { auto right_surface = scene.add_surface({{5, 5}, {2, 2}}); InSequence seq; EXPECT_CALL(*right_surface, consume(_)).Times(2); dispatcher.start(); FakeToucher toucher; EXPECT_TRUE(dispatcher.dispatch(*toucher.touches_at({5, 5}, {6, 6}))); EXPECT_TRUE(dispatcher.dispatch(*toucher.releases_at({5, 5}, {6, 6}))); EXPECT_FALSE(dispatcher.dispatch(*toucher.move_to({5, 6}))); } TEST_F(SurfaceInputDispatcher, touch_gestures_terminated_by_device_reset) { auto left_surface = scene.add_surface({{0, 0}, {1, 1}}); auto right_surface = scene.add_surface({{1, 1}, {1, 1}}); MirInputDeviceId device_id{1}; FakeToucher toucher(device_id); InSequence seq; EXPECT_CALL(*left_surface, consume(mt::TouchEvent(0, 0))).Times(1); EXPECT_CALL(*right_surface, consume(mt::TouchEvent(0, 0))).Times(1); dispatcher.start(); EXPECT_TRUE(dispatcher.dispatch(*toucher.touch_at({0, 0}))); EXPECT_TRUE(dispatcher.dispatch( *mev::make_event(mir_input_configuration_action_device_reset, device_id, std::chrono::nanoseconds{1}))); EXPECT_TRUE(dispatcher.dispatch(*toucher.touch_at({1, 1}))); } TEST_F(SurfaceInputDispatcher, touch_gesture_target_may_vanish_but_things_continue_to_function_as_intended) { auto surface_1 = scene.add_surface({{0, 0}, {1, 1}}); auto surface_2 = scene.add_surface({{0, 0}, {1, 1}}); InSequence seq; EXPECT_CALL(*surface_2, consume(mt::TouchEvent(0, 0))).Times(1); EXPECT_CALL(*surface_1, consume(mt::TouchEvent(0, 0))).Times(1); dispatcher.start(); FakeToucher toucher; EXPECT_TRUE(dispatcher.dispatch(*toucher.touch_at({0, 0}))); scene.remove_surface(surface_2); EXPECT_FALSE(dispatcher.dispatch(*toucher.release_at({0, 0}))); EXPECT_TRUE(dispatcher.dispatch(*toucher.touch_at({0, 0}))); } ./tests/unit-tests/input/test_xcursor_loader.cpp0000644000015600001650000001006512676616157022336 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "examples/xcursor_loader.h" #include "mir/graphics/cursor_image.h" #include "mir_test_framework/executable_path.h" #include "mir_test_framework/temporary_environment_value.h" #include #include #include #include #include #include #include namespace me = mir::examples; namespace mi = mir::input; namespace mg = mir::graphics; namespace mtf = mir_test_framework; namespace { std::string const test_cursor_path{mir_test_framework::test_data_path() + std::string("/testing-cursor-theme")}; } // Warning, XCURSOR_PATH will only be checked ONCE by libxcursor due to static var class XCursorLoaderTest : public ::testing::Test { public: XCursorLoaderTest() : xcursor_path("XCURSOR_PATH", test_cursor_path.c_str()) { } mtf::TemporaryEnvironmentValue xcursor_path; me::XCursorLoader loader; }; namespace { bool raw_argb_is_only_pixel(uint32_t const* raw_argb, size_t size, uint32_t pixel) { for (unsigned int i = 0; i < size; i++) { if (raw_argb[i] != pixel) { printf("Pixel: %u\n", raw_argb[i]); return false; } } return true; } bool cursor_image_is_solid_color(std::shared_ptr const& image, uint32_t pixel) { auto raw_argb = static_cast(image->as_argb_8888()); size_t size = image->size().width.as_uint32_t() * image->size().height.as_uint32_t(); return raw_argb_is_only_pixel(raw_argb, size, pixel); } MATCHER(HasLoaded, "cursor image has loaded and is not nullptr."\ " Test expects cursor images to be installed to the directory the test is ran from") { return arg != nullptr; } MATCHER(IsSolidRed, "") { return cursor_image_is_solid_color(arg, 0xffff0000); } MATCHER(IsSolidGreen, "") { return cursor_image_is_solid_color(arg, 0xff00ff00); } MATCHER(IsSolidBlue, "") { return cursor_image_is_solid_color(arg, 0xff0000ff); } MATCHER(IsSolidBlack, "") { return cursor_image_is_solid_color(arg, 0xff000000); } } TEST_F(XCursorLoaderTest, loads_cursors_from_testing_theme) { auto size = mi::default_cursor_size; auto red_image = loader.image("red", size); auto blue_image = loader.image("blue", size); auto green_image = loader.image("green", size); ASSERT_THAT(red_image, HasLoaded()); ASSERT_THAT(green_image, HasLoaded()); ASSERT_THAT(blue_image, HasLoaded()); EXPECT_THAT(red_image, IsSolidRed()); EXPECT_THAT(green_image, IsSolidGreen()); EXPECT_THAT(blue_image, IsSolidBlue()); } TEST_F(XCursorLoaderTest, only_supports_the_default_size) { EXPECT_THROW({ loader.image("red", {100, 100}); }, std::logic_error); } TEST_F(XCursorLoaderTest, default_image_is_arrow_from_xcursor_theme) { auto size = mi::default_cursor_size; auto arrow_image = loader.image(mir_default_cursor_name, size); // The testing theme uses a solid black image for the "arrow" symbolic // name. ASSERT_THAT(arrow_image, HasLoaded()); EXPECT_THAT(arrow_image, IsSolidBlack()); } TEST_F(XCursorLoaderTest, symbolic_names_which_are_not_present_resolve_to_default) { auto size = mi::default_cursor_size; auto default_image = loader.image(mir_default_cursor_name, size); auto image_with_made_up_name = loader.image("Artickrumbulis", size); EXPECT_EQ(default_image, image_with_made_up_name); } ./tests/unit-tests/input/test_default_input_manager.cpp0000644000015600001650000001077012676616157023643 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "src/server/input/default_input_manager.h" #include "mir/test/fd_utils.h" #include "mir/test/signal.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_input_platform.h" #include "mir/input/platform.h" #include "mir/dispatch/multiplexing_dispatchable.h" #include "mir/dispatch/action_queue.h" #include #include #include #include namespace mt = mir::test; namespace md = mir::dispatch; namespace mtd = mir::test::doubles; using namespace ::testing; namespace { struct DefaultInputManagerTest : ::testing::Test { md::MultiplexingDispatchable multiplexer; md::ActionQueue platform_dispatchable; NiceMock platform; mir::Fd event_hub_fd{eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK)}; mir::input::DefaultInputManager input_manager{mt::fake_shared(multiplexer), mt::fake_shared(platform)}; DefaultInputManagerTest() { ON_CALL(platform, dispatchable()) .WillByDefault(Return(mt::fake_shared(platform_dispatchable))); } bool wait_for_multiplexer_dispatch() { auto queue = std::make_shared(); auto queue_done = std::make_shared(); queue->enqueue([queue_done]() { queue_done->raise(); }); multiplexer.add_watch(queue); bool ret = queue_done->wait_for(std::chrono::seconds{2}); multiplexer.remove_watch(queue); return ret; } }; } TEST_F(DefaultInputManagerTest, starts_platforms_on_start) { EXPECT_CALL(platform, start()).Times(1); EXPECT_CALL(platform, dispatchable()).Times(1); input_manager.start(); // start() is synchronous, all start-up operations should be finished at this point Mock::VerifyAndClearExpectations(&platform); EXPECT_CALL(platform, stop()).Times(1); } TEST_F(DefaultInputManagerTest, starts_platforms_after_start) { EXPECT_CALL(platform, start()).Times(1); EXPECT_CALL(platform, dispatchable()).Times(2); EXPECT_CALL(platform, stop()).Times(1); input_manager.start(); EXPECT_TRUE(wait_for_multiplexer_dispatch()); } TEST_F(DefaultInputManagerTest, stops_platforms_on_stop) { EXPECT_CALL(platform, stop()).Times(1); input_manager.start(); input_manager.stop(); } TEST_F(DefaultInputManagerTest, ignores_spurious_starts) { EXPECT_CALL(platform, start()).Times(1); input_manager.start(); input_manager.start(); EXPECT_TRUE(wait_for_multiplexer_dispatch()); } TEST_F(DefaultInputManagerTest, ignores_spurious_stops) { EXPECT_CALL(platform, start()).Times(1); EXPECT_CALL(platform, stop()).Times(1); input_manager.start(); input_manager.stop(); input_manager.stop(); } TEST_F(DefaultInputManagerTest, deals_with_parallel_starts) { EXPECT_CALL(platform, start()).Times(1); const int more_than_one_thread = 10; std::list threads; for (int i = 0; i != more_than_one_thread; ++i) { threads.emplace_back([this]() { input_manager.start(); }); } while (!threads.empty()) { threads.front().join(); threads.pop_front(); } } TEST_F(DefaultInputManagerTest, deals_with_parallel_stops) { EXPECT_CALL(platform, start()).Times(1); EXPECT_CALL(platform, stop()).Times(1); const int more_than_one_thread = 10; input_manager.start(); std::list threads; for (int i = 0; i != more_than_one_thread; ++i) { threads.emplace_back([this]() { input_manager.stop(); }); } while (!threads.empty()) { threads.front().join(); threads.pop_front(); } } ./tests/unit-tests/input/test_event_builders.cpp0000644000015600001650000001762412676616125022320 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/events/event_builders.h" #include "mir/events/event_private.h" // only needed to validate motion_up/down mapping #include namespace mev = mir::events; namespace { struct InputEventBuilder : public testing::Test { MirInputDeviceId const device_id = 7; std::chrono::nanoseconds const timestamp = std::chrono::nanoseconds(39); std::vector const cookie{}; MirInputEventModifiers const modifiers = mir_input_event_modifier_meta; }; } TEST_F(InputEventBuilder, makes_valid_key_event) { MirKeyboardAction const action = mir_keyboard_action_down; xkb_keysym_t const key_code = 34; int const scan_code = 17; auto ev = mev::make_event(device_id, timestamp, cookie, action, key_code, scan_code, modifiers); auto e = ev.get(); EXPECT_EQ(mir_event_type_input, mir_event_get_type(e)); auto ie = mir_event_get_input_event(e); EXPECT_EQ(mir_input_event_type_key, mir_input_event_get_type(ie)); auto kev = mir_input_event_get_keyboard_event(ie); EXPECT_EQ(action, mir_keyboard_event_action(kev)); EXPECT_EQ(key_code, mir_keyboard_event_key_code(kev)); EXPECT_EQ(scan_code, mir_keyboard_event_scan_code(kev)); EXPECT_EQ(modifiers, mir_keyboard_event_modifiers(kev)); } TEST_F(InputEventBuilder, makes_valid_touch_event) { unsigned touch_count = 3; MirTouchId touch_ids[] = {7, 9, 4}; MirTouchAction actions[] = { mir_touch_action_up, mir_touch_action_change, mir_touch_action_change}; MirTouchTooltype tooltypes[] = {mir_touch_tooltype_unknown, mir_touch_tooltype_finger, mir_touch_tooltype_stylus}; float x_axis_values[] = { 7, 14.3, 19.6 }; float y_axis_values[] = { 3, 9, 11 }; float pressure_values[] = {3, 9, 14.6}; float touch_major_values[] = {11, 9, 14}; float touch_minor_values[] = {13, 3, 9.13}; float size_values[] = {4, 9, 6}; auto ev = mev::make_event(device_id, timestamp, cookie, modifiers); for (unsigned i = 0; i < touch_count; i++) { mev::add_touch(*ev, touch_ids[i], actions[i], tooltypes[i], x_axis_values[i], y_axis_values[i], pressure_values[i], touch_major_values[i], touch_minor_values[i], size_values[i]); } auto e = ev.get(); EXPECT_EQ(mir_event_type_input, mir_event_get_type(e)); auto ie = mir_event_get_input_event(e); EXPECT_EQ(mir_input_event_type_touch, mir_input_event_get_type(ie)); auto tev = mir_input_event_get_touch_event(ie); EXPECT_EQ(modifiers, mir_touch_event_modifiers(tev)); EXPECT_EQ(touch_count, mir_touch_event_point_count(tev)); for (unsigned i = 0; i < touch_count; i++) { EXPECT_EQ(touch_ids[i], mir_touch_event_id(tev, i)); EXPECT_EQ(actions[i], mir_touch_event_action(tev, i)); EXPECT_EQ(tooltypes[i], mir_touch_event_tooltype(tev, i)); EXPECT_EQ(x_axis_values[i], mir_touch_event_axis_value(tev, i, mir_touch_axis_x)); EXPECT_EQ(y_axis_values[i], mir_touch_event_axis_value(tev, i, mir_touch_axis_y)); EXPECT_EQ(pressure_values[i], mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure)); EXPECT_EQ(touch_major_values[i], mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major)); EXPECT_EQ(touch_minor_values[i], mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor)); EXPECT_EQ(size_values[i], mir_touch_event_axis_value(tev, i, mir_touch_axis_size)); } } TEST_F(InputEventBuilder, makes_valid_pointer_event) { MirPointerAction action = mir_pointer_action_enter; auto depressed_buttons = mir_pointer_button_back | mir_pointer_button_tertiary; float x_axis_value = 3.9, y_axis_value = 7.4, hscroll_value = .9, vscroll_value = .3; auto const relative_x_value = 0.0; auto const relative_y_value = 0.0; auto ev = mev::make_event(device_id, timestamp, cookie, modifiers, action, depressed_buttons, x_axis_value, y_axis_value, hscroll_value, vscroll_value, relative_x_value, relative_y_value); auto e = ev.get(); EXPECT_EQ(mir_event_type_input, mir_event_get_type(e)); auto ie = mir_event_get_input_event(e); EXPECT_EQ(mir_input_event_type_pointer, mir_input_event_get_type(ie)); auto pev = mir_input_event_get_pointer_event(ie); EXPECT_EQ(modifiers, mir_pointer_event_modifiers(pev)); EXPECT_EQ(action, mir_pointer_event_action(pev)); EXPECT_TRUE(mir_pointer_event_button_state(pev, mir_pointer_button_back)); EXPECT_TRUE(mir_pointer_event_button_state(pev, mir_pointer_button_tertiary)); EXPECT_FALSE(mir_pointer_event_button_state(pev, mir_pointer_button_primary)); EXPECT_FALSE(mir_pointer_event_button_state(pev, mir_pointer_button_secondary)); EXPECT_FALSE(mir_pointer_event_button_state(pev, mir_pointer_button_forward)); EXPECT_EQ(x_axis_value, mir_pointer_event_axis_value(pev, mir_pointer_axis_x)); EXPECT_EQ(y_axis_value, mir_pointer_event_axis_value(pev, mir_pointer_axis_y)); EXPECT_EQ(hscroll_value, mir_pointer_event_axis_value(pev, mir_pointer_axis_hscroll)); EXPECT_EQ(vscroll_value, mir_pointer_event_axis_value(pev, mir_pointer_axis_vscroll)); } // The following three requirements can be removed as soon as we remove android::InputDispatcher, which is the // only remaining part that relies on the difference between mir_motion_action_pointer_{up,down} and // mir_motion_action_{up,down} and the difference between mir_motion_action_move and mir_motion_action_hover_move. TEST_F(InputEventBuilder, maps_single_touch_down_to_motion_down) { MirTouchAction action = mir_touch_action_down; auto ev = mev::make_event(device_id, timestamp, cookie, modifiers); mev::add_touch(*ev, 0, action, mir_touch_tooltype_finger, 0, 0, 0, 0, 0, 0); auto e = ev.get(); EXPECT_EQ(mir_event_type_input, mir_event_get_type(e)); auto ie = mir_event_get_input_event(e); EXPECT_EQ(mir_input_event_type_touch, mir_input_event_get_type(ie)); auto tev = mir_input_event_get_touch_event(ie); EXPECT_EQ(action, mir_touch_event_action(tev, 0)); } TEST_F(InputEventBuilder, maps_single_touch_up_to_motion_up) { MirTouchAction action = mir_touch_action_up; auto ev = mev::make_event(device_id, timestamp, cookie, modifiers); mev::add_touch(*ev, 0, action, mir_touch_tooltype_finger, 0, 0, 0, 0, 0, 0); auto e = ev.get(); EXPECT_EQ(mir_event_type_input, mir_event_get_type(e)); auto ie = mir_event_get_input_event(e); EXPECT_EQ(mir_input_event_type_touch, mir_input_event_get_type(ie)); auto tev = mir_input_event_get_touch_event(ie); EXPECT_EQ(action, mir_touch_event_action(tev, 0)); } TEST_F(InputEventBuilder, map_to_hover_if_no_button_pressed) { float x_axis_value = 3.9, y_axis_value = 7.4, hscroll_value = .9, vscroll_value = .3; auto const relative_x_value = 0.0; auto const relative_y_value = 0.0; MirPointerAction action = mir_pointer_action_motion; auto ev = mev::make_event(device_id, timestamp, cookie, modifiers, action, 0, x_axis_value, y_axis_value, hscroll_value, vscroll_value, relative_x_value, relative_y_value); auto e = ev.get(); auto ie = mir_event_get_input_event(e); EXPECT_EQ(mir_input_event_type_pointer, mir_input_event_get_type(ie)); auto pev = mir_input_event_get_pointer_event(ie); EXPECT_EQ(modifiers, mir_pointer_event_modifiers(pev)); EXPECT_EQ(action, mir_pointer_event_action(pev)); } ./tests/unit-tests/input/evdev/0000755000015600001650000000000012676616160016641 5ustar jenkinsjenkins./tests/unit-tests/input/evdev/test_evdev_device_detection.cpp0000644000015600001650000000741012676616157025102 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/input/device_capability.h" #include "src/platforms/evdev/libinput_ptr.h" #include "src/platforms/evdev/libinput_device_ptr.h" #include "src/platforms/evdev/libinput_device.h" #include "src/server/report/null_report_factory.h" #include "mir_test_framework/libinput_environment.h" #include #include #include namespace mtf = mir_test_framework; namespace mi = mir::input; namespace mie = mi::evdev; struct EvdevDeviceDetection : public ::testing::TestWithParam> { mtf::LibInputEnvironment env; }; TEST_P(EvdevDeviceDetection, evaluates_expected_input_class) { using namespace testing; auto const& param = GetParam(); udev* ctx = reinterpret_cast(1); auto dev = env.setup_device(std::get<0>(param)); std::shared_ptr lib = mie::make_libinput(ctx); mie::LibInputDevice device(mir::report::null_input_report(), mie::make_libinput_device(lib, dev)); auto info = device.get_device_info(); EXPECT_THAT(info.capabilities, Eq(std::get<1>(param))); } INSTANTIATE_TEST_CASE_P(InputDeviceCapabilityDetection, EvdevDeviceDetection, ::testing::Values( std::make_tuple( mtf::LibInputEnvironment::synaptics_touchpad, mi::DeviceCapability::touchpad|mi::DeviceCapability::pointer ), std::make_tuple( mtf::LibInputEnvironment::laptop_keyboard, mi::DeviceCapability::keyboard|mi::DeviceCapability::alpha_numeric ), std::make_tuple( mtf::LibInputEnvironment::usb_keyboard, mi::DeviceCapability::keyboard|mi::DeviceCapability::alpha_numeric ), std::make_tuple( mtf::LibInputEnvironment::usb_mouse, mi::DeviceCapability::pointer ), std::make_tuple( mtf::LibInputEnvironment::bluetooth_magic_trackpad, mi::DeviceCapability::touchpad|mi::DeviceCapability::pointer ), std::make_tuple( mtf::LibInputEnvironment::mtk_tpd, // device also reports available keys.. mi::DeviceCapabilities{mi::DeviceCapability::touchscreen}| mi::DeviceCapability::keyboard ), std::make_tuple( mtf::LibInputEnvironment::usb_joystick, mi::DeviceCapabilities{mi::DeviceCapability::joystick}| mi::DeviceCapability::keyboard ) )); ./tests/unit-tests/input/evdev/test_evdev_input_platform.cpp0000644000015600001650000001333712676616157024655 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "src/platforms/evdev/platform.h" #include "src/server/report/null_report_factory.h" #include "mir/input/input_device_registry.h" #include "mir/dispatch/dispatchable.h" #include "mir/udev/wrapper.h" #include "mir_test_framework/libinput_environment.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_libinput.h" #include "mir/test/doubles/mock_udev.h" #include #include #include #include #include #include namespace mi = mir::input; namespace mie = mi::evdev; namespace mr = mir::report; namespace mu = mir::udev; namespace mt = mir::test; namespace mtd = mt::doubles; namespace mtf = mir_test_framework; using namespace ::testing; namespace { struct MockInputDeviceRegistry : public mi::InputDeviceRegistry { MOCK_METHOD1(add_device, void(std::shared_ptr const&)); MOCK_METHOD1(remove_device, void(std::shared_ptr const&)); }; struct EvdevInputPlatform : public ::testing::TestWithParam { testing::NiceMock mock_registry; mtf::LibInputEnvironment env; template PtrT to_fake_ptr(unsigned int number) { return reinterpret_cast(number); } template PtrT get_next_fake_ptr(Container const& container) { return to_fake_ptr(container.size()+1); } void setup_groupped_devices(std::initializer_list devices) { auto next_group = env.mock_libinput.get_next_fake_ptr(); for (auto dev : devices) ON_CALL(env.mock_libinput, libinput_device_get_device_group(dev)).WillByDefault(Return(next_group)); } auto create_input_platform() { auto ctx = std::make_unique(); return std::make_unique(mt::fake_shared(mock_registry), mr::null_input_report(), std::move(ctx)); } }; void run_dispatchable(mir::input::Platform& platform) { platform.dispatchable()->dispatch(mir::dispatch::FdEvent::readable); } } TEST_P(EvdevInputPlatform, scans_on_start) { env.add_standard_device(GetParam()); using namespace ::testing; auto platform = create_input_platform(); EXPECT_CALL(mock_registry, add_device(_)); platform->start(); run_dispatchable(*platform); } TEST_P(EvdevInputPlatform, detects_on_hotplug) { using namespace ::testing; auto platform = create_input_platform(); platform->start(); { EXPECT_CALL(mock_registry, add_device(_)); env.add_standard_device(GetParam()); run_dispatchable(*platform); } } TEST_P(EvdevInputPlatform, detects_hot_removal) { using namespace ::testing; auto platform = create_input_platform(); platform->start(); { EXPECT_CALL(mock_registry, add_device(_)); EXPECT_CALL(mock_registry, remove_device(_)); env.add_standard_device(GetParam()); run_dispatchable(*platform); env.remove_standard_device(GetParam()); run_dispatchable(*platform); } } TEST_P(EvdevInputPlatform, removes_devices_on_stop) { using namespace ::testing; auto platform = create_input_platform(); env.add_standard_device(GetParam()); platform->start(); run_dispatchable(*platform); EXPECT_CALL(mock_registry, remove_device(_)); platform->stop(); } INSTANTIATE_TEST_CASE_P(DeviceHandling, EvdevInputPlatform, ::testing::Values(mtf::LibInputEnvironment::synaptics_touchpad, mtf::LibInputEnvironment::usb_keyboard, mtf::LibInputEnvironment::usb_mouse, mtf::LibInputEnvironment::mtk_tpd, mtf::LibInputEnvironment::bluetooth_magic_trackpad)); TEST_F(EvdevInputPlatform, register_ungrouped_devices) { auto platform = create_input_platform(); platform->start(); EXPECT_CALL(mock_registry, add_device(_)).Times(2); env.add_standard_device(mtf::LibInputEnvironment::synaptics_touchpad); env.add_standard_device(mtf::LibInputEnvironment::usb_keyboard); run_dispatchable(*platform); } TEST_F(EvdevInputPlatform, ignore_devices_from_same_group) { auto platform = create_input_platform(); platform->start(); EXPECT_CALL(mock_registry, add_device(_)).Times(1); auto touch_pad = env.setup_device(mtf::LibInputEnvironment::synaptics_touchpad); auto keyboard = env.setup_device(mtf::LibInputEnvironment::usb_keyboard); setup_groupped_devices({touch_pad, keyboard}); env.mock_libinput.setup_device_add_event(touch_pad); env.mock_libinput.setup_device_add_event(keyboard); run_dispatchable(*platform); } TEST_F(EvdevInputPlatform, creates_new_context_on_resume) { using namespace ::testing; auto platform = create_input_platform(); platform->start(); platform->stop(); EXPECT_CALL(env.mock_libinput, libinput_udev_create_context(_,_,_)); platform->start(); } ./tests/unit-tests/input/evdev/CMakeLists.txt0000644000015600001650000000055412676616125021406 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_evdev_device_detection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_evdev_input_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_libinput_device.cpp $ $ ) set( UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/input/evdev/test_libinput_device.cpp0000644000015600001650000010314212676616157023560 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "src/platforms/evdev/libinput_device.h" #include "src/platforms/evdev/button_utils.h" #include "src/server/report/null_report_factory.h" #include "src/server/input/default_event_builder.h" #include "mir/input/input_device_registry.h" #include "mir/input/input_sink.h" #include "mir/input/pointer_settings.h" #include "mir/input/touchpad_settings.h" #include "mir/flags.h" #include "mir/geometry/point.h" #include "mir/geometry/rectangle.h" #include "mir/test/event_matchers.h" #include "mir/test/doubles/mock_libinput.h" #include "mir/test/gmock_fixes.h" #include "mir/udev/wrapper.h" #include "mir/cookie/authority.h" #include "mir_test_framework/libinput_environment.h" #include #include #include #include #include namespace mi = mir::input; namespace mie = mi::evdev; namespace mt = mir::test; namespace mtf = mir_test_framework; namespace mtd = mt::doubles; namespace geom = mir::geometry; namespace { class StubInputDeviceRegistry : public mi::InputDeviceRegistry { public: void add_device(std::shared_ptr const&) override {} void remove_device(std::shared_ptr const&) override {} }; using namespace ::testing; struct MockInputSink : mi::InputSink { MockInputSink() { ON_CALL(*this, bounding_rectangle()) .WillByDefault(Return(geom::Rectangle({0,0}, {100,100}))); } MOCK_METHOD1(handle_input,void(MirEvent &)); MOCK_METHOD1(confine_pointer, void(geom::Point&)); MOCK_CONST_METHOD0(bounding_rectangle, geom::Rectangle()); }; struct MockEventBuilder : mi::EventBuilder { std::shared_ptr const cookie_authority = mir::cookie::Authority::create(); mi::DefaultEventBuilder builder{MirInputDeviceId{3}, cookie_authority}; MockEventBuilder() { ON_CALL(*this, key_event(_,_,_,_)) .WillByDefault(Invoke([this](Timestamp time, MirKeyboardAction action, xkb_keysym_t key, int scan_code) { return builder.key_event(time, action, key, scan_code); })); ON_CALL(*this, touch_event(_)) .WillByDefault(Invoke([this](Timestamp time) { return builder.touch_event(time); })); ON_CALL(*this, add_touch(_,_,_,_,_,_,_,_,_,_)) .WillByDefault(Invoke([this](MirEvent& event, MirTouchId id, MirTouchAction action, MirTouchTooltype tooltype, float x, float y, float major, float minor, float pressure, float size) { return builder.add_touch(event, id, action, tooltype, x, y, major, minor, pressure, size); })); ON_CALL(*this, pointer_event(_, _, _, _, _, _, _)) .WillByDefault(Invoke([this](Timestamp time, MirPointerAction action, MirPointerButtons buttons, float hscroll, float vscroll, float relative_x, float relative_y) { return builder.pointer_event(time, action, buttons, hscroll, vscroll, relative_x, relative_y); })); ON_CALL(*this, configuration_event(_,_)) .WillByDefault(Invoke([this](Timestamp time, MirInputConfigurationAction action) { return builder.configuration_event(time, action); })); } using EventBuilder::Timestamp; MOCK_METHOD4(key_event, mir::EventUPtr(Timestamp, MirKeyboardAction, xkb_keysym_t, int)); MOCK_METHOD1(touch_event, mir::EventUPtr(Timestamp)); MOCK_METHOD10(add_touch, void(MirEvent&, MirTouchId, MirTouchAction, MirTouchTooltype, float, float, float, float, float, float)); MOCK_METHOD7(pointer_event, mir::EventUPtr(Timestamp, MirPointerAction, MirPointerButtons, float, float, float, float)); MOCK_METHOD2(configuration_event, mir::EventUPtr(Timestamp, MirInputConfigurationAction)); }; struct LibInputDevice : public ::testing::Test { mtf::LibInputEnvironment env; ::testing::NiceMock mock_sink; ::testing::NiceMock mock_builder; std::shared_ptr lib; const uint64_t event_time_1 = 1000; const mi::EventBuilder::Timestamp time_stamp_1{std::chrono::microseconds{event_time_1}}; const uint64_t event_time_2 = 2000; const mi::EventBuilder::Timestamp time_stamp_2{std::chrono::microseconds{event_time_2}}; const uint64_t event_time_3 = 3000; const mi::EventBuilder::Timestamp time_stamp_3{std::chrono::microseconds{event_time_3}}; const uint64_t event_time_4 = 4000; const mi::EventBuilder::Timestamp time_stamp_4{std::chrono::microseconds{event_time_4}}; udev *const fake_udev = reinterpret_cast(0xFACE01); LibInputDevice() { lib = mie::make_libinput(fake_udev); } libinput_device* setup_laptop_keyboard() { return env.setup_device(mtf::LibInputEnvironment::laptop_keyboard); } libinput_device* setup_trackpad() { return env.setup_device(mtf::LibInputEnvironment::bluetooth_magic_trackpad); } libinput_device* setup_touchscreen() { return env.setup_device(mtf::LibInputEnvironment::mtk_tpd); } libinput_device* setup_touchpad() { return env.setup_device(mtf::LibInputEnvironment::synaptics_touchpad); } libinput_device* setup_mouse() { return env.setup_device(mtf::LibInputEnvironment::usb_mouse); } void setup_pointer_configuration(libinput_device* dev, double accel_speed, MirPointerHandedness handedness, MirPointerAcceleration profile) { ON_CALL(env.mock_libinput, libinput_device_config_accel_get_speed(dev)) .WillByDefault(Return(accel_speed)); ON_CALL(env.mock_libinput, libinput_device_config_left_handed_get(dev)) .WillByDefault(Return(handedness == mir_pointer_handedness_left)); #if MIR_LIBINPUT_HAS_ACCEL_PROFILE ON_CALL(env.mock_libinput, libinput_device_config_accel_get_profile(dev)) .WillByDefault(Return((profile == mir_pointer_acceleration_none) ? LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT : LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE)); #else (void)profile; #endif } void setup_touchpad_configuration(libinput_device* dev, MirTouchpadClickMode click_mode, MirTouchpadScrollMode scroll_mode, int scroll_button, bool tap_to_click, bool disable_while_typing, bool disable_with_mouse, bool middle_button_emulation) { mir::Flags click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE; if (click_mode & mir_touchpad_click_mode_finger_count) click_method |= LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; if (click_mode & mir_touchpad_click_mode_area_to_click) click_method |= LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; mir::Flags scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; if (scroll_mode & mir_touchpad_scroll_mode_two_finger_scroll) scroll_method |= LIBINPUT_CONFIG_SCROLL_2FG; if (scroll_mode & mir_touchpad_scroll_mode_edge_scroll) scroll_method |= LIBINPUT_CONFIG_SCROLL_EDGE; if (scroll_mode & mir_touchpad_scroll_mode_button_down_scroll) scroll_method |= LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; ON_CALL(env.mock_libinput, libinput_device_config_click_get_method(dev)) .WillByDefault(Return(static_cast(click_method.value()))); ON_CALL(env.mock_libinput, libinput_device_config_scroll_get_method(dev)) .WillByDefault(Return(static_cast(scroll_method.value()))); ON_CALL(env.mock_libinput, libinput_device_config_scroll_get_button(dev)) .WillByDefault(Return(scroll_button)); ON_CALL(env.mock_libinput, libinput_device_config_tap_get_enabled(dev)) .WillByDefault(Return(tap_to_click? LIBINPUT_CONFIG_TAP_ENABLED: LIBINPUT_CONFIG_TAP_DISABLED)); ON_CALL(env.mock_libinput, libinput_device_config_dwt_get_enabled(dev)) .WillByDefault(Return(disable_while_typing? LIBINPUT_CONFIG_DWT_ENABLED: LIBINPUT_CONFIG_DWT_DISABLED)); ON_CALL(env.mock_libinput, libinput_device_config_send_events_get_mode(dev)) .WillByDefault(Return(disable_with_mouse? LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: LIBINPUT_CONFIG_SEND_EVENTS_ENABLED)); ON_CALL(env.mock_libinput, libinput_device_config_middle_emulation_get_enabled(dev)) .WillByDefault(Return(middle_button_emulation? LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED: LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED)); } void process_events(mie::LibInputDevice& device) { for (auto event : env.mock_libinput.events) device.process_event(event); } }; struct LibInputDeviceOnLaptopKeyboard : public LibInputDevice { libinput_device*const fake_device = setup_laptop_keyboard(); mie::LibInputDevice keyboard{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)}; }; struct LibInputDeviceOnMouse : public LibInputDevice { libinput_device*const fake_device = setup_mouse(); mie::LibInputDevice mouse{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)}; }; struct LibInputDeviceOnLaptopKeyboardAndMouse : public LibInputDevice { libinput_device*const fake_device = setup_mouse(); libinput_device*const fake_device_2 = setup_laptop_keyboard(); mie::LibInputDevice keyboard{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device_2)}; mie::LibInputDevice mouse{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)}; }; struct LibInputDeviceOnTouchScreen : public LibInputDevice { libinput_device*const fake_device = setup_touchscreen(); mie::LibInputDevice touch_screen{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)}; }; struct LibInputDeviceOnTouchpad : public LibInputDevice { libinput_device*const fake_device = setup_touchpad(); mie::LibInputDevice touchpad{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)}; }; } TEST_F(LibInputDevice, start_creates_and_refs_libinput_device) { auto * const fake_device = setup_laptop_keyboard(); // according to manual when a new device is detected by udev libinput creates a temporary // device with a ref count 0, which gets distributed via its event loop, and would be removed // after event dispatch. So it needs a manual ref call EXPECT_CALL(env.mock_libinput, libinput_device_ref(fake_device)) .Times(1); mie::LibInputDevice dev(mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)); dev.start(&mock_sink, &mock_builder); } TEST_F(LibInputDevice, open_device_of_group) { auto fake_device = setup_laptop_keyboard(); auto second_fake_device = setup_trackpad(); InSequence seq; // See previous test EXPECT_CALL(env.mock_libinput, libinput_device_ref(fake_device)).Times(1); EXPECT_CALL(env.mock_libinput, libinput_device_ref(second_fake_device)).Times(1); mie::LibInputDevice dev(mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)); dev.add_device_of_group(mie::make_libinput_device(lib, second_fake_device)); dev.start(&mock_sink, &mock_builder); } TEST_F(LibInputDevice, input_info_combines_capabilities) { auto fake_device = setup_laptop_keyboard(); auto second_fake_device = setup_trackpad(); mie::LibInputDevice dev(mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)); dev.add_device_of_group(mie::make_libinput_device(lib, second_fake_device)); auto info = dev.get_device_info(); EXPECT_THAT(info.capabilities, Eq(mi::DeviceCapability::touchpad | mi::DeviceCapability::pointer | mi::DeviceCapability::keyboard | mi::DeviceCapability::alpha_numeric)); } TEST_F(LibInputDevice, removal_unrefs_libinput_device) { auto fake_device = setup_laptop_keyboard(); EXPECT_CALL(env.mock_libinput, libinput_device_unref(fake_device)) .Times(1); mie::LibInputDevice dev(mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)); } TEST_F(LibInputDeviceOnLaptopKeyboard, process_event_converts_key_event) { EXPECT_CALL(mock_builder, key_event(time_stamp_1, mir_keyboard_action_down, _, KEY_A)); EXPECT_CALL(mock_sink, handle_input(AllOf(mt::KeyOfScanCode(KEY_A),mt::KeyDownEvent()))); EXPECT_CALL(mock_builder, key_event(time_stamp_2, mir_keyboard_action_up, _, KEY_A)); EXPECT_CALL(mock_sink, handle_input(AllOf(mt::KeyOfScanCode(KEY_A),mt::KeyUpEvent()))); keyboard.start(&mock_sink, &mock_builder); env.mock_libinput.setup_key_event(fake_device, event_time_1, KEY_A, LIBINPUT_KEY_STATE_PRESSED); env.mock_libinput.setup_key_event(fake_device, event_time_2, KEY_A, LIBINPUT_KEY_STATE_RELEASED); process_events(keyboard); } TEST_F(LibInputDeviceOnLaptopKeyboard, process_event_accumulates_key_state) { InSequence seq; EXPECT_CALL(mock_builder, key_event(time_stamp_1, mir_keyboard_action_down, _, KEY_C)); EXPECT_CALL(mock_sink, handle_input(AllOf(mt::KeyOfScanCode(KEY_C),mt::KeyDownEvent()))); EXPECT_CALL(mock_builder, key_event(time_stamp_2, mir_keyboard_action_down, _, KEY_LEFTALT)); EXPECT_CALL(mock_sink, handle_input(AllOf(mt::KeyOfScanCode(KEY_LEFTALT),mt::KeyDownEvent()))); EXPECT_CALL(mock_builder, key_event(time_stamp_3, mir_keyboard_action_up, _, KEY_C)); EXPECT_CALL(mock_sink, handle_input(AllOf(mt::KeyOfScanCode(KEY_C), mt::KeyUpEvent()))); keyboard.start(&mock_sink, &mock_builder); env.mock_libinput.setup_key_event(fake_device, event_time_1, KEY_C, LIBINPUT_KEY_STATE_PRESSED); env.mock_libinput.setup_key_event(fake_device, event_time_2, KEY_LEFTALT, LIBINPUT_KEY_STATE_PRESSED); env.mock_libinput.setup_key_event(fake_device, event_time_3, KEY_C, LIBINPUT_KEY_STATE_RELEASED); process_events(keyboard); } TEST_F(LibInputDeviceOnMouse, process_event_converts_pointer_event) { float x_movement_1 = 15; float y_movement_1 = 17; float x_movement_2 = 20; float y_movement_2 = 40; EXPECT_CALL(mock_sink, handle_input(mt::PointerEventWithDiff(x_movement_1,y_movement_1))); EXPECT_CALL(mock_sink, handle_input(mt::PointerEventWithDiff(x_movement_2,y_movement_2))); mouse.start(&mock_sink, &mock_builder); env.mock_libinput.setup_pointer_event(fake_device, event_time_1, x_movement_1, y_movement_1); env.mock_libinput.setup_pointer_event(fake_device, event_time_2, x_movement_2, y_movement_2); process_events(mouse); } TEST_F(LibInputDeviceOnMouse, process_event_handles_absolute_pointer_events) { float x1 = 15; float y1 = 17; float x2 = 40; float y2 = 10; EXPECT_CALL(mock_sink, handle_input(mt::PointerEventWithDiff(x1, y1))); EXPECT_CALL(mock_sink, handle_input(mt::PointerEventWithDiff(x2 - x1, y2 - y1))); mouse.start(&mock_sink, &mock_builder); env.mock_libinput.setup_absolute_pointer_event(fake_device, event_time_1, x1, y1); env.mock_libinput.setup_absolute_pointer_event(fake_device, event_time_2, x2, y2); process_events(mouse); } TEST_F(LibInputDeviceOnMouse, process_event_motion_events_with_relative_changes) { float x1 = 15, x2 = 23; float y1 = 17, y2 = 21; EXPECT_CALL(mock_sink, handle_input(mt::PointerEventWithDiff(x1,y1))); EXPECT_CALL(mock_sink, handle_input(mt::PointerEventWithDiff(x2,y2))); mouse.start(&mock_sink, &mock_builder); env.mock_libinput.setup_pointer_event(fake_device, event_time_1, x1, y1); env.mock_libinput.setup_pointer_event(fake_device, event_time_2, x2, y2); process_events(mouse); } TEST_F(LibInputDeviceOnMouse, process_event_handles_press_and_release) { float const x = 0; float const y = 0; geom::Point const pos{x, y}; InSequence seq; EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEventWithButton(pos, mir_pointer_button_primary))); EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEventWithButton(pos, mir_pointer_button_secondary))); EXPECT_CALL(mock_sink, handle_input(mt::ButtonUpEventWithButton(pos, mir_pointer_button_secondary))); EXPECT_CALL(mock_sink, handle_input(mt::ButtonUpEventWithButton(pos, mir_pointer_button_primary))); mouse.start(&mock_sink, &mock_builder); env.mock_libinput.setup_button_event(fake_device, event_time_1, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED); env.mock_libinput.setup_button_event(fake_device, event_time_2, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED); env.mock_libinput.setup_button_event(fake_device, event_time_3, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED); env.mock_libinput.setup_button_event(fake_device, event_time_4, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED); process_events(mouse); } TEST_F(LibInputDeviceOnMouse, process_event_handles_exotic_mouse_buttons) { float const x = 0; float const y = 0; geom::Point const pos{x, y}; InSequence seq; EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEventWithButton(pos, mir_pointer_button_side))); EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEventWithButton(pos, mir_pointer_button_extra))); EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEventWithButton(pos, mir_pointer_button_task))); mouse.start(&mock_sink, &mock_builder); env.mock_libinput.setup_button_event(fake_device, event_time_1, BTN_SIDE, LIBINPUT_BUTTON_STATE_PRESSED); env.mock_libinput.setup_button_event(fake_device, event_time_2, BTN_EXTRA, LIBINPUT_BUTTON_STATE_PRESSED); env.mock_libinput.setup_button_event(fake_device, event_time_3, BTN_TASK, LIBINPUT_BUTTON_STATE_PRESSED); process_events(mouse); } TEST_F(LibInputDeviceOnMouse, process_ignores_events_when_button_conversion_fails) { EXPECT_THROW({mir::input::evdev::to_pointer_button(BTN_JOYSTICK, mir_pointer_handedness_right);}, std::runtime_error); InSequence seq; EXPECT_CALL(mock_sink, handle_input(_)).Times(0); mouse.start(&mock_sink, &mock_builder); env.mock_libinput.setup_button_event(fake_device, event_time_1, BTN_JOYSTICK, LIBINPUT_BUTTON_STATE_PRESSED); process_events(mouse); } TEST_F(LibInputDeviceOnMouse, process_event_handles_scroll) { InSequence seq; // expect two scroll events.. EXPECT_CALL(mock_builder, pointer_event(time_stamp_1, mir_pointer_action_motion, 0, 0.0f, -20.0f, 0.0f, 0.0f)); EXPECT_CALL(mock_sink, handle_input(mt::PointerAxisChange(mir_pointer_axis_vscroll, -20.0f))); EXPECT_CALL(mock_builder, pointer_event(time_stamp_2, mir_pointer_action_motion, 0, 5.0f, 0.0f, 0.0f, 0.0f)); EXPECT_CALL(mock_sink, handle_input(mt::PointerAxisChange(mir_pointer_axis_hscroll, 5.0f))); mouse.start(&mock_sink, &mock_builder); env.mock_libinput.setup_axis_event(fake_device, event_time_1, 0.0, 20.0); env.mock_libinput.setup_axis_event(fake_device, event_time_2, 5.0, 0.0); process_events(mouse); } TEST_F(LibInputDeviceOnTouchScreen, process_event_handles_touch_down_events) { int slot = 0; float major = 6; float minor = 5; float pressure = 0.6f; float x = 100; float y = 7; InSequence seq; EXPECT_CALL(mock_builder, touch_event(time_stamp_1)); EXPECT_CALL(mock_builder, add_touch(_, MirTouchId{0}, mir_touch_action_down, mir_touch_tooltype_finger, x, y, pressure, major, minor, major)); EXPECT_CALL(mock_sink, handle_input(mt::TouchEvent(x, y))); touch_screen.start(&mock_sink, &mock_builder); env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, slot, x, y, major, minor, pressure); env.mock_libinput.setup_touch_frame(fake_device, event_time_1); process_events(touch_screen); } TEST_F(LibInputDeviceOnTouchScreen, process_event_handles_touch_move_events) { int slot = 0; float major = 6; float minor = 5; float pressure = 0.6f; float x = 100; float y = 7; InSequence seq; EXPECT_CALL(mock_builder, touch_event(time_stamp_1)); EXPECT_CALL(mock_builder, add_touch(_, MirTouchId{0}, mir_touch_action_change, mir_touch_tooltype_finger, x, y, pressure, major, minor, major)); EXPECT_CALL(mock_sink, handle_input(mt::TouchMovementEvent())); touch_screen.start(&mock_sink, &mock_builder); env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_MOTION, event_time_1, slot, x, y, major, minor, pressure); env.mock_libinput.setup_touch_frame(fake_device, event_time_1); process_events(touch_screen); } TEST_F(LibInputDeviceOnTouchScreen, process_event_handles_touch_up_events_without_querying_properties) { int slot = 3; float major = 6; float minor = 5; float pressure = 0.6f; float x = 30; float y = 20; InSequence seq; EXPECT_CALL(mock_builder, touch_event(time_stamp_1)); EXPECT_CALL(mock_builder, add_touch(_, MirTouchId{slot}, mir_touch_action_down, mir_touch_tooltype_finger, x, y, pressure, major, minor, major)); EXPECT_CALL(mock_sink, handle_input(mt::TouchEvent(x, y))); EXPECT_CALL(mock_builder, touch_event(time_stamp_2)); EXPECT_CALL(mock_builder, add_touch(_, MirTouchId{slot}, mir_touch_action_up, mir_touch_tooltype_finger, x, y, pressure, major, minor, major)); EXPECT_CALL(mock_sink, handle_input(mt::TouchUpEvent(x, y))); touch_screen.start(&mock_sink, &mock_builder); env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, slot, x, y, major, minor, pressure); env.mock_libinput.setup_touch_frame(fake_device, event_time_1); env.mock_libinput.setup_touch_up_event(fake_device, event_time_2, slot); env.mock_libinput.setup_touch_frame(fake_device, event_time_2); process_events(touch_screen); } TEST_F(LibInputDeviceOnTouchScreen, sends_complete_events) { const int first_slot = 1; const int second_slot = 3; const float major = 6; const float minor = 5; const float pressure = 0.6f; const float first_x = 30; const float first_y = 20; const float second_x = 90; const float second_y = 90; InSequence seq; EXPECT_CALL(mock_sink, handle_input(mt::TouchContact(0, mir_touch_action_down, first_x, first_y))); EXPECT_CALL(mock_sink, handle_input(AllOf(mt::TouchContact(0, mir_touch_action_change, first_x, first_y), mt::TouchContact(1, mir_touch_action_down, second_x, second_y)))); EXPECT_CALL(mock_sink, handle_input(AllOf(mt::TouchContact(0, mir_touch_action_change, first_x, first_y + 5), mt::TouchContact(1, mir_touch_action_change, second_x, second_y)))); EXPECT_CALL(mock_sink, handle_input(AllOf(mt::TouchContact(0, mir_touch_action_change, first_x, first_y + 5), mt::TouchContact(1, mir_touch_action_change, second_x + 5, second_y)))); touch_screen.start(&mock_sink, &mock_builder); env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, first_slot, first_x, first_y, major, minor, pressure); env.mock_libinput.setup_touch_frame(fake_device, event_time_1); env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, second_slot, second_x, second_y, major, minor, pressure); env.mock_libinput.setup_touch_frame(fake_device, event_time_1); env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_MOTION, event_time_2, first_slot, first_x, first_y + 5, major, minor, pressure); env.mock_libinput.setup_touch_frame(fake_device, event_time_2); env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_MOTION, event_time_2, second_slot, second_x + 5, second_y, major, minor, pressure); env.mock_libinput.setup_touch_frame(fake_device, event_time_2); process_events(touch_screen); } TEST_F(LibInputDeviceOnLaptopKeyboard, provides_no_pointer_settings_for_non_pointing_devices) { auto settings = keyboard.get_pointer_settings(); EXPECT_THAT(settings.is_set(), Eq(false)); } TEST_F(LibInputDeviceOnMouse, reads_pointer_settings_from_libinput) { setup_pointer_configuration(mouse.device(), 1, mir_pointer_handedness_right, mir_pointer_acceleration_none); auto optional_settings = mouse.get_pointer_settings(); EXPECT_THAT(optional_settings.is_set(), Eq(true)); auto ptr_settings = optional_settings.value(); EXPECT_THAT(ptr_settings.handedness, Eq(mir_pointer_handedness_right)); EXPECT_THAT(ptr_settings.cursor_acceleration_bias, Eq(1.0)); EXPECT_THAT(ptr_settings.horizontal_scroll_scale, Eq(1.0)); EXPECT_THAT(ptr_settings.vertical_scroll_scale, Eq(1.0)); EXPECT_THAT(ptr_settings.acceleration, Eq(mir_pointer_acceleration_none)); setup_pointer_configuration(mouse.device(), 0.0, mir_pointer_handedness_left, mir_pointer_acceleration_adaptive); optional_settings = mouse.get_pointer_settings(); EXPECT_THAT(optional_settings.is_set(), Eq(true)); ptr_settings = optional_settings.value(); EXPECT_THAT(ptr_settings.handedness, Eq(mir_pointer_handedness_left)); EXPECT_THAT(ptr_settings.cursor_acceleration_bias, Eq(0.0)); EXPECT_THAT(ptr_settings.horizontal_scroll_scale, Eq(1.0)); EXPECT_THAT(ptr_settings.vertical_scroll_scale, Eq(1.0)); EXPECT_THAT(ptr_settings.acceleration, Eq(mir_pointer_acceleration_adaptive)); } TEST_F(LibInputDeviceOnMouse, applies_pointer_settings) { setup_pointer_configuration(mouse.device(), 1, mir_pointer_handedness_right, mir_pointer_acceleration_adaptive); mi::PointerSettings settings(mouse.get_pointer_settings().value()); settings.cursor_acceleration_bias = 1.1; settings.handedness = mir_pointer_handedness_left; settings.acceleration = mir_pointer_acceleration_none; EXPECT_CALL(env.mock_libinput, libinput_device_config_accel_set_speed(mouse.device(), 1.1)).Times(1); #if MIR_LIBINPUT_HAS_ACCEL_PROFILE EXPECT_CALL(env.mock_libinput, libinput_device_config_accel_set_profile(mouse.device(), LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT)).Times(1); #endif EXPECT_CALL(env.mock_libinput, libinput_device_config_left_handed_set(mouse.device(), true)).Times(1); mouse.apply_settings(settings); } TEST_F(LibInputDeviceOnLaptopKeyboardAndMouse, denies_pointer_settings_on_keyboards) { setup_pointer_configuration(mouse.device(), 1, mir_pointer_handedness_right, mir_pointer_acceleration_adaptive); auto settings_from_mouse = mouse.get_pointer_settings(); EXPECT_CALL(env.mock_libinput,libinput_device_config_accel_set_speed(_, _)).Times(0); EXPECT_CALL(env.mock_libinput,libinput_device_config_left_handed_set(_, _)).Times(0); keyboard.apply_settings(settings_from_mouse.value()); } TEST_F(LibInputDeviceOnMouse, scroll_speed_scales_scroll_events) { // expect two scroll events.. EXPECT_CALL(mock_sink, handle_input(mt::PointerAxisChange(mir_pointer_axis_vscroll, 3.0f))); EXPECT_CALL(mock_sink, handle_input(mt::PointerAxisChange(mir_pointer_axis_hscroll, -10.0f))); setup_pointer_configuration(mouse.device(), 1, mir_pointer_handedness_right, mir_pointer_acceleration_none); mi::PointerSettings settings(mouse.get_pointer_settings().value()); settings.vertical_scroll_scale = -1.0; settings.horizontal_scroll_scale = 5.0; mouse.apply_settings(settings); env.mock_libinput.setup_axis_event(fake_device, event_time_1, 0.0, 3.0); env.mock_libinput.setup_axis_event(fake_device, event_time_2, -2.0, 0.0); mouse.start(&mock_sink, &mock_builder); process_events(mouse); } TEST_F(LibInputDeviceOnLaptopKeyboardAndMouse, provides_no_touchpad_settings_for_non_touchpad_devices) { auto val = keyboard.get_touchpad_settings(); EXPECT_THAT(val.is_set(), Eq(false)); val = mouse.get_touchpad_settings(); EXPECT_THAT(val.is_set(), Eq(false)); } TEST_F(LibInputDeviceOnTouchpad, process_event_handles_scroll) { InSequence seq; // expect two scroll events.. EXPECT_CALL(mock_builder, pointer_event(time_stamp_1, mir_pointer_action_motion, 0, 0.0f, -10.0f, 0.0f, 0.0f)); EXPECT_CALL(mock_sink, handle_input(mt::PointerAxisChange(mir_pointer_axis_vscroll, -10.0f))); EXPECT_CALL(mock_builder, pointer_event(time_stamp_2, mir_pointer_action_motion, 0, 1.0f, 0.0f, 0.0f, 0.0f)); EXPECT_CALL(mock_sink, handle_input(mt::PointerAxisChange(mir_pointer_axis_hscroll, 1.0f))); env.mock_libinput.setup_finger_axis_event(fake_device, event_time_1, 0.0, 150.0); env.mock_libinput.setup_finger_axis_event(fake_device, event_time_2, 15.0, 0.0); touchpad.start(&mock_sink, &mock_builder); process_events(touchpad); } TEST_F(LibInputDeviceOnTouchpad, reads_touchpad_settings_from_libinput) { setup_touchpad_configuration(fake_device, mir_touchpad_click_mode_finger_count, mir_touchpad_scroll_mode_edge_scroll, 0, true, false, true, false); auto settings = touchpad.get_touchpad_settings().value(); EXPECT_THAT(settings.click_mode, Eq(mir_touchpad_click_mode_finger_count)); EXPECT_THAT(settings.scroll_mode, Eq(mir_touchpad_scroll_mode_edge_scroll)); EXPECT_THAT(settings.tap_to_click, Eq(true)); EXPECT_THAT(settings.disable_while_typing, Eq(false)); EXPECT_THAT(settings.disable_with_mouse, Eq(true)); EXPECT_THAT(settings.middle_mouse_button_emulation, Eq(false)); } TEST_F(LibInputDeviceOnTouchpad, applies_touchpad_settings) { setup_touchpad_configuration(fake_device, mir_touchpad_click_mode_finger_count, mir_touchpad_scroll_mode_two_finger_scroll, 0, true, false, true, false); mi::TouchpadSettings settings(touchpad.get_touchpad_settings().value()); settings.scroll_mode = mir_touchpad_scroll_mode_button_down_scroll; settings.click_mode = mir_touchpad_click_mode_finger_count; settings.button_down_scroll_button = KEY_A; settings.tap_to_click = true; settings.disable_while_typing = false; settings.disable_with_mouse = true; settings.middle_mouse_button_emulation = true; EXPECT_CALL(env.mock_libinput, libinput_device_config_scroll_set_method(touchpad.device(), LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN)); EXPECT_CALL(env.mock_libinput, libinput_device_config_click_set_method(touchpad.device(), LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)); EXPECT_CALL(env.mock_libinput, libinput_device_config_scroll_set_button(touchpad.device(), KEY_A)); EXPECT_CALL(env.mock_libinput, libinput_device_config_tap_set_enabled(touchpad.device(), LIBINPUT_CONFIG_TAP_ENABLED)); EXPECT_CALL(env.mock_libinput, libinput_device_config_dwt_set_enabled(touchpad.device(), LIBINPUT_CONFIG_DWT_DISABLED)); EXPECT_CALL(env.mock_libinput, libinput_device_config_send_events_set_mode( touchpad.device(), LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)); EXPECT_CALL(env.mock_libinput, libinput_device_config_middle_emulation_set_enabled( touchpad.device(), LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED)); touchpad.apply_settings(settings); } TEST_F(LibInputDevice, device_ptr_keeps_libinput_context_alive) { auto fake_dev = setup_touchpad(); InSequence seq; EXPECT_CALL(env.mock_libinput, libinput_device_ref(fake_dev)); EXPECT_CALL(env.mock_libinput, libinput_device_unref(fake_dev)); EXPECT_CALL(env.mock_libinput, libinput_unref(env.li_context)); auto device_ptr = mie::make_libinput_device(lib, fake_dev); lib.reset(); device_ptr.reset(); } ./tests/unit-tests/input/android/0000755000015600001650000000000012676616160017150 5ustar jenkinsjenkins./tests/unit-tests/input/android/test_input_consumer.cpp0000644000015600001650000002404112676616157023774 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "androidfw/Input.h" #include "androidfw/InputTransport.h" #include "src/server/input/android/android_input_channel.h" #include "mir/input/android/event_conversion_helpers.h" #include "mir/input/android/android_input_lexicon.h" #include "mir/geometry/displacement.h" #include "mir/geometry/point.h" #include #include #include #include #include using namespace std::literals::chrono_literals; namespace mia = mir::input::android; namespace geom = mir::geometry; namespace { struct EventFactory : android::InputEventFactoryInterface { android::KeyEvent key; android::MotionEvent motion; android::KeyEvent* createKeyEvent() { return &key; } android::MotionEvent* createMotionEvent() { return &motion; } }; } struct InputConsumerTest : ::testing::Test { MOCK_METHOD3(pointer_movement,void(geom::Point pos, geom::Displacement movement, geom::Displacement scroll)); uint32_t seq{0}; mir::cookie::Blob default_cookie; EventFactory events; std::chrono::milliseconds current_frame_time = 0ms; mia::AndroidInputChannel channel; geom::Displacement no_move{0,0}; geom::Displacement no_scroll{0,0}; geom::Point origin{0,0}; droidinput::sp client_channel = new droidinput::InputChannel("test_client", channel.client_fd()); droidinput::sp server_channel = new droidinput::InputChannel("test_server", channel.server_fd()); droidinput::InputPublisher publisher{server_channel}; droidinput::InputConsumer consumer{client_channel}; InputConsumerTest() { std::memset(&default_cookie, 0, sizeof default_cookie); } const MirInputDeviceId touchscreen_device = 2; const MirInputDeviceId mouse_device = 3; struct TouchEvent { int action; // only one action per event - android InputTransport restriction std::vector positions; std::chrono::nanoseconds tp; }; void send_touch_event(TouchEvent const& event) { std::vector coords(event.positions.size()); std::vector properties(event.positions.size()); std::memset(coords.data(), 0, sizeof coords[0] * coords.size()); std::memset(properties.data(), 0, sizeof properties[0] * properties.size()); auto const x_offset = 0.0f; auto const y_offset = 0.0f; auto const x_precision = 0; auto const y_precision = 0; auto const flags = 0; auto const edge_flags = 0; auto const button_state = 0; int contacts_in_event = 0; for (auto const& contact : event.positions) { coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_X, contact.x.as_float()); coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_Y, contact.y.as_float()); coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 5); coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 5); coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 5); coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.7); properties[contacts_in_event].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; properties[contacts_in_event].id = contacts_in_event; } publisher.publishMotionEvent(++seq, touchscreen_device, AINPUT_SOURCE_TOUCHSCREEN, event.action, flags, edge_flags, 0, button_state, x_offset, y_offset, x_precision, y_precision, default_cookie, event.tp, event.tp, contacts_in_event, properties.data(), coords.data()); } struct PointerEvent { MirPointerAction action; MirPointerButtons buttons; geom::Point position; geom::Displacement movement; geom::Displacement scroll; std::chrono::nanoseconds tp; }; void send_pointer_event(PointerEvent const& event) { droidinput::PointerCoords pointer_coord; droidinput::PointerProperties pointer_properties; std::memset(&pointer_coord, 0, sizeof(pointer_coord)); std::memset(&pointer_properties, 0, sizeof(pointer_properties)); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_X, event.position.x.as_float()); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_Y, event.position.y.as_float()); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, event.scroll.dx.as_float()); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, event.scroll.dy.as_float()); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_RX, event.movement.dx.as_float()); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_RY, event.movement.dy.as_float()); pointer_properties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; pointer_properties.id = 0; auto const x_offset = 0.0f; auto const y_offset = 0.0f; auto const x_precision = 0; auto const y_precision = 0; auto const flags = 0; auto const buttons = 0; auto const modifiers = 0; auto const edge_flags = 0; publisher.publishMotionEvent(++seq, mouse_device, AINPUT_SOURCE_MOUSE, mia::android_pointer_action_from_mir(event.action, event.buttons), flags, edge_flags, modifiers, buttons, x_offset, y_offset, x_precision, y_precision, default_cookie, event.tp, event.tp, 1, &pointer_properties, &pointer_coord); } void handle_event(droidinput::InputEvent* event) { if (event->getType() == AINPUT_EVENT_TYPE_KEY) { } else { if (mia::android_source_id_is_pointer_device(event->getSource())) { auto mev = static_cast(event); pointer_movement( {mev->getX(0), mev->getY(0)}, {mev->getRawAxisValue(AMOTION_EVENT_AXIS_RX, 0), mev->getRawAxisValue(AMOTION_EVENT_AXIS_RY, 0)}, {mev->getRawAxisValue(AMOTION_EVENT_AXIS_HSCROLL, 0), mev->getRawAxisValue(AMOTION_EVENT_AXIS_VSCROLL, 0)} ); } } } void receive_events() { const auto fake_update_rate = 1ms; android::InputEvent *received_event = nullptr; uint32_t seq_id; do { auto result = consumer.consume(&events, true, current_frame_time, &seq_id, &received_event); if (result == droidinput::OK) handle_event(received_event); if (consumer.hasPendingBatch()) current_frame_time += fake_update_rate; } while(consumer.hasPendingBatch() || consumer.hasDeferredEvent()); } void advance_frame_time_to(std::chrono::milliseconds time) { current_frame_time = time; } }; TEST_F(InputConsumerTest, emits_single_move_event_on_old_pointer_messages) { EXPECT_CALL(*this, pointer_movement(geom::Point{4,11}, geom::Displacement{2,1}, no_scroll)); send_pointer_event({mir_pointer_action_motion, 0, {2.0, 10.0}, {0.0, 0.0}, no_scroll, 0ns}); send_pointer_event({mir_pointer_action_motion, 0, {3.0, 11.0}, {1.0, 1.0}, no_scroll, 1ms}); send_pointer_event({mir_pointer_action_motion, 0, {4.0, 11.0}, {1.0, 0.0}, no_scroll, 2ms}); advance_frame_time_to(16ms); receive_events(); } TEST_F(InputConsumerTest, emits_move_events_on_recent_messages) { EXPECT_CALL(*this, pointer_movement(geom::Point{10, 5}, geom::Displacement{0, 0}, no_scroll)); EXPECT_CALL(*this, pointer_movement(geom::Point{15, 8}, geom::Displacement{5, 3}, no_scroll)); EXPECT_CALL(*this, pointer_movement(geom::Point{12,10}, geom::Displacement{-3, 2}, no_scroll)); send_pointer_event({mir_pointer_action_motion, 0, {10.0, 5.0}, {0.0, 0.0}, no_scroll, 0ns}); send_pointer_event({mir_pointer_action_motion, 0, {15.0, 8.0}, {5.0, 3.0}, no_scroll, 1ms}); send_pointer_event({mir_pointer_action_motion, 0, {12.0, 10.0}, {-3.0,2.0}, no_scroll, 2ms}); advance_frame_time_to(2ms); receive_events(); } TEST_F(InputConsumerTest, emits_scroll_events_on_each_recent_scroll_messages) { EXPECT_CALL(*this, pointer_movement(origin, no_move, geom::Displacement{3, 0})); EXPECT_CALL(*this, pointer_movement(origin, no_move, geom::Displacement{0, 5})); EXPECT_CALL(*this, pointer_movement(origin, no_move, geom::Displacement{2, 5})); send_pointer_event({mir_pointer_action_motion, 0, origin, no_move, {3.0, 0.0}, 0ns}); send_pointer_event({mir_pointer_action_motion, 0, origin, no_move, {0.0, 5.0}, 1ms}); send_pointer_event({mir_pointer_action_motion, 0, origin, no_move, {2.0, 5.0}, 2ms}); advance_frame_time_to(2ms); receive_events(); } TEST_F(InputConsumerTest, emits_accumulated_scroll_event_on_old_messages) { EXPECT_CALL(*this, pointer_movement(origin, no_move, geom::Displacement{5, 10})); send_pointer_event({mir_pointer_action_motion, 0, origin, no_move, {3.0, 0.0}, 0ns}); send_pointer_event({mir_pointer_action_motion, 0, origin, no_move, {0.0, 5.0}, 1ms}); send_pointer_event({mir_pointer_action_motion, 0, origin, no_move, {2.0, 5.0}, 2ms}); advance_frame_time_to(16ms); receive_events(); } ./tests/unit-tests/input/android/test_android_input_channel_factory.cpp0000644000015600001650000000224712676616125026777 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/server/input/android/android_input_channel.h" #include "src/server/input/android/input_channel_factory.h" #include #include #include namespace mia = mir::input::android; TEST(AndroidInputChannelFactory, channel_factory_returns_input_channel_with_fds) { mia::InputChannelFactory factory; auto package = factory.make_input_channel(); EXPECT_NE(nullptr, std::dynamic_pointer_cast(package)); } ./tests/unit-tests/input/android/test_android_communication_package.cpp0000644000015600001650000000255212676616125026740 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/server/input/android/android_input_channel.h" #include #include #include #include namespace droidinput = android; namespace mia = mir::input::android; TEST(AndroidInputChannel, packages_own_valid_fds) { int server_fd, client_fd; { mia::AndroidInputChannel package; server_fd = package.server_fd(); client_fd = package.client_fd(); EXPECT_LE(0, server_fd); EXPECT_LE(0, client_fd); EXPECT_EQ(fcntl(server_fd, F_GETFD), 0); EXPECT_EQ(fcntl(client_fd, F_GETFD), 0); } EXPECT_LT(fcntl(server_fd, F_GETFD), 0); EXPECT_LT(fcntl(client_fd, F_GETFD), 0); } ./tests/unit-tests/input/android/test_android_input_sender.cpp0000644000015600001650000004712312676616125025122 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/events/event_private.h" #include "src/server/input/android/android_input_channel.h" #include "src/server/input/android/input_sender.h" #include "src/server/input/default_event_builder.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/stub_scene_surface.h" #include "mir/test/doubles/mock_input_surface.h" #include "mir/test/doubles/stub_scene.h" #include "mir/test/doubles/mock_scene.h" #include "mir/test/doubles/triggered_main_loop.h" #include "mir/test/fake_shared.h" #include "mir/test/event_matchers.h" #include "androidfw/Input.h" #include "androidfw/InputTransport.h" #include "mir/input/input_report.h" #include "mir/cookie/authority.h" #include "mir/cookie/blob.h" #include #include #include //#include #include namespace mi = mir::input; namespace ms = mir::scene; namespace mia = mi::android; namespace mt = mir::test; namespace mr = mir::report; namespace mtd = mt::doubles; namespace droidinput = android; using testing::_; namespace { class MockInputEventFactory : public droidinput::InputEventFactoryInterface { public: MOCK_METHOD0(createKeyEvent, droidinput::KeyEvent*()); MOCK_METHOD0(createMotionEvent, droidinput::MotionEvent*()); }; class MockInputReport : public mir::input::InputReport { public: MOCK_METHOD4(received_event_from_kernel, void(int64_t when, int type, int code, int value)); MOCK_METHOD3(published_key_event, void(int dest_fd, uint32_t seq_id, int64_t event_time)); MOCK_METHOD3(published_motion_event, void(int dest_fd, uint32_t seq_id, int64_t event_time)); MOCK_METHOD2(opened_input_device, void(char const* device_name, char const* input_platform)); MOCK_METHOD2(failed_to_open_input_device, void(char const* device_name, char const* input_platform)); }; class FakeScene : public mtd::StubScene { public: void add_observer(std::shared_ptr const& observer) override { this->observer = observer; } void remove_observer(std::weak_ptr const&) override { observer.reset(); } std::shared_ptr observer; }; } class AndroidInputSender : public ::testing::Test { public: int const test_scan_code = 32; size_t const test_pointer_count = 2; float const test_x_coord[2] = {12, 23}; float const test_y_coord[2] = {17, 9}; MirTouchTooltype const tool = mir_touch_tooltype_finger; float const pressure = 0.6f; float const major = 8; float const minor = 4; float const size = 8; mir::geometry::Displacement const movement{10, -10}; std::shared_ptr const cookie_authority; mi::DefaultEventBuilder builder; AndroidInputSender() : cookie_authority(mir::cookie::Authority::create_from({0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})), builder(MirInputDeviceId(), cookie_authority), key_event(builder.key_event(std::chrono::nanoseconds(1), mir_keyboard_action_down, 7, test_scan_code)), motion_event(builder.touch_event(std::chrono::nanoseconds(-1))), pointer_event(builder.pointer_event(std::chrono::nanoseconds(123), mir_pointer_action_motion, mir_pointer_button_primary, 0.0f, 0.0f, movement.dx.as_float(), movement.dy.as_float())) { using namespace ::testing; builder.add_touch(*motion_event, 0, mir_touch_action_change, tool, test_x_coord[0], test_y_coord[0], pressure, major, minor, size); builder.add_touch(*motion_event, 1, mir_touch_action_change, tool, test_x_coord[1], test_y_coord[1], pressure, major, minor, size); ON_CALL(event_factory, createKeyEvent()).WillByDefault(Return(&client_key_event)); ON_CALL(event_factory, createMotionEvent()).WillByDefault(Return(&client_motion_event)); } void register_surface() { fake_scene.observer->surface_added(&stub_surface); } void deregister_surface() { fake_scene.observer->surface_removed(&stub_surface); } std::shared_ptr channel = std::make_shared(); mtd::StubSceneSurface stub_surface{channel->server_fd()}; droidinput::sp client_channel{new droidinput::InputChannel(droidinput::String8("test"), channel->client_fd())}; droidinput::InputConsumer consumer{client_channel}; mtd::TriggeredMainLoop loop; testing::NiceMock mock_input_report; mir::EventUPtr key_event; mir::EventUPtr motion_event; mir::EventUPtr pointer_event; droidinput::MotionEvent client_motion_event; droidinput::KeyEvent client_key_event; testing::NiceMock event_factory; droidinput::InputEvent * event= nullptr; uint32_t seq = 0; FakeScene fake_scene; mia::InputSender sender{mt::fake_shared(fake_scene), mt::fake_shared(loop), mt::fake_shared(mock_input_report)}; }; TEST_F(AndroidInputSender, subscribes_to_scene) { using namespace ::testing; NiceMock mock_scene; EXPECT_CALL(mock_scene, add_observer(_)); mia::InputSender sender(mt::fake_shared(mock_scene), mt::fake_shared(loop), mr::null_input_report()); } TEST_F(AndroidInputSender, throws_on_unknown_channel) { EXPECT_THROW(sender.send_event(*key_event, channel), boost::exception); } TEST_F(AndroidInputSender,throws_on_deregistered_channels) { register_surface(); deregister_surface(); EXPECT_THROW(sender.send_event(*key_event, channel), boost::exception); } TEST_F(AndroidInputSender, first_send_on_surface_registers_server_fd) { using namespace ::testing; register_surface(); EXPECT_CALL(loop, register_fd_handler(ElementsAre(channel->server_fd()),_,_)); sender.send_event(*key_event, channel); } TEST_F(AndroidInputSender, second_send_on_surface_does_not_register_server_fd) { using namespace ::testing; register_surface(); EXPECT_CALL(loop, register_fd_handler(ElementsAre(channel->server_fd()),_,_)).Times(1); sender.send_event(*key_event, channel); sender.send_event(*key_event, channel); } TEST_F(AndroidInputSender, removal_of_surface_after_send_unregisters_server_fd) { using namespace ::testing; register_surface(); EXPECT_CALL(loop, unregister_fd_handler(_)).Times(1); sender.send_event(*key_event, channel); sender.send_event(*key_event, channel); deregister_surface(); Mock::VerifyAndClearExpectations(&loop); } TEST_F(AndroidInputSender, can_send_consumeable_mir_key_events) { register_surface(); sender.send_event(*key_event, channel); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(-1), &seq, &event)); EXPECT_EQ(&client_key_event, event); EXPECT_EQ(test_scan_code, client_key_event.getScanCode()); } TEST_F(AndroidInputSender, reports_published_key_events) { register_surface(); auto expected_time = mir_input_event_get_event_time(mir_event_get_input_event(key_event.get())); EXPECT_CALL(mock_input_report, published_key_event(channel->server_fd(),_,expected_time)); sender.send_event(*key_event, channel); } TEST_F(AndroidInputSender, reports_published_motion_events) { register_surface(); auto expected_time = mir_input_event_get_event_time(mir_event_get_input_event(motion_event.get())); EXPECT_CALL(mock_input_report, published_motion_event(channel->server_fd(),_,expected_time)); sender.send_event(*motion_event, channel); } TEST_F(AndroidInputSender, can_send_consumeable_mir_motion_events) { using namespace ::testing; register_surface(); sender.send_event(*motion_event, channel); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(-1), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(test_pointer_count, client_motion_event.getPointerCount()); EXPECT_EQ(0, client_motion_event.getXOffset()); EXPECT_EQ(0, client_motion_event.getYOffset()); for (size_t i = 0; i != test_pointer_count; ++i) { EXPECT_EQ(test_x_coord[i], client_motion_event.getRawX(i)) << "When i=" << i; EXPECT_EQ(test_x_coord[i], client_motion_event.getX(i)) << "When i=" << i; EXPECT_EQ(test_y_coord[i], client_motion_event.getRawY(i)) << "When i=" << i; EXPECT_EQ(test_y_coord[i], client_motion_event.getY(i)) << "When i=" << i; EXPECT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, client_motion_event.getToolType(i)) << "When i=" << i; } EXPECT_EQ(AINPUT_SOURCE_TOUCHSCREEN, client_motion_event.getSource()); } TEST_F(AndroidInputSender, non_input_clients_dont_crash_the_server) { // Regression test for LP: #1528438 register_surface(); EXPECT_NO_THROW( { for (int i = 0; i < 10000; ++i) sender.send_event(*motion_event, channel); }); } TEST_F(AndroidInputSender, sends_pointer_events) { using namespace ::testing; register_surface(); sender.send_event(*pointer_event, channel); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(-1), &seq, &event)); EXPECT_EQ(1, client_motion_event.getPointerCount()); EXPECT_EQ(movement.dx.as_float(), client_motion_event.getAxisValue(AMOTION_EVENT_AXIS_RX, 0)); EXPECT_EQ(movement.dy.as_float(), client_motion_event.getAxisValue(AMOTION_EVENT_AXIS_RY, 0)); EXPECT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, client_motion_event.getToolType(0)); EXPECT_EQ(AINPUT_SOURCE_MOUSE, client_motion_event.getSource()); } TEST_F(AndroidInputSender, response_keeps_fd_registered) { using namespace ::testing; register_surface(); EXPECT_CALL(loop, unregister_fd_handler(_)).Times(0); sender.send_event(*key_event, channel); consumer.consume(&event_factory, true, std::chrono::nanoseconds(-1), &seq, &event); consumer.sendFinishedSignal(seq, true); loop.trigger_pending_fds(); Mock::VerifyAndClearExpectations(&loop); } TEST_F(AndroidInputSender, finish_signal_triggers_success_callback_as_consumed) { register_surface(); sender.send_event(*motion_event, channel); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(-1), &seq, &event)); consumer.sendFinishedSignal(seq, true); loop.trigger_pending_fds(); } TEST_F(AndroidInputSender, finish_signal_triggers_success_callback_as_not_consumed) { register_surface(); sender.send_event(*motion_event, channel); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(-1), &seq, &event)); consumer.sendFinishedSignal(seq, false); loop.trigger_pending_fds(); } TEST_F(AndroidInputSender, encodes_multiple_touch_contact_downs_as_multiple_events) { // TODO When the rework of the transport encoding is finished we might want to revisit this decision register_surface(); auto touch_ev_with_simultaneous_down = builder.touch_event(std::chrono::nanoseconds(86)); builder.add_touch(*touch_ev_with_simultaneous_down, 2, mir_touch_action_down, tool, 50, 60, pressure, major, minor, size); builder.add_touch(*touch_ev_with_simultaneous_down, 4, mir_touch_action_down, tool, 80, 90, pressure, major, minor, size); sender.send_event(*touch_ev_with_simultaneous_down, channel); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(86), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, client_motion_event.getActionMasked()); EXPECT_EQ(0, client_motion_event.getActionIndex()); EXPECT_EQ(2, client_motion_event.getPointerId(0)); EXPECT_EQ(1, client_motion_event.getPointerCount()); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(86), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, client_motion_event.getActionMasked()); EXPECT_EQ(1, client_motion_event.getActionIndex()); EXPECT_EQ(2, client_motion_event.getPointerId(0)); EXPECT_EQ(4, client_motion_event.getPointerId(1)); EXPECT_EQ(2, client_motion_event.getPointerCount()); } TEST_F(AndroidInputSender, encodes_multiple_touch_contact_downs_as_multiple_events_but_keeps_only_moved_contacts_in_place) { // TODO When the rework of the transport encoding is finished we might want to revisit this decision register_surface(); auto touch_ev_with_simultaneous_down = builder.touch_event(std::chrono::nanoseconds(86)); builder.add_touch(*touch_ev_with_simultaneous_down, 2, mir_touch_action_down, tool, 50, 60, pressure, major, minor, size); builder.add_touch(*touch_ev_with_simultaneous_down, 3, mir_touch_action_change, tool, 10, 10, pressure, major, minor, size); builder.add_touch(*touch_ev_with_simultaneous_down, 4, mir_touch_action_down, tool, 80, 90, pressure, major, minor, size); sender.send_event(*touch_ev_with_simultaneous_down, channel); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(86), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, client_motion_event.getActionMasked()); EXPECT_EQ(0, client_motion_event.getActionIndex()); EXPECT_EQ(2, client_motion_event.getPointerId(0)); EXPECT_EQ(3, client_motion_event.getPointerId(1)); EXPECT_EQ(2, client_motion_event.getPointerCount()); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(86), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, client_motion_event.getActionMasked()); EXPECT_EQ(2, client_motion_event.getActionIndex()); EXPECT_EQ(2, client_motion_event.getPointerId(0)); EXPECT_EQ(3, client_motion_event.getPointerId(1)); EXPECT_EQ(4, client_motion_event.getPointerId(2)); EXPECT_EQ(3, client_motion_event.getPointerCount()); } TEST_F(AndroidInputSender, encodes_multiple_releases_in_several_events) { // TODO When the rework of the transport encoding is finished we might want to revisit this decision register_surface(); auto touch_ev_with_simultaneous_down = builder.touch_event(std::chrono::nanoseconds(86)); builder.add_touch(*touch_ev_with_simultaneous_down, 2, mir_touch_action_up, tool, 50, 60, pressure, major, minor, size); builder.add_touch(*touch_ev_with_simultaneous_down, 3, mir_touch_action_change, tool, 10, 10, pressure, major, minor, size); builder.add_touch(*touch_ev_with_simultaneous_down, 4, mir_touch_action_up, tool, 80, 90, pressure, major, minor, size); sender.send_event(*touch_ev_with_simultaneous_down, channel); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(86), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(AMOTION_EVENT_ACTION_UP, client_motion_event.getActionMasked()); EXPECT_EQ(0, client_motion_event.getActionIndex()); EXPECT_EQ(2, client_motion_event.getPointerId(0)); EXPECT_EQ(3, client_motion_event.getPointerId(1)); EXPECT_EQ(4, client_motion_event.getPointerId(2)); EXPECT_EQ(3, client_motion_event.getPointerCount()); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(86), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(AMOTION_EVENT_ACTION_UP, client_motion_event.getActionMasked()); EXPECT_EQ(1, client_motion_event.getActionIndex()); EXPECT_EQ(3, client_motion_event.getPointerId(0)); EXPECT_EQ(4, client_motion_event.getPointerId(1)); EXPECT_EQ(2, client_motion_event.getPointerCount()); } TEST_F(AndroidInputSender, encodes_combinations_of_up_and_down_one_by_one) { // TODO When the rework of the transport encoding is finished we might want to revisit this decision register_surface(); auto touch_ev_with_simultaneous_down = builder.touch_event(std::chrono::nanoseconds(86)); builder.add_touch(*touch_ev_with_simultaneous_down, 2, mir_touch_action_up, tool, 50, 60, pressure, major, minor, size); builder.add_touch(*touch_ev_with_simultaneous_down, 3, mir_touch_action_change, tool, 10, 10, pressure, major, minor, size); builder.add_touch(*touch_ev_with_simultaneous_down, 4, mir_touch_action_down, tool, 80, 90, pressure, major, minor, size); sender.send_event(*touch_ev_with_simultaneous_down, channel); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(86), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(AMOTION_EVENT_ACTION_UP, client_motion_event.getActionMasked()); EXPECT_EQ(0, client_motion_event.getActionIndex()); EXPECT_EQ(2, client_motion_event.getPointerId(0)); EXPECT_EQ(3, client_motion_event.getPointerId(1)); EXPECT_EQ(2, client_motion_event.getPointerCount()); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(86), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, client_motion_event.getActionMasked()); EXPECT_EQ(1, client_motion_event.getActionIndex()); EXPECT_EQ(3, client_motion_event.getPointerId(0)); EXPECT_EQ(4, client_motion_event.getPointerId(1)); EXPECT_EQ(2, client_motion_event.getPointerCount()); } TEST_F(AndroidInputSender, encodes_combinations_of_down_and_up_one_by_one) { // TODO When the rework of the transport encoding is finished we might want to revisit this decision register_surface(); auto touch_ev_with_simultaneous_down = builder.touch_event(std::chrono::nanoseconds(86)); builder.add_touch(*touch_ev_with_simultaneous_down, 2, mir_touch_action_down, tool, 50, 60, pressure, major, minor, size); builder.add_touch(*touch_ev_with_simultaneous_down, 3, mir_touch_action_change, tool, 10, 10, pressure, major, minor, size); builder.add_touch(*touch_ev_with_simultaneous_down, 4, mir_touch_action_up, tool, 80, 90, pressure, major, minor, size); sender.send_event(*touch_ev_with_simultaneous_down, channel); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(86), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, client_motion_event.getActionMasked()); EXPECT_EQ(0, client_motion_event.getActionIndex()); EXPECT_EQ(2, client_motion_event.getPointerId(0)); EXPECT_EQ(3, client_motion_event.getPointerId(1)); EXPECT_EQ(4, client_motion_event.getPointerId(2)); EXPECT_EQ(3, client_motion_event.getPointerCount()); EXPECT_EQ(droidinput::OK, consumer.consume(&event_factory, true, std::chrono::nanoseconds(86), &seq, &event)); EXPECT_EQ(&client_motion_event, event); EXPECT_EQ(AMOTION_EVENT_ACTION_UP, client_motion_event.getActionMasked()); EXPECT_EQ(2, client_motion_event.getActionIndex()); EXPECT_EQ(2, client_motion_event.getPointerId(0)); EXPECT_EQ(3, client_motion_event.getPointerId(1)); EXPECT_EQ(4, client_motion_event.getPointerId(2)); EXPECT_EQ(3, client_motion_event.getPointerCount()); } ./tests/unit-tests/input/android/CMakeLists.txt0000644000015600001650000000063212676616157021717 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_android_input_lexicon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_android_input_channel_factory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_android_communication_package.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_android_input_sender.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_input_consumer.cpp ) set( UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/input/android/test_android_input_lexicon.cpp0000644000015600001650000002310212676616125025272 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/input/android/android_input_lexicon.h" #include "mir/cookie/blob.h" #include #include #include namespace mi = mir::input; namespace mia = mir::input::android; TEST(AndroidInputLexicon, translates_key_events) { using namespace ::testing; auto android_key_ev = new android::KeyEvent(); const int32_t device_id = 1; const int32_t source_id = AINPUT_SOURCE_KEYBOARD; const int32_t action = AKEY_EVENT_ACTION_DOWN; const int32_t flags = 4; const int32_t key_code = 5; const int32_t scan_code = 6; const int32_t meta_state = AMETA_ALT_ON; const int32_t repeat_count = 0; const mir::cookie::Blob cookie{{14}}; auto const down_time = std::chrono::nanoseconds(9); auto const event_time = std::chrono::nanoseconds(10); android_key_ev->initialize(device_id, source_id, action, flags, key_code, scan_code, meta_state, repeat_count, cookie, down_time, event_time); auto mir_ev = mia::Lexicon::translate(android_key_ev); EXPECT_EQ(mir_event_type_input, mir_event_get_type(mir_ev.get())); auto iev = mir_event_get_input_event(mir_ev.get()); EXPECT_EQ(device_id, mir_input_event_get_device_id(iev)); EXPECT_EQ(event_time.count(), mir_input_event_get_event_time(iev)); EXPECT_EQ(mir_input_event_type_key, mir_input_event_get_type(iev)); auto kev = mir_input_event_get_keyboard_event(iev); EXPECT_EQ(mir_keyboard_action_down, mir_keyboard_event_action(kev)); EXPECT_EQ(mir_input_event_modifier_alt, mir_keyboard_event_modifiers(kev)); EXPECT_EQ(key_code, mir_keyboard_event_key_code(kev)); EXPECT_EQ(scan_code, mir_keyboard_event_scan_code(kev)); delete android_key_ev; } TEST(AndroidInputLexicon, translates_single_pointer_motion_events) { using namespace ::testing; auto android_motion_ev = new android::MotionEvent; // Common event properties const int32_t device_id = 1; const int32_t source_id = AINPUT_SOURCE_TOUCHSCREEN; const int32_t action = AMOTION_EVENT_ACTION_MOVE; const int32_t flags = 4; const int32_t edge_flags = 5; const int32_t meta_state = 6; const int32_t button_state = 0; const float x_offset = 8; const float y_offset = 9; const float x_precision = 10; const float y_precision = 11; const mir::cookie::Blob cookie{{14}}; auto const down_time = std::chrono::nanoseconds(12); auto const event_time = std::chrono::nanoseconds(13); const size_t pointer_count = 1; // Pointer specific properties (i.e. per touch) const int pointer_id = 0; droidinput::PointerProperties pointer_properties; pointer_properties.id = pointer_id; droidinput::PointerCoords pointer_coords; pointer_coords.clear(); const float x_axis = 100.0; const float y_axis = 200.0; const float touch_minor = 300.0; const float touch_major = 400.0; const float size = 500.0; const float pressure = 600.0; const float orientation = 700.0; pointer_coords.setAxisValue(AMOTION_EVENT_AXIS_X, x_axis); pointer_coords.setAxisValue(AMOTION_EVENT_AXIS_Y, y_axis); pointer_coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touch_major); pointer_coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touch_minor); pointer_coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); pointer_coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); pointer_coords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); pointer_properties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; android_motion_ev->initialize(device_id, source_id, action, flags, edge_flags, meta_state, button_state, x_offset, y_offset, x_precision, y_precision, cookie, down_time, event_time, pointer_count, &pointer_properties, &pointer_coords); auto mir_ev = mia::Lexicon::translate(android_motion_ev); EXPECT_EQ(mir_event_type_input, mir_event_get_type(mir_ev.get())); auto iev = mir_event_get_input_event(mir_ev.get()); // Common event properties EXPECT_EQ(device_id, mir_input_event_get_device_id(iev)); EXPECT_EQ(event_time.count(), mir_input_event_get_event_time(iev)); EXPECT_EQ(mir_input_event_type_touch, mir_input_event_get_type(iev)); auto tev = mir_input_event_get_touch_event(iev); EXPECT_EQ(pointer_count, mir_touch_event_point_count(tev)); EXPECT_EQ(pointer_id, mir_touch_event_id(tev, 0)); // Notice these two coordinates are offset by x/y offset EXPECT_EQ(x_axis + x_offset, mir_touch_event_axis_value(tev, 0, mir_touch_axis_x)); EXPECT_EQ(y_axis + y_offset, mir_touch_event_axis_value(tev, 0, mir_touch_axis_y)); EXPECT_EQ(touch_major, mir_touch_event_axis_value(tev, 0, mir_touch_axis_touch_major)); EXPECT_EQ(touch_minor, mir_touch_event_axis_value(tev, 0, mir_touch_axis_touch_minor)); EXPECT_EQ(size, mir_touch_event_axis_value(tev, 0, mir_touch_axis_size)); EXPECT_EQ(pressure, mir_touch_event_axis_value(tev, 0, mir_touch_axis_pressure)); delete android_motion_ev; } TEST(AndroidInputLexicon, translates_multi_pointer_motion_events) { using namespace ::testing; auto android_motion_ev = new android::MotionEvent; // Common event properties const int32_t device_id = 1; const int32_t source_id = AINPUT_SOURCE_TOUCHSCREEN; const int32_t action = 3 | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); const int32_t flags = 4; const int32_t edge_flags = 5; const int32_t meta_state = 6; const float x_offset = 8; const float y_offset = 9; const float x_precision = 10; const float y_precision = 11; const mir::cookie::Blob cookie{{14}}; const std::chrono::nanoseconds down_time = std::chrono::nanoseconds(12); const std::chrono::nanoseconds event_time = std::chrono::nanoseconds(13); const size_t pointer_count = 2; const int pointer_id[2] = {1, 2}; droidinput::PointerProperties pointer_properties[2]; droidinput::PointerCoords pointer_coords[2]; pointer_coords[0].clear(); pointer_coords[1].clear(); const float x_axis[2] = {100.0, 1000.0}; const float y_axis[2] = {200.0, 2000.0}; const float touch_minor[2] = {300.0, 3000.0}; const float touch_major[2] = {400.0, 4000.0}; const float size[2] = {500.0, 5000.0}; const float pressure[2] = {600.0, 6000.0}; const float orientation[2] = {700.0, 7000.0}; for (size_t p = 0; p < pointer_count; p++) { pointer_properties[p].id = pointer_id[p]; pointer_coords[p].setAxisValue(AMOTION_EVENT_AXIS_X, x_axis[p]); pointer_coords[p].setAxisValue(AMOTION_EVENT_AXIS_Y, y_axis[p]); pointer_coords[p].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touch_major[p]); pointer_coords[p].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touch_minor[p]); pointer_coords[p].setAxisValue(AMOTION_EVENT_AXIS_SIZE, size[p]); pointer_coords[p].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure[p]); pointer_coords[p].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation[p]); pointer_properties[p].toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; } android_motion_ev->initialize(device_id, source_id, action, flags, edge_flags, meta_state, 0, x_offset, y_offset, x_precision, y_precision, cookie, down_time, event_time, pointer_count, pointer_properties, pointer_coords); auto mir_ev = mia::Lexicon::translate(android_motion_ev); EXPECT_EQ(mir_event_type_input, mir_event_get_type(mir_ev.get())); auto iev = mir_event_get_input_event(mir_ev.get()); // Common event properties EXPECT_EQ(device_id, mir_input_event_get_device_id(iev)); EXPECT_EQ(event_time.count(), mir_input_event_get_event_time(iev)); EXPECT_EQ(mir_input_event_type_touch, mir_input_event_get_type(iev)); auto tev = mir_input_event_get_touch_event(iev); EXPECT_EQ(pointer_count, mir_touch_event_point_count(tev)); for (unsigned i = 0; i < pointer_count; i++) { EXPECT_EQ(pointer_id[i], mir_touch_event_id(tev, i)); EXPECT_EQ(x_axis[i] + x_offset, mir_touch_event_axis_value(tev, i, mir_touch_axis_x)); EXPECT_EQ(y_axis[i] + y_offset, mir_touch_event_axis_value(tev, i, mir_touch_axis_y)); EXPECT_EQ(touch_major[i], mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major)); EXPECT_EQ(touch_minor[i], mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor)); EXPECT_EQ(size[i], mir_touch_event_axis_value(tev, i, mir_touch_axis_size)); EXPECT_EQ(pressure[i], mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure)); } delete android_motion_ev; } ./tests/unit-tests/input/test_input_platform_probing.cpp0000644000015600001650000001540112676616157024065 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include #include "mir/input/input_probe.h" #include "mir/input/platform.h" #include "src/server/report/null_report_factory.h" #include "mir/options/option.h" #include "mir/emergency_cleanup_registry.h" #include "mir_test_framework/udev_environment.h" #include "mir_test_framework/executable_path.h" #include "mir_test_framework/stub_input_platform.h" #include "mir/test/doubles/mock_x11.h" #include "mir/test/doubles/mock_libinput.h" #include "mir/test/doubles/mock_option.h" #include "mir/test/doubles/mock_input_device_registry.h" #include "src/platforms/evdev/platform.h" #include "src/platforms/mesa/server/x11/input/input_platform.h" #include "mir/test/fake_shared.h" namespace mt = mir::test; namespace mtd = mt::doubles; namespace mi = mir::input; namespace mr = mir::report; namespace mtf = mir_test_framework; using namespace ::testing; namespace { struct StubEmergencyCleanupRegistry : mir::EmergencyCleanupRegistry { void add(mir::EmergencyCleanupHandler const&) override {} void add(mir::ModuleEmergencyCleanupHandler) override {} }; char const platform_input_lib[] = "platform-input-lib"; char const vt[] = "vt"; char const platform_path[] = "platform-path"; struct InputPlatformProbe : ::testing::Test { InputPlatformProbe() { ON_CALL(mock_options, is_set(StrEq(platform_input_lib))).WillByDefault(Return(false)); ON_CALL(mock_options, is_set(StrEq(platform_path))).WillByDefault(Return(true)); ON_CALL(mock_options, get(StrEq(platform_path),Matcher(_))) .WillByDefault(Return(platform_path_value)); ON_CALL(mock_options, get(StrEq(platform_path))) .WillByDefault(Invoke( [this](char const*) -> boost::any const& { return platform_path_value_as_any; })); ON_CALL(mock_options, get(StrEq(platform_input_lib))) .WillByDefault(Invoke( [this](char const*) -> boost::any const& { return platform_input_lib_value_as_any; })); } void disable_x11() { #ifdef MIR_BUILD_PLATFORM_MESA_X11 ON_CALL(mock_x11, XOpenDisplay(_)).WillByDefault(Return(nullptr)); #endif } // replace with with mocks for udev and evdev to simulate root or non-root // access on evdev devices, and enable the disabled test case(s) below. mtf::UdevEnvironment env; #ifdef MIR_BUILD_PLATFORM_MESA_X11 NiceMock mock_x11; #endif NiceMock mock_libinput; NiceMock mock_options; mtd::MockInputDeviceRegistry mock_registry; StubEmergencyCleanupRegistry stub_emergency; std::shared_ptr stub_prober_report{mr::null_shared_library_prober_report()}; std::string platform_path_value{mtf::server_platform_path()}; boost::any platform_path_value_as_any{platform_path_value}; std::string platform_input_lib_value; boost::any platform_input_lib_value_as_any; }; template struct OfPtrTypeMatcher { template bool MatchAndExplain(T&& p, MatchResultListener* /* listener */) const { return dynamic_cast(&*p) != nullptr; } void DescribeTo(::std::ostream* os) const { *os << "is a or derived from a " << typeid(Expected).name(); } void DescribeNegationTo(::std::ostream* os) const { *os << "is not or not derived from " << typeid(Expected).name(); } }; template inline auto OfPtrType() { return MakePolymorphicMatcher(OfPtrTypeMatcher()); } } TEST_F(InputPlatformProbe, stub_platform_not_picked_up_by_default) { disable_x11(); auto platform = mi::probe_input_platforms(mock_options, mt::fake_shared(stub_emergency), mt::fake_shared(mock_registry), mr::null_input_report(), *stub_prober_report); EXPECT_THAT(platform, OfPtrType()); } #ifdef MIR_BUILD_PLATFORM_MESA_X11 TEST_F(InputPlatformProbe, x11_platform_found_and_used_when_display_connection_works) { auto platform = mi::probe_input_platforms(mock_options, mt::fake_shared(stub_emergency), mt::fake_shared(mock_registry), mr::null_input_report(), *stub_prober_report); EXPECT_THAT(platform, OfPtrType()); } TEST_F(InputPlatformProbe, when_multiple_x11_platforms_are_eligible_only_one_is_selected) { auto const real_lib = mtf::server_platform_path() + "server-mesa-x11.so." MIR_SERVER_GRAPHICS_PLATFORM_ABI_STRING; auto const fake_lib = mtf::server_platform_path() + "server-mesa-x11.so.0"; ASSERT_THAT(real_lib, Ne(fake_lib)); remove(fake_lib.c_str()); ASSERT_THAT(link(real_lib.c_str(), fake_lib.c_str()), Eq(0)); auto platform = mi::probe_input_platforms(mock_options, mt::fake_shared(stub_emergency), mt::fake_shared(mock_registry), mr::null_input_report(), *stub_prober_report); EXPECT_THAT(platform, OfPtrType()); remove(fake_lib.c_str()); } TEST_F(InputPlatformProbe, x11_input_platform_not_used_when_vt_specified) { ON_CALL(mock_options, is_set(StrEq(vt))).WillByDefault(Return(true)); auto platform = mi::probe_input_platforms(mock_options, mt::fake_shared(stub_emergency), mt::fake_shared(mock_registry), mr::null_input_report(), *stub_prober_report); EXPECT_THAT(platform, OfPtrType()); } #endif TEST_F(InputPlatformProbe, allows_forcing_stub_input_platform) { ON_CALL(mock_options, is_set(StrEq(platform_input_lib))).WillByDefault(Return(true)); platform_input_lib_value = mtf::server_input_platform("input-stub.so"); platform_input_lib_value_as_any = platform_input_lib_value; auto platform = mi::probe_input_platforms(mock_options, mt::fake_shared(stub_emergency), mt::fake_shared(mock_registry), mr::null_input_report(), *stub_prober_report); EXPECT_THAT(platform, OfPtrType()); } ./tests/unit-tests/input/test_cursor_controller.cpp0000644000015600001650000003243712676616125023065 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/server/input/cursor_controller.h" #include "mir/thread_safe_list.h" #include "mir/input/surface.h" #include "mir/input/scene.h" #include "mir/scene/observer.h" #include "mir/scene/surface_observer.h" #include "mir/graphics/cursor_image.h" #include "mir/graphics/cursor.h" #include "mir_toolkit/common.h" #include "mir_toolkit/cursors.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/stub_scene_surface.h" #include "mir/test/doubles/stub_input_scene.h" #include #include #include #include #include #include namespace mi = mir::input; namespace mg = mir::graphics; namespace ms = mir::scene; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mt::doubles; namespace { struct NamedCursorImage : public mg::CursorImage { NamedCursorImage(std::string const& name) : cursor_name(name) { } void const* as_argb_8888() const override { return nullptr; } geom::Size size() const override { return geom::Size{16, 16}; } geom::Displacement hotspot() const override { return geom::Displacement{0, 0}; } std::string const cursor_name; }; struct ZeroSizedCursorImage : public mg::CursorImage { void const* as_argb_8888() const override { return nullptr; } geom::Size size() const override { return geom::Size{}; } geom::Displacement hotspot() const override { return geom::Displacement{0, 0}; } }; bool cursor_is_named(mg::CursorImage const& i, std::string const& name) { auto image = dynamic_cast(&i); assert(image); return image->cursor_name == name; } MATCHER(DefaultCursorImage, "") { return cursor_is_named(arg, mir_default_cursor_name); } MATCHER_P(CursorNamed, name, "") { return cursor_is_named(arg, name); } struct MockCursor : public mg::Cursor { MOCK_METHOD0(show, void()); MOCK_METHOD1(show, void(mg::CursorImage const&)); MOCK_METHOD0(hide, void()); MOCK_METHOD1(move_to, void(geom::Point)); }; // TODO: This should only inherit from mi::Surface but to use the Scene observer we need an // ms::Surface base class. struct StubInputSurface : public mtd::StubSceneSurface { StubInputSurface(geom::Rectangle const& input_bounds, std::shared_ptr const& cursor_image) : mtd::StubSceneSurface(0), bounds(input_bounds), cursor_image_(cursor_image) { } std::string name() const override { return std::string(); } geom::Rectangle input_bounds() const override { // We could return bounds here but lets make sure the cursor controller // is only using input_area_contains. return geom::Rectangle(); } bool input_area_contains(geom::Point const& point) const override { return bounds.contains(point); } std::shared_ptr input_channel() const override { return nullptr; } mi::InputReceptionMode reception_mode() const override { return mi::InputReceptionMode::normal; } std::shared_ptr cursor_image() const override { return cursor_image_; } void set_cursor_image(std::shared_ptr const& image) override { cursor_image_ = image; { std::unique_lock lk(observer_guard); for (auto o : observers) o->cursor_image_set_to(*image); } } void add_observer(std::shared_ptr const& observer) override { std::unique_lock lk(observer_guard); observers.push_back(observer); } void remove_observer(std::weak_ptr const& observer) override { auto o = observer.lock(); assert(o); auto it = std::find(observers.begin(), observers.end(), o); observers.erase(it); } void post_frame() { for (auto observer : observers) { observer->frame_posted(1, geom::Size{0,0}); } } void set_cursor_image_without_notifications(std::shared_ptr const& image) { cursor_image_ = image; } geom::Rectangle const bounds; std::shared_ptr cursor_image_; std::mutex observer_guard; std::vector> observers; }; struct StubScene : public mtd::StubInputScene { StubScene(std::initializer_list> const& targets) : targets(targets.begin(), targets.end()) { } void for_each(std::function const&)> const& callback) override { for (auto const& target : targets) callback(target); } void add_observer(std::shared_ptr const& observer) override { observers.add(observer); for (auto target : targets) { observers.for_each([&target](std::shared_ptr const& observer) { observer->surface_exists(target.get()); }); } } void remove_observer(std::weak_ptr const& observer) override { auto o = observer.lock(); assert(o); observers.remove(o); } void add_surface(std::shared_ptr const& surface) { targets.push_back(surface); observers.for_each([&surface](std::shared_ptr const& observer) { observer->surface_added(surface.get()); }); } // TODO: Should be mi::Surface. See comment on StubInputSurface. std::vector> targets; mir::ThreadSafeList> observers; }; struct TestCursorController : public testing::Test { TestCursorController() : default_cursor_image(std::make_shared(mir_default_cursor_name)) { } geom::Rectangle const rect_0_0_1_1{{0, 0}, {1, 1}}; geom::Rectangle const rect_1_1_1_1{{1, 1}, {1, 1}}; std::string const cursor_name_1 = "test-cursor-1"; std::string const cursor_name_2 = "test-cursor-2"; MockCursor cursor; std::shared_ptr const default_cursor_image; }; } TEST_F(TestCursorController, moves_cursor) { using namespace ::testing; StubScene targets({}); mi::CursorController controller(mt::fake_shared(targets), mt::fake_shared(cursor), default_cursor_image); InSequence seq; EXPECT_CALL(cursor, move_to(geom::Point{geom::X{1.0f}, geom::Y{1.0f}})); EXPECT_CALL(cursor, move_to(geom::Point{geom::X{0.0f}, geom::Y{0.0f}})); controller.cursor_moved_to(1.0f, 1.0f); controller.cursor_moved_to(0.0f, 0.0f); } TEST_F(TestCursorController, updates_cursor_image_when_entering_surface) { using namespace ::testing; StubInputSurface surface{rect_1_1_1_1, std::make_shared(cursor_name_1)}; StubScene targets({mt::fake_shared(surface)}); mi::CursorController controller(mt::fake_shared(targets), mt::fake_shared(cursor), default_cursor_image); EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); controller.cursor_moved_to(1.0f, 1.0f); } TEST_F(TestCursorController, surface_with_no_cursor_image_hides_cursor) { using namespace ::testing; StubInputSurface surface{rect_1_1_1_1, nullptr}; StubScene targets({mt::fake_shared(surface)}); mi::CursorController controller(mt::fake_shared(targets), mt::fake_shared(cursor), default_cursor_image); EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); EXPECT_CALL(cursor, hide()).Times(1); controller.cursor_moved_to(1.0f, 1.0f); } TEST_F(TestCursorController, takes_cursor_image_from_topmost_surface) { using namespace ::testing; StubInputSurface surface_1{rect_1_1_1_1, std::make_shared(cursor_name_1)}; StubInputSurface surface_2{rect_1_1_1_1, std::make_shared(cursor_name_2)}; StubScene targets({mt::fake_shared(surface_1), mt::fake_shared(surface_2)}); mi::CursorController controller(mt::fake_shared(targets), mt::fake_shared(cursor), default_cursor_image); EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); EXPECT_CALL(cursor, show(CursorNamed(cursor_name_2))).Times(1); controller.cursor_moved_to(1.0f, 1.0f); } TEST_F(TestCursorController, restores_cursor_when_leaving_surface) { using namespace ::testing; StubInputSurface surface{rect_1_1_1_1, std::make_shared(cursor_name_1)}; StubScene targets({mt::fake_shared(surface)}); mi::CursorController controller(mt::fake_shared(targets), mt::fake_shared(cursor), default_cursor_image); EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); { InSequence seq; EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); EXPECT_CALL(cursor, show(DefaultCursorImage())).Times(1); } controller.cursor_moved_to(1.0f, 1.0f); controller.cursor_moved_to(2.0f, 2.0f); } TEST_F(TestCursorController, change_in_cursor_request_triggers_image_update_without_cursor_motion) { using namespace ::testing; StubInputSurface surface{rect_1_1_1_1, std::make_shared(cursor_name_1)}; StubScene targets({mt::fake_shared(surface)}); mi::CursorController controller(mt::fake_shared(targets), mt::fake_shared(cursor), default_cursor_image); EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); { InSequence seq; EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); EXPECT_CALL(cursor, show(CursorNamed(cursor_name_2))).Times(1); } controller.cursor_moved_to(1.0f, 1.0f); surface.set_cursor_image(std::make_shared(cursor_name_2)); } TEST_F(TestCursorController, change_in_scene_triggers_image_update) { using namespace ::testing; // Here we also demonstrate that the cursor begins at 0,0. StubInputSurface surface{rect_0_0_1_1, std::make_shared(cursor_name_1)}; StubScene targets({}); mi::CursorController controller(mt::fake_shared(targets), mt::fake_shared(cursor), default_cursor_image); EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); targets.add_surface(mt::fake_shared(surface)); } TEST_F(TestCursorController, cursor_image_not_reset_needlessly) { using namespace ::testing; auto image = std::make_shared(cursor_name_1); // Here we also demonstrate that the cursor begins at 0,0. StubInputSurface surface1{rect_0_0_1_1, image}; StubInputSurface surface2{rect_0_0_1_1, image}; StubScene targets({}); mi::CursorController controller(mt::fake_shared(targets), mt::fake_shared(cursor), default_cursor_image); EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); targets.add_surface(mt::fake_shared(surface1)); targets.add_surface(mt::fake_shared(surface2)); } TEST_F(TestCursorController, updates_cursor_image_when_surface_posts_first_frame) { using namespace ::testing; StubInputSurface surface{rect_0_0_1_1, std::make_shared(cursor_name_1)}; StubScene targets({mt::fake_shared(surface)}); // Cursor is set when we first create the controller // because of the existing surface EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); mi::CursorController controller(mt::fake_shared(targets), mt::fake_shared(cursor), default_cursor_image); Mock::VerifyAndClearExpectations(&cursor); surface.set_cursor_image_without_notifications( std::make_shared(cursor_name_2)); // First post should lead to cursor update EXPECT_CALL(cursor, show(CursorNamed(cursor_name_2))).Times(1); surface.post_frame(); Mock::VerifyAndClearExpectations(&cursor); // Second post should have no effect EXPECT_CALL(cursor, show(_)).Times(0); surface.post_frame(); } TEST_F(TestCursorController, zero_sized_image_hides_cursor) { using namespace ::testing; auto image = std::make_shared(); // Here we also demonstrate that the cursor begins at 0,0. StubInputSurface surface{rect_0_0_1_1, image}; StubScene targets({}); mi::CursorController controller(mt::fake_shared(targets), mt::fake_shared(cursor), default_cursor_image); EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); EXPECT_CALL(cursor, hide()).Times(1); targets.add_surface(mt::fake_shared(surface)); } ./tests/unit-tests/input/test_display_input_region.cpp0000644000015600001650000000563512676616125023534 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/input/display_input_region.h" #include #include namespace mi = mir::input; namespace geom = mir::geometry; namespace { geom::Rectangles const rects{ geom::Rectangle{{0,0}, {800,600}}, geom::Rectangle{{0,600}, {100,100}}, geom::Rectangle{{800,0}, {100,100}} }; } TEST(DisplayInputRegionTest, returns_correct_bounding_rectangle) { geom::Rectangle const expected_bounding_rect{geom::Point{0,0}, geom::Size{800,600}}; mi::DisplayInputRegion input_region; input_region.set_input_rectangles(rects); auto rect = input_region.bounding_rectangle(); EXPECT_EQ(expected_bounding_rect, rect); } TEST(DisplayInputRegionTest, confines_point_to_closest_valid_position) { mi::DisplayInputRegion input_region; input_region.set_input_rectangles(rects); std::vector> point_tuples{ std::make_tuple(geom::Point{0,0}, geom::Point{0,0}), std::make_tuple(geom::Point{900,50}, geom::Point{899,50}), std::make_tuple(geom::Point{850,100}, geom::Point{850,99}), std::make_tuple(geom::Point{801,100}, geom::Point{801,99}), std::make_tuple(geom::Point{800,101}, geom::Point{799,101}), std::make_tuple(geom::Point{800,600}, geom::Point{799,599}), std::make_tuple(geom::Point{-1,700}, geom::Point{0,699}), std::make_tuple(geom::Point{-1,-1}, geom::Point{0,0}), std::make_tuple(geom::Point{-1,50}, geom::Point{0,50}), std::make_tuple(geom::Point{799,-1}, geom::Point{799,0}), std::make_tuple(geom::Point{800,-1}, geom::Point{800,0}) }; for (auto const& t : point_tuples) { geom::Point confined_point{std::get<0>(t)}; geom::Point const expected_point{std::get<1>(t)}; input_region.confine(confined_point); EXPECT_EQ(expected_point, confined_point); } } TEST(DisplayInputRegionTest, returns_empty_bounding_rectangle_when_there_are_no_outputs) { geom::Rectangles const empty_rects{}; geom::Rectangle const empty_rect{}; mi::DisplayInputRegion input_region; input_region.set_input_rectangles(empty_rects); auto const bounding_rect = input_region.bounding_rectangle(); EXPECT_EQ(empty_rect, bounding_rect); } ./tests/unit-tests/input/test_default_input_device_hub.cpp0000644000015600001650000001605512676616157024330 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "src/server/input/default_input_device_hub.h" #include "mir/test/doubles/mock_input_device.h" #include "mir/test/doubles/mock_input_device_observer.h" #include "mir/test/doubles/mock_input_dispatcher.h" #include "mir/test/doubles/mock_input_region.h" #include "mir/test/doubles/mock_input_seat.h" #include "mir/test/doubles/mock_event_sink.h" #include "mir/test/doubles/stub_cursor_listener.h" #include "mir/test/doubles/stub_touch_visualizer.h" #include "mir/test/doubles/triggered_main_loop.h" #include "mir/test/event_matchers.h" #include "mir/test/fake_shared.h" #include "mir/dispatch/action_queue.h" #include "mir/geometry/rectangles.h" #include "mir/dispatch/multiplexing_dispatchable.h" #include "mir/events/event_builders.h" #include "mir/input/cursor_listener.h" #include "mir/cookie/authority.h" #include "mir/graphics/buffer.h" #include "mir/input/device.h" #include "mir/input/input_device.h" #include #include #include namespace mi = mir::input; namespace mt = mir::test; namespace mtd = mt::doubles; using namespace std::literals::chrono_literals; using namespace ::testing; namespace { template T const& const_ref(std::shared_ptr const& ptr) { return *ptr; } template T const& const_ref(T const& value) { return value; } MATCHER_P(WithName, name, std::string(negation?"isn't":"is") + " name:" + std::string(name)) { return const_ref(arg).name() == name; } } struct InputDeviceHubTest : ::testing::Test { mtd::TriggeredMainLoop observer_loop; std::shared_ptr cookie_authority = mir::cookie::Authority::create(); mir::dispatch::MultiplexingDispatchable multiplexer; NiceMock mock_seat; NiceMock mock_sink; mi::DefaultInputDeviceHub hub{mt::fake_shared(mock_sink), mt::fake_shared(mock_seat), mt::fake_shared(multiplexer), mt::fake_shared(observer_loop), cookie_authority}; NiceMock mock_observer; NiceMock device{"device","dev-1", mi::DeviceCapability::unknown}; NiceMock another_device{"another_device","dev-2", mi::DeviceCapability::keyboard}; NiceMock third_device{"third_device","dev-3", mi::DeviceCapability::keyboard}; std::chrono::nanoseconds arbitrary_timestamp; void capture_input_sink(NiceMock& dev, mi::InputSink*& sink, mi::EventBuilder*& builder) { ON_CALL(dev,start(_,_)) .WillByDefault(Invoke([&sink,&builder](mi::InputSink* input_sink, mi::EventBuilder* event_builder) { sink = input_sink; builder = event_builder; } )); } }; TEST_F(InputDeviceHubTest, input_device_hub_starts_device) { EXPECT_CALL(device,start(_,_)); hub.add_device(mt::fake_shared(device)); } TEST_F(InputDeviceHubTest, input_device_hub_stops_device_on_removal) { EXPECT_CALL(device,stop()); hub.add_device(mt::fake_shared(device)); hub.remove_device(mt::fake_shared(device)); } TEST_F(InputDeviceHubTest, input_device_hub_ignores_removal_of_unknown_devices) { EXPECT_CALL(device,start(_,_)).Times(0); EXPECT_CALL(device,stop()).Times(0); EXPECT_THROW(hub.remove_device(mt::fake_shared(device));, std::logic_error); } TEST_F(InputDeviceHubTest, input_device_hub_start_stop_happens_in_order) { InSequence seq; EXPECT_CALL(device, start(_,_)); EXPECT_CALL(another_device, start(_,_)); EXPECT_CALL(third_device, start(_,_)); EXPECT_CALL(another_device, stop()); EXPECT_CALL(device, stop()); EXPECT_CALL(third_device, stop()); hub.add_device(mt::fake_shared(device)); hub.add_device(mt::fake_shared(another_device)); hub.add_device(mt::fake_shared(third_device)); hub.remove_device(mt::fake_shared(another_device)); hub.remove_device(mt::fake_shared(device)); hub.remove_device(mt::fake_shared(third_device)); } TEST_F(InputDeviceHubTest, observers_receive_devices_on_add) { std::shared_ptr handle_1, handle_2; InSequence seq; EXPECT_CALL(mock_observer,device_added(WithName("device"))).WillOnce(SaveArg<0>(&handle_1)); EXPECT_CALL(mock_observer,device_added(WithName("another_device"))).WillOnce(SaveArg<0>(&handle_2)); EXPECT_CALL(mock_observer,changes_complete()); hub.add_device(mt::fake_shared(device)); hub.add_device(mt::fake_shared(another_device)); hub.add_observer(mt::fake_shared(mock_observer)); observer_loop.trigger_server_actions(); EXPECT_THAT(handle_1,Ne(handle_2)); EXPECT_THAT(handle_1->unique_id(),Ne(handle_2->unique_id())); } TEST_F(InputDeviceHubTest, seat_receives_devices) { InSequence seq; EXPECT_CALL(mock_seat, add_device(WithName("device"))); EXPECT_CALL(mock_seat, add_device(WithName("another_device"))); EXPECT_CALL(mock_seat, remove_device(WithName("device"))); hub.add_device(mt::fake_shared(device)); hub.add_device(mt::fake_shared(another_device)); hub.remove_device(mt::fake_shared(device)); } TEST_F(InputDeviceHubTest, throws_on_duplicate_add) { hub.add_device(mt::fake_shared(device)); EXPECT_THROW(hub.add_device(mt::fake_shared(device)), std::logic_error); } TEST_F(InputDeviceHubTest, throws_on_spurious_remove) { hub.add_device(mt::fake_shared(device)); hub.remove_device(mt::fake_shared(device)); EXPECT_THROW(hub.remove_device(mt::fake_shared(device)), std::logic_error); } TEST_F(InputDeviceHubTest, throws_on_invalid_handles) { EXPECT_THROW(hub.add_device(std::shared_ptr()), std::logic_error); EXPECT_THROW(hub.remove_device(std::shared_ptr()), std::logic_error); } TEST_F(InputDeviceHubTest, observers_receive_device_changes) { InSequence seq; EXPECT_CALL(mock_observer, changes_complete()); EXPECT_CALL(mock_observer, device_added(WithName("device"))); EXPECT_CALL(mock_observer, changes_complete()); EXPECT_CALL(mock_observer, device_removed(WithName("device"))); EXPECT_CALL(mock_observer, changes_complete()); hub.add_observer(mt::fake_shared(mock_observer)); hub.add_device(mt::fake_shared(device)); hub.remove_device(mt::fake_shared(device)); observer_loop.trigger_server_actions(); } ./tests/unit-tests/input/test_input_event.cpp0000644000015600001650000003251112676616125021636 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include #include #include "mir/events/event_private.h" #include "mir_toolkit/events/input/input_event.h" using namespace testing; // See: https://bugs.launchpad.net/mir/+bug/1311699 #define MIR_EVENT_ACTION_POINTER_INDEX_MASK 0xff00 #define MIR_EVENT_ACTION_POINTER_INDEX_SHIFT 8; #define MIR_EVENT_ACTION_MASK 0xff // TODO: Refactor event initializers namespace { // Never exposed in old event, so lets avoid leaking it in to a header now. enum { AINPUT_SOURCE_CLASS_MASK = 0x000000ff, AINPUT_SOURCE_CLASS_BUTTON = 0x00000001, AINPUT_SOURCE_CLASS_POINTER = 0x00000002, AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004, AINPUT_SOURCE_CLASS_POSITION = 0x00000008, AINPUT_SOURCE_CLASS_JOYSTICK = 0x00000010 }; enum { AINPUT_SOURCE_UNKNOWN = 0x00000000, AINPUT_SOURCE_KEYBOARD = 0x00000100 | AINPUT_SOURCE_CLASS_BUTTON, AINPUT_SOURCE_DPAD = 0x00000200 | AINPUT_SOURCE_CLASS_BUTTON, AINPUT_SOURCE_GAMEPAD = 0x00000400 | AINPUT_SOURCE_CLASS_BUTTON, AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER, AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER, AINPUT_SOURCE_STYLUS = 0x00004000 | AINPUT_SOURCE_CLASS_POINTER, AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION, AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION, AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK, AINPUT_SOURCE_ANY = 0xffffff00 }; MirEvent a_key_ev() { MirEvent key_ev; memset(&key_ev, 0, sizeof(key_ev)); key_ev.type = mir_event_type_key; return key_ev; } MirEvent a_motion_ev(int device_class = AINPUT_SOURCE_UNKNOWN) { MirEvent motion_ev; memset(&motion_ev, 0, sizeof(motion_ev)); motion_ev.type = mir_event_type_motion; motion_ev.motion.source_id = device_class; return motion_ev; } } TEST(InputEvent, old_style_key_and_motion_events_are_input_events) { auto key_ev = a_key_ev(); EXPECT_EQ(mir_event_type_input, mir_event_get_type(&key_ev)); EXPECT_EQ(mir_input_event_type_key, mir_input_event_get_type(mir_event_get_input_event(&key_ev))); auto motion_ev = a_motion_ev(); EXPECT_EQ(mir_event_type_input, mir_event_get_type(&motion_ev)); EXPECT_NE(nullptr, mir_event_get_input_event(&motion_ev)); } // MirInputEvent properties common to all events. TEST(CommonInputEventProperties, device_id_taken_from_old_style_event) { int64_t dev_id_1 = 17, dev_id_2 = 23; auto old_ev = a_motion_ev(); old_ev.type = mir_event_type_motion; old_ev.motion.device_id = dev_id_1; EXPECT_EQ(dev_id_1, mir_input_event_get_device_id( mir_event_get_input_event(&old_ev))); old_ev.type = mir_event_type_key; old_ev.motion.device_id = dev_id_2; EXPECT_EQ(dev_id_2, mir_input_event_get_device_id( mir_event_get_input_event(&old_ev))); } TEST(CommonInputEventProperties, event_time_taken_from_old_style_event) { std::chrono::nanoseconds event_time_1{79}, event_time_2{83}; auto old_ev = a_motion_ev(); old_ev.motion.event_time = event_time_1; EXPECT_EQ(event_time_1.count(), mir_input_event_get_event_time( mir_event_get_input_event(&old_ev))); old_ev.type = mir_event_type_key; old_ev.key.event_time = event_time_2; EXPECT_EQ(event_time_2.count(), mir_input_event_get_event_time( mir_event_get_input_event(&old_ev))); } TEST(KeyInputEventProperties, timestamp_taken_from_old_style_event) { std::chrono::nanoseconds event_time_1{79}, event_time_2{83}; auto old_ev = a_key_ev(); auto const keyboard_event = mir_input_event_get_keyboard_event(mir_event_get_input_event(&old_ev)); for (auto expected : {event_time_1, event_time_2}) { old_ev.key.event_time = expected; auto const input_event = mir_keyboard_event_input_event(keyboard_event); EXPECT_THAT(mir_input_event_get_event_time(input_event), Eq(expected.count())); } } TEST(KeyInputEventProperties, up_and_down_actions_copied_from_old_style_event) { auto old_ev = a_key_ev(); old_ev.key.action = mir_keyboard_action_down; auto new_kev = mir_input_event_get_keyboard_event(mir_event_get_input_event(&old_ev)); EXPECT_EQ(mir_keyboard_action_down, mir_keyboard_event_action(new_kev)); old_ev.key.action = mir_keyboard_action_up; EXPECT_EQ(mir_keyboard_action_up, mir_keyboard_event_action(new_kev)); } TEST(KeyInputEventProperties, keycode_scancode_and_modifiers_taken_from_old_style_event) { xkb_keysym_t key_code = 171; int scan_code = 31; MirInputEventModifiers modifiers = mir_input_event_modifier_shift; auto old_ev = a_key_ev(); old_ev.key.key_code = key_code; old_ev.key.scan_code = scan_code; old_ev.key.modifiers = modifiers; auto new_kev = mir_input_event_get_keyboard_event(mir_event_get_input_event(&old_ev)); EXPECT_EQ(key_code, mir_keyboard_event_key_code(new_kev)); EXPECT_EQ(scan_code, mir_keyboard_event_scan_code(new_kev)); EXPECT_EQ(modifiers, mir_keyboard_event_modifiers(new_kev)); } TEST(TouchEventProperties, timestamp_taken_from_old_style_event) { std::chrono::nanoseconds event_time_1{79}, event_time_2{83}; auto old_ev = a_motion_ev(AINPUT_SOURCE_TOUCHSCREEN); auto const touch_event = mir_input_event_get_touch_event(mir_event_get_input_event(&old_ev)); for (auto expected : {event_time_1, event_time_2}) { old_ev.motion.event_time = expected; auto const input_event = mir_touch_event_input_event(touch_event); EXPECT_THAT(mir_input_event_get_event_time(input_event), Eq(expected.count())); } } TEST(TouchEventProperties, touch_count_taken_from_pointer_count) { unsigned const pointer_count = 3; auto old_ev = a_motion_ev(AINPUT_SOURCE_TOUCHSCREEN); old_ev.motion.pointer_count = pointer_count; auto tev = mir_input_event_get_touch_event(mir_event_get_input_event(&old_ev)); EXPECT_EQ(pointer_count, mir_touch_event_point_count(tev)); } TEST(TouchEventProperties, touch_id_comes_from_pointer_coordinates) { unsigned const touch_id = 31; auto old_ev = a_motion_ev(AINPUT_SOURCE_TOUCHSCREEN); old_ev.motion.pointer_count = 1; old_ev.motion.pointer_coordinates[0].id = touch_id; auto tev = mir_input_event_get_touch_event(mir_event_get_input_event(&old_ev)); EXPECT_EQ(touch_id, mir_touch_event_id(tev, 0)); } // mir_motion_action_up/down represent the start of a gesture. pointers only go up/down one at a time TEST(TouchEventProperties, down_and_up_actions_are_taken_from_old_event) { auto old_ev = a_motion_ev(AINPUT_SOURCE_TOUCHSCREEN); old_ev.motion.pointer_count = 1; old_ev.motion.pointer_coordinates[0].action = mir_touch_action_change; auto tev = mir_input_event_get_touch_event(mir_event_get_input_event(&old_ev)); EXPECT_EQ(mir_touch_action_change, mir_touch_event_action(tev, 0)); } TEST(TouchEventProperties, tool_type_copied_from_old_pc) { auto old_ev = a_motion_ev(AINPUT_SOURCE_TOUCHSCREEN); auto& old_mev = old_ev.motion; old_mev.pointer_count = 3; old_mev.pointer_coordinates[0].tool_type = mir_touch_tooltype_unknown; old_mev.pointer_coordinates[1].tool_type = mir_touch_tooltype_finger; old_mev.pointer_coordinates[2].tool_type = mir_touch_tooltype_stylus; auto tev = mir_input_event_get_touch_event(mir_event_get_input_event(&old_ev)); EXPECT_EQ(mir_touch_tooltype_unknown, mir_touch_event_tooltype(tev, 0)); EXPECT_EQ(mir_touch_tooltype_finger, mir_touch_event_tooltype(tev, 1)); EXPECT_EQ(mir_touch_tooltype_stylus, mir_touch_event_tooltype(tev, 2)); } TEST(TouchEventProperties, axis_values_used_by_qtmir_copied) { float x_value = 19, y_value = 23, touch_major = .3, touch_minor = .2, pressure = .9, size = 1111; auto old_ev = a_motion_ev(AINPUT_SOURCE_TOUCHSCREEN); old_ev.motion.pointer_count = 1; auto &old_pc = old_ev.motion.pointer_coordinates[0]; old_pc.x = x_value; old_pc.y = y_value; old_pc.touch_major = touch_major; old_pc.touch_minor = touch_minor; old_pc.pressure = pressure; old_pc.size = size; auto tev = mir_input_event_get_touch_event(mir_event_get_input_event(&old_ev)); EXPECT_EQ(x_value, mir_touch_event_axis_value(tev, 0, mir_touch_axis_x)); EXPECT_EQ(y_value, mir_touch_event_axis_value(tev, 0, mir_touch_axis_y)); EXPECT_EQ(touch_major, mir_touch_event_axis_value(tev, 0, mir_touch_axis_touch_major)); EXPECT_EQ(touch_minor, mir_touch_event_axis_value(tev, 0, mir_touch_axis_touch_minor)); EXPECT_EQ(pressure, mir_touch_event_axis_value(tev, 0, mir_touch_axis_pressure)); EXPECT_EQ(size, mir_touch_event_axis_value(tev, 0, mir_touch_axis_size)); } /* Pointer and touch event differentiation */ namespace { struct DeviceClassTestParameters { MirInputEventType expected_type; int32_t device_class; }; struct DeviceClassTest : public testing::Test, testing::WithParamInterface { }; } TEST_P(DeviceClassTest, pointer_and_touch_events_differentiated_on_device_class) { auto const& param = GetParam(); auto old_ev = a_motion_ev(param.device_class); auto iev = mir_event_get_input_event(&old_ev); EXPECT_EQ(param.expected_type, mir_input_event_get_type(iev)); } INSTANTIATE_TEST_CASE_P(MouseDeviceClassTest, DeviceClassTest, ::testing::Values(DeviceClassTestParameters{mir_input_event_type_pointer, AINPUT_SOURCE_MOUSE})); INSTANTIATE_TEST_CASE_P(TouchpadDeviceClassTest, DeviceClassTest, ::testing::Values(DeviceClassTestParameters{mir_input_event_type_pointer, AINPUT_SOURCE_TOUCHPAD})); INSTANTIATE_TEST_CASE_P(TrackballDeviceClassTest, DeviceClassTest, ::testing::Values(DeviceClassTestParameters{mir_input_event_type_pointer, AINPUT_SOURCE_TRACKBALL})); INSTANTIATE_TEST_CASE_P(TouchscreenDeviceClassTest, DeviceClassTest, ::testing::Values(DeviceClassTestParameters{mir_input_event_type_touch, AINPUT_SOURCE_TOUCHSCREEN})); INSTANTIATE_TEST_CASE_P(StylusDeviceClassTest, DeviceClassTest, ::testing::Values(DeviceClassTestParameters{mir_input_event_type_touch, AINPUT_SOURCE_STYLUS})); /* Pointer event property accessors */ TEST(PointerInputEventProperties, timestamp_taken_from_old_style_event) { std::chrono::nanoseconds event_time_1{79}, event_time_2{83}; auto old_ev = a_motion_ev(AINPUT_SOURCE_MOUSE); auto const pointer_event = mir_input_event_get_pointer_event(mir_event_get_input_event(&old_ev)); for (auto expected : {event_time_1, event_time_2}) { old_ev.motion.event_time = expected; auto const input_event = mir_pointer_event_input_event(pointer_event); EXPECT_THAT(mir_input_event_get_event_time(input_event), Eq(expected.count())); } } TEST(PointerInputEventProperties, modifiers_taken_from_old_style_ev) { MirInputEventModifiers modifiers = mir_input_event_modifier_shift; auto old_ev = a_motion_ev(AINPUT_SOURCE_MOUSE); old_ev.motion.modifiers = modifiers; auto pointer_event = mir_input_event_get_pointer_event(mir_event_get_input_event(&old_ev)); EXPECT_EQ(modifiers, mir_pointer_event_modifiers(pointer_event)); } TEST(PointerInputEventProperties, button_state_translated) { auto old_ev = a_motion_ev(AINPUT_SOURCE_MOUSE); old_ev.motion.buttons = mir_pointer_button_primary; auto pev = mir_input_event_get_pointer_event(mir_event_get_input_event(&old_ev)); EXPECT_TRUE(mir_pointer_event_button_state(pev, mir_pointer_button_primary)); EXPECT_FALSE(mir_pointer_event_button_state(pev, mir_pointer_button_secondary)); old_ev.motion.buttons |= mir_pointer_button_secondary; EXPECT_TRUE(mir_pointer_event_button_state(pev, mir_pointer_button_primary)); EXPECT_TRUE(mir_pointer_event_button_state(pev, mir_pointer_button_secondary)); EXPECT_FALSE(mir_pointer_event_button_state(pev, mir_pointer_button_tertiary)); EXPECT_FALSE(mir_pointer_event_button_state(pev, mir_pointer_button_back)); EXPECT_FALSE(mir_pointer_event_button_state(pev, mir_pointer_button_forward)); } TEST(PointerInputEventProperties, axis_values_copied) { float x = 7, y = 9.3, hscroll = 13, vscroll = 17; auto old_ev = a_motion_ev(AINPUT_SOURCE_MOUSE); old_ev.motion.pointer_count = 0; old_ev.motion.pointer_coordinates[0].x = x; old_ev.motion.pointer_coordinates[0].y = y; old_ev.motion.pointer_coordinates[0].vscroll = vscroll; old_ev.motion.pointer_coordinates[0].hscroll = hscroll; auto pev = mir_input_event_get_pointer_event(mir_event_get_input_event(&old_ev)); EXPECT_EQ(x, mir_pointer_event_axis_value(pev, mir_pointer_axis_x)); EXPECT_EQ(y, mir_pointer_event_axis_value(pev, mir_pointer_axis_y)); EXPECT_EQ(vscroll, mir_pointer_event_axis_value(pev, mir_pointer_axis_vscroll)); EXPECT_EQ(hscroll, mir_pointer_event_axis_value(pev, mir_pointer_axis_hscroll)); } ./tests/unit-tests/input/test_x11_module.cpp0000644000015600001650000000506512676616125021260 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/shared_library.h" #include "mir/input/platform.h" #include "mir/test/doubles/mock_x11.h" #include "mir/test/doubles/mock_option.h" #include "mir_test_framework/executable_path.h" #include #include namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace mo = mir::options; using namespace ::testing; namespace { auto get_x11_platform() { auto path = mtf::server_platform("server-mesa-x11"); return std::make_shared(path); } char const probe_input_platform_symbol[] = "probe_input_platform"; char const host_socket_opt[] = "host-socket"; struct X11Platform : Test { NiceMock options; NiceMock mock_x11; std::shared_ptr library{get_x11_platform()}; }; } TEST_F(X11Platform, probes_as_unsupported_without_display) { ON_CALL(mock_x11, XOpenDisplay(_)) .WillByDefault(Return(nullptr)); auto probe_fun = library->load_function(probe_input_platform_symbol); EXPECT_THAT(probe_fun(options), Eq(mir::input::PlatformPriority::unsupported)); } TEST_F(X11Platform, probes_as_supported_with_display) { // default setup of MockX11 already provides fake objects auto probe_fun = library->load_function(probe_input_platform_symbol); EXPECT_THAT(probe_fun(options), Ge(mir::input::PlatformPriority::supported)); } TEST_F(X11Platform, probes_as_unsupported_on_nested_configs) { ON_CALL(options,is_set(StrEq(host_socket_opt))) .WillByDefault(Return(true)); ON_CALL(options,get(StrEq(host_socket_opt), Matcher(_))) .WillByDefault(Return("something")); auto probe_fun = library->load_function(probe_input_platform_symbol); EXPECT_THAT(probe_fun(options), Eq(mir::input::PlatformPriority::unsupported)); } ./tests/unit-tests/input/test_default_device.cpp0000644000015600001650000001246012676616157022247 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "src/server/input/default_device.h" #include "mir/input/input_device.h" #include "mir/input/touchpad_configuration.h" #include "mir/input/pointer_configuration.h" #include "mir/dispatch/action_queue.h" #include #include #include namespace mi = mir::input; namespace md = mir::dispatch; using namespace ::testing; namespace { struct MockInputDevice : public mi::InputDevice { MOCK_METHOD2(start, void(mi::InputSink* destination, mi::EventBuilder* builder)); MOCK_METHOD0(stop, void()); MOCK_METHOD0(get_device_info, mi::InputDeviceInfo()); MOCK_CONST_METHOD0(get_pointer_settings, mir::optional_value()); MOCK_METHOD1(apply_settings, void(mi::PointerSettings const&)); MOCK_CONST_METHOD0(get_touchpad_settings, mir::optional_value()); MOCK_METHOD1(apply_settings, void(mi::TouchpadSettings const&)); }; struct DefaultDevice : Test { NiceMock touchpad; NiceMock mouse; NiceMock keyboard; std::shared_ptr queue{std::make_shared()}; DefaultDevice() { using optional_pointer_settings = mir::optional_value; using optional_touchpad_settings = mir::optional_value; ON_CALL(touchpad, get_device_info()) .WillByDefault(Return(mi::InputDeviceInfo{"name", "unique", mi::DeviceCapability::touchpad|mi::DeviceCapability::pointer})); ON_CALL(touchpad, get_pointer_settings()) .WillByDefault(Return(optional_pointer_settings{mi::PointerSettings{}})); ON_CALL(touchpad, get_touchpad_settings()) .WillByDefault(Return(optional_touchpad_settings{mi::TouchpadSettings{}})); ON_CALL(mouse, get_device_info()) .WillByDefault(Return(mi::InputDeviceInfo{"name", "unique", mi::DeviceCapability::pointer})); ON_CALL(mouse, get_pointer_settings()).WillByDefault(Return(optional_pointer_settings{mi::PointerSettings{}})); ON_CALL(mouse, get_touchpad_settings()).WillByDefault(Return(optional_touchpad_settings{})); ON_CALL(keyboard, get_device_info()) .WillByDefault(Return(mi::InputDeviceInfo{"name", "unique", mi::DeviceCapability::keyboard})); ON_CALL(keyboard, get_pointer_settings()).WillByDefault(Return(optional_pointer_settings{})); ON_CALL(keyboard, get_touchpad_settings()).WillByDefault(Return(optional_touchpad_settings{})); } }; } TEST_F(DefaultDevice, refuses_touchpad_config_on_mice) { mi::DefaultDevice dev(MirInputDeviceId{17}, queue, mouse); mi::TouchpadConfiguration touch_conf; EXPECT_THROW({dev.apply_touchpad_configuration(touch_conf);}, std::invalid_argument); } TEST_F(DefaultDevice, refuses_touchpad_and_pointer_settings_on_keyboards) { mi::DefaultDevice dev(MirInputDeviceId{17}, queue, keyboard); mi::TouchpadConfiguration touch_conf; mi::PointerConfiguration pointer_conf; EXPECT_THROW({dev.apply_touchpad_configuration(touch_conf);}, std::invalid_argument); EXPECT_THROW({dev.apply_pointer_configuration(pointer_conf);}, std::invalid_argument); } TEST_F(DefaultDevice, accepts_pointer_config_on_mice) { mi::DefaultDevice dev(MirInputDeviceId{17}, queue, mouse); mi::PointerConfiguration pointer_conf; EXPECT_CALL(mouse, apply_settings(Matcher(_))); dev.apply_pointer_configuration(pointer_conf); queue->dispatch(md::FdEvent::readable); } TEST_F(DefaultDevice, accepts_touchpad_and_pointer_config_on_touchpads) { mi::DefaultDevice dev(MirInputDeviceId{17}, queue, touchpad); mi::TouchpadConfiguration touch_conf; mi::PointerConfiguration pointer_conf; EXPECT_CALL(touchpad, apply_settings(Matcher(_))); EXPECT_CALL(touchpad, apply_settings(Matcher(_))); dev.apply_touchpad_configuration(touch_conf); dev.apply_pointer_configuration(pointer_conf); queue->dispatch(md::FdEvent::readable); queue->dispatch(md::FdEvent::readable); } TEST_F(DefaultDevice, ensures_cursor_accleration_bias_is_in_range) { mi::DefaultDevice dev(MirInputDeviceId{17}, queue, touchpad); mi::PointerConfiguration pointer_conf; pointer_conf.cursor_acceleration_bias = 3.0; EXPECT_THROW({dev.apply_pointer_configuration(pointer_conf);}, std::invalid_argument); pointer_conf.cursor_acceleration_bias = -2.0; EXPECT_THROW({dev.apply_pointer_configuration(pointer_conf);}, std::invalid_argument); pointer_conf.cursor_acceleration_bias = 1.0; EXPECT_NO_THROW(dev.apply_pointer_configuration(pointer_conf)); } ./tests/unit-tests/input/test_x11_platform.cpp0000644000015600001650000001700012676616157021614 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Cemil Azizoglu */ #include #include #include "src/server/input/default_event_builder.h" #include "src/platforms/mesa/server/x11/input/input_platform.h" #include "src/platforms/mesa/server/x11/input/input_device.h" #include "mir/events/event_private.h" #include "mir/dispatch/dispatchable.h" #include "mir_toolkit/event.h" #include "mir_toolkit/events/input/input_event.h" #include "mir/test/doubles/mock_input_sink.h" #include "mir/test/doubles/mock_input_device_registry.h" #include "mir/test/doubles/mock_x11.h" #include "mir/test/fake_shared.h" #include "mir/cookie/authority.h" #include "mir/test/event_matchers.h" namespace md = mir::dispatch; namespace mi = mir::input; namespace mt = mir::test; namespace mtd = mt::doubles; using namespace ::testing; namespace { struct X11PlatformTest : ::testing::Test { NiceMock mock_pointer_sink; NiceMock mock_keyboard_sink; NiceMock mock_x11; NiceMock mock_registry; mir::input::DefaultEventBuilder builder{0, mir::cookie::Authority::create()}; mir::input::X::XInputPlatform x11_platform{ mt::fake_shared(mock_registry), std::shared_ptr<::Display>( XOpenDisplay(nullptr), [](::Display* display) { XCloseDisplay(display); })}; std::vector> devices; void capture_devices() { ON_CALL(mock_registry, add_device(_)) .WillByDefault(Invoke([this](auto const& dev) { devices.push_back(dev); auto const info = dev->get_device_info(); if (contains(info.capabilities, mi::DeviceCapability::pointer)) dev->start(&mock_pointer_sink, &builder); else if (contains(info.capabilities, mi::DeviceCapability::keyboard)) dev->start(&mock_keyboard_sink, &builder); }) ); } void prepare_event_processing() { capture_devices(); x11_platform.start(); } void process_input_event() { x11_platform.dispatchable()->dispatch(md::FdEvent::readable); } }; } MATCHER(ButtonUpEventWithNoButtonsPressed, "") { auto pev = mt::maybe_pointer_event(mt::to_address(arg)); return mt::button_event_matches(pev, 0, 0, mir_pointer_action_button_up, 0, true, true, false); } TEST_F(X11PlatformTest, registers_two_devices) { EXPECT_CALL(mock_registry, add_device(_)).Times(2); x11_platform.start(); } TEST_F(X11PlatformTest, unregisters_devices_on_stop) { EXPECT_CALL(mock_registry, add_device(_)).Times(2); EXPECT_CALL(mock_registry, remove_device(_)).Times(2); x11_platform.start(); x11_platform.stop(); } TEST_F(X11PlatformTest, registered_devices_mimic_mouse_and_keyboard) { capture_devices(); x11_platform.start(); ASSERT_THAT(devices.size(), Eq(2)); auto const& first_info = devices[0]->get_device_info(); auto const& second_info = devices[1]->get_device_info(); EXPECT_FALSE(first_info.capabilities == second_info.capabilities); EXPECT_TRUE(contains(first_info.capabilities | second_info.capabilities, mi::DeviceCapability::pointer)); EXPECT_TRUE(contains(first_info.capabilities | second_info.capabilities, mi::DeviceCapability::keyboard)); } TEST_F(X11PlatformTest, dispatches_input_events_to_sink) { prepare_event_processing(); ON_CALL(mock_x11, XNextEvent(_,_)) .WillByDefault(DoAll(SetArgPointee<1>(mock_x11.fake_x11.keypress_event_return), Return(1))); EXPECT_CALL(mock_keyboard_sink, handle_input(_)) .Times(Exactly(1)); process_input_event(); } TEST_F(X11PlatformTest, button_release_has_no_buttons_pressed) { prepare_event_processing(); ON_CALL(mock_x11, XNextEvent(_,_)) .WillByDefault(DoAll(SetArgPointee<1>(mock_x11.fake_x11.button_release_event_return), Return(1))); EXPECT_CALL(mock_pointer_sink, handle_input(ButtonUpEventWithNoButtonsPressed())) .Times(Exactly(1)); process_input_event(); } TEST_F(X11PlatformTest, button4_button5_converted_to_vscroll) { prepare_event_processing(); ON_CALL(mock_x11, XNextEvent(_,_)) .WillByDefault(DoAll(SetArgPointee<1>(mock_x11.fake_x11.vscroll_event_return), Return(1))); EXPECT_CALL(mock_pointer_sink, handle_input(mt::PointerMovementEvent())) .Times(Exactly(1)); process_input_event(); } TEST_F(X11PlatformTest, motion_event_trigger_pointer_movement) { mock_x11.fake_x11.pending_events = 2; prepare_event_processing(); InSequence seq; mock_x11.fake_x11.motion_event_return.xmotion.x = 20; mock_x11.fake_x11.motion_event_return.xmotion.y = 20; EXPECT_CALL(mock_x11, XNextEvent(_,_)) .WillOnce(DoAll(SetArgPointee<1>(mock_x11.fake_x11.motion_event_return), Return(1))); EXPECT_CALL(mock_pointer_sink, handle_input(mt::PointerEventWithDiff(20, 20))); mock_x11.fake_x11.motion_event_return.xmotion.x = 40; mock_x11.fake_x11.motion_event_return.xmotion.y = 25; EXPECT_CALL(mock_x11, XNextEvent(_,_)) .WillOnce(DoAll(SetArgPointee<1>(mock_x11.fake_x11.motion_event_return), Return(1))); EXPECT_CALL(mock_pointer_sink, handle_input(mt::PointerEventWithDiff(20, 5))); process_input_event(); process_input_event(); } TEST_F(X11PlatformTest, grabs_keyboard) { prepare_event_processing(); ON_CALL(mock_x11, XNextEvent(_,_)) .WillByDefault(DoAll(SetArgPointee<1>(mock_x11.fake_x11.focus_in_event_return), Return(1))); EXPECT_CALL(mock_x11, XGrabKeyboard(_,_,_,_,_,_)) .Times(Exactly(1)); EXPECT_CALL(mock_pointer_sink, handle_input(_)) .Times(Exactly(0)); EXPECT_CALL(mock_keyboard_sink, handle_input(_)) .Times(Exactly(0)); process_input_event(); } TEST_F(X11PlatformTest, ungrabs_keyboard) { prepare_event_processing(); ON_CALL(mock_x11, XNextEvent(_,_)) .WillByDefault(DoAll(SetArgPointee<1>(mock_x11.fake_x11.focus_out_event_return), Return(1))); EXPECT_CALL(mock_x11, XUngrabKeyboard(_,_)) .Times(Exactly(1)); EXPECT_CALL(mock_pointer_sink, handle_input(_)) .Times(Exactly(0)); EXPECT_CALL(mock_keyboard_sink, handle_input(_)) .Times(Exactly(0)); process_input_event(); } TEST_F(X11PlatformTest, does_not_block_on_events) { prepare_event_processing(); ON_CALL(mock_x11, XPending(_)) .WillByDefault(Return(0)); EXPECT_CALL(mock_x11, XNextEvent(_,_)) .Times(Exactly(0)); process_input_event(); } ./tests/unit-tests/input/test_seat_input_device_tracker.cpp0000644000015600001650000003175312676616125024512 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "src/server/input/seat_input_device_tracker.h" #include "src/server/input/default_event_builder.h" #include "mir/test/doubles/mock_input_device.h" #include "mir/test/doubles/mock_input_dispatcher.h" #include "mir/test/doubles/mock_input_region.h" #include "mir/test/doubles/mock_cursor_listener.h" #include "mir/test/doubles/mock_touch_visualizer.h" #include "mir/test/event_matchers.h" #include "mir/test/fake_shared.h" #include "mir/geometry/rectangles.h" #include "mir/cookie/authority.h" #include #include #include namespace mt = mir::test; namespace mtd = mt::doubles; namespace mi = mir::input; namespace { template using Nice = ::testing::NiceMock; using namespace ::testing; struct SeatInputDeviceTracker : ::testing::Test { mi::EventBuilder* builder; Nice mock_dispatcher; Nice mock_region; Nice mock_cursor_listener; Nice mock_visualizer; MirInputDeviceId some_device{8712}; MirInputDeviceId another_device{1246}; MirInputDeviceId third_device{86}; std::shared_ptr cookie_factory = mir::cookie::Authority::create(); mi::DefaultEventBuilder some_device_builder{some_device, cookie_factory}; mi::DefaultEventBuilder another_device_builder{another_device, cookie_factory}; mi::DefaultEventBuilder third_device_builder{third_device, cookie_factory}; mi::SeatInputDeviceTracker tracker{mt::fake_shared(mock_dispatcher), mt::fake_shared(mock_visualizer), mt::fake_shared(mock_cursor_listener), mt::fake_shared(mock_region)}; std::chrono::nanoseconds arbitrary_timestamp; }; } TEST_F(SeatInputDeviceTracker, throws_on_unknown_device) { EXPECT_CALL(mock_dispatcher, dispatch(_)).Times(0); EXPECT_THROW( { tracker.dispatch(*some_device_builder.touch_event(arbitrary_timestamp)); }, std::logic_error); } TEST_F(SeatInputDeviceTracker, dispatch_posts_to_input_dispatcher) { EXPECT_CALL(mock_dispatcher, dispatch(_)); tracker.add_device(some_device); tracker.dispatch(*some_device_builder.touch_event(arbitrary_timestamp)); } TEST_F(SeatInputDeviceTracker, forwards_touch_spots_to_visualizer) { using Spot = mi::TouchVisualizer::Spot; InSequence seq; EXPECT_CALL(mock_visualizer, visualize_touches(ElementsAreArray({Spot{{4, 2}, 10}}))); EXPECT_CALL(mock_visualizer, visualize_touches(ElementsAreArray({Spot{{10, 10}, 30}, Spot{{100, 34}, 0}}))); EXPECT_CALL(mock_visualizer, visualize_touches(ElementsAreArray({Spot{{70, 10}, 30}}))); EXPECT_CALL(mock_visualizer, visualize_touches(ElementsAreArray(std::vector()))); tracker.add_device(some_device); auto touch_event_1 = some_device_builder.touch_event(arbitrary_timestamp); some_device_builder.add_touch(*touch_event_1, 0, mir_touch_action_down, mir_touch_tooltype_finger, 4.0f, 2.0f, 10.0f, 15.0f, 5.0f, 4.0f); auto touch_event_2 = some_device_builder.touch_event(arbitrary_timestamp); some_device_builder.add_touch(*touch_event_2, 0, mir_touch_action_change, mir_touch_tooltype_finger, 10.0f, 10.0f, 30.0f, 15.0f, 5.0f, 4.0f); some_device_builder.add_touch(*touch_event_2, 1, mir_touch_action_down, mir_touch_tooltype_finger, 100.0f, 34.0f, 0.0f, 15.0f, 5.0f, 4.0f); auto touch_event_3 = some_device_builder.touch_event(arbitrary_timestamp); some_device_builder.add_touch(*touch_event_3, 0, mir_touch_action_up, mir_touch_tooltype_finger, 100.0f, 34.0f, 0.0f, 15.0f, 5.0f, 4.0f); some_device_builder.add_touch(*touch_event_3, 1, mir_touch_action_change, mir_touch_tooltype_finger, 70.0f, 10.0f, 30.0f, 15.0f, 5.0f, 4.0f); auto touch_event_4 = some_device_builder.touch_event(arbitrary_timestamp); some_device_builder.add_touch(*touch_event_4, 1, mir_touch_action_up, mir_touch_tooltype_finger, 70.0f, 10.0f, 30.0f, 15.0f, 5.0f, 4.0f); tracker.dispatch(*touch_event_1); tracker.dispatch(*touch_event_2); tracker.dispatch(*touch_event_3); tracker.dispatch(*touch_event_4); } TEST_F(SeatInputDeviceTracker, removal_of_touch_device_removes_spots) { using Spot = mi::TouchVisualizer::Spot; InSequence seq; EXPECT_CALL(mock_visualizer, visualize_touches(ElementsAreArray({Spot{{4, 2}, 10}}))); EXPECT_CALL(mock_visualizer, visualize_touches(ElementsAreArray(std::vector()))); tracker.add_device(some_device); auto touch_event_1 = some_device_builder.touch_event(arbitrary_timestamp); some_device_builder.add_touch(*touch_event_1, 0, mir_touch_action_down, mir_touch_tooltype_finger, 4.0f, 2.0f, 10.0f, 15.0f, 5.0f, 4.0f); tracker.dispatch(*touch_event_1); tracker.remove_device(some_device); } TEST_F(SeatInputDeviceTracker, pointer_movement_updates_cursor) { auto const move_x = 20.0f, move_y = 40.0f; EXPECT_CALL(mock_cursor_listener, cursor_moved_to(move_x, move_y)).Times(1); tracker.add_device(some_device); tracker.dispatch(*some_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f, move_x, move_y)); } TEST_F(SeatInputDeviceTracker, key_strokes_of_modifier_key_update_modifier) { const MirInputEventModifiers shift_left = mir_input_event_modifier_shift_left | mir_input_event_modifier_shift; InSequence seq; EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(shift_left))); EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(mir_input_event_modifier_none))); tracker.add_device(some_device); tracker.dispatch(*some_device_builder.key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_LEFTSHIFT)); tracker.dispatch(*some_device_builder.key_event(arbitrary_timestamp, mir_keyboard_action_up, 0, KEY_LEFTSHIFT)); } TEST_F(SeatInputDeviceTracker, modifier_events_on_different_keyboards_do_not_change_modifier_state) { const MirInputEventModifiers shift_left = mir_input_event_modifier_shift_left | mir_input_event_modifier_shift; InSequence seq; EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(shift_left))); EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(mir_input_event_modifier_none))); tracker.add_device(some_device); tracker.add_device(another_device); tracker.dispatch(*some_device_builder.key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_LEFTSHIFT)); tracker.dispatch(*another_device_builder.key_event(arbitrary_timestamp, mir_keyboard_action_up, 0, KEY_A)); } TEST_F(SeatInputDeviceTracker, modifier_events_on_different_keyboards_contribute_to_pointer_event_modifier_state) { const MirInputEventModifiers r_alt_modifier = mir_input_event_modifier_alt_right | mir_input_event_modifier_alt; const MirInputEventModifiers shift_right = mir_input_event_modifier_shift_right | mir_input_event_modifier_shift; InSequence seq; EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(r_alt_modifier))); EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(shift_right))); EXPECT_CALL(mock_dispatcher, dispatch(mt::PointerEventWithModifiers(shift_right | r_alt_modifier))); tracker.add_device(some_device); tracker.add_device(another_device); tracker.add_device(third_device); tracker.dispatch(*some_device_builder.key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_RIGHTALT)); tracker.dispatch( *another_device_builder.key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_RIGHTSHIFT)); tracker.dispatch( *third_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 12, 40)); } TEST_F(SeatInputDeviceTracker, device_removal_removes_modifier_flags) { const MirInputEventModifiers r_alt_modifier = mir_input_event_modifier_alt_right | mir_input_event_modifier_alt; const MirInputEventModifiers shift_right = mir_input_event_modifier_shift_right | mir_input_event_modifier_shift; InSequence seq; EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(r_alt_modifier))); EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(shift_right))); EXPECT_CALL(mock_dispatcher, dispatch(mt::PointerEventWithModifiers(r_alt_modifier))); tracker.add_device(some_device); tracker.add_device(another_device); tracker.add_device(third_device); tracker.dispatch(*some_device_builder.key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_RIGHTALT)); tracker.dispatch( *another_device_builder.key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_RIGHTSHIFT)); tracker.remove_device(another_device); tracker.dispatch( *third_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 12, 40)); } TEST_F(SeatInputDeviceTracker, pointer_movement_from_different_devices_change_cursor_position) { InSequence seq; EXPECT_CALL(mock_cursor_listener, cursor_moved_to(23, 20)); EXPECT_CALL(mock_cursor_listener, cursor_moved_to(41, 10)); EXPECT_CALL(mock_cursor_listener, cursor_moved_to(43, 20)); tracker.add_device(some_device); tracker.add_device(another_device); tracker.dispatch( *some_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 23, 20)); tracker.dispatch( *another_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 18, -10)); tracker.dispatch( *another_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 2, 10)); } TEST_F(SeatInputDeviceTracker, tracks_a_single_button_state_for_multiple_pointing_devices) { int const x = 0, y = 0; MirPointerButtons no_buttons = 0; InSequence seq; EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsDown(x, y, mir_pointer_button_primary))); EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsDown(x, y, mir_pointer_button_primary | mir_pointer_button_secondary))); EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsUp(x, y, mir_pointer_button_secondary))); EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsUp(x, y, no_buttons))); tracker.add_device(some_device); tracker.add_device(another_device); tracker.dispatch(*some_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_button_down, mir_pointer_button_primary, 0, 0, 0, 0)); tracker.dispatch(*another_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_button_down, mir_pointer_button_secondary, 0, 0, 0, 0)); tracker.dispatch( *some_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_button_up, no_buttons, 0, 0, 0, 0)); tracker.dispatch(*another_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_button_up, no_buttons, 0, 0, 0, 0)); } TEST_F(SeatInputDeviceTracker, pointing_device_removal_removes_pressed_button_state) { int const x = 0, y = 0; InSequence seq; EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsDown(x, y, mir_pointer_button_primary))); EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsDown(x, y, mir_pointer_button_primary | mir_pointer_button_secondary))); EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsDown(x, y, mir_pointer_button_secondary))); tracker.add_device(some_device); tracker.add_device(another_device); tracker.dispatch(*some_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_button_down, mir_pointer_button_primary, 0, 0, 0, 0)); tracker.dispatch(*another_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_button_down, mir_pointer_button_secondary, 0, 0, 0, 0)); tracker.remove_device(some_device); tracker.dispatch(*another_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, mir_pointer_button_secondary, 0, 0, 0, 0)); } ./tests/unit-tests/input/CMakeLists.txt0000644000015600001650000000277312676616157020307 0ustar jenkinsjenkinsadd_subdirectory(android) add_subdirectory(evdev) list(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_event_filter_chain_dispatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_input_region.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_cursor_controller.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_xcursor_loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_touchspot_controller.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_input_event.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_event_builders.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_default_device.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_default_input_device_hub.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_default_input_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_input_dispatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_seat_input_device_tracker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_key_repeat_dispatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_validator.cpp ) list(APPEND UMOCK_UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_input_platform_probing.cpp $ $ ) if (MIR_BUILD_PLATFORM_MESA_X11) list(APPEND UMOCK_UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_x11_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_x11_module.cpp $ $ ) endif() set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) set(UMOCK_UNIT_TEST_SOURCES ${UMOCK_UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/input/test_key_repeat_dispatcher.cpp0000644000015600001650000002113512676616157023641 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/server/input/key_repeat_dispatcher.h" #include "mir/events/event_private.h" #include "mir/events/event_builders.h" #include "mir/time/alarm.h" #include "mir/time/alarm_factory.h" #include "mir/cookie/authority.h" #include "mir/input/input_device_observer.h" #include "mir/input/pointer_configuration.h" #include "mir/input/touchpad_configuration.h" #include "mir/input/device.h" #include "mir/test/fake_shared.h" #include "mir/test/event_matchers.h" #include "mir/test/doubles/mock_input_dispatcher.h" #include "mir/test/doubles/mock_input_device_hub.h" #include "linux/input.h" #include #include namespace mi = mir::input; namespace mev = mir::events; namespace mt = mir::test; namespace mtd = mt::doubles; using namespace ::testing; namespace { struct MockAlarm : public mir::time::Alarm { MOCK_METHOD0(cancel, bool()); MOCK_CONST_METHOD0(state, mir::time::Alarm::State()); MOCK_METHOD1(reschedule_in, bool(std::chrono::milliseconds)); MOCK_METHOD1(reschedule_for, bool(mir::time::Timestamp)); // destructor cancels the alarm ~MockAlarm() { cancel(); } }; struct MockAlarmFactory : public mir::time::AlarmFactory { MOCK_METHOD1(create_alarm_adapter, mir::time::Alarm*(std::function const&)); std::unique_ptr create_alarm(std::function const& cb) { return std::unique_ptr(create_alarm_adapter(cb)); } std::unique_ptr create_alarm(std::shared_ptr const&) { return nullptr; } }; struct StubDevice : public mi::Device { MirInputDeviceId device_id; std::string device_name; StubDevice(MirInputDeviceId id) : device_id(id) {} StubDevice(MirInputDeviceId id, std::string device_name) : device_id{id}, device_name{device_name} { } MirInputDeviceId id() const { return device_id; } mi::DeviceCapabilities capabilities() const {return mi::DeviceCapability::keyboard;} std::string name() const {return device_name;} std::string unique_id() const {return {};} mir::optional_value pointer_configuration() const {return {};} void apply_pointer_configuration(mi::PointerConfiguration const&) {;} mir::optional_value touchpad_configuration() const {return {};} void apply_touchpad_configuration(mi::TouchpadConfiguration const&) {} }; struct KeyRepeatDispatcher : public testing::Test { KeyRepeatDispatcher(bool on_arale = false) : dispatcher(mock_next_dispatcher, mock_alarm_factory, cookie_authority, true, repeat_time, repeat_delay, on_arale) { ON_CALL(hub,add_observer(_)).WillByDefault(SaveArg<0>(&observer)); dispatcher.set_input_device_hub(mt::fake_shared(hub)); } void simulate_device_removal() { StubDevice dev(test_device); observer->device_removed(mt::fake_shared(dev)); observer->changes_complete(); } const MirInputDeviceId test_device = 123; std::shared_ptr mock_next_dispatcher = std::make_shared(); std::shared_ptr mock_alarm_factory = std::make_shared(); std::shared_ptr cookie_authority = mir::cookie::Authority::create(); std::chrono::milliseconds const repeat_time{2}; std::chrono::milliseconds const repeat_delay{1}; std::shared_ptr observer; NiceMock hub; mi::KeyRepeatDispatcher dispatcher; mir::EventUPtr a_key_down_event() { return mev::make_event(test_device, std::chrono::nanoseconds(0), std::vector{}, mir_keyboard_action_down, 0, 0, mir_input_event_modifier_alt); } mir::EventUPtr a_key_up_event() { return mev::make_event(test_device, std::chrono::nanoseconds(0), std::vector{}, mir_keyboard_action_up, 0, 0, mir_input_event_modifier_alt); } }; struct KeyRepeatDispatcherOnArale : KeyRepeatDispatcher { KeyRepeatDispatcherOnArale() : KeyRepeatDispatcher(true){}; MirInputDeviceId mtk_id = 32; void add_mtk_tpd() { StubDevice dev(mtk_id, "mtk-tpd"); observer->device_added(mt::fake_shared(dev)); observer->changes_complete(); } mir::EventUPtr mtk_key_down_event() { return mev::make_event(mtk_id, std::chrono::nanoseconds(0), std::vector{}, mir_keyboard_action_down, 0, KEY_LEFTALT, mir_input_event_modifier_none); } }; } TEST_F(KeyRepeatDispatcher, forwards_start_stop) { InSequence seq; EXPECT_CALL(*mock_next_dispatcher, start()).Times(1); EXPECT_CALL(*mock_next_dispatcher, stop()).Times(1); dispatcher.start(); dispatcher.stop(); } TEST_F(KeyRepeatDispatcher, schedules_alarm_to_repeat_key_down) { MockAlarm *mock_alarm = new MockAlarm; // deleted by AlarmFactory std::function alarm_function; InSequence seq; EXPECT_CALL(*mock_alarm_factory, create_alarm_adapter(_)).Times(1). WillOnce(DoAll(SaveArg<0>(&alarm_function), Return(mock_alarm))); // Once for initial down and again when invoked EXPECT_CALL(*mock_alarm, reschedule_in(repeat_time)).Times(1).WillOnce(Return(true)); EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyDownEvent())).Times(1); EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyRepeatEvent())).Times(1); EXPECT_CALL(*mock_alarm, reschedule_in(repeat_delay)).Times(1).WillOnce(Return(true)); EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyUpEvent())).Times(1); // Schedule the repeat dispatcher.dispatch(*a_key_down_event()); // Trigger the repeat alarm_function(); // Trigger the cancel dispatcher.dispatch(*a_key_up_event()); } TEST_F(KeyRepeatDispatcher, stops_repeat_on_device_removal) { MockAlarm *mock_alarm = new MockAlarm; std::function alarm_function; bool alarm_canceled = false; InSequence seq; EXPECT_CALL(*mock_alarm_factory, create_alarm_adapter(_)).Times(1). WillOnce(DoAll(SaveArg<0>(&alarm_function), Return(mock_alarm))); // Once for initial down and again when invoked EXPECT_CALL(*mock_alarm, reschedule_in(repeat_time)).Times(1).WillOnce(Return(true)); EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyDownEvent())).Times(1); EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyRepeatEvent())).Times(1); EXPECT_CALL(*mock_alarm, reschedule_in(repeat_delay)).Times(1).WillOnce(Return(true)); ON_CALL(*mock_alarm, cancel()).WillByDefault(Invoke([&](){alarm_canceled = true; return true;})); dispatcher.dispatch(*a_key_down_event()); alarm_function(); Mock::VerifyAndClearExpectations(mock_alarm); // mock_alarm will be deleted after this simulate_device_removal(); EXPECT_THAT(alarm_canceled, Eq(true)); } TEST_F(KeyRepeatDispatcherOnArale, no_repeat_alarm_on_mtk_tpd) { EXPECT_CALL(*mock_alarm_factory, create_alarm_adapter(_)).Times(0); EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyDownEvent())).Times(1); EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyRepeatEvent())).Times(0); add_mtk_tpd(); dispatcher.dispatch(*mtk_key_down_event()); } TEST_F(KeyRepeatDispatcherOnArale, repeat_for_regular_keys) { MockAlarm *mock_alarm = new MockAlarm; std::function alarm_function; EXPECT_CALL(*mock_alarm_factory, create_alarm_adapter(_)).Times(1). WillOnce(DoAll(SaveArg<0>(&alarm_function), Return(mock_alarm))); // Once for initial down and again when invoked EXPECT_CALL(*mock_alarm, reschedule_in(repeat_delay)).Times(1).WillOnce(Return(true)); EXPECT_CALL(*mock_alarm, reschedule_in(repeat_time)).Times(1).WillOnce(Return(true)); EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyDownEvent())).Times(1); EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyRepeatEvent())).Times(1); add_mtk_tpd(); dispatcher.dispatch(*a_key_down_event()); alarm_function(); } ./tests/unit-tests/input/test_validator.cpp0000644000015600001650000002532212676616125021265 0ustar jenkinsjenkins/* * Copyright© 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/input/validator.h" #include "mir/events/event_private.h" #include "mir/events/event_builders.h" #include "mir/test/fake_shared.h" #include "mir/test/event_matchers.h" #include "mir/test/doubles/mock_input_dispatcher.h" #include #include namespace mi = mir::input; namespace mev = mir::events; namespace mt = mir::test; namespace mtd = mt::doubles; using namespace ::testing; namespace mir { struct MockEventSink { MOCK_METHOD1(handle, void(MirEvent const&)); }; } namespace { struct Validator : public ::testing::Test { Validator() : rewriter([this](MirEvent const& ev) {input_sink.handle(ev);}) { } mir::MockEventSink input_sink; mi::Validator rewriter; }; void add_another_touch(mir::EventUPtr const& ev, MirTouchId id, MirTouchAction action) { mev::add_touch(*ev, id, action, mir_touch_tooltype_finger, 0, 0, 0, 0, 0, 0); } mir::EventUPtr make_touch(MirTouchId id, MirTouchAction action) { auto ev = mev::make_event(MirInputDeviceId(0), std::chrono::nanoseconds(0), std::vector{}, mir_input_event_modifier_none); add_another_touch(ev, id, action); return ev; } } // We make a touch that represents two unseen touch ID's changing // this way we expect the server to generate two seperate events to first // report the down actions TEST_F(Validator, missing_touch_downs_are_inserted) { auto touch = make_touch(0, mir_touch_action_change); add_another_touch(touch, 1, mir_touch_action_change); auto inserted_down_id0 = make_touch(0, mir_touch_action_down); auto inserted_down_id1 = make_touch(0, mir_touch_action_change); add_another_touch(inserted_down_id1, 1, mir_touch_action_down); InSequence seq; EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_down_id0))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_down_id1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch))); rewriter.validate_and_dispatch(*touch); } // In this case we first put two touches down, then we show an event which // reports one of them changing without the others, in this case we // must insert a touch up event for the ID which has gone missing. TEST_F(Validator, missing_touch_releases_are_inserted) { auto touch_1 = make_touch(0, mir_touch_action_down); auto touch_2 = make_touch(0, mir_touch_action_change); add_another_touch(touch_2, 1, mir_touch_action_down); auto touch_3 = make_touch(0, mir_touch_action_change); auto inserted_release = make_touch(0, mir_touch_action_change); add_another_touch(inserted_release, 1, mir_touch_action_up); InSequence seq; EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_2))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_release))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_3))); rewriter.validate_and_dispatch(*touch_1); rewriter.validate_and_dispatch(*touch_2); rewriter.validate_and_dispatch(*touch_3); } // Here we put 3 touches down (0, 1, 2) and then send an event which // shows a change only for touch (2). This means we have to insert missing // releases for both touches which have gone missing. TEST_F(Validator, multiple_missing_releases_are_inserted) { auto touch_1 = make_touch(0, mir_touch_action_down); auto touch_2 = make_touch(0, mir_touch_action_change); add_another_touch(touch_2, 1, mir_touch_action_down); auto touch_3 = make_touch(0, mir_touch_action_change); add_another_touch(touch_3, 1, mir_touch_action_change); add_another_touch(touch_3, 2, mir_touch_action_down); auto touch_4 = make_touch(2, mir_touch_action_change); auto inserted_release_id1 = make_touch(0, mir_touch_action_change); add_another_touch(inserted_release_id1, 1, mir_touch_action_up); add_another_touch(inserted_release_id1, 2, mir_touch_action_change); auto inserted_release_id0 = make_touch(0, mir_touch_action_up); add_another_touch(inserted_release_id0, 2, mir_touch_action_change); InSequence seq; EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_2))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_3))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_release_id1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_release_id0))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_4))); rewriter.validate_and_dispatch(*touch_1); rewriter.validate_and_dispatch(*touch_2); rewriter.validate_and_dispatch(*touch_3); rewriter.validate_and_dispatch(*touch_4); } // In this case we put two touches down (0, 1) and then we show a touch // with (0, 2). Here we expect point 1 to be released and then point 2 to be put down // before reporting the touch with ids (0, 2) TEST_F(Validator, missing_up_and_down_is_inserted) { auto touch_1 = make_touch(0, mir_touch_action_down); auto touch_2 = make_touch(0, mir_touch_action_change); add_another_touch(touch_2, 1, mir_touch_action_down); auto touch_3 = make_touch(0, mir_touch_action_change); add_another_touch(touch_3, 1, mir_touch_action_change); auto touch_4 = make_touch(0, mir_touch_action_change); add_another_touch(touch_4, 2, mir_touch_action_change); auto inserted_release_id1 = make_touch(0, mir_touch_action_change); add_another_touch(inserted_release_id1, 1, mir_touch_action_up); auto inserted_down_id2 = make_touch(0, mir_touch_action_change); add_another_touch(inserted_down_id2, 2, mir_touch_action_down); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_2))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_3))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_release_id1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_down_id2))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_4))); rewriter.validate_and_dispatch(*touch_1); rewriter.validate_and_dispatch(*touch_2); rewriter.validate_and_dispatch(*touch_3); rewriter.validate_and_dispatch(*touch_4); } // This is a variation of the previous test TEST_F(Validator, missing_up_and_down_and_up_is_inserted_variation) { auto touch_1 = make_touch(0, mir_touch_action_down); auto touch_2 = make_touch(0, mir_touch_action_change); add_another_touch(touch_2, 1, mir_touch_action_down); auto touch_3 = make_touch(1, mir_touch_action_change); add_another_touch(touch_3, 2, mir_touch_action_up); auto inserted_up_id0 = make_touch(0, mir_touch_action_up); add_another_touch(inserted_up_id0, 1, mir_touch_action_change); auto inserted_down_id2 = make_touch(1, mir_touch_action_change); add_another_touch(inserted_down_id2, 2, mir_touch_action_down); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_2))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_up_id0))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_down_id2))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_3))); rewriter.validate_and_dispatch(*touch_1); rewriter.validate_and_dispatch(*touch_2); rewriter.validate_and_dispatch(*touch_3); } // In this case we put two touches down (0, 1) and then we release touch 1 // now as the next event we just show (0,1) changing as if point 1 had never // been released. We ensure that a touch down for 1 is inserted before we // show the change event. TEST_F(Validator, down_is_inserted_before_released_touch_reappears) { auto touch_1 = make_touch(0, mir_touch_action_down); auto touch_2 = make_touch(0, mir_touch_action_change); add_another_touch(touch_2, 1, mir_touch_action_down); auto touch_3 = make_touch(0, mir_touch_action_change); add_another_touch(touch_3, 1, mir_touch_action_change); auto touch_4 = make_touch(0, mir_touch_action_change); add_another_touch(touch_4, 1, mir_touch_action_up); auto const& touch_5 = touch_3; auto inserted_down_id1 = make_touch(0, mir_touch_action_change); add_another_touch(inserted_down_id1, 1, mir_touch_action_down); InSequence seq; EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_2))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_3))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_4))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_down_id1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_5))); rewriter.validate_and_dispatch(*touch_1); rewriter.validate_and_dispatch(*touch_2); rewriter.validate_and_dispatch(*touch_3); rewriter.validate_and_dispatch(*touch_4); rewriter.validate_and_dispatch(*touch_5); } // Here we put a single point down and then show it dissapearing while another ID appears...similar // to the missing up and down is inserted case but with only one touch point. We have to inject a release // for the first touch point and a down for the new touch point. TEST_F(Validator, up_and_down_inserted_when_id_changes) { auto touch_1 = make_touch(0, mir_touch_action_down); auto touch_2 = make_touch(1, mir_touch_action_change); auto inserted_up_id0 = make_touch(0, mir_touch_action_up); auto inserted_down_id1 = make_touch(1, mir_touch_action_down); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_up_id0))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*inserted_down_id1))); EXPECT_CALL(input_sink, handle(mt::MirTouchEventMatches(*touch_2))); rewriter.validate_and_dispatch(*touch_1); rewriter.validate_and_dispatch(*touch_2); } ./tests/unit-tests/test_default_emergency_cleanup.cpp0000644000015600001650000000340012676616125023323 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/default_emergency_cleanup.h" #include #include namespace { struct MockHandler { MOCK_METHOD0(call, void()); }; } TEST(DefaultEmergencyCleanupTest, works_without_any_handlers) { mir::DefaultEmergencyCleanup cleanup; cleanup(); } TEST(DefaultEmergencyCleanupTest, calls_single_handler) { mir::DefaultEmergencyCleanup cleanup; MockHandler handler; cleanup.add([&handler] { handler.call(); }); EXPECT_CALL(handler, call()); cleanup(); } TEST(DefaultEmergencyCleanupTest, calls_multiple_handlers_in_order) { mir::DefaultEmergencyCleanup cleanup; MockHandler first_handler; MockHandler second_handler; MockHandler third_handler; cleanup.add([&first_handler] { first_handler.call(); }); cleanup.add([&second_handler] { second_handler.call(); }); cleanup.add([&third_handler] { third_handler.call(); }); testing::InSequence s; EXPECT_CALL(first_handler, call()); EXPECT_CALL(second_handler, call()); EXPECT_CALL(third_handler, call()); cleanup(); } ./tests/unit-tests/renderers/0000755000015600001650000000000012676616124016362 5ustar jenkinsjenkins./tests/unit-tests/renderers/gl/0000755000015600001650000000000012676616126016766 5ustar jenkinsjenkins./tests/unit-tests/renderers/gl/test_gl_renderer.cpp0000644000015600001650000002632112676616125023024 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Sam Spilsbury */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using testing::SetArgPointee; using testing::InSequence; using testing::Return; using testing::ReturnRef; using testing::Pointee; using testing::AnyNumber; using testing::AtLeast; using testing::_; namespace mt=mir::test; namespace mtd=mir::test::doubles; namespace mg=mir::graphics; namespace mgl=mir::gl; namespace mrg = mir::renderer::gl; namespace { const GLint stub_v_shader = 1; const GLint stub_f_shader = 2; const GLint stub_program = 1; const GLint transform_uniform_location = 1; const GLint alpha_uniform_location = 2; const GLint position_attr_location = 3; const GLint texcoord_attr_location = 4; const GLint screen_to_gl_coords_uniform_location = 5; const GLint tex_uniform_location = 6; const GLint display_transform_uniform_location = 7; const GLint centre_uniform_location = 8; void SetUpMockProgramData(mtd::MockGL &mock_gl) { /* Uniforms and Attributes */ ON_CALL(mock_gl, glGetAttribLocation(stub_program, "position")) .WillByDefault(Return(position_attr_location)); ON_CALL(mock_gl, glGetAttribLocation(stub_program, "texcoord")) .WillByDefault(Return(texcoord_attr_location)); ON_CALL(mock_gl, glGetUniformLocation(stub_program, "tex")) .WillByDefault(Return(tex_uniform_location)); ON_CALL(mock_gl, glGetUniformLocation(stub_program, "centre")) .WillByDefault(Return(centre_uniform_location)); ON_CALL(mock_gl, glGetUniformLocation(stub_program, "display_transform")) .WillByDefault(Return(display_transform_uniform_location)); ON_CALL(mock_gl, glGetUniformLocation(stub_program, "transform")) .WillByDefault(Return(transform_uniform_location)); ON_CALL(mock_gl, glGetUniformLocation(stub_program, "screen_to_gl_coords")) .WillByDefault(Return(screen_to_gl_coords_uniform_location)); ON_CALL(mock_gl, glGetUniformLocation(stub_program, "alpha")) .WillByDefault(Return(alpha_uniform_location)); } class GLRenderer : public testing::Test { public: GLRenderer() { //Mock defaults ON_CALL(mock_gl, glCreateShader(GL_VERTEX_SHADER)) .WillByDefault(Return(stub_v_shader)); ON_CALL(mock_gl, glCreateShader(GL_FRAGMENT_SHADER)) .WillByDefault(Return(stub_f_shader)); ON_CALL(mock_gl, glCreateProgram()) .WillByDefault(Return(stub_program)); ON_CALL(mock_gl, glGetProgramiv(_,_,_)) .WillByDefault(SetArgPointee<2>(GL_TRUE)); ON_CALL(mock_gl, glGetShaderiv(_,_,_)) .WillByDefault(SetArgPointee<2>(GL_TRUE)); //A mix of defaults and silencing from here on out EXPECT_CALL(mock_gl, glUseProgram(_)).Times(AnyNumber()); EXPECT_CALL(mock_gl, glActiveTexture(_)).Times(AnyNumber()); EXPECT_CALL(mock_gl, glUniformMatrix4fv(_, _, GL_FALSE, _)) .Times(AnyNumber()); EXPECT_CALL(mock_gl, glUniform1f(_, _)).Times(AnyNumber()); EXPECT_CALL(mock_gl, glUniform2f(_, _, _)).Times(AnyNumber()); EXPECT_CALL(mock_gl, glBindBuffer(_, _)).Times(AnyNumber()); EXPECT_CALL(mock_gl, glVertexAttribPointer(_, _, _, _, _, _)) .Times(AnyNumber()); EXPECT_CALL(mock_gl, glEnableVertexAttribArray(_)).Times(AnyNumber()); EXPECT_CALL(mock_gl, glDrawArrays(_, _, _)).Times(AnyNumber()); EXPECT_CALL(mock_gl, glDisableVertexAttribArray(_)).Times(AnyNumber()); mock_buffer = std::make_shared(); EXPECT_CALL(*mock_buffer, gl_bind_to_texture()).Times(AnyNumber()); EXPECT_CALL(*mock_buffer, native_buffer_base()).Times(AnyNumber()); EXPECT_CALL(*mock_buffer, id()) .WillRepeatedly(Return(mir::graphics::BufferID(789))); EXPECT_CALL(*mock_buffer, size()) .WillRepeatedly(Return(mir::geometry::Size{123, 456})); renderable = std::make_shared>(); EXPECT_CALL(*renderable, id()).WillRepeatedly(Return(&renderable)); EXPECT_CALL(*renderable, buffer()).WillRepeatedly(Return(mock_buffer)); EXPECT_CALL(*renderable, shaped()).WillRepeatedly(Return(false)); EXPECT_CALL(*renderable, alpha()).WillRepeatedly(Return(1.0f)); EXPECT_CALL(*renderable, transformation()).WillRepeatedly(Return(trans)); EXPECT_CALL(*renderable, screen_position()) .WillRepeatedly(Return(mir::geometry::Rectangle{{1,2},{3,4}})); EXPECT_CALL(mock_gl, glDisable(_)).Times(AnyNumber()); renderable_list.push_back(renderable); InSequence s; SetUpMockProgramData(mock_gl); EXPECT_CALL(mock_gl, glGetUniformLocation(stub_program, _)) .WillRepeatedly(Return(screen_to_gl_coords_uniform_location)); } testing::NiceMock mock_gl; testing::NiceMock mock_egl; std::shared_ptr mock_buffer; mtd::StubGLDisplayBuffer display_buffer{{{1, 2}, {3, 4}}}; testing::NiceMock mock_display_buffer; std::shared_ptr> renderable; mg::RenderableList renderable_list; glm::mat4 trans; }; } TEST_F(GLRenderer, disables_blending_for_rgbx_surfaces) { InSequence seq; EXPECT_CALL(*renderable, shaped()) .WillOnce(Return(false)); EXPECT_CALL(mock_gl, glDisable(GL_BLEND)); mrg::Renderer renderer(display_buffer); renderer.render(renderable_list); } TEST_F(GLRenderer, enables_blending_for_rgba_surfaces) { EXPECT_CALL(*renderable, shaped()).WillOnce(Return(true)); EXPECT_CALL(mock_gl, glDisable(GL_BLEND)).Times(0); EXPECT_CALL(mock_gl, glEnable(GL_BLEND)); mrg::Renderer renderer(display_buffer); renderer.render(renderable_list); } TEST_F(GLRenderer, enables_blending_for_rgbx_translucent_surfaces) { EXPECT_CALL(*renderable, alpha()).WillRepeatedly(Return(0.5f)); EXPECT_CALL(*renderable, shaped()).WillOnce(Return(false)); EXPECT_CALL(mock_gl, glDisable(GL_BLEND)).Times(0); EXPECT_CALL(mock_gl, glEnable(GL_BLEND)); mrg::Renderer renderer(display_buffer); renderer.render(renderable_list); } TEST_F(GLRenderer, uses_premultiplied_src_alpha_for_rgba_surfaces) { EXPECT_CALL(*renderable, shaped()).WillOnce(Return(true)); EXPECT_CALL(mock_gl, glDisable(GL_BLEND)).Times(0); EXPECT_CALL(mock_gl, glEnable(GL_BLEND)); EXPECT_CALL(mock_gl, glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); mrg::Renderer renderer(display_buffer); renderer.render(renderable_list); } TEST_F(GLRenderer, avoids_src_alpha_for_rgbx_blending) // LP: #1423462 { EXPECT_CALL(*renderable, alpha()).WillRepeatedly(Return(0.5f)); EXPECT_CALL(*renderable, shaped()).WillOnce(Return(false)); EXPECT_CALL(mock_gl, glDisable(GL_BLEND)).Times(0); EXPECT_CALL(mock_gl, glEnable(GL_BLEND)); EXPECT_CALL(mock_gl, glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA, GL_ZERO, GL_ONE)); mrg::Renderer renderer(display_buffer); renderer.render(renderable_list); } TEST_F(GLRenderer, binds_for_every_primitive_when_tessellate_is_overridden) { //'listening to the tests', it would be a bit easier to use a tessellator mock of some sort struct OverriddenTessellateRenderer : public mrg::Renderer { OverriddenTessellateRenderer( mg::DisplayBuffer& display_buffer, unsigned int num_primitives) : Renderer(display_buffer), num_primitives(num_primitives) { } void tessellate(std::vector& primitives, mg::Renderable const&) const override { primitives.resize(num_primitives); for(GLuint i=0; i < num_primitives; i++) { auto& p = primitives[i]; p.type = 0; p.tex_id = i % 2; p.nvertices = 0; } } unsigned int num_primitives; }; int bind_count = 6; EXPECT_CALL(mock_gl, glBindTexture(GL_TEXTURE_2D, _)) .Times(AtLeast(bind_count)); OverriddenTessellateRenderer renderer(display_buffer, bind_count); renderer.render(renderable_list); } TEST_F(GLRenderer, clears_all_channels_zero) { InSequence seq; EXPECT_CALL(mock_gl, glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); EXPECT_CALL(mock_gl, glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); EXPECT_CALL(mock_gl, glClear(_)); mrg::Renderer renderer(display_buffer); renderer.render(renderable_list); } TEST_F(GLRenderer, makes_display_buffer_current_when_created) { EXPECT_CALL(mock_display_buffer, make_current()); mrg::Renderer renderer(mock_display_buffer); testing::Mock::VerifyAndClearExpectations(&mock_display_buffer); } TEST_F(GLRenderer, releases_display_buffer_current_when_destroyed) { mrg::Renderer renderer(mock_display_buffer); EXPECT_CALL(mock_display_buffer, release_current()); } TEST_F(GLRenderer, makes_display_buffer_current_before_deleting_programs) { mrg::Renderer renderer(mock_display_buffer); InSequence seq; EXPECT_CALL(mock_display_buffer, make_current()); EXPECT_CALL(mock_display_buffer, swap_buffers()); EXPECT_CALL(mock_display_buffer, make_current()); EXPECT_CALL(mock_gl, glDeleteProgram(_)).Times(AtLeast(1)); EXPECT_CALL(mock_gl, glDeleteShader(_)).Times(AtLeast(1)); EXPECT_CALL(mock_display_buffer, release_current()); renderer.render(renderable_list); } TEST_F(GLRenderer, makes_display_buffer_current_before_rendering) { mrg::Renderer renderer(mock_display_buffer); InSequence seq; EXPECT_CALL(mock_display_buffer, make_current()); EXPECT_CALL(mock_gl, glClear(_)); renderer.render(renderable_list); testing::Mock::VerifyAndClearExpectations(&mock_display_buffer); } TEST_F(GLRenderer, swaps_buffers_after_rendering) { mrg::Renderer renderer(mock_display_buffer); InSequence seq; EXPECT_CALL(mock_gl, glDrawArrays(_, _, _)).Times(AnyNumber()); EXPECT_CALL(mock_display_buffer, swap_buffers()); renderer.render(renderable_list); } ./tests/unit-tests/renderers/gl/CMakeLists.txt0000644000015600001650000000021512676616125021523 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_gl_renderer.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/logging/0000755000015600001650000000000012676616126016021 5ustar jenkinsjenkins./tests/unit-tests/logging/message_processor_report.cpp0000644000015600001650000001237712676616125023654 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "src/server/report/logging/message_processor_report.h" #include "mir/logging/logger.h" #include "mir/test/fake_shared.h" #include #include using namespace testing; namespace ml = mir::logging; namespace mrl = mir::report::logging; namespace { class MockClock : public mir::time::Clock { public: MOCK_CONST_METHOD0(now, mir::time::Timestamp()); MOCK_CONST_METHOD1(min_wait_until, mir::time::Duration(mir::time::Timestamp)); ~MockClock() noexcept(true) {} }; class MockLogger : public ml::Logger { public: MOCK_METHOD3(log, void(ml::Severity severity, const std::string& message, const std::string& component)); ~MockLogger() noexcept(true) {} }; struct MessageProcessorReport : public Test { MockLogger logger; MockClock clock; mir::report::logging::MessageProcessorReport report; MessageProcessorReport() : report(mir::test::fake_shared(logger), mir::test::fake_shared(clock)) { EXPECT_CALL(logger, log( ml::Severity::debug, _, "frontend::MessageProcessor")).Times(AnyNumber()); } }; } TEST_F(MessageProcessorReport, everything_fine) { mir::time::Timestamp a_time; EXPECT_CALL(clock, now()).Times(2).WillRepeatedly(Return(a_time)); EXPECT_CALL(logger, log( ml::Severity::informational, EndsWith(": a_function(), elapsed=0µs"), "frontend::MessageProcessor")).Times(1); report.received_invocation(this, 1, "a_function"); report.completed_invocation(this, 1, true); } TEST_F(MessageProcessorReport, slow_call) { mir::time::Timestamp a_time; mir::time::Timestamp another_time = a_time + std::chrono::microseconds(1234); EXPECT_CALL(clock, now()).Times(2) .WillOnce(Return(a_time)).WillOnce(Return(another_time)); EXPECT_CALL(logger, log( ml::Severity::informational, EndsWith("elapsed=1234µs"), "frontend::MessageProcessor")).Times(1); report.received_invocation(this, 1, __PRETTY_FUNCTION__); report.completed_invocation(this, 1, true); } TEST_F(MessageProcessorReport, reports_disconnect) { mir::time::Timestamp a_time; EXPECT_CALL(clock, now()).Times(2).WillRepeatedly(Return(a_time)); EXPECT_CALL(logger, log( ml::Severity::informational, HasSubstr("(disconnecting)"), "frontend::MessageProcessor")).Times(1); report.received_invocation(this, 1, __PRETTY_FUNCTION__); report.completed_invocation(this, 1, false); } TEST_F(MessageProcessorReport, reports_error_during_call) { const char* testError = "***Test error***"; mir::time::Timestamp a_time; EXPECT_CALL(clock, now()).Times(2).WillRepeatedly(Return(a_time)); EXPECT_CALL(logger, log( ml::Severity::informational, HasSubstr(testError), "frontend::MessageProcessor")).Times(1); report.received_invocation(this, 1, __PRETTY_FUNCTION__); report.exception_handled(this, 1, std::runtime_error(testError)); report.completed_invocation(this, 1, false); } TEST_F(MessageProcessorReport, reports_unknown_method) { EXPECT_CALL(clock, now()).Times(0); EXPECT_CALL(logger, log( ml::Severity::warning, HasSubstr("UNKNOWN method=\"unknown_function_name\""), "frontend::MessageProcessor")).Times(1); report.unknown_method(this, 1, "unknown_function_name"); } TEST_F(MessageProcessorReport, reports_error_deserializing_call) { const char* testError = "***Test error***"; EXPECT_CALL(logger, log( ml::Severity::informational, HasSubstr(testError), "frontend::MessageProcessor")).Times(1); report.exception_handled(this, std::runtime_error(testError)); } TEST_F(MessageProcessorReport, logs_a_debug_message_when_invocation_starts) { mir::time::Timestamp a_time; EXPECT_CALL(clock, now()).Times(AnyNumber()).WillRepeatedly(Return(a_time)); EXPECT_CALL(logger, log( ml::Severity::informational, HasSubstr("Calls outstanding on exit:"), "frontend::MessageProcessor")).Times(AnyNumber()); EXPECT_CALL(logger, log( ml::Severity::debug, _, "frontend::MessageProcessor")).Times(1); report.received_invocation(this, 1, __PRETTY_FUNCTION__); } TEST_F(MessageProcessorReport, logs_incomplete_calls_on_destruction) { mir::time::Timestamp a_time; EXPECT_CALL(clock, now()).Times(AnyNumber()).WillRepeatedly(Return(a_time)); EXPECT_CALL(logger, log( ml::Severity::informational, HasSubstr("Calls outstanding on exit:"), "frontend::MessageProcessor")).Times(1); report.received_invocation(this, 1, __PRETTY_FUNCTION__); } ./tests/unit-tests/logging/test_compositor_report.cpp0000644000015600001650000001255412676616125023363 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "src/server/report/logging/compositor_report.h" #include "mir/logging/logger.h" #include "mir/test/doubles/advanceable_clock.h" #include #include #include using namespace std; namespace mtd = mir::test::doubles; namespace mrl = mir::report::logging; namespace ml = mir::logging; namespace { class Recorder : public ml::Logger { public: void log(ml::Severity, string const& message, string const&) { last = message; } string const& last_message() const { return last; } bool last_message_contains(char const* substr) { return last.find(substr) != string::npos; } bool scrape(float& fps, float& frame_time) const { return sscanf(last.c_str(), "Display %*s averaged %f FPS, %f ms/frame", &fps, &frame_time) == 2; } private: string last; }; struct LoggingCompositorReport : ::testing::Test { std::shared_ptr const clock = std::make_shared(); std::shared_ptr const recorder = make_shared(); mrl::CompositorReport report{recorder, clock}; }; } // namespace TEST_F(LoggingCompositorReport, calculates_accurate_stats) { /* * This test just verifies the important stats; FPS and frame time. * We don't need to validate all the numbers because maintaining low * coupling to log formats is more important. */ const void* const display_id = nullptr; int target_fps = 60; for (int frame = 0; frame < target_fps*3; frame++) { report.began_frame(display_id); clock->advance_by(chrono::microseconds(1000000 / target_fps)); report.rendered_frame(display_id); report.finished_frame(display_id); } float measured_fps, measured_frame_time; ASSERT_TRUE(recorder->scrape(measured_fps, measured_frame_time)) << recorder->last_message(); EXPECT_LE(59.9f, measured_fps); EXPECT_GE(60.1f, measured_fps); EXPECT_LE(16.5f, measured_frame_time); EXPECT_GE(16.7f, measured_frame_time); clock->advance_by(chrono::microseconds(5000000)); target_fps = 100; for (int frame = 0; frame < target_fps*3; frame++) { report.began_frame(display_id); clock->advance_by(chrono::microseconds(1000000 / target_fps)); report.rendered_frame(display_id); report.finished_frame(display_id); } ASSERT_TRUE(recorder->scrape(measured_fps, measured_frame_time)) << recorder->last_message(); EXPECT_FLOAT_EQ(100.0f, measured_fps); EXPECT_FLOAT_EQ(10.0f, measured_frame_time); } TEST_F(LoggingCompositorReport, survives_pause_resume) { const void* const before = "before"; const void* const after = "after"; report.started(); report.began_frame(before); clock->advance_by(chrono::microseconds(12345)); report.rendered_frame(before); report.finished_frame(before); report.stopped(); clock->advance_by(chrono::microseconds(12345678)); report.started(); report.began_frame(after); clock->advance_by(chrono::microseconds(12345)); report.rendered_frame(after); report.finished_frame(after); clock->advance_by(chrono::microseconds(12345678)); report.began_frame(after); clock->advance_by(chrono::microseconds(12345)); report.rendered_frame(after); report.finished_frame(after); report.stopped(); } TEST_F(LoggingCompositorReport, reports_bypass_only_when_changed) { const void* const id = "My Screen"; report.started(); report.began_frame(id); report.rendered_frame(id); report.finished_frame(id); EXPECT_TRUE(recorder->last_message_contains("bypass OFF")) << recorder->last_message(); for (int f = 0; f < 3; ++f) { report.began_frame(id); report.rendered_frame(id); report.finished_frame(id); clock->advance_by(chrono::microseconds(12345678)); } EXPECT_FALSE(recorder->last_message_contains("bypass ")) << recorder->last_message(); report.began_frame(id); report.finished_frame(id); EXPECT_TRUE(recorder->last_message_contains("bypass ON")) << recorder->last_message(); report.stopped(); } TEST_F(LoggingCompositorReport, bypass_has_no_render_time) { // Regression test for LP: #1408906 const void* const id = "My Screen"; report.started(); for (int f = 0; f < 3; ++f) { report.began_frame(id); clock->advance_by(chrono::microseconds(1234)); report.finished_frame(id); clock->advance_by(chrono::microseconds(12345678)); } EXPECT_TRUE(recorder->last_message_contains("0.000 ms/frame")) << recorder->last_message(); report.stopped(); } ./tests/unit-tests/logging/test_display_report.cpp0000644000015600001650000001104512676616125022624 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/server/report/logging/display_report.h" #include "mir/logging/logger.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/advanceable_clock.h" #include #include #include namespace ml = mir::logging; namespace mrl = mir::report::logging; namespace mtd = mir::test::doubles; namespace { class MockLogger : public ml::Logger { public: MOCK_METHOD3(log, void(ml::Severity severity, const std::string& message, const std::string& component)); ~MockLogger() noexcept(true) {} }; struct DisplayReport : public testing::Test { std::shared_ptr const clock{std::make_shared()}; std::shared_ptr logger{std::make_shared()}; mtd::MockEGL mock_egl; }; char const* const component = "graphics"; #define STRMACRO(X) #X std::string egl_string_mapping [] = { STRMACRO(EGL_BUFFER_SIZE), STRMACRO(EGL_ALPHA_SIZE), STRMACRO(EGL_BLUE_SIZE), STRMACRO(EGL_GREEN_SIZE), STRMACRO(EGL_RED_SIZE), STRMACRO(EGL_DEPTH_SIZE), STRMACRO(EGL_STENCIL_SIZE), STRMACRO(EGL_CONFIG_CAVEAT), STRMACRO(EGL_CONFIG_ID), STRMACRO(EGL_LEVEL), STRMACRO(EGL_MAX_PBUFFER_HEIGHT), STRMACRO(EGL_MAX_PBUFFER_PIXELS), STRMACRO(EGL_MAX_PBUFFER_WIDTH), STRMACRO(EGL_NATIVE_RENDERABLE), STRMACRO(EGL_NATIVE_VISUAL_ID), STRMACRO(EGL_NATIVE_VISUAL_TYPE), STRMACRO(EGL_SAMPLES), STRMACRO(EGL_SAMPLE_BUFFERS), STRMACRO(EGL_SURFACE_TYPE), STRMACRO(EGL_TRANSPARENT_TYPE), STRMACRO(EGL_TRANSPARENT_BLUE_VALUE), STRMACRO(EGL_TRANSPARENT_GREEN_VALUE), STRMACRO(EGL_TRANSPARENT_RED_VALUE), STRMACRO(EGL_BIND_TO_TEXTURE_RGB), STRMACRO(EGL_BIND_TO_TEXTURE_RGBA), STRMACRO(EGL_MIN_SWAP_INTERVAL), STRMACRO(EGL_MAX_SWAP_INTERVAL), STRMACRO(EGL_LUMINANCE_SIZE), STRMACRO(EGL_ALPHA_MASK_SIZE), STRMACRO(EGL_COLOR_BUFFER_TYPE), STRMACRO(EGL_RENDERABLE_TYPE), STRMACRO(EGL_MATCH_NATIVE_PIXMAP), STRMACRO(EGL_CONFORMANT), STRMACRO(EGL_SLOW_CONFIG), STRMACRO(EGL_NON_CONFORMANT_CONFIG), STRMACRO(EGL_TRANSPARENT_RGB), STRMACRO(EGL_RGB_BUFFER), STRMACRO(EGL_LUMINANCE_BUFFER), STRMACRO(EGL_FRAMEBUFFER_TARGET_ANDROID) }; #undef STRMACRO } TEST_F(DisplayReport, eglconfig) { using namespace testing; EGLDisplay disp = reinterpret_cast(9); EGLConfig config = reinterpret_cast(8); int dummy_value = 7; EXPECT_CALL(mock_egl, eglGetConfigAttrib(disp, config,_,_)) .Times(AnyNumber()) .WillRepeatedly(DoAll(SetArgPointee<3>(dummy_value),Return(EGL_TRUE))); EXPECT_CALL(*logger, log( ml::Severity::informational, "Display EGL Configuration:", component)); for(auto &i : egl_string_mapping) { EXPECT_CALL(*logger, log( ml::Severity::informational, " [" + i + "] : " + std::to_string(dummy_value), component)); } mrl::DisplayReport report(logger, clock); report.report_egl_configuration(disp, config); } TEST_F(DisplayReport, reports_vsync) { std::chrono::milliseconds interval(1500); unsigned int display1_id {1223}; unsigned int display2_id {4492}; std::string display1_name(std::to_string(display1_id)); std::string display2_name(std::to_string(display2_id)); EXPECT_CALL(*logger, log( ml::Severity::informational, "2 vsync events on [" + display1_name + "] over " + std::to_string(interval.count()) + "ms", component)); EXPECT_CALL(*logger, log( ml::Severity::informational, "1 vsync events on [" + display2_name + "] over " + std::to_string(interval.count()) + "ms", component)); mrl::DisplayReport report(logger, clock); report.report_vsync(display1_id); report.report_vsync(display2_id); clock->advance_by(interval); report.report_vsync(display1_id); } ./tests/unit-tests/logging/test_legacy_input_report.cpp0000644000015600001650000000550612676616125023647 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/report/legacy_input_report.h" #include "mir/logging/logger.h" #include #include "mir/test/fake_shared.h" #include #include namespace ml = mir::logging; namespace mri = mir::report::legacy_input; using testing::_; namespace { class MockLogger : public ml::Logger { public: MOCK_METHOD3(log, void(ml::Severity severity, const std::string& message, const std::string& component)); ~MockLogger() noexcept(true) {} }; struct InputReport : public testing::Test { MockLogger logger; InputReport() { mri::initialize(mir::test::fake_shared(logger)); } }; char const* const component = "android-input"; char const* const LOG_TAG = "Foo"; } TEST_F(InputReport, debug_message) { // default minimum log priority is "informational". "debug" is lower than that. EXPECT_CALL(logger, log(_, _, _)).Times(0); ALOG(LOG_DEBUG, NULL, "Test function is %s", __PRETTY_FUNCTION__); } TEST_F(InputReport, unknown_message) { char const* const unknown = "Unknown message"; // default minimum log priority is "informational". "unknown" is lower than that. // Actually, I don't think this is even a valid priority. EXPECT_CALL(logger, log(_, _, _)).Times(0); ALOG(LOG_UNKNOWN, NULL, unknown); } TEST_F(InputReport, verbose_message) { char const* const verbose = "A very long story. (OK, I lied.)"; // default minimum log priority is "informational". "verbose" is lower than that. EXPECT_CALL(logger, log(_, _, _)).Times(0); ALOG(LOG_VERBOSE, NULL, verbose); } TEST_F(InputReport, info_message) { EXPECT_CALL(logger, log( ml::Severity::informational, "[Foo]Some informational message", component)); ALOGI("Some informational message"); } TEST_F(InputReport, warning_message) { EXPECT_CALL(logger, log( ml::Severity::warning, "[Foo]Warning!!!", component)); ALOGW("Warning!!!"); } TEST_F(InputReport, error_message) { EXPECT_CALL(logger, log( ml::Severity::error, "[Foo]An error occurred!", component)); ALOGE("An error occurred!"); } ./tests/unit-tests/logging/CMakeLists.txt0000644000015600001650000000047712676616125020570 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/message_processor_report.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_legacy_input_report.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_report.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_compositor_report.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/library_example.h0000644000015600001650000000230112676616125017716 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_UNIT_TESTS_LIBRARY_EXAMPLE_H_ #define MIR_UNIT_TESTS_LIBRARY_EXAMPLE_H_ #include "mir/module_deleter.h" class SomeInterface { public: SomeInterface() = default; virtual ~SomeInterface() = default; virtual void can_be_executed() = 0; SomeInterface& operator=(SomeInterface const&) = delete; SomeInterface(SomeInterface const&) = delete; }; using ModuleFunction = mir::UniqueModulePtr (*)(); extern "C" mir::UniqueModulePtr module_create_instance(); #endif ./tests/unit-tests/test_lockable_callback.cpp0000644000015600001650000000366412676616125021536 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre * */ #include "mir/lockable_callback_wrapper.h" #include "mir/basic_callback.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_lockable_callback.h" #include #include namespace mt = mir::test; namespace mtd = mir::test::doubles; TEST(LockableCallbackWrapper, forwards_calls_to_wrapper) { using namespace ::testing; mtd::MockLockableCallback mock_lockable_callback; bool pre_hook_called{false}; bool post_hook_called{false}; mir::LockableCallbackWrapper wrapper{ mt::fake_shared(mock_lockable_callback), [&pre_hook_called] {pre_hook_called = true;}, [&post_hook_called] { post_hook_called = true; }}; EXPECT_CALL(mock_lockable_callback, lock()); wrapper.lock(); EXPECT_CALL(mock_lockable_callback, unlock()); wrapper.unlock(); EXPECT_CALL(mock_lockable_callback, functor()); wrapper(); EXPECT_THAT(pre_hook_called, Eq(true)); EXPECT_THAT(post_hook_called, Eq(true)); } TEST(BasicCallback, forwards_calls_to_provided_function) { using namespace ::testing; bool callback_invoked{false}; mir::BasicCallback callback{[&callback_invoked] { callback_invoked = true; }}; callback(); EXPECT_THAT(callback_invoked, Eq(true)); } ./tests/unit-tests/library_example.cpp0000644000015600001650000000200712676616125020254 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "library_example.h" struct Implementation : SomeInterface { void can_be_executed() override { ++local_var; } ~Implementation() { --local_var; } int local_var{0}; }; mir::UniqueModulePtr module_create_instance() { return mir::make_module_ptr(); } ./tests/unit-tests/test_mir_cookie.cpp0000644000015600001650000001224612676616157020267 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/cookie/authority.h" #include "mir/cookie/cookie.h" #include #include #include #include namespace { void drain_dev_random() { // Flush the entropy pool int fd = open("/dev/random", O_RDONLY | O_NONBLOCK); ASSERT_THAT(fd, ::testing::Ge(0)); char buf[256]; while (read(fd, buf, sizeof buf) > 0) {} close(fd); } } // anonymous namespace TEST(MirCookieAuthority, attests_real_timestamp) { std::vector secret{ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xde, 0x01 }; auto authority = mir::cookie::Authority::create_from(secret); uint64_t mock_timestamp{0x322322322332}; auto cookie = authority->make_cookie(mock_timestamp); EXPECT_NO_THROW({ authority->make_cookie(cookie->serialize()); }); } TEST(MirCookieAuthority, doesnt_attest_faked_mac) { std::vector secret{ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xde, 0x01 }; auto authority = mir::cookie::Authority::create_from(secret); std::vector cookie{ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xde, 0x01 }; EXPECT_THROW({ authority->make_cookie(cookie); }, mir::cookie::SecurityCheckError); } TEST(MirCookieAuthority, timestamp_trusted_with_different_secret_doesnt_attest) { std::vector alice{ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xde, 0x01 }; std::vector bob{ 0x01, 0x02, 0x44, 0xd8, 0xee, 0x0f, 0xde, 0x01 }; auto alices_authority = mir::cookie::Authority::create_from(alice); auto bobs_authority = mir::cookie::Authority::create_from(bob); uint64_t mock_timestamp{0x01020304}; EXPECT_THROW({ auto alices_cookie = alices_authority->make_cookie(mock_timestamp); auto bobs_cookie = bobs_authority->make_cookie(mock_timestamp); alices_authority->make_cookie(bobs_cookie->serialize()); bobs_authority->make_cookie(alices_cookie->serialize()); }, mir::cookie::SecurityCheckError); } TEST(MirCookieAuthority, throw_when_secret_size_to_small) { std::vector bob(mir::cookie::Authority::minimum_secret_size - 1); EXPECT_THROW({ auto authority = mir::cookie::Authority::create_from(bob); }, std::logic_error); } TEST(MirCookieAuthority, saves_a_secret) { using namespace testing; std::vector secret; mir::cookie::Authority::create_saving(secret); EXPECT_THAT(secret.size(), Ge(mir::cookie::Authority::minimum_secret_size)); } TEST(MirCookieAuthority, timestamp_trusted_with_saved_secret_does_attest) { uint64_t timestamp = 23; std::vector secret; auto source_authority = mir::cookie::Authority::create_saving(secret); auto sink_authority = mir::cookie::Authority::create_from(secret); auto cookie = source_authority->make_cookie(timestamp); EXPECT_NO_THROW({ sink_authority->make_cookie(cookie->serialize()); }); } TEST(MirCookieAuthority, internally_generated_secret_has_optimum_size) { using namespace testing; std::vector secret; mir::cookie::Authority::create_saving(secret); EXPECT_THAT(secret.size(), Eq(mir::cookie::Authority::optimal_secret_size())); } TEST(MirCookieAuthority, optimal_secret_size_is_larger_than_minimum_size) { using namespace testing; EXPECT_THAT(mir::cookie::Authority::optimal_secret_size(), Ge(mir::cookie::Authority::minimum_secret_size)); } TEST(MirCookieAuthority, DISABLED_given_low_entropy_does_not_hang_or_crash) { // Regression test for LP: #1536662 and LP: #1541188 using namespace testing; drain_dev_random(); auto start = std::chrono::high_resolution_clock::now(); EXPECT_NO_THROW( mir::cookie::Authority::create() ); auto duration = std::chrono::high_resolution_clock::now() - start; int seconds = std::chrono::duration_cast(duration).count(); EXPECT_THAT(seconds, Lt(15)); } TEST(MirCookieAuthority, DISABLED_makes_cookies_quickly) { // Regression test for LP: #1536662 and LP: #1541188 using namespace testing; drain_dev_random(); uint64_t timestamp = 23; std::vector secret; auto source_authority = mir::cookie::Authority::create_saving(secret); drain_dev_random(); auto start = std::chrono::high_resolution_clock::now(); auto cookie = source_authority->make_cookie(timestamp); auto duration = std::chrono::high_resolution_clock::now() - start; int seconds = std::chrono::duration_cast(duration).count(); EXPECT_THAT(seconds, Lt(5)); } ./tests/unit-tests/shared-libraries/0000755000015600001650000000000012676616160017611 5ustar jenkinsjenkins./tests/unit-tests/shared-libraries/empty_input.c0000644000015600001650000000000012676616125022321 0ustar jenkinsjenkins./tests/unit-tests/shared-libraries/libi386.so0000755000015600001650000001520412676616125021342 0ustar jenkinsjenkinsELF4L4 (   $$PtdQtdRtdGNU&"#:M6.GRWY @  BE|ɍqX R" a 8   u   h  __gmon_start___init_fini_ITM_deregisterTMCloneTable_ITM_registerTMCloneTable__cxa_finalize_Jv_RegisterClassesa_symbollibc.so.6_edata__bss_start_endGLIBC_2.1.3~si    SOÏt*[ hh$ffffffUS')Ѓvt RЃ]ÍvUS)tt PRу]É'USW×u't  5ƃ]É'USGu]9't P҃U]S[;|4XzR|  @0F J tx?;*2$"@ AB F `~ h o84t  X@oooo  GCC: (Ubuntu 4.9.1-15ubuntu1) 4.9.1.symtab.strtab.shstrtab.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rel.dyn.rel.plt.init.text.fini.eh_frame_hdr.eh_frame.init_array.fini_array.jcr.dynamic.got.got.plt.data.bss.comment8t4Xh     0      . A` W f      & B I O"k z      h crtstuff.c__JCR_LIST__deregister_tm_clonesregister_tm_clones__do_global_dtors_auxcompleted.6877__do_global_dtors_aux_fini_array_entryframe_dummy__frame_dummy_init_array_entrybla.c__FRAME_END____JCR_END____x86.get_pc_thunk.bx__dso_handle_DYNAMIC__TMC_END___GLOBAL_OFFSET_TABLE__ITM_deregisterTMCloneTable_edata_fini__cxa_finalize@@GLIBC_2.1.3__gmon_start__a_symbol_end__bss_start_Jv_RegisterClasses_ITM_registerTMCloneTable_init$.o88<8 tt@44HoUo d @m BXX vhh#q0|?00`     0$<$`+ ./tests/unit-tests/shared-libraries/libarm64.so0000755000015600001650000002032012676616157021602 0ustar jenkinsjenkinsELF@P@8@  8@  $$QtdRtd    GNUn4Dʙ &_"@ 4BE|WqXvT<Yy   R" a 8       __gmon_start___init_fini_ITM_deregisterTMCloneTable_ITM_registerTMCloneTable__cxa_finalize_Jv_RegisterClasseslibc.so.6_edata__bss_start__bss_start____bss_end____end___endGLIBC_2.17u  p{{_{G?    Ր@ ֐@" րG@_ց!``!!?8T!GA _ց!``!!C!A!ABGB@_{ `b@9@5G@ R`b9 @{¨_{7@a{!G ?{{_u    o 0` ooo\o GCC: (Ubuntu/Linaro 4.9.3-12ubuntu1) 4.9.3\`         Z ] Z Z jZ Z  Z   8 p   ?  K j x    Z  "   ( 7 < DP d ~ /usr/lib/gcc-cross/aarch64-linux-gnu/4.9/../../../../aarch64-linux-gnu/lib/../lib/crti.o$xcall_weak_fn/usr/lib/gcc-cross/aarch64-linux-gnu/4.9/../../../../aarch64-linux-gnu/lib/../lib/crtn.ocrtstuff.c__JCR_LIST__deregister_tm_clones$d__do_global_dtors_auxcompleted.7583__do_global_dtors_aux_fini_array_entryframe_dummy__frame_dummy_init_array_entryempty_input.c__FRAME_END____JCR_END____dso_handle_DYNAMIC__TMC_END___GLOBAL_OFFSET_TABLE__ITM_deregisterTMCloneTable__bss_start____cxa_finalize@@GLIBC_2.17_edata_fini__bss_end____gmon_start___end__end____bss_start_Jv_RegisterClasses_ITM_registerTMCloneTable_init.symtab.strtab.shstrtab.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.text.fini.eh_frame.init_array.fini_array.jcr.dynamic.got.got.plt.data.bss.comment$.oH8 @Ho\\"Uo dnB``0xs@~        0(0+tH9 ./tests/unit-tests/shared-libraries/README0000644000015600001650000000055012676616125020472 0ustar jenkinsjenkinsBinary Test Data ================ This directory contains files for testing our shared library loading code. The lib$ARCH.so files are minimal valid ELF objects of the appropriate architectures, generated by running (on the relevant architecture, ARCH): * touch foo.cpp * gcc -o lib$ARCH.so -shared foo.cpp These binary objects are then stored in bzr. ./tests/unit-tests/shared-libraries/libppc64el.so0000755000015600001650000020715012676616125022131 0ustar jenkinsjenkinsELF`@(@8@0X  $$QtdRtdGNU H ™H=鑵[ @  BE|qX  R" a 8 8`8` ` x__gmon_start___init_fini_ITM_deregisterTMCloneTable_ITM_registerTMCloneTable__cxa_finalize_Jv_RegisterClasseslibc.so.6_edata__bss_start_endGLIBC_2.17uP&& &(&0&HPAH} NL~9)}#HP+@>/A })N!8!|N !|B?;z~| P|p|p|A^ /A }IN!8!|N ```!|B ?;z(?/@(>/A>iHK9 ? 8!|N !|B?;y> }#Kx#/@ 8!|K``>/A})N!K~}iN `~}iN `````=k|B9k}|}lXP= ,0| | Z}`ZN ``!|B|8!N u  o L p ooooGCC: (Ubuntu 4.9.2-20ubuntu1) 4.9.2.symtab.strtab.shstrtab.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.text.fini.eh_frame.init_array.fini_array.jcr.got2.dynamic.got.data.bss.commentL    ,     .@ A P f   ,   0 CPY a m   "     crtstuff.c__JCR_LIST__deregister_tm_clonesregister_tm_clonescompleted.8845__do_global_dtors_aux__do_global_dtors_aux_fini_array_entryframe_dummy__frame_dummy_init_array_entryfoo.cpp__FRAME_END____JCR_END__00000000.plt_pic32.__gmon_start__00008000.got2.plt_pic32.__cxa_finalize@@GLIBC_2.1.3__glink_PLTresolve__dso_handle_DYNAMIC__glink__TMC_END___GLOBAL_OFFSET_TABLE__ITM_deregisterTMCloneTable_edata_fini__cxa_finalize@@GLIBC_2.1.3__gmon_start___end__bss_start_Jv_RegisterClasses_ITM_registerTMCloneTable_init$.o48 LL@  HoUo d nB xD~ ,,,$s  0 $0 p- |./tests/unit-tests/shared-libraries/libarmhf.so0000755000015600001650000001653612676616125021757 0ustar jenkinsjenkinsELF(4l4 (DD  $$QtdRtdGNU$Ӗ^/[V&v$s' 9B 4BE|WɍqXvT<Yy R"  a 8 0,00u( , ,8 0__gmon_start___init_fini_ITM_deregisterTMCloneTable_ITM_registerTMCloneTable__cxa_finalize_Jv_RegisterClassesa_symbollibc.so.6_edata__bss_start__bss_start____bss_end____end___endGLIBC_2.4~ii ( $ @- -ƏʌƏʌ0 0 R/ԋJHzDKxD2{D*pGJX+G K H{D JxDzDs[pGIRX*FG$ JzD Kx{Dj JX#K{DhK"{Dp|JfZHxDKh{DJXG@, x` zz*{hDF 7F]{pG@-~  8od @otooPo (GCC: (Ubuntu/Linaro 4.8.2-16ubuntu4) 4.8.2A2aeabi(7-A A  ".symtab.strtab.shstrtab.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rel.dyn.rel.plt.init.text.fini.eh_frame.init_array.fini_array.jcr.dynamic.got.data.bss.comment.ARM.attributes$.oL8 dd @HoPP$Uott d @m B v q,|88@@ (((,,0,+pW3|0D dPt   8 @  (,^ a n8 ^ ^8 q^ ^< @ A n` p q n n(  n ',n6 ] n nin, n@ @ n( ,^ n ^ " ,)04,;8 A0M \( e0j0r,~   /usr/lib/gcc-cross/arm-linux-gnueabihf/4.8/../../../../arm-linux-gnueabihf/lib/../lib/crti.o$acall_weak_fn$d/usr/lib/gcc-cross/arm-linux-gnueabihf/4.8/../../../../arm-linux-gnueabihf/lib/../lib/crtn.ocrtstuff.c__JCR_LIST__$tderegister_tm_clonesregister_tm_clones__do_global_dtors_auxcompleted.9525__do_global_dtors_aux_fini_array_entryframe_dummy__frame_dummy_init_array_entrylibamd64.c__FRAME_END____JCR_END____dso_handle_DYNAMIC__TMC_END___GLOBAL_OFFSET_TABLE___cxa_finalize@@GLIBC_2.4_ITM_deregisterTMCloneTable__bss_start___bss_end___edata_fini__bss_end____gmon_start__a_symbol_end__end____bss_start_Jv_RegisterClasses_ITM_registerTMCloneTable_init./tests/unit-tests/shared-libraries/libinvalid.so.30000644000015600001650000000000012676616125022421 0ustar jenkinsjenkins./tests/unit-tests/shared-libraries/libamd64.so0000755000015600001650000001725712676616125021576 0ustar jenkinsjenkinsELF>@@@8@$$   08   $$PtdQtdRtd  GNU4x0/2_? @  BE|ɍqX @  a 8 R"0 8 u u0  @ __gmon_start___init_fini_ITM_deregisterTMCloneTable_ITM_registerTMCloneTable__cxa_finalize_Jv_RegisterClassesa_symbollibc.so.6_edata__bss_start_endGLIBC_2.2.5~ui  @ ( (         HH HtH5 % @% h% hH H= UH)HHw]H$ Ht]@Hi H=b UH)HHHH?HHu]H Ht]H@=) u'H= UHt H= ]h] fffff.H= t&H HtUH= H]WKUH}EE,ЋE]HH;8`zRx $0FJ w?;*3$"DmAC W @~ @   oh0  0P o0ooo v( GCC: (Ubuntu 4.8.3-5ubuntu2) 4.8.3.symtab.strtab.shstrtab.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.text.fini.eh_frame_hdr.eh_frame.init_array.fini_array.jcr.dynamic.got.got.plt.data.bss.comment$.o<8 008@hhHoUo00 dPPnB0 x@@s``0~ d     ( (( (0 000#S* 0h0P @ `         ( 0    . A W0 f  @   (  0   10 8 > M uV8 [0 g { " @crtstuff.c__JCR_LIST__deregister_tm_clonesregister_tm_clones__do_global_dtors_auxcompleted.6973__do_global_dtors_aux_fini_array_entryframe_dummy__frame_dummy_init_array_entrylibamd64.c__FRAME_END____JCR_END____dso_handle_DYNAMIC__TMC_END___GLOBAL_OFFSET_TABLE__ITM_deregisterTMCloneTable_edata_fini__gmon_start__a_symbol_end__bss_start_Jv_RegisterClasses_ITM_registerTMCloneTable__cxa_finalize@@GLIBC_2.2.5_init./tests/unit-tests/CMakeLists.txt0000644000015600001650000001407112676616157017142 0ustar jenkinsjenkinsinclude(CMakeDependentOption) add_definitions( -DMIR_CLIENT_PLATFORM_VERSION="${MIR_CLIENT_PLATFORM_VERSION}" -DMIR_SERVER_GRAPHICS_PLATFORM_ABI_STRING="${MIR_SERVER_GRAPHICS_PLATFORM_ABI}" ) if (MIR_BUILD_PLATFORM_ANDROID) add_definitions(-DMIR_BUILD_PLATFORM_ANDROID) endif() if (MIR_BUILD_PLATFORM_MESA_KMS) add_definitions(-DMIR_BUILD_PLATFORM_MESA_KMS) endif() if (MIR_BUILD_PLATFORM_MESA_X11) add_definitions(-DMIR_BUILD_PLATFORM_MESA_X11) endif() include_directories( ${CMAKE_SOURCE_DIR} ${ANDROID_HEADERS_INCLUDE_DIRS} ${DRM_INCLUDE_DIRS} ${GBM_INCLUDE_DIRS} ${UMOCKDEV_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/include/cookie ${PROJECT_SOURCE_DIR}/src/include/cookie ${PROJECT_SOURCE_DIR}/src/include/platform ${PROJECT_SOURCE_DIR}/src/include/server ${PROJECT_SOURCE_DIR}/src/include/client ${PROJECT_SOURCE_DIR}/src/include/common ${PROJECT_SOURCE_DIR}/src/include/gl ${PROJECT_SOURCE_DIR}/src/platforms/common/client ${PROJECT_SOURCE_DIR}/src/platforms/mesa/server/common ${PROJECT_SOURCE_DIR}/src/platforms/android/include ${GLIB_INCLUDE_DIRS} ) add_library(example SHARED library_example.cpp) target_link_libraries(example mircommon) set_target_properties( example PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-data/shared-libraries OUTPUT_NAME example PREFIX "" ) install(TARGETS example LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/share/mir-test-data/shared-libraries ) # Umockdev uses glib, which uses the deprecated "register" allocation specifier set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dregister=") set(UMOCK_UNIT_TEST_SOURCES test_udev_wrapper.cpp) set( UNIT_TEST_SOURCES test_gmock_fixes.cpp test_recursive_read_write_mutex.cpp test_glib_main_loop.cpp shared_library_test.cpp test_raii.cpp test_variable_length_array.cpp test_thread_name.cpp test_default_emergency_cleanup.cpp test_thread_safe_list.cpp test_fatal.cpp test_fd.cpp test_flags.cpp test_shared_library_prober.cpp test_lockable_callback.cpp test_module_deleter.cpp test_mir_cookie.cpp ) CMAKE_DEPENDENT_OPTION( MIR_RUN_UNIT_TESTS "Run unit tests as part of default testing" ON "MIR_BUILD_UNIT_TESTS" OFF) add_subdirectory(options/) add_subdirectory(client/) add_subdirectory(compositor/) add_subdirectory(frontend/) add_subdirectory(logging/) add_subdirectory(shell/) add_subdirectory(geometry/) add_subdirectory(graphics/) add_subdirectory(input/) add_subdirectory(android_input/) add_subdirectory(scene/) add_subdirectory(thread/) add_subdirectory(dispatch/) add_subdirectory(renderers/gl) link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) mir_add_wrapped_executable(mir_unit_tests ${UNIT_TEST_SOURCES} $ $ ${MIR_SERVER_OBJECTS} ${MIR_PLATFORM_OBJECTS} ${MIR_COMMON_OBJECTS} ) mir_precompiled_header(mir_unit_tests ${CMAKE_CURRENT_SOURCE_DIR}/precompiled.hpp) add_dependencies(mir_unit_tests GMock) uses_android_input(mir_unit_tests) target_link_libraries( mir_unit_tests exampleserverconfig mirdraw demo-shell mircommon client_platform_common mirclient-static mirclientrpc-static mirclientlttng-static mir-test-static mir-test-framework-static ${PROTOBUF_LITE_LIBRARIES} ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} # Link in pthread. ) mir_add_wrapped_executable(mir_umock_unit_tests ${UMOCK_UNIT_TEST_SOURCES} $ ${MIR_SERVER_OBJECTS} ${MIR_PLATFORM_OBJECTS} ${MIR_COMMON_OBJECTS} ) add_dependencies(mir_umock_unit_tests GMock) target_link_libraries( mir_umock_unit_tests exampleserverconfig mirdraw demo-shell mircommon client_platform_common mirclient-static mirclientrpc-static mirclientlttng-static mir-test-static mir-test-framework-static ${PROTOBUF_LITE_LIBRARIES} ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY} ${Boost_LIBRARIES} ${UMOCKDEV_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} # Link in pthread. ) if(MIR_TEST_PLATFORM STREQUAL "mesa-kms" OR MIR_TEST_PLATFORM STREQUAL "mesa-x11") target_link_libraries(mir_unit_tests mirsharedmesaservercommon-static ) target_link_libraries(mir_umock_unit_tests mirsharedmesaservercommon-static ) endif() if (MIR_BUILD_PLATFORM_ANDROID) target_link_libraries(mir_umock_unit_tests mirsharedandroid-static ${LIBHARDWARE_LDFLAGS} ${LIBHARDWARE_LIBRARIES} ${ANDROID_PROPERTIES_LDFLAGS} ${ANDROID_PROPERTIES_LIBRARIES} ) target_link_libraries(mir_unit_tests mirsharedandroid-static ${LIBHARDWARE_LDFLAGS} ${LIBHARDWARE_LIBRARIES} ${ANDROID_PROPERTIES_LDFLAGS} ${ANDROID_PROPERTIES_LIBRARIES} ) endif() target_link_libraries(mir_unit_tests mir-test-doubles-static mir-test-doubles-platform-static ) target_link_libraries(mir_umock_unit_tests mir-test-doubles-static mir-test-doubles-platform-static ) if (MIR_RUN_UNIT_TESTS) mir_discover_tests_with_fd_leak_detection(mir_unit_tests G_SLICE=always-malloc G_DEBUG=gc-friendly) mir_discover_tests_with_fd_leak_detection(mir_umock_unit_tests LD_PRELOAD=libumockdev-preload.so.0 G_SLICE=always-malloc G_DEBUG=gc-friendly) endif (MIR_RUN_UNIT_TESTS) add_custom_command(TARGET mir_unit_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/shared-libraries ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-data/shared-libraries COMMENT "Copying test data to build dir..." ) file(GLOB SO_FILES ${CMAKE_CURRENT_SOURCE_DIR}/shared-libraries/*.so*) install(FILES ${SO_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/mir-test-data/shared-libraries) add_library(loadable_test_dso SHARED ${CMAKE_CURRENT_SOURCE_DIR}/shared-libraries/empty_input.c) set_target_properties(loadable_test_dso PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-data/shared-libraries COMPILE_FLAGS "-Wno-pedantic" OUTPUT_NAME this-arch ) install(TARGETS loadable_test_dso LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/share/mir-test-data/shared-libraries ) add_dependencies(mir_unit_tests loadable_test_dso) ./tests/unit-tests/graphics/0000755000015600001650000000000012676616160016171 5ustar jenkinsjenkins./tests/unit-tests/graphics/test_software_cursor.cpp0000644000015600001650000002175512676616125023176 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/graphics/software_cursor.h" #include "mir/graphics/cursor_image.h" #include "mir/graphics/renderable.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/stub_input_scene.h" #include "mir/test/fake_shared.h" #include #include namespace mg = mir::graphics; namespace mtd = mir::test::doubles; namespace mt = mir::test; namespace geom = mir::geometry; namespace { struct MockInputScene : mtd::StubInputScene { MOCK_METHOD1(add_input_visualization, void(std::shared_ptr const&)); MOCK_METHOD1(remove_input_visualization, void(std::weak_ptr const&)); MOCK_METHOD0(emit_scene_changed, void()); }; struct StubCursorImage : mg::CursorImage { StubCursorImage(geom::Displacement const& hotspot) : hotspot_{hotspot}, pixels( size().width.as_uint32_t() * size().height.as_uint32_t() * bytes_per_pixel, 0x55) { } void const* as_argb_8888() const { return pixels.data(); } geom::Size size() const { return {64, 64}; } geom::Displacement hotspot() const { return hotspot_; } private: static size_t const bytes_per_pixel = 4; geom::Displacement const hotspot_; std::vector pixels; }; struct SoftwareCursor : testing::Test { StubCursorImage stub_cursor_image{{3,4}}; StubCursorImage another_stub_cursor_image{{10,9}}; mtd::StubBufferAllocator stub_buffer_allocator; testing::NiceMock mock_input_scene; mg::SoftwareCursor cursor{ mt::fake_shared(stub_buffer_allocator), mt::fake_shared(mock_input_scene)}; }; MATCHER_P(RenderableWithPosition, s, "") { return s == arg->screen_position().top_left; } MATCHER_P(RenderableWithSize, s, "") { return s == arg->screen_position().size; } MATCHER_P(WeakPtrEq, sp, "") { return sp == arg.lock(); } ACTION_TEMPLATE(SavePointerToArg, HAS_1_TEMPLATE_PARAMS(int, k), AND_1_VALUE_PARAMS(output)) { *output = &std::get(args); } } TEST_F(SoftwareCursor, is_added_to_scene_when_shown) { using namespace testing; EXPECT_CALL(mock_input_scene, add_input_visualization(_)); cursor.show(stub_cursor_image); } TEST_F(SoftwareCursor, is_removed_from_scene_on_destruction) { using namespace testing; InSequence s; EXPECT_CALL(mock_input_scene, add_input_visualization(_)); EXPECT_CALL(mock_input_scene, remove_input_visualization(_)); cursor.show(stub_cursor_image); } TEST_F(SoftwareCursor, is_removed_from_scene_when_hidden) { using namespace testing; InSequence s; EXPECT_CALL(mock_input_scene, add_input_visualization(_)); EXPECT_CALL(mock_input_scene, remove_input_visualization(_)); cursor.show(stub_cursor_image); cursor.hide(); Mock::VerifyAndClearExpectations(&mock_input_scene); } TEST_F(SoftwareCursor, renderable_has_cursor_size) { using namespace testing; EXPECT_CALL(mock_input_scene, add_input_visualization( RenderableWithSize(stub_cursor_image.size()))); cursor.show(stub_cursor_image); } TEST_F(SoftwareCursor, places_renderable_at_origin_offset_by_hotspot) { using namespace testing; auto const pos = geom::Point{0,0} - stub_cursor_image.hotspot(); EXPECT_CALL(mock_input_scene, add_input_visualization(RenderableWithPosition(pos))); cursor.show(stub_cursor_image); } TEST_F(SoftwareCursor, moves_scene_renderable_offset_by_hotspot_when_moved) { using namespace testing; std::shared_ptr cursor_renderable; EXPECT_CALL(mock_input_scene, add_input_visualization(_)) .WillOnce(SaveArg<0>(&cursor_renderable)); cursor.show(stub_cursor_image); geom::Point const new_position{12,34}; cursor.move_to(new_position); EXPECT_THAT(cursor_renderable->screen_position().top_left, Eq(new_position - stub_cursor_image.hotspot())); } TEST_F(SoftwareCursor, notifies_scene_when_moving) { using namespace testing; EXPECT_CALL(mock_input_scene, emit_scene_changed()); cursor.show(stub_cursor_image); cursor.move_to({22,23}); } TEST_F(SoftwareCursor, multiple_shows_just_show) { using namespace testing; InSequence s; EXPECT_CALL(mock_input_scene, add_input_visualization(_)); EXPECT_CALL(mock_input_scene, remove_input_visualization(_)); EXPECT_CALL(mock_input_scene, add_input_visualization(_)); EXPECT_CALL(mock_input_scene, remove_input_visualization(_)); // removal on destruction cursor.show(stub_cursor_image); cursor.hide(); cursor.show(); cursor.show(); } TEST_F(SoftwareCursor, creates_renderable_with_filled_buffer) { using namespace testing; size_t const image_size = 4 * stub_cursor_image.size().width.as_uint32_t() * stub_cursor_image.size().height.as_uint32_t(); auto const image_data = static_cast(stub_cursor_image.as_argb_8888()); std::shared_ptr cursor_renderable; EXPECT_CALL(mock_input_scene, add_input_visualization(_)). WillOnce(SaveArg<0>(&cursor_renderable)); cursor.show(stub_cursor_image); auto buffer = static_cast(cursor_renderable->buffer().get()); EXPECT_THAT(buffer->written_pixels, ElementsAreArray(image_data, image_size)); } TEST_F(SoftwareCursor, does_not_hide_or_move_when_already_hidden) { using namespace testing; EXPECT_CALL(mock_input_scene, remove_input_visualization(_)).Times(0); EXPECT_CALL(mock_input_scene, emit_scene_changed()).Times(0); // Already hidden, nothing should happen cursor.hide(); // Hidden, nothing should happen cursor.move_to({3,4}); } TEST_F(SoftwareCursor, creates_new_renderable_for_new_cursor_image) { using namespace testing; std::shared_ptr first_cursor_renderable; EXPECT_CALL(mock_input_scene, add_input_visualization(_)). WillOnce(SaveArg<0>(&first_cursor_renderable)); cursor.show(stub_cursor_image); Mock::VerifyAndClearExpectations(&mock_input_scene); EXPECT_CALL(mock_input_scene, remove_input_visualization(WeakPtrEq(first_cursor_renderable))); EXPECT_CALL(mock_input_scene, add_input_visualization(Ne(first_cursor_renderable))); cursor.show(another_stub_cursor_image); Mock::VerifyAndClearExpectations(&mock_input_scene); } TEST_F(SoftwareCursor, places_new_cursor_renderable_at_correct_position) { using namespace testing; auto const cursor_position = geom::Point{3, 4}; cursor.show(stub_cursor_image); cursor.move_to(cursor_position); Mock::VerifyAndClearExpectations(&mock_input_scene); auto const renderable_position = cursor_position - another_stub_cursor_image.hotspot(); EXPECT_CALL(mock_input_scene, add_input_visualization(RenderableWithPosition(renderable_position))); cursor.show(another_stub_cursor_image); } //lp: #1413211 TEST_F(SoftwareCursor, new_buffer_on_each_show) { struct MockBufferAllocator : public mg::GraphicBufferAllocator { MOCK_METHOD1(alloc_buffer, std::shared_ptr(mg::BufferProperties const&)); std::vector supported_pixel_formats() { return {mir_pixel_format_abgr_8888}; } } mock_allocator; EXPECT_CALL(mock_allocator, alloc_buffer(testing::_)) .Times(3) .WillRepeatedly(testing::Return(std::make_shared()));; mg::SoftwareCursor cursor{ mt::fake_shared(mock_allocator), mt::fake_shared(mock_input_scene)}; cursor.show(another_stub_cursor_image); cursor.show(another_stub_cursor_image); cursor.show(stub_cursor_image); } //lp: 1483779 TEST_F(SoftwareCursor, doesnt_try_to_remove_after_hiding) { using namespace testing; Sequence seq; EXPECT_CALL(mock_input_scene, add_input_visualization(_)) .InSequence(seq); EXPECT_CALL(mock_input_scene, remove_input_visualization(_)) .InSequence(seq); EXPECT_CALL(mock_input_scene, add_input_visualization(_)) .InSequence(seq); cursor.show(stub_cursor_image); cursor.hide(); //should remove here cursor.show(stub_cursor_image); //should add, but not remove a second time Mock::VerifyAndClearExpectations(&mock_input_scene); } ./tests/unit-tests/graphics/offscreen/0000755000015600001650000000000012676616126020145 5ustar jenkinsjenkins./tests/unit-tests/graphics/offscreen/CMakeLists.txt0000644000015600001650000000022412676616125022702 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_offscreen_display.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/graphics/offscreen/test_offscreen_display.cpp0000644000015600001650000001235312676616125025412 0ustar jenkinsjenkins#include "mir/graphics/display_buffer.h" #include "src/server/graphics/offscreen/display.h" #include "mir/graphics/default_display_configuration_policy.h" #include "mir/renderer/gl/render_target.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/as_render_target.h" #include #include #include namespace mg=mir::graphics; namespace mgo=mir::graphics::offscreen; namespace mtd=mir::test::doubles; namespace mr = mir::report; namespace mt = mir::test; namespace { class OffscreenDisplayTest : public ::testing::Test { public: OffscreenDisplayTest() { using namespace ::testing; ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); mock_egl.provide_egl_extensions(); mock_gl.provide_gles_extensions(); ON_CALL(mock_gl, glCheckFramebufferStatus(_)) .WillByDefault(Return(GL_FRAMEBUFFER_COMPLETE)); } ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; EGLNativeDisplayType const native_display{reinterpret_cast(0x12345)}; }; } TEST_F(OffscreenDisplayTest, uses_basic_platform_egl_native_display) { using namespace ::testing; InSequence s; EXPECT_CALL(mock_egl, eglGetDisplay(native_display)); EXPECT_CALL(mock_egl, eglInitialize(mock_egl.fake_egl_display, _, _)); EXPECT_CALL(mock_egl, eglTerminate(mock_egl.fake_egl_display)); mgo::Display display{ native_display, std::make_shared(), mr::null_display_report()}; } TEST_F(OffscreenDisplayTest, orientation_normal) { using namespace ::testing; mgo::Display display{ native_display, std::make_shared(), mr::null_display_report()}; int count = 0; display.for_each_display_sync_group([&](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([&](mg::DisplayBuffer& db) { ++count; EXPECT_EQ(mir_orientation_normal, db.orientation()); }); }); EXPECT_TRUE(count); } TEST_F(OffscreenDisplayTest, never_enables_predictive_bypass) { mgo::Display display{ native_display, std::make_shared(), mr::null_display_report()}; int groups = 0; display.for_each_display_sync_group([&](mg::DisplaySyncGroup& group){ ++groups; EXPECT_EQ(0, group.recommended_sleep().count()); }); EXPECT_TRUE(groups); } TEST_F(OffscreenDisplayTest, makes_fbo_current_rendering_target) { using namespace ::testing; GLuint const fbo{66}; /* Creates GL framebuffer objects */ EXPECT_CALL(mock_gl, glGenFramebuffers(1,_)) .Times(AtLeast(1)) .WillRepeatedly(SetArgPointee<1>(fbo)); /* Provide unique EGL contexts */ std::vector contexts; EXPECT_CALL(mock_egl, eglCreateContext(_,_,_,_)) .Times(AtLeast(1)) .WillRepeatedly(WithoutArgs(Invoke( [&] () { contexts.push_back(0); return reinterpret_cast(&contexts.back()); }))); mgo::Display display{ native_display, std::make_shared(), mr::null_display_report()}; Mock::VerifyAndClearExpectations(&mock_gl); /* Binds the GL framebuffer objects */ display.for_each_display_sync_group([&](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([&](mg::DisplayBuffer& db) { EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,Ne(EGL_NO_CONTEXT))); EXPECT_CALL(mock_gl, glBindFramebuffer(_,Ne(0))); mt::as_render_target(db)->make_current(); Mock::VerifyAndClearExpectations(&mock_egl); Mock::VerifyAndClearExpectations(&mock_gl); }); }); /* Contexts are released at teardown */ display.for_each_display_sync_group([&](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([&](mg::DisplayBuffer&) { EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,EGL_NO_CONTEXT)); }); }); } TEST_F(OffscreenDisplayTest, restores_previous_state_on_fbo_setup_failure) { using namespace ::testing; GLuint const old_fbo{66}; GLuint const new_fbo{67}; EXPECT_CALL(mock_gl, glGetIntegerv(GL_FRAMEBUFFER_BINDING, _)) .WillOnce(SetArgPointee<1>(old_fbo)); EXPECT_CALL(mock_gl, glGetIntegerv(GL_VIEWPORT, _)); InSequence s; EXPECT_CALL(mock_gl, glGenFramebuffers(1,_)) .WillOnce(SetArgPointee<1>(new_fbo)); EXPECT_CALL(mock_gl, glBindFramebuffer(_,new_fbo)); EXPECT_CALL(mock_gl, glCheckFramebufferStatus(_)) .WillOnce(Return(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)); EXPECT_CALL(mock_gl, glBindFramebuffer(_,old_fbo)); EXPECT_THROW({ mgo::Display display( native_display, std::make_shared(), mr::null_display_report()); }, std::runtime_error); } ./tests/unit-tests/graphics/nested/0000755000015600001650000000000012676616126017455 5ustar jenkinsjenkins./tests/unit-tests/graphics/nested/test_nested_display_configuration.cpp0000644000015600001650000001434112676616125027160 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "src/server/graphics/nested/nested_display_configuration.h" #include "mir_display_configuration_builder.h" #include "mir/test/display_config_matchers.h" #include #include #include namespace mg = mir::graphics; namespace mgn = mir::graphics::nested; namespace geom = mir::geometry; namespace mt = mir::test; using namespace testing; namespace { struct MockCardVisitor { MOCK_CONST_METHOD1(f, void(mg::DisplayConfigurationCard const&)); }; struct MockOutputVisitor { MOCK_CONST_METHOD1(f, void(mg::DisplayConfigurationOutput const&)); }; } TEST(NestedDisplayConfiguration, empty_configuration_is_read_correctly) { auto empty_configuration = std::shared_ptr( new MirDisplayConfiguration{0, nullptr, 0, nullptr}); mgn::NestedDisplayConfiguration config(empty_configuration); config.for_each_card([](mg::DisplayConfigurationCard const&) { FAIL(); }); config.for_each_output([](mg::DisplayConfigurationOutput const&) { FAIL(); }); } TEST(NestedDisplayConfiguration, trivial_configuration_has_one_card) { mgn::NestedDisplayConfiguration config(mt::build_trivial_configuration()); MockCardVisitor cv; EXPECT_CALL(cv, f(_)).Times(Exactly(1)); config.for_each_card([&cv](mg::DisplayConfigurationCard const& card) { cv.f(card); }); } TEST(NestedDisplayConfiguration, trivial_configuration_has_one_output) { mgn::NestedDisplayConfiguration config(mt::build_trivial_configuration()); MockOutputVisitor ov; EXPECT_CALL(ov, f(_)).Times(Exactly(1)); config.for_each_output([&ov](mg::DisplayConfigurationOutput const& output) { ov.f(output); }); } TEST(NestedDisplayConfiguration, trivial_configuration_can_be_configured) { auto const mir_config = mt::build_trivial_configuration(); auto const default_current_output_format = mir_config->outputs[0].current_format; geom::Point const new_top_left{10,20}; mgn::NestedDisplayConfiguration config(mir_config); config.for_each_output([&](mg::UserDisplayConfigurationOutput& output) { output.used = true; output.top_left = new_top_left; }); MockOutputVisitor ov; EXPECT_CALL(ov, f(_)).Times(Exactly(1)); config.for_each_output([&](mg::DisplayConfigurationOutput const& output) { ov.f(output); EXPECT_EQ(true, output.used); EXPECT_EQ(new_top_left, output.top_left); EXPECT_EQ(0, output.current_mode_index); EXPECT_EQ(default_current_output_format, output.current_format); }); } // Validation tests once stood here. They've now been replaced by more // portable validation logic which can be found in: // TEST(DisplayConfiguration, ... TEST(NestedDisplayConfiguration, non_trivial_configuration_has_two_cards) { mgn::NestedDisplayConfiguration config(mt::build_non_trivial_configuration()); MockCardVisitor cv; EXPECT_CALL(cv, f(_)).Times(Exactly(2)); config.for_each_card([&cv](mg::DisplayConfigurationCard const& card) { cv.f(card); }); } TEST(NestedDisplayConfiguration, non_trivial_configuration_has_three_outputs) { mgn::NestedDisplayConfiguration config(mt::build_non_trivial_configuration()); MockOutputVisitor ov; EXPECT_CALL(ov, f(_)).Times(Exactly(3)); config.for_each_output([&ov](mg::DisplayConfigurationOutput const& output) { ov.f(output); }); } TEST(NestedDisplayConfiguration, non_trivial_configuration_can_be_configured) { auto const mir_config = mt::build_non_trivial_configuration(); mg::DisplayConfigurationOutputId const id(mir_config->outputs[1].output_id); geom::Point const top_left{100,200}; mgn::NestedDisplayConfiguration config(mir_config); config.for_each_output([&](mg::UserDisplayConfigurationOutput& output) { if (output.id == id) { output.used = true; output.top_left = top_left; output.current_mode_index = 1; output.current_format = mir_pixel_format_argb_8888; } }); MockOutputVisitor ov; EXPECT_CALL(ov, f(_)).Times(Exactly(3)); config.for_each_output([&](mg::DisplayConfigurationOutput const& output) { ov.f(output); if (output.id == id) { EXPECT_EQ(true, output.used); EXPECT_EQ(top_left, output.top_left); EXPECT_EQ(1, output.current_mode_index); EXPECT_EQ(mir_pixel_format_argb_8888, output.current_format); } }); } TEST(NestedDisplayConfiguration, clone_matches_original_configuration) { mgn::NestedDisplayConfiguration config(mt::build_non_trivial_configuration()); auto cloned_config = config.clone(); EXPECT_THAT(*cloned_config, mir::test::DisplayConfigMatches(config)); } TEST(NestedDisplayConfiguration, clone_is_independent_of_original_configuration) { mgn::NestedDisplayConfiguration config(mt::build_non_trivial_configuration()); auto cloned_config = config.clone(); config.for_each_output( [] (mg::UserDisplayConfigurationOutput& output) { output.power_mode = mir_power_mode_off; }); cloned_config->for_each_output( [] (mg::UserDisplayConfigurationOutput& output) { output.power_mode = mir_power_mode_on; }); // Check that changes to cloned_config haven't affected original config config.for_each_output( [] (mg::DisplayConfigurationOutput const& output) { EXPECT_THAT(output.power_mode, Eq(mir_power_mode_off)); }); } ./tests/unit-tests/graphics/nested/mir_display_configuration_builder.cpp0000644000015600001650000001572512676616125027143 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_display_configuration_builder.h" #include namespace mt = mir::test; namespace { uint32_t const default_num_modes = 0; MirDisplayMode* const default_modes = nullptr; uint32_t const default_preferred_mode = 0; uint32_t const default_current_mode = 0; uint32_t const default_num_output_formats = 0; MirPixelFormat* const default_output_formats = nullptr; MirPixelFormat const default_current_output_format = mir_pixel_format_abgr_8888; uint32_t const default_card_id = 1; uint32_t const second_card_id = 2; uint32_t const default_output_id = 0; uint32_t const second_output_id = 1; uint32_t const third_output_id = 2; auto const default_type = MirDisplayOutputType(0); int32_t const default_position_x = 0; int32_t const default_position_y = 0; uint32_t const default_connected = 1; uint32_t const default_used = 1; uint32_t const default_physical_width_mm = 0; uint32_t const default_physical_height_mm = 0; template std::shared_ptr build_test_config( MirDisplayOutput const (&outputs)[NoOfOutputs], MirDisplayCard const (&cards)[NoOfCards]) { auto out_tmp = new MirDisplayOutput[NoOfOutputs]; std::copy(outputs, outputs+NoOfOutputs, out_tmp); auto card_tmp = new MirDisplayCard[NoOfCards]; std::copy(cards, cards+NoOfCards, card_tmp); return std::shared_ptr( new MirDisplayConfiguration{NoOfOutputs, out_tmp, NoOfCards, card_tmp}, [] (MirDisplayConfiguration* conf) { std::for_each( conf->outputs, conf->outputs + conf->num_outputs, [] (MirDisplayOutput const& output) { delete[] output.modes; delete[] output.output_formats; }); delete[] conf->outputs; delete[] conf->cards; delete conf; }); } template void init_output( MirDisplayOutput* output, MirDisplayMode const (&modes)[NoOfModes], MirPixelFormat const (&formats)[NoOfFormats]) { auto mode_tmp = new MirDisplayMode[NoOfModes]; std::copy(modes, modes+NoOfModes, mode_tmp); auto format_tmp = new MirPixelFormat[NoOfFormats]; std::copy(formats, formats+NoOfFormats, format_tmp); output->num_modes = NoOfModes; output->modes = mode_tmp; output->preferred_mode = 0; output->current_mode = 0; output->num_output_formats = NoOfFormats; output->output_formats = format_tmp; output->current_format = NoOfFormats > 0 ? formats[0] : mir_pixel_format_invalid; } template void init_outputs( MirDisplayOutput (&outputs)[NoOfOutputs], MirDisplayMode const (&modes)[NoOfModes], MirPixelFormat const (&formats)[NoOfFormats]) { for(auto output = outputs; output != outputs+NoOfOutputs; ++output) init_output(output, modes, formats); } } std::shared_ptr mt::build_trivial_configuration() { static MirDisplayCard const cards[] {{default_card_id,1}}; static MirDisplayMode const modes[] = {{ 1080, 1920, 4.33f }}; static MirPixelFormat const formats[] = { mir_pixel_format_abgr_8888 }; MirDisplayOutput outputs[] {{ default_num_modes, default_modes, default_preferred_mode, default_current_mode, default_num_output_formats, default_output_formats, default_current_output_format, default_card_id, default_output_id, default_type, default_position_x, default_position_y, default_connected, default_used, default_physical_width_mm, default_physical_height_mm, mir_power_mode_on, mir_orientation_normal }}; init_output(outputs, modes, formats); return build_test_config(outputs, cards); } std::shared_ptr mt::build_non_trivial_configuration() { static MirDisplayCard const cards[] { {default_card_id,1}, {second_card_id,2}}; static MirDisplayMode const modes[] = { { 1080, 1920, 4.33f }, { 1080, 1920, 1.11f }}; static MirPixelFormat const formats[] = { mir_pixel_format_abgr_8888, mir_pixel_format_xbgr_8888, mir_pixel_format_argb_8888, mir_pixel_format_xrgb_8888, mir_pixel_format_bgr_888}; MirDisplayOutput outputs[] { { default_num_modes, default_modes, default_preferred_mode, default_current_mode, default_num_output_formats, default_output_formats, default_current_output_format, default_card_id, default_output_id, default_type, default_position_x, default_position_y, default_connected, default_used, default_physical_width_mm, default_physical_height_mm, mir_power_mode_on, mir_orientation_normal }, { default_num_modes, default_modes, default_preferred_mode, default_current_mode, default_num_output_formats, default_output_formats, default_current_output_format, second_card_id, second_output_id, default_type, default_position_x, default_position_y, default_connected, default_used, default_physical_width_mm, default_physical_height_mm, mir_power_mode_on, mir_orientation_normal }, { default_num_modes, default_modes, default_preferred_mode, default_current_mode, default_num_output_formats, default_output_formats, default_current_output_format, second_card_id, third_output_id, default_type, default_position_x, default_position_y, default_connected, default_used, default_physical_width_mm, default_physical_height_mm, mir_power_mode_on, mir_orientation_normal }, }; init_outputs(outputs, modes, formats); return build_test_config(outputs, cards); } ./tests/unit-tests/graphics/nested/mir_display_configuration_builder.h0000644000015600001650000000213412676616125026576 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_MIR_DISPLAY_CONFIGURATION_BUILDER_H_ #define MIR_TEST_MIR_DISPLAY_CONFIGURATION_BUILDER_H_ #include "mir_toolkit/client_types.h" #include namespace mir { namespace test { std::shared_ptr build_trivial_configuration(); std::shared_ptr build_non_trivial_configuration(); } } #endif /* MIR_TEST_MIR_DISPLAY_CONFIGURATION_BUILDER_H_ */ ./tests/unit-tests/graphics/nested/test_nested_cursor.cpp0000644000015600001650000000544112676616125024102 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/server/graphics/nested/cursor.h" #include "mir/graphics/cursor_image.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/stub_host_connection.h" #include #include namespace mg = mir::graphics; namespace mgn = mg::nested; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mt::doubles; namespace { struct MockHostConnection : public mtd::StubHostConnection { MOCK_METHOD1(set_cursor_image, void(mg::CursorImage const&)); MOCK_METHOD0(hide_cursor, void()); }; struct StubCursorImage : public mg::CursorImage { void const* as_argb_8888() const { return this; } geom::Size size() const { return geom::Size{16, 16}; } geom::Displacement hotspot() const { return geom::Displacement{0, 0}; } }; MATCHER_P(CursorImageEquals, image, "") { return arg.as_argb_8888() == image; } struct NestedCursor : public testing::Test { StubCursorImage a_cursor_image, another_cursor_image; std::shared_ptr connection = std::make_shared(); }; } TEST_F(NestedCursor, sets_default_cursor_image) { EXPECT_CALL(*connection, set_cursor_image(CursorImageEquals(a_cursor_image.as_argb_8888()))).Times(1); mgn::Cursor cursor(connection, mt::fake_shared(a_cursor_image)); } TEST_F(NestedCursor, can_set_other_images) { EXPECT_CALL(*connection, set_cursor_image(CursorImageEquals(a_cursor_image.as_argb_8888()))).Times(1); EXPECT_CALL(*connection, set_cursor_image(CursorImageEquals(another_cursor_image.as_argb_8888()))).Times(1); mgn::Cursor cursor(connection, mt::fake_shared(a_cursor_image)); cursor.show(another_cursor_image); } TEST_F(NestedCursor, hides_cursor) { using namespace ::testing; EXPECT_CALL(*connection, set_cursor_image(CursorImageEquals(a_cursor_image.as_argb_8888()))).Times(1); EXPECT_CALL(*connection, hide_cursor()).Times(1); EXPECT_CALL(*connection, set_cursor_image(CursorImageEquals(another_cursor_image.as_argb_8888()))).Times(1); mgn::Cursor cursor(connection, mt::fake_shared(a_cursor_image)); cursor.hide(); cursor.show(another_cursor_image); } ./tests/unit-tests/graphics/nested/test_nested_display.cpp0000644000015600001650000002017512676616125024233 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/graphics/nested/display.h" #include "src/server/graphics/nested/host_connection.h" #include "src/server/graphics/nested/host_surface.h" #include "src/server/report/null/display_report.h" #include "mir/graphics/default_display_configuration_policy.h" #include "src/server/input/null_input_dispatcher.h" #include "mir_display_configuration_builder.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl_config.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/stub_host_connection.h" #include "mir/test/doubles/stub_cursor_listener.h" #include "mir/test/doubles/null_platform.h" #include "mir/test/fake_shared.h" #include #include namespace mg = mir::graphics; namespace mgn = mir::graphics::nested; namespace mt = mir::test; namespace mi = mir::input; namespace mtd = mir::test::doubles; namespace { class SingleDisplayHostConnection : public mtd::StubHostConnection { public: std::shared_ptr create_display_config() override { return mt::build_trivial_configuration(); } }; class MockApplyDisplayConfigHostConnection : public SingleDisplayHostConnection { public: MOCK_METHOD1(apply_display_config, void(MirDisplayConfiguration&)); }; struct NestedDisplay : testing::Test { std::unique_ptr create_nested_display( std::shared_ptr const& platform, std::shared_ptr const& gl_config) { auto nested_display_raw = new mgn::Display{ platform, std::make_shared(), mt::fake_shared(null_input_dispatcher), mt::fake_shared(null_display_report), mt::fake_shared(default_conf_policy), gl_config, std::make_shared()}; return std::unique_ptr{nested_display_raw}; } testing::NiceMock mock_egl; mi::NullInputDispatcher null_input_dispatcher; mir::report::null::DisplayReport null_display_report; mg::CloneDisplayConfigurationPolicy default_conf_policy; mtd::StubGLConfig stub_gl_config; std::shared_ptr null_platform{ std::make_shared()}; }; } TEST_F(NestedDisplay, respects_gl_config) { using namespace testing; mtd::MockGLConfig mock_gl_config; EGLint const depth_bits{24}; EGLint const stencil_bits{8}; EXPECT_CALL(mock_gl_config, depth_buffer_bits()) .Times(AtLeast(1)) .WillRepeatedly(Return(depth_bits)); EXPECT_CALL(mock_gl_config, stencil_buffer_bits()) .Times(AtLeast(1)) .WillRepeatedly(Return(stencil_bits)); EXPECT_CALL(mock_egl, eglChooseConfig( _, AllOf(mtd::EGLConfigContainsAttrib(EGL_DEPTH_SIZE, depth_bits), mtd::EGLConfigContainsAttrib(EGL_STENCIL_SIZE, stencil_bits)), _,_,_)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); auto const nested_display = create_nested_display( null_platform, mt::fake_shared(mock_gl_config)); } TEST_F(NestedDisplay, has_alpha_channel) { using namespace testing; // mt::build_trivial_configuration sets mir_pixel_format_abgr_8888 EXPECT_CALL(mock_egl, eglChooseConfig( _, mtd::EGLConfigContainsAttrib(EGL_ALPHA_SIZE, 8), _,_,_)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); auto const nested_display = create_nested_display( null_platform, mt::fake_shared(stub_gl_config)); } TEST_F(NestedDisplay, does_not_change_host_display_configuration_at_construction) { using namespace testing; MockApplyDisplayConfigHostConnection host_connection; EXPECT_CALL(host_connection, apply_display_config(_)) .Times(0); auto const nested_display = create_nested_display( null_platform, mt::fake_shared(stub_gl_config)); } // Regression test for LP: #1372276 TEST_F(NestedDisplay, keeps_platform_alive) { using namespace testing; auto const nested_display = create_nested_display( null_platform, mt::fake_shared(stub_gl_config)); std::weak_ptr weak_platform = null_platform; null_platform.reset(); EXPECT_FALSE(weak_platform.expired()); } TEST_F(NestedDisplay, never_enables_predictive_bypass) { // This test can be removed after it's implemented (after nested bypass) auto const nested_display = create_nested_display( null_platform, mt::fake_shared(stub_gl_config)); int groups = 0; nested_display->for_each_display_sync_group( [&groups](mg::DisplaySyncGroup& g) { EXPECT_EQ(0, g.recommended_sleep().count()); ++groups; } ); ASSERT_NE(0, groups); } TEST_F(NestedDisplay, makes_context_current_on_creation_and_releases_on_destruction) { using namespace testing; EXPECT_CALL(mock_egl, eglMakeCurrent(_, EGL_NO_SURFACE, EGL_NO_SURFACE, Ne(EGL_NO_CONTEXT))); auto const nested_display = create_nested_display( null_platform, mt::fake_shared(stub_gl_config)); Mock::VerifyAndClearExpectations(&mock_egl); EXPECT_CALL(mock_egl, eglMakeCurrent(_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); } TEST_F(NestedDisplay, shared_context_uses_matching_egl_attributes) { using namespace testing; NiceMock mock_gl_config; EGLint const depth_bits{24}; EGLint const stencil_bits{8}; std::string extensions{"EGL_KHR_surfaceless_context"}; ON_CALL(mock_gl_config, depth_buffer_bits()) .WillByDefault(Return(depth_bits)); ON_CALL(mock_gl_config, stencil_buffer_bits()) .WillByDefault(Return(stencil_bits)); ON_CALL(mock_egl, eglChooseConfig(_,_,_,_,_)) .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); ON_CALL(mock_egl, eglQueryString(_, EGL_EXTENSIONS)) .WillByDefault(Return(extensions.c_str())); // mt::build_trivial_configuration sets mir_pixel_format_abgr_8888 EGLint const expected_alpha_bits = 8; EGLint const expected_red_bits = 8; EGLint const expected_green_bits = 8; EGLint const expected_blue_bits = 8; EXPECT_CALL(mock_egl, eglChooseConfig( _, AllOf(mtd::EGLConfigContainsAttrib(EGL_DEPTH_SIZE, depth_bits), mtd::EGLConfigContainsAttrib(EGL_STENCIL_SIZE, stencil_bits), mtd::EGLConfigContainsAttrib(EGL_RED_SIZE, expected_red_bits), mtd::EGLConfigContainsAttrib(EGL_GREEN_SIZE, expected_green_bits), mtd::EGLConfigContainsAttrib(EGL_BLUE_SIZE, expected_blue_bits), mtd::EGLConfigContainsAttrib(EGL_ALPHA_SIZE, expected_alpha_bits)), _,_,_)) .Times(AtLeast(1)); auto const nested_display = create_nested_display( null_platform, mt::fake_shared(mock_gl_config)); auto dummy_context = nested_display->create_gl_context(); } ./tests/unit-tests/graphics/nested/CMakeLists.txt0000644000015600001650000000051512676616125022215 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_nested_display_configuration.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_nested_display.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mir_display_configuration_builder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_nested_cursor.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/graphics/test_display.h0000644000015600001650000001335712676616125021060 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Cemil Azizoglu */ #ifndef TEST_DISPLAY_H_ #define TEST_DISPLAY_H_ #include "mir/test/doubles/mock_display_configuration.h" #include "mir/test/display_config_matchers.h" TEST_F(DisplayTestGeneric, configure_disallows_invalid_configuration) { using namespace testing; auto display = create_display(); mir::test::doubles::MockDisplayConfiguration config; EXPECT_CALL(config, valid()) .WillOnce(Return(false)); EXPECT_THROW({display->configure(config);}, std::logic_error); // Determining what counts as a valid configuration is a much trickier // platform-dependent exercise, so won't be tested here. } #ifdef MIR_DISABLE_TESTS_ON_X11 TEST_F(DisplayTestGeneric, DISABLED_gl_context_make_current_uses_shared_context) #else TEST_F(DisplayTestGeneric, gl_context_make_current_uses_shared_context) #endif { using namespace testing; EGLContext const shared_context{reinterpret_cast(0x111)}; EGLContext const display_buffer_context{reinterpret_cast(0x222)}; EGLContext const new_context{reinterpret_cast(0x333)}; EXPECT_CALL(mock_egl, eglCreateContext(_,_,EGL_NO_CONTEXT,_)) .WillOnce(Return(shared_context)); EXPECT_CALL(mock_egl, eglCreateContext(_,_,shared_context,_)) .WillOnce(Return(display_buffer_context)); auto display = create_display(); Mock::VerifyAndClearExpectations(&mock_egl); { InSequence s; EXPECT_CALL(mock_egl, eglCreateContext(_,_,shared_context,_)) .WillOnce(Return(new_context)); EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,new_context)); EXPECT_CALL(mock_egl, eglGetCurrentContext()) .WillOnce(Return(new_context)); EXPECT_CALL(mock_egl, eglMakeCurrent(_,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)); auto gl_ctx = display->create_gl_context(); ASSERT_NE(nullptr, gl_ctx); gl_ctx->make_current(); } Mock::VerifyAndClearExpectations(&mock_egl); /* Possible display shutdown sequence, depending on the platform */ EXPECT_CALL(mock_egl, eglGetCurrentContext()) .Times(AtLeast(0)); EXPECT_CALL(mock_egl, eglMakeCurrent(_,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)) .Times(AtLeast(0)); } TEST_F(DisplayTestGeneric, gl_context_releases_context) { using namespace testing; auto display = create_display(); { InSequence s; EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,Ne(EGL_NO_CONTEXT))); EXPECT_CALL(mock_egl, eglMakeCurrent(_,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)); auto gl_ctx = display->create_gl_context(); ASSERT_NE(nullptr, gl_ctx); gl_ctx->make_current(); gl_ctx->release_current(); Mock::VerifyAndClearExpectations(&mock_egl); } /* Possible display shutdown sequence, depending on the platform */ EXPECT_CALL(mock_egl, eglMakeCurrent(_,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)) .Times(AtLeast(0)); } #ifdef MIR_DISABLE_TESTS_ON_X11 TEST_F(DisplayTestGeneric, DISABLED_does_not_expose_display_buffer_for_output_with_power_mode_off) #else TEST_F(DisplayTestGeneric, does_not_expose_display_buffer_for_output_with_power_mode_off) #endif { using namespace testing; auto display = create_display(); int db_count{0}; display->for_each_display_sync_group([&](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([&] (mg::DisplayBuffer&) { ++db_count; }); }); EXPECT_THAT(db_count, Eq(1)); auto conf = display->configuration(); conf->for_each_output( [] (mg::UserDisplayConfigurationOutput& output) { output.power_mode = mir_power_mode_off; }); display->configure(*conf); db_count = 0; display->for_each_display_sync_group([&](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([&] (mg::DisplayBuffer&) { ++db_count; }); }); EXPECT_THAT(db_count, Eq(0)); } TEST_F(DisplayTestGeneric, returns_configuration_whose_clone_matches_original_configuration) { using namespace testing; auto display = create_display(); auto config = display->configuration(); auto cloned_config = config->clone(); EXPECT_THAT(*cloned_config, mir::test::DisplayConfigMatches(std::cref(*config))); } TEST_F(DisplayTestGeneric, returns_configuration_whose_clone_is_independent_of_original_configuration) { using namespace testing; auto display = create_display(); auto config = display->configuration(); auto cloned_config = config->clone(); config->for_each_output( [] (mg::UserDisplayConfigurationOutput& output) { output.power_mode = mir_power_mode_off; }); cloned_config->for_each_output( [] (mg::UserDisplayConfigurationOutput& output) { output.power_mode = mir_power_mode_on; }); // Check that changes to cloned_config haven't affected original config config->for_each_output( [] (mg::DisplayConfigurationOutput const& output) { EXPECT_THAT(output.power_mode, Eq(mir_power_mode_off)); }); } #endif // TEST_DISPLAY_H_ ./tests/unit-tests/graphics/test_pixel_format_utils.cpp0000644000015600001650000001320612676616125023650 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir_toolkit/common.h" #include "mir/graphics/pixel_format_utils.h" #include #include using namespace mir::graphics; TEST(MirPixelFormatUtils, contains_alpha) { EXPECT_FALSE(contains_alpha(mir_pixel_format_xbgr_8888)); EXPECT_FALSE(contains_alpha(mir_pixel_format_bgr_888)); EXPECT_FALSE(contains_alpha(mir_pixel_format_rgb_888)); EXPECT_FALSE(contains_alpha(mir_pixel_format_xrgb_8888)); EXPECT_FALSE(contains_alpha(mir_pixel_format_xbgr_8888)); EXPECT_FALSE(contains_alpha(mir_pixel_format_rgb_565)); EXPECT_TRUE(contains_alpha(mir_pixel_format_argb_8888)); EXPECT_TRUE(contains_alpha(mir_pixel_format_abgr_8888)); EXPECT_TRUE(contains_alpha(mir_pixel_format_rgba_5551)); EXPECT_TRUE(contains_alpha(mir_pixel_format_rgba_4444)); EXPECT_FALSE(contains_alpha(mir_pixel_format_invalid)); EXPECT_FALSE(contains_alpha(mir_pixel_formats)); } TEST(MirPixelFormatUtils, red_channel_depths) { EXPECT_EQ(8, red_channel_depth(mir_pixel_format_xbgr_8888)); EXPECT_EQ(8, red_channel_depth(mir_pixel_format_bgr_888)); EXPECT_EQ(8, red_channel_depth(mir_pixel_format_rgb_888)); EXPECT_EQ(8, red_channel_depth(mir_pixel_format_xrgb_8888)); EXPECT_EQ(8, red_channel_depth(mir_pixel_format_xbgr_8888)); EXPECT_EQ(8, red_channel_depth(mir_pixel_format_argb_8888)); EXPECT_EQ(8, red_channel_depth(mir_pixel_format_abgr_8888)); EXPECT_EQ(5, red_channel_depth(mir_pixel_format_rgb_565)); EXPECT_EQ(5, red_channel_depth(mir_pixel_format_rgba_5551)); EXPECT_EQ(4, red_channel_depth(mir_pixel_format_rgba_4444)); EXPECT_EQ(0, red_channel_depth(mir_pixel_format_invalid)); EXPECT_EQ(0, red_channel_depth(mir_pixel_formats)); } TEST(MirPixelFormatUtils, blue_channel_depths) { EXPECT_EQ(8, blue_channel_depth(mir_pixel_format_xbgr_8888)); EXPECT_EQ(8, blue_channel_depth(mir_pixel_format_bgr_888)); EXPECT_EQ(8, blue_channel_depth(mir_pixel_format_rgb_888)); EXPECT_EQ(8, blue_channel_depth(mir_pixel_format_xrgb_8888)); EXPECT_EQ(8, blue_channel_depth(mir_pixel_format_xbgr_8888)); EXPECT_EQ(8, blue_channel_depth(mir_pixel_format_argb_8888)); EXPECT_EQ(8, blue_channel_depth(mir_pixel_format_abgr_8888)); EXPECT_EQ(5, blue_channel_depth(mir_pixel_format_rgb_565)); EXPECT_EQ(5, blue_channel_depth(mir_pixel_format_rgba_5551)); EXPECT_EQ(4, blue_channel_depth(mir_pixel_format_rgba_4444)); EXPECT_EQ(0, blue_channel_depth(mir_pixel_format_invalid)); EXPECT_EQ(0, blue_channel_depth(mir_pixel_formats)); } TEST(MirPixelFormatUtils, green_channel_depths) { EXPECT_EQ(8, green_channel_depth(mir_pixel_format_xbgr_8888)); EXPECT_EQ(8, green_channel_depth(mir_pixel_format_bgr_888)); EXPECT_EQ(8, green_channel_depth(mir_pixel_format_rgb_888)); EXPECT_EQ(8, green_channel_depth(mir_pixel_format_xrgb_8888)); EXPECT_EQ(8, green_channel_depth(mir_pixel_format_xbgr_8888)); EXPECT_EQ(8, green_channel_depth(mir_pixel_format_argb_8888)); EXPECT_EQ(8, green_channel_depth(mir_pixel_format_abgr_8888)); EXPECT_EQ(6, green_channel_depth(mir_pixel_format_rgb_565)); EXPECT_EQ(5, green_channel_depth(mir_pixel_format_rgba_5551)); EXPECT_EQ(4, green_channel_depth(mir_pixel_format_rgba_4444)); EXPECT_EQ(0, green_channel_depth(mir_pixel_format_invalid)); EXPECT_EQ(0, green_channel_depth(mir_pixel_formats)); } TEST(MirPixelFormatUtils, alpha_channel_depths) { EXPECT_EQ(0, alpha_channel_depth(mir_pixel_format_xbgr_8888)); EXPECT_EQ(0, alpha_channel_depth(mir_pixel_format_bgr_888)); EXPECT_EQ(0, alpha_channel_depth(mir_pixel_format_rgb_888)); EXPECT_EQ(0, alpha_channel_depth(mir_pixel_format_xrgb_8888)); EXPECT_EQ(0, alpha_channel_depth(mir_pixel_format_xbgr_8888)); EXPECT_EQ(8, alpha_channel_depth(mir_pixel_format_argb_8888)); EXPECT_EQ(8, alpha_channel_depth(mir_pixel_format_abgr_8888)); EXPECT_EQ(0, alpha_channel_depth(mir_pixel_format_rgb_565)); EXPECT_EQ(1, alpha_channel_depth(mir_pixel_format_rgba_5551)); EXPECT_EQ(4, alpha_channel_depth(mir_pixel_format_rgba_4444)); EXPECT_EQ(0, alpha_channel_depth(mir_pixel_format_invalid)); EXPECT_EQ(0, alpha_channel_depth(mir_pixel_formats)); } TEST(MirPixelFormatUtils, valid_mir_pixel_format) { EXPECT_TRUE(valid_pixel_format(mir_pixel_format_xbgr_8888)); EXPECT_TRUE(valid_pixel_format(mir_pixel_format_bgr_888)); EXPECT_TRUE(valid_pixel_format(mir_pixel_format_rgb_888)); EXPECT_TRUE(valid_pixel_format(mir_pixel_format_xrgb_8888)); EXPECT_TRUE(valid_pixel_format(mir_pixel_format_xbgr_8888)); EXPECT_TRUE(valid_pixel_format(mir_pixel_format_argb_8888)); EXPECT_TRUE(valid_pixel_format(mir_pixel_format_abgr_8888)); EXPECT_TRUE(valid_pixel_format(mir_pixel_format_rgb_565)); EXPECT_TRUE(valid_pixel_format(mir_pixel_format_rgba_5551)); EXPECT_TRUE(valid_pixel_format(mir_pixel_format_rgba_4444)); EXPECT_FALSE(valid_pixel_format(mir_pixel_format_invalid)); EXPECT_FALSE(valid_pixel_format(mir_pixel_formats)); } ./tests/unit-tests/graphics/test_display_configuration.cpp0000644000015600001650000002320512676616125024333 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/display_configuration.h" #include namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { mg::DisplayConfigurationOutput const tmpl_output { mg::DisplayConfigurationOutputId{3}, mg::DisplayConfigurationCardId{2}, mg::DisplayConfigurationOutputType::dvid, { mir_pixel_format_abgr_8888 }, { {geom::Size{10, 20}, 60.0}, {geom::Size{10, 20}, 59.0}, {geom::Size{15, 20}, 59.0} }, 0, geom::Size{10, 20}, true, true, geom::Point(), 2, mir_pixel_format_abgr_8888, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }; } TEST(DisplayConfiguration, same_cards_compare_equal) { mg::DisplayConfigurationCardId const id{1}; size_t const max_outputs{3}; mg::DisplayConfigurationCard const card1{id, max_outputs}; mg::DisplayConfigurationCard const card2 = card1; EXPECT_EQ(card1, card1); EXPECT_EQ(card1, card2); EXPECT_EQ(card2, card1); } TEST(DisplayConfiguration, different_cards_compare_unequal) { mg::DisplayConfigurationCardId const id1{1}; mg::DisplayConfigurationCardId const id2{2}; size_t const max_outputs1{3}; size_t const max_outputs2{4}; mg::DisplayConfigurationCard const card1{id1, max_outputs1}; mg::DisplayConfigurationCard const card2{id1, max_outputs2}; mg::DisplayConfigurationCard const card3{id2, max_outputs1}; EXPECT_NE(card1, card2); EXPECT_NE(card2, card1); EXPECT_NE(card2, card3); EXPECT_NE(card3, card2); EXPECT_NE(card1, card3); EXPECT_NE(card3, card1); } TEST(DisplayConfiguration, same_modes_compare_equal) { geom::Size const size{10, 20}; double const vrefresh{59.9}; mg::DisplayConfigurationMode const mode1{size, vrefresh}; mg::DisplayConfigurationMode const mode2 = mode1; EXPECT_EQ(mode1, mode1); EXPECT_EQ(mode1, mode2); EXPECT_EQ(mode2, mode1); } TEST(DisplayConfiguration, different_modes_compare_unequal) { geom::Size const size1{10, 20}; geom::Size const size2{10, 21}; double const vrefresh1{59.9}; double const vrefresh2{60.0}; mg::DisplayConfigurationMode const mode1{size1, vrefresh1}; mg::DisplayConfigurationMode const mode2{size1, vrefresh2}; mg::DisplayConfigurationMode const mode3{size2, vrefresh1}; EXPECT_NE(mode1, mode2); EXPECT_NE(mode2, mode1); EXPECT_NE(mode2, mode3); EXPECT_NE(mode3, mode2); EXPECT_NE(mode1, mode3); EXPECT_NE(mode3, mode1); } TEST(DisplayConfiguration, same_outputs_compare_equal) { mg::DisplayConfigurationOutput const output1 = tmpl_output; mg::DisplayConfigurationOutput output2 = tmpl_output; EXPECT_EQ(output1, output1); EXPECT_EQ(output1, output2); EXPECT_EQ(output2, output1); } TEST(DisplayConfiguration, outputs_with_different_ids_compare_unequal) { mg::DisplayConfigurationOutput const output1 = tmpl_output; mg::DisplayConfigurationOutput output2 = tmpl_output; mg::DisplayConfigurationOutput output3 = tmpl_output; output2.id = mg::DisplayConfigurationOutputId{15}; output3.card_id = mg::DisplayConfigurationCardId{12}; EXPECT_NE(output1, output2); EXPECT_NE(output2, output1); EXPECT_NE(output2, output3); EXPECT_NE(output3, output2); EXPECT_NE(output1, output3); EXPECT_NE(output3, output1); } TEST(DisplayConfiguration, outputs_with_different_modes_compare_unequal) { mg::DisplayConfigurationOutput const output1 = tmpl_output; mg::DisplayConfigurationOutput output2 = tmpl_output; mg::DisplayConfigurationOutput output3 = tmpl_output; std::vector const modes2 { {geom::Size{10, 20}, 60.0}, {geom::Size{10, 20}, 59.9}, {geom::Size{15, 20}, 59.0} }; std::vector const modes3 { {geom::Size{10, 20}, 60.0}, {geom::Size{10, 20}, 59.0} }; output2.modes = modes2; output3.modes = modes3; EXPECT_NE(output1, output2); EXPECT_NE(output2, output1); EXPECT_NE(output2, output3); EXPECT_NE(output3, output2); EXPECT_NE(output1, output3); EXPECT_NE(output3, output1); } TEST(DisplayConfiguration, outputs_with_different_physical_size_compare_unequal) { mg::DisplayConfigurationOutput const output1 = tmpl_output; mg::DisplayConfigurationOutput output2 = tmpl_output; geom::Size const physical_size2{11, 20}; output2.physical_size_mm = physical_size2; EXPECT_NE(output1, output2); EXPECT_NE(output2, output1); } TEST(DisplayConfiguration, outputs_with_different_connected_status_compare_unequal) { mg::DisplayConfigurationOutput const output1 = tmpl_output; mg::DisplayConfigurationOutput output2 = tmpl_output; output2.connected = false; EXPECT_NE(output1, output2); EXPECT_NE(output2, output1); } TEST(DisplayConfiguration, outputs_with_different_current_mode_index_compare_unequal) { mg::DisplayConfigurationOutput const output1 = tmpl_output; mg::DisplayConfigurationOutput output2 = tmpl_output; output2.current_mode_index = 0; EXPECT_NE(output1, output2); EXPECT_NE(output2, output1); } TEST(DisplayConfiguration, outputs_with_different_preferred_mode_index_compare_unequal) { mg::DisplayConfigurationOutput const output1 = tmpl_output; mg::DisplayConfigurationOutput output2 = tmpl_output; output2.preferred_mode_index = 1; EXPECT_NE(output1, output2); EXPECT_NE(output2, output1); } TEST(DisplayConfiguration, outputs_with_different_orientation_compare_unequal) { mg::DisplayConfigurationOutput a = tmpl_output; mg::DisplayConfigurationOutput b = tmpl_output; EXPECT_EQ(a, b); EXPECT_EQ(b, a); a.orientation = mir_orientation_left; b.orientation = mir_orientation_inverted; EXPECT_NE(a, b); EXPECT_NE(b, a); } TEST(DisplayConfiguration, outputs_with_different_power_mode_compare_equal) { mg::DisplayConfigurationOutput a = tmpl_output; mg::DisplayConfigurationOutput b = tmpl_output; EXPECT_EQ(a, b); EXPECT_EQ(b, a); a.power_mode = mir_power_mode_on; b.power_mode = mir_power_mode_off; EXPECT_EQ(a, b); EXPECT_EQ(b, a); } TEST(DisplayConfiguration, outputs_with_different_scaling_factors_compare_unequal) { mg::DisplayConfigurationOutput a = tmpl_output; mg::DisplayConfigurationOutput b = tmpl_output; EXPECT_EQ(a, b); EXPECT_EQ(b, a); a.scale = 2.0f; b.scale = 3.0f; EXPECT_NE(a, b); EXPECT_NE(b, a); } TEST(DisplayConfiguration, outputs_with_different_form_factors_compare_unequal) { mg::DisplayConfigurationOutput a = tmpl_output; mg::DisplayConfigurationOutput b = tmpl_output; EXPECT_EQ(a, b); EXPECT_EQ(b, a); a.form_factor = mir_form_factor_monitor; b.form_factor = mir_form_factor_projector; EXPECT_NE(a, b); EXPECT_NE(b, a); } TEST(DisplayConfiguration, output_extents_uses_current_mode) { mg::DisplayConfigurationOutput out = tmpl_output; out.current_mode_index = 2; ASSERT_NE(out.modes[0], out.modes[2]); EXPECT_EQ(out.modes[out.current_mode_index].size, out.extents().size); } TEST(DisplayConfiguration, output_extents_rotates_with_orientation) { mg::DisplayConfigurationOutput out = tmpl_output; auto const& size = out.modes[out.current_mode_index].size; int w = size.width.as_int(); int h = size.height.as_int(); ASSERT_NE(w, h); geom::Rectangle normal{out.top_left, {w, h}}; geom::Rectangle swapped{out.top_left, {h, w}}; out.orientation = mir_orientation_normal; EXPECT_EQ(normal, out.extents()); out.orientation = mir_orientation_inverted; EXPECT_EQ(normal, out.extents()); out.orientation = mir_orientation_left; EXPECT_EQ(swapped, out.extents()); out.orientation = mir_orientation_right; EXPECT_EQ(swapped, out.extents()); } TEST(DisplayConfiguration, default_valid) { mg::DisplayConfigurationOutput out = tmpl_output; EXPECT_TRUE(out.valid()); } TEST(DisplayConfiguration, used_and_disconnected_invalid) { mg::DisplayConfigurationOutput out = tmpl_output; out.used = true; out.connected = false; EXPECT_FALSE(out.valid()); } TEST(DisplayConfiguration, unsupported_format_invalid) { mg::DisplayConfigurationOutput out = tmpl_output; out.current_format = mir_pixel_format_xbgr_8888; EXPECT_FALSE(out.valid()); } TEST(DisplayConfiguration, unsupported_current_mode_invalid) { mg::DisplayConfigurationOutput out = tmpl_output; out.current_mode_index = 123; EXPECT_FALSE(out.valid()); } TEST(DisplayConfiguration, unsupported_preferred_mode_valid) { // Not having a preferred mode is allowed (LP: #1395405) mg::DisplayConfigurationOutput out = tmpl_output; out.preferred_mode_index = 456; EXPECT_TRUE(out.valid()); } TEST(DisplayConfiguration, output_extents_empty_when_there_are_no_modes) { mg::DisplayConfigurationOutput out = tmpl_output; out.modes.clear(); out.current_mode_index = 0; geom::Rectangle empty{}; EXPECT_EQ(empty, out.extents()); } ./tests/unit-tests/graphics/test_egl_extensions.cpp0000644000015600001650000000415412676616125022767 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #include "mir/graphics/egl_extensions.h" #include "mir/test/doubles/mock_egl.h" #include #include #include namespace mg = mir::graphics; namespace mtd = mir::test::doubles; using namespace testing; typedef mtd::MockEGL::generic_function_pointer_t func_ptr_t; class EGLExtensions : public ::testing::Test { protected: virtual void SetUp() { } testing::NiceMock mock_egl; }; TEST_F(EGLExtensions, constructor_throws_if_egl_image_not_supported) { ON_CALL(mock_egl, eglGetProcAddress(StrEq("eglCreateImageKHR"))) .WillByDefault(Return(reinterpret_cast(0))); ON_CALL(mock_egl, eglGetProcAddress(StrEq("eglDestroyImageKHR"))) .WillByDefault(Return(reinterpret_cast(0))); EXPECT_THROW({ mg::EGLExtensions extensions; }, std::runtime_error); } TEST_F(EGLExtensions, constructor_throws_if_gl_oes_egl_image_not_supported) { ON_CALL(mock_egl, eglGetProcAddress(StrEq("glEGLImageTargetTexture2DOES"))) .WillByDefault(Return(reinterpret_cast(0))); EXPECT_THROW({ mg::EGLExtensions extensions; }, std::runtime_error); } TEST_F(EGLExtensions, success_has_sane_function_hooks) { mg::EGLExtensions extensions; EXPECT_NE(nullptr, extensions.eglCreateImageKHR); EXPECT_NE(nullptr, extensions.eglDestroyImageKHR); EXPECT_NE(nullptr, extensions.glEGLImageTargetTexture2DOES); } ./tests/unit-tests/graphics/egl_mock/0000755000015600001650000000000012676616125017752 5ustar jenkinsjenkins./tests/unit-tests/graphics/egl_mock/egl_mock_test.cpp0000644000015600001650000000330512676616125023276 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Thomas Voss */ #include "mir/test/doubles/mock_egl.h" #include "gtest/gtest.h" namespace mtd = mir::test::doubles; TEST(EglMock, demo) { using namespace ::testing; mtd::MockEGL mock; EXPECT_CALL(mock, eglGetError()).Times(Exactly(1)); eglGetError(); } #define EGL_MOCK_TEST(test, egl_call, mock_egl_call) \ TEST(EglMock, test_##test) \ { \ using namespace ::testing; \ \ mtd::MockEGL mock; \ EXPECT_CALL(mock, mock_egl_call).Times(Exactly(1)); \ egl_call; \ } \ EGL_MOCK_TEST(eglGetError, eglGetError(), eglGetError()) EGL_MOCK_TEST(eglGetDisplay, eglGetDisplay(NULL), eglGetDisplay(NULL)) ./tests/unit-tests/graphics/egl_mock/CMakeLists.txt0000644000015600001650000000021412676616125022507 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/egl_mock_test.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/graphics/test_egl_error.cpp0000644000015600001650000000544712676616125021727 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/egl_error.h" #include "mir/test/doubles/mock_egl.h" #include #include namespace mg = mir::graphics; namespace mtd = mir::test::doubles; namespace { std::string to_hex_string(int n) { std::stringstream ss; ss << std::showbase << std::hex << n; return ss.str(); } struct EGLErrorTest : ::testing::Test { testing::NiceMock mock_egl; std::string const user_message{"Something failed"}; std::string const egl_error_code_str{"EGL_BAD_MATCH"}; int const egl_error_code{EGL_BAD_MATCH}; }; } TEST_F(EGLErrorTest, produces_correct_error_code) { using namespace testing; EXPECT_CALL(mock_egl, eglGetError()) .WillOnce(Return(egl_error_code)); std::error_code ec; try { throw mg::egl_error(""); } catch (std::system_error const& error) { ec = error.code(); } EXPECT_THAT(ec.value(), Eq(egl_error_code)); } TEST_F(EGLErrorTest, produces_message_with_user_string_and_error_code_for_known_error) { using namespace testing; EXPECT_CALL(mock_egl, eglGetError()) .WillOnce(Return(egl_error_code)); std::string error_message; try { throw mg::egl_error(user_message); } catch (std::system_error const& error) { error_message = error.what(); } EXPECT_THAT(error_message, HasSubstr(user_message)); EXPECT_THAT(error_message, HasSubstr(egl_error_code_str)); EXPECT_THAT(error_message, HasSubstr(to_hex_string(egl_error_code))); } TEST_F(EGLErrorTest, produces_message_with_user_string_and_error_code_for_unknown_error) { using namespace testing; int const unknown_egl_error_code{0x131313}; EXPECT_CALL(mock_egl, eglGetError()) .WillOnce(Return(unknown_egl_error_code)); std::string error_message; try { throw mg::egl_error(user_message); } catch (std::system_error const& error) { error_message = error.what(); } EXPECT_THAT(error_message, HasSubstr(user_message)); EXPECT_THAT(error_message, HasSubstr(to_hex_string(unknown_egl_error_code))); } ./tests/unit-tests/graphics/mesa/0000755000015600001650000000000012676616160017116 5ustar jenkinsjenkins./tests/unit-tests/graphics/mesa/kms/0000755000015600001650000000000012676616160017710 5ustar jenkinsjenkins./tests/unit-tests/graphics/mesa/kms/mock_kms_output.h0000644000015600001650000000312612676616125023307 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MOCK_KMS_OUTPUT_H_ #define MOCK_KMS_OUTPUT_H_ #include "src/platforms/mesa/server/kms/kms_output.h" #include namespace mir { namespace test { struct MockKMSOutput : public graphics::mesa::KMSOutput { MOCK_METHOD0(reset, void()); MOCK_METHOD2(configure, void(geometry::Displacement, size_t)); MOCK_CONST_METHOD0(size, geometry::Size()); MOCK_CONST_METHOD0(max_refresh_rate, int()); MOCK_METHOD1(set_crtc, bool(uint32_t)); MOCK_METHOD0(clear_crtc, void()); MOCK_METHOD1(schedule_page_flip, bool(uint32_t)); MOCK_METHOD0(wait_for_page_flip, void()); MOCK_METHOD1(set_cursor, void(gbm_bo*)); MOCK_METHOD1(move_cursor, void(geometry::Point)); MOCK_METHOD0(clear_cursor, void()); MOCK_CONST_METHOD0(has_cursor, bool()); MOCK_METHOD1(set_power_mode, void(MirPowerMode)); }; } // namespace test } // namespace mir #endif // MOCK_KMS_OUTPUT_H_ ./tests/unit-tests/graphics/mesa/kms/test_buffer_allocator.cpp0000644000015600001650000002745112676616125024776 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/test/doubles/null_emergency_cleanup.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "src/platforms/mesa/server/kms/platform.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "src/platforms/mesa/server/common/buffer_allocator.h" #include "mir/graphics/buffer_properties.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir_test_framework/udev_environment.h" #include #include #include #include #include #include #include #include namespace mg = mir::graphics; namespace mgm = mir::graphics::mesa; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; class MesaBufferAllocatorTest : public ::testing::Test { protected: virtual void SetUp() { using namespace testing; fake_devices.add_standard_device("standard-drm-devices"); size = geom::Size{300, 200}; pf = mir_pixel_format_argb_8888; usage = mg::BufferUsage::hardware; buffer_properties = mg::BufferProperties{size, pf, usage}; ON_CALL(mock_gbm, gbm_bo_get_handle(_)) .WillByDefault(Return(mock_gbm.fake_gbm.bo_handle)); platform = std::make_shared( mir::report::null_display_report(), std::make_shared(), *std::make_shared(), mgm::BypassOption::allowed); allocator.reset(new mgm::BufferAllocator( platform->gbm->device, mgm::BypassOption::allowed, mgm::BufferImportMethod::gbm_native_pixmap)); } // Defaults geom::Size size; MirPixelFormat pf; mg::BufferUsage usage; mg::BufferProperties buffer_properties; ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; std::shared_ptr platform; std::unique_ptr allocator; mtf::UdevEnvironment fake_devices; }; TEST_F(MesaBufferAllocatorTest, allocator_returns_non_null_buffer) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); EXPECT_TRUE(allocator->alloc_buffer(buffer_properties).get() != NULL); } TEST_F(MesaBufferAllocatorTest, large_hardware_buffers_bypass) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); const mg::BufferProperties properties(geom::Size{1280, 800}, mir_pixel_format_argb_8888, mg::BufferUsage::hardware); auto buf = allocator->alloc_buffer(properties); ASSERT_TRUE(buf.get() != NULL); EXPECT_TRUE(buf->native_buffer_handle()->flags & mir_buffer_flag_can_scanout); } TEST_F(MesaBufferAllocatorTest, small_buffers_dont_bypass) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); const mg::BufferProperties properties(geom::Size{100, 100}, mir_pixel_format_argb_8888, mg::BufferUsage::hardware); auto buf = allocator->alloc_buffer(properties); ASSERT_TRUE(buf.get() != NULL); EXPECT_FALSE(buf->native_buffer_handle()->flags & mir_buffer_flag_can_scanout); } TEST_F(MesaBufferAllocatorTest, software_buffers_dont_bypass) { using namespace testing; const mg::BufferProperties properties(geom::Size{1920, 1200}, mir_pixel_format_argb_8888, mg::BufferUsage::software); auto buf = allocator->alloc_buffer(properties); ASSERT_TRUE(buf.get() != NULL); EXPECT_FALSE(buf->native_buffer_handle()->flags & mir_buffer_flag_can_scanout); } TEST_F(MesaBufferAllocatorTest, bypass_disables_when_option_is_disabled) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); const mg::BufferProperties properties(geom::Size{1280, 800}, mir_pixel_format_argb_8888, mg::BufferUsage::hardware); mgm::BufferAllocator alloc( platform->gbm->device, mgm::BypassOption::prohibited, mgm::BufferImportMethod::gbm_native_pixmap); auto buf = alloc.alloc_buffer(properties); ASSERT_TRUE(buf.get() != NULL); EXPECT_FALSE(buf->native_buffer_handle()->flags & mir_buffer_flag_can_scanout); } TEST_F(MesaBufferAllocatorTest, correct_buffer_format_translation_argb_8888) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,GBM_FORMAT_ARGB8888,_)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); allocator->alloc_buffer(mg::BufferProperties{size, mir_pixel_format_argb_8888, usage}); } TEST_F(MesaBufferAllocatorTest, correct_buffer_format_translation_xrgb_8888) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,GBM_FORMAT_XRGB8888,_)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); allocator->alloc_buffer(mg::BufferProperties{size, mir_pixel_format_xrgb_8888, usage}); } MATCHER_P(has_flag_set, flag, "") { return arg & flag; } TEST_F(MesaBufferAllocatorTest, creates_hardware_rendering_buffer) { using namespace testing; mg::BufferProperties properties{size, pf, mg::BufferUsage::hardware}; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,has_flag_set(GBM_BO_USE_RENDERING))); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); allocator->alloc_buffer(properties); } TEST_F(MesaBufferAllocatorTest, creates_software_rendering_buffer) { using namespace testing; mg::BufferProperties properties{size, pf, mg::BufferUsage::software}; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)).Times(0); allocator->alloc_buffer(properties); } TEST_F(MesaBufferAllocatorTest, creates_hardware_rendering_buffer_for_undefined_usage) { using namespace testing; mg::BufferProperties properties{size, pf, mg::BufferUsage::undefined}; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,has_flag_set(GBM_BO_USE_RENDERING))); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); allocator->alloc_buffer(properties); } TEST_F(MesaBufferAllocatorTest, requests_correct_buffer_dimensions) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,size.width.as_uint32_t(),size.height.as_uint32_t(),_,_)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); allocator->alloc_buffer(buffer_properties); } TEST_F(MesaBufferAllocatorTest, correct_buffer_handle_is_destroyed) { using namespace testing; gbm_bo* bo{reinterpret_cast(0xabcd)}; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)) .WillOnce(Return(bo)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(bo)); allocator->alloc_buffer(buffer_properties); } TEST_F(MesaBufferAllocatorTest, throws_on_buffer_creation_failure) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)) .WillOnce(Return(reinterpret_cast(0))); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)) .Times(0); EXPECT_THROW({ allocator->alloc_buffer(buffer_properties); }, std::runtime_error); } TEST_F(MesaBufferAllocatorTest, supported_pixel_formats_contain_common_formats) { auto supported_pixel_formats = allocator->supported_pixel_formats(); auto argb_8888_count = std::count(supported_pixel_formats.begin(), supported_pixel_formats.end(), mir_pixel_format_argb_8888); auto xrgb_8888_count = std::count(supported_pixel_formats.begin(), supported_pixel_formats.end(), mir_pixel_format_xrgb_8888); EXPECT_EQ(1, argb_8888_count); EXPECT_EQ(1, xrgb_8888_count); } TEST_F(MesaBufferAllocatorTest, supported_pixel_formats_have_sane_default_in_first_position) { auto supported_pixel_formats = allocator->supported_pixel_formats(); ASSERT_FALSE(supported_pixel_formats.empty()); EXPECT_EQ(mir_pixel_format_argb_8888, supported_pixel_formats[0]); } TEST_F(MesaBufferAllocatorTest, screencast_can_create_buffer) { // Regression test for LP: #1475571 using namespace testing; // Not expected to be called any more, but if it is... ON_CALL(mock_gbm, gbm_device_is_format_supported(_,_,GBM_BO_USE_SCANOUT)) .WillByDefault(Return(0)); EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)); EXPECT_NO_THROW({ allocator->alloc_buffer( mg::BufferProperties{{1920,1080}, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware}); }); } MATCHER_P(GbmImportMatch, value, "import data matches") { using namespace testing; auto data = reinterpret_cast(arg); EXPECT_THAT(data->fd, Eq(value.fd)); EXPECT_THAT(data->width, Eq(value.width)); EXPECT_THAT(data->height, Eq(value.height)); EXPECT_THAT(data->stride, Eq(value.stride)); EXPECT_THAT(data->format, Eq(value.format)); return !(::testing::Test::HasFailure()); } TEST_F(MesaBufferAllocatorTest, reconstructs_from_native_type) { using namespace testing; MirNativeBuffer native_buffer; uint32_t stride {22}; native_buffer.fd_items = 1; native_buffer.fd[0] = 33; native_buffer.width = size.width.as_uint32_t(); native_buffer.height = size.height.as_uint32_t(); native_buffer.stride = stride; native_buffer.flags = GBM_BO_USE_RENDERING; struct gbm_import_fd_data expected_data { native_buffer.fd[0], size.width.as_uint32_t(), size.height.as_uint32_t(), stride, pf }; EXPECT_CALL(mock_gbm, gbm_bo_import(_, GBM_BO_IMPORT_FD, GbmImportMatch(expected_data), native_buffer.flags )) .WillOnce(Return(mock_gbm.fake_gbm.bo)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(mock_gbm.fake_gbm.bo)); auto buffer = allocator->reconstruct_from(&native_buffer, pf); ASSERT_THAT(buffer, Ne(nullptr)); } TEST_F(MesaBufferAllocatorTest, reconstruct_throws_with_invalid_native_type) { MirNativeBuffer native_buffer; native_buffer.fd_items = 0; EXPECT_THROW({ auto buffer = allocator->reconstruct_from(&native_buffer, pf); }, std::logic_error); native_buffer.fd_items = 2; EXPECT_THROW({ auto buffer = allocator->reconstruct_from(&native_buffer, pf); }, std::logic_error); } TEST_F(MesaBufferAllocatorTest, reconstruct_throws_if_gbm_cannot_import) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_import(_, _, _, _)) .WillOnce(Return(nullptr)); MirNativeBuffer native_buffer; native_buffer.fd_items = 1; EXPECT_THROW({ auto buffer = allocator->reconstruct_from(&native_buffer, pf); }, std::system_error); } ./tests/unit-tests/graphics/mesa/kms/test_kms_page_flipper.cpp0000644000015600001650000004074112676616125024771 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/platforms/mesa/server/kms/kms_page_flipper.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_display_report.h" #include "src/server/report/null_report_factory.h" #include "mir/test/fake_shared.h" #include #include #include #include #include #include #include namespace mg = mir::graphics; namespace mgm = mir::graphics::mesa; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace { class KMSPageFlipperTest : public ::testing::Test { public: KMSPageFlipperTest() : page_flipper{mock_drm.fake_drm.fd(), mt::fake_shared(report)} { } testing::NiceMock report; testing::NiceMock mock_drm; mgm::KMSPageFlipper page_flipper; }; ACTION_P(InvokePageFlipHandler, param) { int const dont_care{0}; char dummy; arg1->page_flip_handler(dont_care, dont_care, dont_care, dont_care, *param); ASSERT_EQ(1, read(arg0, &dummy, 1)); } } TEST_F(KMSPageFlipperTest, schedule_flip_calls_drm_page_flip) { using namespace testing; uint32_t const crtc_id{10}; uint32_t const fb_id{101}; EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), crtc_id, fb_id, _, _)) .Times(1); page_flipper.schedule_flip(crtc_id, fb_id); } TEST_F(KMSPageFlipperTest, double_schedule_flip_throws) { using namespace testing; uint32_t const crtc_id{10}; uint32_t const fb_id{101}; EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), crtc_id, fb_id, _, _)) .Times(1); page_flipper.schedule_flip(crtc_id, fb_id); EXPECT_THROW({ page_flipper.schedule_flip(crtc_id, fb_id); }, std::logic_error); } TEST_F(KMSPageFlipperTest, wait_for_flip_handles_drm_event) { using namespace testing; uint32_t const crtc_id{10}; uint32_t const fb_id{101}; void* user_data{nullptr}; EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), crtc_id, fb_id, _, _)) .Times(1) .WillOnce(DoAll(SaveArg<4>(&user_data), Return(0))); EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _)) .Times(1) .WillOnce(DoAll(InvokePageFlipHandler(&user_data), Return(0))); page_flipper.schedule_flip(crtc_id, fb_id); /* Fake a DRM event */ EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1)); page_flipper.wait_for_flip(crtc_id); } TEST_F(KMSPageFlipperTest, wait_for_flip_reports_vsync) { using namespace testing; uint32_t const crtc_id{10}; uint32_t const fb_id{101}; void* user_data{nullptr}; ON_CALL(mock_drm, drmModePageFlip(_, _, _, _, _)) .WillByDefault(DoAll(SaveArg<4>(&user_data), Return(0))); ON_CALL(mock_drm, drmHandleEvent(_, _)) .WillByDefault(DoAll(InvokePageFlipHandler(&user_data), Return(0))); EXPECT_CALL(report, report_vsync(crtc_id)); page_flipper.schedule_flip(crtc_id, fb_id); EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1)); page_flipper.wait_for_flip(crtc_id); } TEST_F(KMSPageFlipperTest, wait_for_non_scheduled_page_flip_doesnt_block) { using namespace testing; uint32_t const crtc_id{10}; EXPECT_CALL(mock_drm, drmModePageFlip(_, _, _, _, _)) .Times(0); EXPECT_CALL(mock_drm, drmHandleEvent(_, _)) .Times(0); page_flipper.wait_for_flip(crtc_id); } TEST_F(KMSPageFlipperTest, failure_in_wait_for_flip_throws) { using namespace testing; uint32_t const crtc_id{10}; uint32_t const fb_id{101}; void* user_data{nullptr}; EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), crtc_id, fb_id, _, _)) .Times(1) .WillOnce(DoAll(SaveArg<4>(&user_data), Return(0))); EXPECT_CALL(mock_drm, drmHandleEvent(_, _)) .Times(0); page_flipper.schedule_flip(crtc_id, fb_id); /* Cause a failure in wait_for_flip */ close(mock_drm.fake_drm.fd()); EXPECT_THROW({ page_flipper.wait_for_flip(crtc_id); }, std::runtime_error); } TEST_F(KMSPageFlipperTest, wait_for_flips_interleaved) { using namespace testing; uint32_t const fb_id{101}; std::vector const crtc_ids{10, 11, 12}; std::vector user_data{nullptr, nullptr, nullptr}; EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), _, fb_id, _, _)) .Times(3) .WillOnce(DoAll(SaveArg<4>(&user_data[0]), Return(0))) .WillOnce(DoAll(SaveArg<4>(&user_data[1]), Return(0))) .WillOnce(DoAll(SaveArg<4>(&user_data[2]), Return(0))); EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _)) .Times(3) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[1]), Return(0))) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[2]), Return(0))) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[0]), Return(0))); for (auto crtc_id : crtc_ids) page_flipper.schedule_flip(crtc_id, fb_id); /* Fake 3 DRM events */ EXPECT_EQ(3, write(mock_drm.fake_drm.write_fd(), "abc", 3)); for (auto crtc_id : crtc_ids) page_flipper.wait_for_flip(crtc_id); } namespace { class PageFlippingFunctor { public: PageFlippingFunctor(mgm::KMSPageFlipper& page_flipper, uint32_t crtc_id) : page_flipper(page_flipper), crtc_id{crtc_id}, done{false}, num_page_flips{0}, num_waits{0} { } void operator()() { while (!done) { page_flipper.schedule_flip(crtc_id, 0); num_page_flips++; std::this_thread::sleep_for(std::chrono::milliseconds{1}); page_flipper.wait_for_flip(crtc_id); num_waits++; } } int page_flip_count() { return num_page_flips; } int wait_count() { return num_waits; } void stop() { done = true; } private: mgm::KMSPageFlipper& page_flipper; uint32_t const crtc_id; std::atomic done; std::atomic num_page_flips; std::atomic num_waits; }; } TEST_F(KMSPageFlipperTest, threads_switch_worker) { using namespace testing; size_t const worker_index{0}; size_t const other_index{1}; std::vector const crtc_ids{10, 11}; std::vector user_data{nullptr, nullptr}; std::vector> page_flipping_functors; std::vector page_flipping_threads; std::thread::id tid; EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), _, _, _, _)) .Times(2) .WillOnce(DoAll(SaveArg<4>(&user_data[worker_index]), Return(0))) .WillOnce(DoAll(SaveArg<4>(&user_data[other_index]), Return(0))); /* * The first event releases the original worker, hence we expect that * then the other thread will become the worker. */ EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _)) .Times(2) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[worker_index]), Return(0))) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[other_index]), Return(0))); /* Start the page-flipping threads */ for (auto crtc_id : crtc_ids) { auto pf = std::unique_ptr(new PageFlippingFunctor{page_flipper, crtc_id}); page_flipping_functors.push_back(std::move(pf)); page_flipping_threads.push_back(std::thread{std::ref(*page_flipping_functors.back())}); /* Wait for page-flip request and tell flipper to stop after this iteration */ while (page_flipping_functors.back()->page_flip_count() == 0) std::this_thread::sleep_for(std::chrono::milliseconds{1}); page_flipping_functors.back()->stop(); /* Wait until the (first) thread has become the worker */ while (tid == std::thread::id()) { std::this_thread::sleep_for(std::chrono::milliseconds{1}); tid = page_flipper.debug_get_worker_tid(); } } EXPECT_EQ(page_flipping_threads[worker_index].get_id(), tid); /* Fake a DRM event */ EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1)); page_flipping_threads[worker_index].join(); /* Wait for the worker to switch */ while (tid != page_flipping_threads[other_index].get_id()) { std::this_thread::sleep_for(std::chrono::milliseconds{1}); tid = page_flipper.debug_get_worker_tid(); } /* Fake another DRM event to unblock the remaining thread */ EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1)); page_flipping_threads[other_index].join(); } TEST_F(KMSPageFlipperTest, threads_worker_notifies_non_worker) { using namespace testing; size_t const worker_index{0}; size_t const other_index{1}; std::vector const crtc_ids{10, 11}; std::vector user_data{nullptr, nullptr}; std::vector> page_flipping_functors; std::vector page_flipping_threads; std::thread::id tid; EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), _, _, _, _)) .Times(2) .WillOnce(DoAll(SaveArg<4>(&user_data[worker_index]), Return(0))) .WillOnce(DoAll(SaveArg<4>(&user_data[other_index]), Return(0))); /* * The first event releases the non-worker thread, hence we expect that * original worker not change. */ EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _)) .Times(2) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[other_index]), Return(0))) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[worker_index]), Return(0))); /* Start the page-flipping threads */ for (auto crtc_id : crtc_ids) { auto pf = std::unique_ptr(new PageFlippingFunctor{page_flipper, crtc_id}); page_flipping_functors.push_back(std::move(pf)); page_flipping_threads.push_back(std::thread{std::ref(*page_flipping_functors.back())}); /* Wait for page-flip request and tell flipper to stop after this iteration */ while (page_flipping_functors.back()->page_flip_count() == 0) std::this_thread::sleep_for(std::chrono::milliseconds{1}); page_flipping_functors.back()->stop(); /* Wait until the (first) thread has become the worker */ while (tid == std::thread::id()) { std::this_thread::sleep_for(std::chrono::milliseconds{1}); tid = page_flipper.debug_get_worker_tid(); } } EXPECT_EQ(page_flipping_threads[worker_index].get_id(), tid); /* Fake a DRM event */ EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1)); /* Wait for the non-worker thread to exit */ page_flipping_threads[other_index].join(); /* Check that the worker hasn't changed */ EXPECT_EQ(page_flipping_threads[worker_index].get_id(), page_flipper.debug_get_worker_tid()); /* Fake another DRM event to unblock the remaining thread */ EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1)); page_flipping_threads[worker_index].join(); } namespace { class PageFlipCounter { public: void add_flip(uint32_t crtc_id, void* user_data) { std::lock_guard lock{data_mutex}; data.push_back({CountType::flip, crtc_id}); pending_flips.insert(user_data); } void add_handle_event(uint32_t crtc_id) { std::lock_guard lock{data_mutex}; data.push_back({CountType::handle_event, crtc_id}); } int count_flips() { std::lock_guard lock{data_mutex}; return std::count_if(begin(data), end(data), [](CountElement& e) -> bool { return e.type == CountType::flip; }); } int count_handle_events() { std::lock_guard lock{data_mutex}; return std::count_if(begin(data), end(data), [](CountElement& e) -> bool { return e.type == CountType::handle_event; }); } bool no_consecutive_flips_for_same_crtc_id() { std::lock_guard lock{data_mutex}; std::unordered_set pending_crtc_ids; for (auto& e : data) { if (e.type == CountType::flip) { if (pending_crtc_ids.find(e.crtc_id) != pending_crtc_ids.end()) return false; pending_crtc_ids.insert(e.crtc_id); } else if (e.type == CountType::handle_event) { if (pending_crtc_ids.find(e.crtc_id) == pending_crtc_ids.end()) return false; pending_crtc_ids.erase(e.crtc_id); } } return true; } void* get_pending_flip_data() { std::lock_guard lock{data_mutex}; auto iter = pending_flips.begin(); if (iter == pending_flips.end()) { return 0; } else { auto d = *iter; pending_flips.erase(iter); return d; } } private: enum class CountType {flip, handle_event}; struct CountElement { CountType type; uint32_t crtc_id; }; std::vector data; std::unordered_set pending_flips; std::mutex data_mutex; }; ACTION_P(InvokePageFlipHandlerWithPendingData, counter) { int const dont_care{0}; int const drm_fd{arg0}; char dummy; auto user_data = static_cast(counter->get_pending_flip_data()); uint32_t const crtc_id{user_data->crtc_id}; /* Remove the event from the drm event queue */ ASSERT_EQ(1, read(drm_fd, &dummy, 1)); /* Call the page flip handler */ arg1->page_flip_handler(dont_care, dont_care, dont_care, dont_care, user_data); /* Record this call */ counter->add_handle_event(crtc_id); } ACTION_P2(AddPageFlipEvent, counter, write_drm_fd) { uint32_t const crtc_id{arg0}; void* const user_data{arg1}; /* Record this call */ counter->add_flip(crtc_id, user_data); /* Add an event to the drm event queue */ EXPECT_EQ(1, write(write_drm_fd, "a", 1)); } } TEST_F(KMSPageFlipperTest, threads_concurrent_page_flips_dont_deadlock) { using namespace testing; std::vector const crtc_ids{10, 11, 12}; std::vector> page_flipping_functors; std::vector page_flipping_threads; PageFlipCounter counter; EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), _, _, _, _)) .WillRepeatedly(DoAll(WithArgs<1,4>(AddPageFlipEvent(&counter, mock_drm.fake_drm.write_fd())), Return(0))); EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _)) .WillRepeatedly(DoAll(InvokePageFlipHandlerWithPendingData(&counter), Return(0))); /* Set up threads */ for (auto crtc_id : crtc_ids) { auto pf = std::unique_ptr(new PageFlippingFunctor{page_flipper, crtc_id}); page_flipping_functors.push_back(std::move(pf)); page_flipping_threads.push_back(std::thread{std::ref(*page_flipping_functors.back())}); } /* Wait for at least min_flips page-flips to be processed */ int const min_flips{100}; while (counter.count_flips() < min_flips) std::this_thread::sleep_for(std::chrono::milliseconds{1}); /* Tell the flippers to stop and wait for them to finish */ for (auto& pf : page_flipping_functors) pf->stop(); for (auto& pf_thread : page_flipping_threads) pf_thread.join(); /* Sanity checks */ EXPECT_EQ(counter.count_flips(), counter.count_handle_events()); EXPECT_TRUE(counter.no_consecutive_flips_for_same_crtc_id()); } ./tests/unit-tests/graphics/mesa/kms/test_display_configuration.cpp0000644000015600001650000006227212676616157026066 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include #include "mir/graphics/display_configuration.h" #include "mir/graphics/display.h" #include "mir/graphics//default_display_configuration_policy.h" #include "mir/time/steady_clock.h" #include "mir/glib_main_loop.h" #include "src/platforms/mesa/server/kms/platform.h" #include "src/platforms/mesa/server/kms/kms_display_configuration.h" #include "src/server/report/null_report_factory.h" #include "mir/test/signal.h" #include "mir/test/auto_unblock_thread.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/stub_gl_program_factory.h" #include "mir_test_framework/udev_environment.h" #include #include #include namespace mg = mir::graphics; namespace mgm = mir::graphics::mesa; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { mg::DisplayConfigurationMode conf_mode_from_drm_mode(drmModeModeInfo const& mode) { geom::Size const size{mode.hdisplay, mode.vdisplay}; double vrefresh_hz{0.0}; /* Calculate vertical refresh rate from DRM mode information */ if (mode.htotal != 0.0 && mode.vtotal != 0.0) { vrefresh_hz = (mode.clock * 100000LL / ((long)mode.htotal * (long)mode.vtotal)) / 100.0; } return mg::DisplayConfigurationMode{size, vrefresh_hz}; } struct MainLoop { MainLoop() { int const owner{0}; mt::Signal mainloop_started; ml.enqueue(&owner, [&] { mainloop_started.raise(); }); mt::AutoUnblockThread t([this]{ml.stop();}, [this]{ml.run();}); bool const started = mainloop_started.wait_for(std::chrono::seconds(10)); if (!started) throw std::runtime_error("Failed to start main loop"); ml_thread = std::move(t); } mir::GLibMainLoop ml{std::make_shared()}; mt::AutoUnblockThread ml_thread; }; class MesaDisplayConfigurationTest : public ::testing::Test { public: MesaDisplayConfigurationTest() { using namespace testing; /* Needed for display start-up */ ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); mock_egl.provide_egl_extensions(); mock_gl.provide_gles_extensions(); setup_sample_modes(); fake_devices.add_standard_device("standard-drm-devices"); } std::shared_ptr create_platform() { return std::make_shared( mir::report::null_display_report(), std::make_shared(), *std::make_shared(), mgm::BypassOption::allowed); } std::shared_ptr create_display( std::shared_ptr const& platform) { return platform->create_display( std::make_shared(), std::make_shared()); } void setup_sample_modes() { using fake = mtd::FakeDRMResources; /* Add DRM modes */ modes0.push_back(fake::create_mode(1920, 1080, 138500, 2080, 1111, fake::NormalMode)); modes0.push_back(fake::create_mode(1920, 1080, 148500, 2200, 1125, fake::PreferredMode)); modes0.push_back(fake::create_mode(1680, 1050, 119000, 1840, 1080, fake::NormalMode)); modes0.push_back(fake::create_mode(832, 624, 57284, 1152, 667, fake::NormalMode)); /* Add the DisplayConfiguration modes corresponding to the DRM modes */ for (auto const& mode : modes0) conf_modes0.push_back(conf_mode_from_drm_mode(mode)); modes1.push_back(fake::create_mode(123, 456, 138500, 2080, 1111, fake::NormalMode)); modes1.push_back(fake::create_mode(789, 1011, 148500, 2200, 1125, fake::NormalMode)); modes1.push_back(fake::create_mode(1213, 1415, 119000, 1840, 1080, fake::PreferredMode)); for (auto const& mode : modes1) conf_modes1.push_back(conf_mode_from_drm_mode(mode)); } ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; std::vector modes0; std::vector conf_modes0; std::vector modes1; std::vector conf_modes1; std::vector modes_empty; mtf::UdevEnvironment fake_devices; }; } TEST_F(MesaDisplayConfigurationTest, configuration_is_read_correctly) { using namespace ::testing; /* Set up DRM resources */ uint32_t const invalid_id{0}; uint32_t const crtc0_id{10}; uint32_t const encoder0_id{20}; uint32_t const encoder1_id{21}; uint32_t const connector0_id{30}; uint32_t const connector1_id{31}; uint32_t const connector2_id{32}; geom::Size const connector0_physical_size_mm{480, 270}; geom::Size const connector1_physical_size_mm{}; geom::Size const connector2_physical_size_mm{}; std::vector possible_encoder_ids_empty; uint32_t const possible_crtcs_mask_empty{0}; size_t const max_simultaneous_outputs{1}; mtd::FakeDRMResources& resources(mock_drm.fake_drm); resources.reset(); resources.add_crtc(crtc0_id, modes0[1]); resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty); resources.add_encoder(encoder1_id, invalid_id, possible_crtcs_mask_empty); resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA, DRM_MODE_CONNECTED, encoder0_id, modes0, possible_encoder_ids_empty, connector0_physical_size_mm); resources.add_connector(connector1_id, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_DISCONNECTED, invalid_id, modes_empty, possible_encoder_ids_empty, connector1_physical_size_mm); resources.add_connector(connector2_id, DRM_MODE_CONNECTOR_eDP, DRM_MODE_DISCONNECTED, encoder1_id, modes_empty, possible_encoder_ids_empty, connector2_physical_size_mm); resources.prepare(); /* Expected results */ std::vector const expected_cards = { { mg::DisplayConfigurationCardId{0}, max_simultaneous_outputs } }; std::vector const expected_outputs = { { mg::DisplayConfigurationOutputId{connector0_id}, mg::DisplayConfigurationCardId{0}, mg::DisplayConfigurationOutputType::hdmia, {}, conf_modes0, 1, connector0_physical_size_mm, true, true, geom::Point(), 1, mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }, { mg::DisplayConfigurationOutputId{connector1_id}, mg::DisplayConfigurationCardId{0}, mg::DisplayConfigurationOutputType::unknown, {}, std::vector(), std::numeric_limits::max(), connector1_physical_size_mm, false, false, geom::Point(), std::numeric_limits::max(), mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }, { mg::DisplayConfigurationOutputId{connector2_id}, mg::DisplayConfigurationCardId{0}, mg::DisplayConfigurationOutputType::edp, {}, std::vector(), std::numeric_limits::max(), connector2_physical_size_mm, false, false, geom::Point(), std::numeric_limits::max(), mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor } }; /* Test body */ auto display = create_display(create_platform()); auto conf = display->configuration(); size_t card_count{0}; conf->for_each_card([&](mg::DisplayConfigurationCard const& card) { ASSERT_LT(card_count, expected_cards.size()); EXPECT_EQ(expected_cards[card_count], card) << "card_count: " << card_count; ++card_count; }); size_t output_count{0}; conf->for_each_output([&](mg::DisplayConfigurationOutput const& output) { ASSERT_LT(output_count, expected_outputs.size()); EXPECT_EQ(expected_outputs[output_count], output) << "output_count: " << output_count; ++output_count; }); EXPECT_EQ(expected_outputs.size(), output_count); } TEST_F(MesaDisplayConfigurationTest, get_kms_connector_id_returns_correct_id) { uint32_t const crtc0_id{10}; uint32_t const encoder0_id{20}; uint32_t const possible_crtcs_mask_empty{0}; std::vector const connector_ids{30, 31}; std::vector encoder_ids{20}; /* Set up DRM resources */ mtd::FakeDRMResources& resources(mock_drm.fake_drm); resources.reset(); resources.add_crtc(crtc0_id, modes0[1]); resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty); for (auto id : connector_ids) { resources.add_connector(id, DRM_MODE_CONNECTOR_DVID, DRM_MODE_CONNECTED, encoder0_id, modes0, encoder_ids, geom::Size()); } resources.prepare(); /* Test body */ auto display = create_display(create_platform()); std::shared_ptr conf = display->configuration(); auto const& kms_conf = std::static_pointer_cast(conf); size_t output_count{0}; conf->for_each_output([&](mg::DisplayConfigurationOutput const& output) { ASSERT_LT(output_count, connector_ids.size()); EXPECT_EQ(connector_ids[output_count], kms_conf->get_kms_connector_id(output.id)); ++output_count; }); } TEST_F(MesaDisplayConfigurationTest, get_kms_connector_id_throws_on_invalid_id) { uint32_t const crtc0_id{10}; uint32_t const encoder0_id{20}; uint32_t const possible_crtcs_mask_empty{0}; std::vector const connector_ids{30, 31}; std::vector encoder_ids{20}; /* Set up DRM resources */ mtd::FakeDRMResources& resources(mock_drm.fake_drm); resources.reset(); resources.add_crtc(crtc0_id, modes0[1]); resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty); for (auto id : connector_ids) { resources.add_connector(id, DRM_MODE_CONNECTOR_VGA, DRM_MODE_CONNECTED, encoder0_id, modes0, encoder_ids, geom::Size()); } resources.prepare(); /* Test body */ auto display = create_display(create_platform()); std::shared_ptr conf = display->configuration(); auto const& kms_conf = std::static_pointer_cast(conf); EXPECT_THROW({ kms_conf->get_kms_connector_id(mg::DisplayConfigurationOutputId{29}); }, std::runtime_error); EXPECT_THROW({ kms_conf->get_kms_connector_id(mg::DisplayConfigurationOutputId{32}); }, std::runtime_error); } TEST_F(MesaDisplayConfigurationTest, returns_updated_configuration) { using namespace ::testing; using namespace std::chrono_literals; uint32_t const invalid_id{0}; std::vector const crtc_ids{10, 11}; std::vector const encoder_ids{20, 21}; std::vector const connector_ids{30, 31}; std::vector const connector_physical_sizes_mm_before{ {480, 270}, {} }; std::vector const connector_physical_sizes_mm_after{ {}, {512, 642} }; std::vector possible_encoder_ids_empty; uint32_t const possible_crtcs_mask_empty{0}; size_t const max_simultaneous_outputs{1}; /* Expected results */ std::vector const expected_cards = { { mg::DisplayConfigurationCardId{0}, max_simultaneous_outputs } }; std::vector const expected_outputs_before = { { mg::DisplayConfigurationOutputId(connector_ids[0]), mg::DisplayConfigurationCardId{0}, mg::DisplayConfigurationOutputType::composite, {}, conf_modes0, 1, connector_physical_sizes_mm_before[0], true, true, geom::Point(), 1, mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }, { mg::DisplayConfigurationOutputId(connector_ids[1]), mg::DisplayConfigurationCardId{0}, mg::DisplayConfigurationOutputType::vga, {}, std::vector(), std::numeric_limits::max(), connector_physical_sizes_mm_before[1], false, false, geom::Point(), std::numeric_limits::max(), mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }, }; std::vector const expected_outputs_after = { { mg::DisplayConfigurationOutputId(connector_ids[0]), mg::DisplayConfigurationCardId{0}, mg::DisplayConfigurationOutputType::composite, {}, std::vector(), std::numeric_limits::max(), connector_physical_sizes_mm_after[0], false, true, geom::Point(), 1, // Ensure current_mode_index is remembered even still mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }, { mg::DisplayConfigurationOutputId(connector_ids[1]), mg::DisplayConfigurationCardId{0}, mg::DisplayConfigurationOutputType::vga, {}, conf_modes0, 1, connector_physical_sizes_mm_after[1], true, false, geom::Point(), 1, mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }, }; /* Set up DRM resources and check */ mtd::FakeDRMResources& resources(mock_drm.fake_drm); auto const syspath = fake_devices.add_device("drm", "card2", NULL, {}, {"DEVTYPE", "drm_minor"}); resources.reset(); resources.add_crtc(crtc_ids[0], modes0[1]); resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask_empty); resources.add_encoder(encoder_ids[1], invalid_id, possible_crtcs_mask_empty); resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite, DRM_MODE_CONNECTED, encoder_ids[0], modes0, possible_encoder_ids_empty, connector_physical_sizes_mm_before[0]); resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_VGA, DRM_MODE_DISCONNECTED, invalid_id, modes_empty, possible_encoder_ids_empty, connector_physical_sizes_mm_before[1]); resources.prepare(); auto display = create_display(create_platform()); auto conf = display->configuration(); size_t card_count{0}; conf->for_each_card([&](mg::DisplayConfigurationCard const& card) { ASSERT_LT(card_count, expected_cards.size()); EXPECT_EQ(expected_cards[card_count], card) << "card_count: " << card_count; ++card_count; }); size_t output_count{0}; conf->for_each_output([&](mg::DisplayConfigurationOutput const& output) { ASSERT_LT(output_count, expected_outputs_before.size()); EXPECT_EQ(expected_outputs_before[output_count], output) << "output_count: " << output_count; ++output_count; }); EXPECT_EQ(expected_outputs_before.size(), output_count); /* Reset DRM resources and check again */ resources.reset(); resources.add_crtc(crtc_ids[1], modes0[1]); resources.add_encoder(encoder_ids[0], invalid_id, possible_crtcs_mask_empty); resources.add_encoder(encoder_ids[1], crtc_ids[1], possible_crtcs_mask_empty); resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite, DRM_MODE_DISCONNECTED, invalid_id, modes_empty, possible_encoder_ids_empty, connector_physical_sizes_mm_after[0]); resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_VGA, DRM_MODE_CONNECTED, encoder_ids[1], modes0, possible_encoder_ids_empty, connector_physical_sizes_mm_after[1]); resources.prepare(); /* Fake a device change so display can fetch updated configuration*/ MainLoop ml; mt::Signal handler_signal; display->register_configuration_change_handler(ml.ml, [&handler_signal]{handler_signal.raise();}); fake_devices.emit_device_changed(syspath); ASSERT_TRUE(handler_signal.wait_for(10s)); conf = display->configuration(); card_count = 0; conf->for_each_card([&](mg::DisplayConfigurationCard const& card) { ASSERT_LT(card_count, expected_cards.size()); EXPECT_EQ(expected_cards[card_count], card) << "card_count: " << card_count; ++card_count; }); output_count = 0; conf->for_each_output([&](mg::DisplayConfigurationOutput const& output) { ASSERT_LT(output_count, expected_outputs_after.size()); EXPECT_EQ(expected_outputs_after[output_count], output) << "output_count: " << output_count; ++output_count; }); EXPECT_EQ(expected_outputs_after.size(), output_count); } TEST_F(MesaDisplayConfigurationTest, new_monitor_defaults_to_preferred_mode) { using namespace ::testing; using namespace std::chrono_literals; uint32_t const crtc_ids[1] = {10}; uint32_t const encoder_ids[2] = {20, 21}; uint32_t const connector_ids[1] = {30}; geom::Size const connector_physical_sizes_mm_before{480, 270}; geom::Size const connector_physical_sizes_mm_after{512, 642}; std::vector possible_encoder_ids_empty; uint32_t const possible_crtcs_mask_empty{0}; int const noutputs = 1; mg::DisplayConfigurationOutput const expected_outputs_before[noutputs] = { { mg::DisplayConfigurationOutputId(connector_ids[0]), mg::DisplayConfigurationCardId{0}, mg::DisplayConfigurationOutputType::composite, {}, conf_modes0, 1, connector_physical_sizes_mm_before, true, true, geom::Point(), 1, mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }, }; mg::DisplayConfigurationOutput const expected_outputs_after[noutputs] = { { mg::DisplayConfigurationOutputId(connector_ids[0]), mg::DisplayConfigurationCardId{0}, mg::DisplayConfigurationOutputType::composite, {}, conf_modes1, 2, connector_physical_sizes_mm_after, true, true, geom::Point(), 2, // current_mode_index changed to preferred as the list of // available modes has also changed mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }, }; mtd::FakeDRMResources& resources(mock_drm.fake_drm); auto const syspath = fake_devices.add_device("drm", "card2", NULL, {}, {"DEVTYPE", "drm_minor"}); resources.reset(); resources.add_crtc(crtc_ids[0], modes0[1]); resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask_empty); resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite, DRM_MODE_CONNECTED, encoder_ids[0], modes0, possible_encoder_ids_empty, connector_physical_sizes_mm_before); resources.prepare(); auto display = create_display(create_platform()); auto conf = display->configuration(); int output_count = 0; conf->for_each_output([&](mg::DisplayConfigurationOutput const& output) { ASSERT_LT(output_count, noutputs); EXPECT_EQ(expected_outputs_before[output_count], output) << "output_count: " << output_count; ++output_count; }); EXPECT_EQ(noutputs, output_count); // Now simulate a change of monitor with different capabilities where the // old current_mode does not exist. Mir should choose the preferred mode. resources.reset(); resources.add_crtc(crtc_ids[0], modes1[1]); resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask_empty); resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite, DRM_MODE_CONNECTED, encoder_ids[1], modes1, possible_encoder_ids_empty, connector_physical_sizes_mm_after); resources.prepare(); MainLoop ml; mt::Signal handler_signal; display->register_configuration_change_handler(ml.ml, [&handler_signal]{handler_signal.raise();}); fake_devices.emit_device_changed(syspath); ASSERT_TRUE(handler_signal.wait_for(10s)); conf = display->configuration(); output_count = 0; conf->for_each_output([&](mg::DisplayConfigurationOutput const& output) { ASSERT_LT(output_count, noutputs); EXPECT_EQ(expected_outputs_after[output_count], output) << "output_count: " << output_count; ++output_count; }); EXPECT_EQ(noutputs, output_count); } TEST_F(MesaDisplayConfigurationTest, does_not_query_drm_unnecesarily) { using namespace ::testing; using namespace std::chrono_literals; uint32_t const crtc_id{10}; uint32_t const encoder_id{20}; uint32_t const connector_id{30}; geom::Size const connector_physical_sizes_mm{480, 270}; std::vector possible_encoder_ids_empty; uint32_t const possible_crtcs_mask_empty{0}; mtd::FakeDRMResources& resources(mock_drm.fake_drm); resources.reset(); resources.add_crtc(crtc_id, modes0[1]); resources.add_encoder(encoder_id, crtc_id, possible_crtcs_mask_empty); resources.add_connector(connector_id, DRM_MODE_CONNECTOR_Composite, DRM_MODE_CONNECTED, encoder_id, modes0, possible_encoder_ids_empty, connector_physical_sizes_mm); resources.prepare(); auto const syspath = fake_devices.add_device("drm", "card2", NULL, {}, {"DEVTYPE", "drm_minor"}); auto display = create_display(create_platform()); // No display change reported yet so display should not query drm EXPECT_CALL(mock_drm, drmModeGetConnector(_,_)).Times(0); display->configuration(); // Now signal a device change MainLoop ml; mt::Signal handler_signal; display->register_configuration_change_handler(ml.ml, [&handler_signal]{handler_signal.raise();}); fake_devices.emit_device_changed(syspath); ASSERT_TRUE(handler_signal.wait_for(10s)); EXPECT_CALL(mock_drm, drmModeGetConnector(_,_)).Times(1); display->configuration(); } ./tests/unit-tests/graphics/mesa/kms/test_nested_authentication.cpp0000644000015600001650000000522512676616125026041 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/graphics/nested_context.h" #include "mir/graphics/platform_operation_message.h" #include "src/platforms/mesa/server/kms/nested_authentication.h" #include "mir_toolkit/mesa/platform_operation.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_nested_context.h" #include "mir/test/fake_shared.h" #include #include namespace mg = mir::graphics; namespace mgm = mir::graphics::mesa; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace { struct NestedAuthentication : public ::testing::Test { ::testing::NiceMock mock_drm; mtd::MockNestedContext mock_nested_context; mgm::NestedAuthentication auth{mt::fake_shared(mock_nested_context)}; }; } TEST_F(NestedAuthentication, uses_nested_context_for_auth_magic) { using namespace testing; unsigned int const magic{332211}; MirMesaAuthMagicRequest const request{magic}; mg::PlatformOperationMessage msg; msg.data.resize(sizeof(request)); std::memcpy(msg.data.data(), &request, sizeof(request)); MirMesaAuthMagicResponse const success_response{0}; mg::PlatformOperationMessage auth_magic_success_response; auth_magic_success_response.data.resize(sizeof(success_response)); std::memcpy(auth_magic_success_response.data.data(), &success_response, sizeof(success_response)); EXPECT_CALL(mock_nested_context, platform_operation(MirMesaPlatformOperation::auth_magic, msg)) .WillOnce(Return(auth_magic_success_response)); auth.auth_magic(magic); } TEST_F(NestedAuthentication, uses_nested_context_for_auth_fd) { using namespace testing; mg::PlatformOperationMessage msg; int const auth_fd{13}; mg::PlatformOperationMessage const response{{}, {auth_fd}}; EXPECT_CALL(mock_nested_context, platform_operation(MirMesaPlatformOperation::auth_fd, msg)) .WillOnce(Return(response)); EXPECT_THAT(auth.authenticated_fd(), Eq(auth_fd)); } ./tests/unit-tests/graphics/mesa/kms/test_display_multi_monitor.cpp0000644000015600001650000004455512676616125026117 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/display.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/platform.h" #include "src/platforms/mesa/server/kms/platform.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/graphics/display_configuration_policy.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/stub_gl_program_factory.h" #include "mir_test_framework/udev_environment.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include #include #include namespace mg = mir::graphics; namespace mgm = mg::mesa; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { class ClonedDisplayConfigurationPolicy : public mg::DisplayConfigurationPolicy { public: void apply_to(mg::DisplayConfiguration& conf) { conf.for_each_output( [&](mg::UserDisplayConfigurationOutput& conf_output) { if (conf_output.connected && conf_output.modes.size() > 0) { conf_output.used = true; conf_output.top_left = geom::Point{0, 0}; conf_output.current_mode_index = conf_output.preferred_mode_index; conf_output.power_mode = mir_power_mode_on; } else { conf_output.used = false; conf_output.power_mode = mir_power_mode_off; } }); } }; class SideBySideDisplayConfigurationPolicy : public mg::DisplayConfigurationPolicy { public: void apply_to(mg::DisplayConfiguration& conf) { int max_x = 0; conf.for_each_output( [&](mg::UserDisplayConfigurationOutput& conf_output) { if (conf_output.connected && conf_output.modes.size() > 0) { conf_output.used = true; conf_output.top_left = geom::Point{max_x, 0}; conf_output.current_mode_index = conf_output.preferred_mode_index; conf_output.power_mode = mir_power_mode_on; conf_output.orientation = mir_orientation_normal; max_x += conf_output.modes[conf_output.preferred_mode_index].size.width.as_int(); } else { conf_output.used = false; conf_output.power_mode = mir_power_mode_off; } }); } }; class MesaDisplayMultiMonitorTest : public ::testing::Test { public: MesaDisplayMultiMonitorTest() { using namespace testing; /* Needed for display start-up */ ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); mock_egl.provide_egl_extensions(); mock_gl.provide_gles_extensions(); /* * Silence uninteresting calls called when cleaning up resources in * the MockGBM destructor, and which are not handled by NiceMock<>. */ EXPECT_CALL(mock_gbm, gbm_bo_get_device(_)) .Times(AtLeast(0)); EXPECT_CALL(mock_gbm, gbm_device_get_fd(_)) .Times(AtLeast(0)); fake_devices.add_standard_device("standard-drm-devices"); } std::shared_ptr create_platform() { return std::make_shared( mir::report::null_display_report(), std::make_shared(), *std::make_shared(), mgm::BypassOption::allowed); } std::shared_ptr create_display_cloned( std::shared_ptr const& platform) { return platform->create_display( std::make_shared(), std::make_shared()); } std::shared_ptr create_display_side_by_side( std::shared_ptr const& platform) { return platform->create_display( std::make_shared(), std::make_shared()); } void setup_outputs(int connected, int disconnected) { using fake = mtd::FakeDRMResources; mtd::FakeDRMResources& resources(mock_drm.fake_drm); modes0.clear(); modes0.push_back(fake::create_mode(1920, 1080, 138500, 2080, 1111, fake::NormalMode)); modes0.push_back(fake::create_mode(1920, 1080, 148500, 2200, 1125, fake::PreferredMode)); modes0.push_back(fake::create_mode(1680, 1050, 119000, 1840, 1080, fake::NormalMode)); modes0.push_back(fake::create_mode(832, 624, 57284, 1152, 667, fake::NormalMode)); geom::Size const connector_physical_size_mm{1597, 987}; resources.reset(); uint32_t const crtc_base_id{10}; uint32_t const encoder_base_id{20}; uint32_t const connector_base_id{30}; for (int i = 0; i < connected; i++) { uint32_t const crtc_id{crtc_base_id + i}; uint32_t const encoder_id{encoder_base_id + i}; uint32_t const all_crtcs_mask{0xff}; crtc_ids.push_back(crtc_id); resources.add_crtc(crtc_id, drmModeModeInfo()); encoder_ids.push_back(encoder_id); resources.add_encoder(encoder_id, crtc_id, all_crtcs_mask); } for (int i = 0; i < connected; i++) { uint32_t const connector_id{connector_base_id + i}; connector_ids.push_back(connector_id); resources.add_connector(connector_id, DRM_MODE_CONNECTOR_VGA, DRM_MODE_CONNECTED, encoder_ids[i], modes0, encoder_ids, connector_physical_size_mm); } for (int i = 0; i < disconnected; i++) { uint32_t const connector_id{connector_base_id + connected + i}; connector_ids.push_back(connector_id); resources.add_connector(connector_id, DRM_MODE_CONNECTOR_VGA, DRM_MODE_DISCONNECTED, 0, modes_empty, encoder_ids, geom::Size{}); } resources.prepare(); } testing::NiceMock mock_egl; testing::NiceMock mock_gl; testing::NiceMock mock_drm; testing::NiceMock mock_gbm; std::vector modes0; std::vector modes_empty; std::vector crtc_ids; std::vector encoder_ids; std::vector connector_ids; mtf::UdevEnvironment fake_devices; }; } TEST_F(MesaDisplayMultiMonitorTest, create_display_sets_all_connected_crtcs) { using namespace testing; int const num_connected_outputs{3}; int const num_disconnected_outputs{2}; uint32_t const fb_id{66}; setup_outputs(num_connected_outputs, num_disconnected_outputs); /* Create DRM FBs */ EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), _, _, _, _, _, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0))); ExpectationSet crtc_setups; /* All crtcs are set */ for (int i = 0; i < num_connected_outputs; i++) { crtc_setups += EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i], fb_id, _, _, Pointee(connector_ids[i]), _, _)) .Times(AtLeast(1)); } /* All crtcs are restored at teardown */ for (int i = 0; i < num_connected_outputs; i++) { EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i], Ne(fb_id), _, _, Pointee(connector_ids[i]), _, _)) .Times(1) .After(crtc_setups); } auto display = create_display_cloned(create_platform()); } TEST_F(MesaDisplayMultiMonitorTest, create_display_creates_shared_egl_contexts) { using namespace testing; int const num_connected_outputs{3}; int const num_disconnected_outputs{2}; EGLContext const shared_context{reinterpret_cast(0x77)}; setup_outputs(num_connected_outputs, num_disconnected_outputs); /* Will create only one shared context */ EXPECT_CALL(mock_egl, eglCreateContext(_, _, EGL_NO_CONTEXT, _)) .WillOnce(Return(shared_context)); /* Will use the shared context when creating other contexts */ EXPECT_CALL(mock_egl, eglCreateContext(_, _, shared_context, _)) .Times(AtLeast(1)); { InSequence s; /* Contexts are made current to initialize DisplayBuffers */ EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,Ne(shared_context))) .Times(AtLeast(1)); /* The shared context is made current finally */ EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,shared_context)) .Times(1); /* Contexts are released at teardown */ EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,EGL_NO_CONTEXT)) .Times(AtLeast(1)); } auto display = create_display_cloned(create_platform()); } namespace { ACTION_P(InvokePageFlipHandler, param) { int const dont_care{0}; char dummy; arg1->page_flip_handler(dont_care, dont_care, dont_care, dont_care, *param); ASSERT_EQ(1, read(arg0, &dummy, 1)); } } TEST_F(MesaDisplayMultiMonitorTest, flip_flips_all_connected_crtcs) { using namespace testing; int const num_connected_outputs{3}; int const num_disconnected_outputs{2}; uint32_t const fb_id{66}; std::vector user_data(num_connected_outputs, nullptr); setup_outputs(num_connected_outputs, num_disconnected_outputs); /* Create DRM FBs */ EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), _, _, _, _, _, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0))); /* All crtcs are flipped */ for (int i = 0; i < num_connected_outputs; i++) { EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), crtc_ids[i], fb_id, _, _)) .Times(2) .WillRepeatedly(DoAll(SaveArg<4>(&user_data[i]), Return(0))); /* Emit fake DRM page-flip events */ EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1)); } /* Handle the events properly */ EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _)) .Times(num_connected_outputs) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[0]), Return(0))) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[1]), Return(0))) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[2]), Return(0))); auto display = create_display_cloned(create_platform()); /* First frame: Page flips are scheduled, but not waited for */ display->for_each_display_sync_group([](mg::DisplaySyncGroup& group) { group.post(); }); /* Second frame: Previous page flips finish (drmHandleEvent) and new ones are scheduled */ display->for_each_display_sync_group([](mg::DisplaySyncGroup& group) { group.post(); }); } namespace { struct FBIDContainer { FBIDContainer(uint32_t base_fb_id) : last_fb_id{base_fb_id} {} int add_fb2(int, uint32_t, uint32_t, uint32_t, uint32_t[4], uint32_t[4], uint32_t[4], uint32_t *buf_id, uint32_t) { *buf_id = last_fb_id; fb_ids.insert(last_fb_id); ++last_fb_id; return 0; } bool check_fb_id(uint32_t i) { if (fb_ids.find(i) != fb_ids.end()) { fb_ids.erase(i); return true; } return false; } std::unordered_set fb_ids; uint32_t last_fb_id; }; MATCHER_P(IsValidFB, fb_id_container, "") { return fb_id_container->check_fb_id(arg); } } TEST_F(MesaDisplayMultiMonitorTest, create_display_uses_different_drm_fbs_for_side_by_side) { using namespace testing; int const num_connected_outputs{3}; int const num_disconnected_outputs{2}; uint32_t const base_fb_id{66}; FBIDContainer fb_id_container{base_fb_id}; setup_outputs(num_connected_outputs, num_disconnected_outputs); /* Create DRM FBs */ EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), _, _, _, _, _, _, _, _)) .Times(num_connected_outputs) .WillRepeatedly(Invoke(&fb_id_container, &FBIDContainer::add_fb2)); ExpectationSet crtc_setups; /* All crtcs are set */ for (int i = 0; i < num_connected_outputs; i++) { crtc_setups += EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i], IsValidFB(&fb_id_container), _, _, Pointee(connector_ids[i]), _, _)) .Times(AtLeast(1)); } /* All crtcs are restored at teardown */ for (int i = 0; i < num_connected_outputs; i++) { EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i], 0, _, _, Pointee(connector_ids[i]), _, _)) .Times(1) .After(crtc_setups); } auto display = create_display_side_by_side(create_platform()); } TEST_F(MesaDisplayMultiMonitorTest, configure_clears_unused_connected_outputs) { using namespace testing; int const num_connected_outputs{3}; int const num_disconnected_outputs{2}; setup_outputs(num_connected_outputs, num_disconnected_outputs); auto display = create_display_cloned(create_platform()); Mock::VerifyAndClearExpectations(&mock_drm); /* All unused connected outputs are cleared */ for (int i = 0; i < num_connected_outputs; i++) { EXPECT_CALL(mock_drm, drmModeSetCursor(mock_drm.fake_drm.fd(), crtc_ids[i], 0, 0, 0)) .Times(1); EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i], 0, 0, 0, nullptr, 0, nullptr)) .Times(1); } /* Set all outputs to unused */ auto conf = display->configuration(); conf->for_each_output( [&](mg::UserDisplayConfigurationOutput& output) { output.used = false; output.current_mode_index = output.preferred_mode_index; output.current_format = mir_pixel_format_xrgb_8888; output.power_mode = mir_power_mode_on; }); display->configure(*conf); Mock::VerifyAndClearExpectations(&mock_drm); /* All crtcs are restored at teardown */ for (int i = 0; i < num_connected_outputs; i++) { EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i], 0, _, _, Pointee(connector_ids[i]), _, _)) .Times(1); } } TEST_F(MesaDisplayMultiMonitorTest, resume_clears_unused_connected_outputs) { using namespace testing; int const num_connected_outputs{3}; int const num_disconnected_outputs{2}; setup_outputs(num_connected_outputs, num_disconnected_outputs); auto display = create_display_cloned(create_platform()); /* Set all outputs to unused */ auto conf = display->configuration(); conf->for_each_output( [&](mg::UserDisplayConfigurationOutput& output) { output.used = false; output.current_mode_index = output.preferred_mode_index; output.current_format = mir_pixel_format_xrgb_8888; output.power_mode = mir_power_mode_on; }); display->configure(*conf); display->pause(); Mock::VerifyAndClearExpectations(&mock_drm); /* All unused connected outputs are cleared */ for (int i = 0; i < num_connected_outputs; i++) { EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i], 0, 0, 0, nullptr, 0, nullptr)) .Times(1); } display->resume(); Mock::VerifyAndClearExpectations(&mock_drm); /* All crtcs are restored at teardown */ for (int i = 0; i < num_connected_outputs; i++) { EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i], 0, _, _, Pointee(connector_ids[i]), _, _)) .Times(1); } } ./tests/unit-tests/graphics/mesa/kms/test_platform.cpp0000644000015600001650000002571112676616125023306 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/platform_ipc_package.h" #include "mir/graphics/event_handler_register.h" #include "mir/graphics/platform_ipc_operations.h" #include "mir/graphics/platform_operation_message.h" #include "src/platforms/mesa/server/kms/platform.h" #include "src/server/report/null_report_factory.h" #include "mir/emergency_cleanup_registry.h" #include "mir/shared_library.h" #include "mir/options/program_option.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/mock_buffer_ipc_message.h" #include "mir/test/doubles/mock_virtual_terminal.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include #include "mir_test_framework/udev_environment.h" #include "mir_test_framework/executable_path.h" #include "mir/test/pipe.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir/test/doubles/fd_matcher.h" #include #include #include #include #include #include namespace mg = mir::graphics; namespace mgm = mir::graphics::mesa; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { const char probe_platform[] = "probe_graphics_platform"; class MesaGraphicsPlatform : public ::testing::Test { public: void SetUp() { ::testing::Mock::VerifyAndClearExpectations(&mock_drm); ::testing::Mock::VerifyAndClearExpectations(&mock_gbm); fake_devices.add_standard_device("standard-drm-devices"); } std::shared_ptr create_platform() { return std::make_shared( mir::report::null_display_report(), std::make_shared(), *std::make_shared(), mgm::BypassOption::allowed); } ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; mtf::UdevEnvironment fake_devices; }; } TEST_F(MesaGraphicsPlatform, connection_ipc_package) { using namespace testing; mir::test::Pipe auth_pipe; int const auth_fd{auth_pipe.read_fd()}; /* First time for master DRM fd, second for authenticated fd */ EXPECT_CALL(mock_drm, open(_,_,_)) .WillOnce(Return(mock_drm.fake_drm.fd())); EXPECT_CALL(mock_drm, drmOpen(_,_)) .WillOnce(Return(auth_fd)); /* Expect proper authorization */ EXPECT_CALL(mock_drm, drmGetMagic(auth_fd,_)); EXPECT_CALL(mock_drm, drmAuthMagic(mock_drm.fake_drm.fd(),_)); EXPECT_CALL(mock_drm, drmClose(mock_drm.fake_drm.fd())); /* Expect authenticated fd to be closed when package is destroyed */ EXPECT_CALL(mock_drm, drmClose(auth_fd)); EXPECT_NO_THROW ( auto platform = create_platform(); auto ipc_ops = platform->make_ipc_operations(); auto pkg = ipc_ops->connection_ipc_package(); ASSERT_TRUE(pkg.get()); ASSERT_EQ(std::vector::size_type{1}, pkg->ipc_fds.size()); ASSERT_EQ(auth_fd, pkg->ipc_fds[0]); ); } TEST_F(MesaGraphicsPlatform, a_failure_while_creating_a_platform_results_in_an_error) { using namespace ::testing; EXPECT_CALL(mock_drm, open(_,_,_)) .WillRepeatedly(Return(-1)); try { auto platform = create_platform(); } catch(...) { return; } FAIL() << "Expected an exception to be thrown."; } TEST_F(MesaGraphicsPlatform, fails_if_no_resources) { using namespace ::testing; EXPECT_CALL(mock_drm, drmModeGetResources(_)) .Times(Exactly(1)) .WillOnce(Return(reinterpret_cast(0))); EXPECT_CALL(mock_drm, drmModeFreeResources(_)) .Times(Exactly(0)); EXPECT_THROW({ auto platform = create_platform(); }, std::runtime_error) << "Expected that c'tor of Platform throws"; } TEST_F(MesaGraphicsPlatform, egl_native_display_is_gbm_device) { auto platform = create_platform(); EXPECT_EQ(mock_gbm.fake_gbm.device, platform->egl_native_display()); } namespace { class ConcurrentCallDetector { public: ConcurrentCallDetector() : threads_in_call{0}, detected_concurrent_calls_{false} { } void call() { if (threads_in_call.fetch_add(1) > 0) detected_concurrent_calls_ = true; std::this_thread::sleep_for(std::chrono::milliseconds{1}); --threads_in_call; } bool detected_concurrent_calls() { return detected_concurrent_calls_; } private: std::atomic threads_in_call; std::atomic detected_concurrent_calls_; }; } /* * This test is not 100% reliable in theory (we are trying to recreate a race * condition after all!), but it can only produce false successes, not false * failures, so it's safe to use. In practice it is reliable enough: I get a * 100% failure rate for this test (1000 out of 1000 repetitions) when testing * without the fix for the race condition we are testing for. */ TEST_F(MesaGraphicsPlatform, drm_close_not_called_concurrently_on_ipc_package_destruction) { using namespace testing; unsigned int const num_threads{10}; unsigned int const num_iterations{10}; ConcurrentCallDetector detector; ON_CALL(mock_drm, drmClose(_)) .WillByDefault(DoAll(InvokeWithoutArgs(&detector, &ConcurrentCallDetector::call), Return(0))); auto platform = create_platform(); std::shared_ptr ipc_ops = platform->make_ipc_operations(); std::vector threads; for (unsigned int i = 0; i < num_threads; i++) { threads.push_back(std::thread{ [ipc_ops] { for (unsigned int i = 0; i < num_iterations; i++) { ipc_ops->connection_ipc_package(); } }}); } for (auto& t : threads) t.join(); EXPECT_FALSE(detector.detected_concurrent_calls()); } struct StubEmergencyCleanupRegistry : mir::EmergencyCleanupRegistry { void add(mir::EmergencyCleanupHandler const&) override { } void add(mir::ModuleEmergencyCleanupHandler handler) override { this->handler = std::move(handler); } mir::ModuleEmergencyCleanupHandler handler; }; TEST_F(MesaGraphicsPlatform, restores_vt_on_emergency_cleanup) { using namespace testing; auto const mock_vt = std::make_shared(); StubEmergencyCleanupRegistry emergency_cleanup_registry; mgm::Platform platform{ mir::report::null_display_report(), mock_vt, emergency_cleanup_registry, mgm::BypassOption::allowed}; EXPECT_CALL(*mock_vt, restore()); (*emergency_cleanup_registry.handler)(); Mock::VerifyAndClearExpectations(mock_vt.get()); } TEST_F(MesaGraphicsPlatform, releases_drm_on_emergency_cleanup) { using namespace testing; auto const null_vt = std::make_shared(); StubEmergencyCleanupRegistry emergency_cleanup_registry; mgm::Platform platform{ mir::report::null_display_report(), null_vt, emergency_cleanup_registry, mgm::BypassOption::allowed}; int const success_code = 0; EXPECT_CALL(mock_drm, drmDropMaster(mock_drm.fake_drm.fd())) .WillOnce(Return(success_code)); (*emergency_cleanup_registry.handler)(); Mock::VerifyAndClearExpectations(&mock_drm); } TEST_F(MesaGraphicsPlatform, does_not_propagate_emergency_cleanup_exceptions) { using namespace testing; auto const mock_vt = std::make_shared(); StubEmergencyCleanupRegistry emergency_cleanup_registry; mgm::Platform platform{ mir::report::null_display_report(), mock_vt, emergency_cleanup_registry, mgm::BypassOption::allowed}; EXPECT_CALL(*mock_vt, restore()) .WillOnce(Throw(std::runtime_error("vt restore exception"))); EXPECT_CALL(mock_drm, drmDropMaster(mock_drm.fake_drm.fd())) .WillOnce(Throw(std::runtime_error("drm drop master exception"))); (*emergency_cleanup_registry.handler)(); Mock::VerifyAndClearExpectations(&mock_drm); } TEST_F(MesaGraphicsPlatform, probe_returns_unsupported_when_no_drm_udev_devices) { mtf::UdevEnvironment udev_environment; mir::options::ProgramOption options; mir::SharedLibrary platform_lib{mtf::server_platform("graphics-mesa-kms")}; auto probe = platform_lib.load_function(probe_platform); EXPECT_EQ(mg::PlatformPriority::unsupported, probe(options)); } TEST_F(MesaGraphicsPlatform, probe_returns_best_when_master) { mtf::UdevEnvironment udev_environment; boost::program_options::options_description po; mir::options::ProgramOption options; udev_environment.add_standard_device("standard-drm-devices"); mir::SharedLibrary platform_lib{mtf::server_platform("graphics-mesa-kms")}; auto probe = platform_lib.load_function(probe_platform); EXPECT_EQ(mg::PlatformPriority::best, probe(options)); } TEST_F(MesaGraphicsPlatform, probe_returns_in_between_when_cant_set_master) { // Regression test for LP: #1528082 using namespace testing; mtf::UdevEnvironment udev_environment; boost::program_options::options_description po; mir::options::ProgramOption options; udev_environment.add_standard_device("standard-drm-devices"); EXPECT_CALL(mock_drm, drmSetMaster(_)) .WillOnce(Return(-1)); mir::SharedLibrary platform_lib{mtf::server_platform("graphics-mesa-kms")}; auto probe = platform_lib.load_function(probe_platform); auto prio = probe(options); EXPECT_THAT(prio, Gt(mg::PlatformPriority::unsupported)); EXPECT_THAT(prio, Lt(mg::PlatformPriority::supported)); } TEST_F(MesaGraphicsPlatform, probe_returns_best_when_drm_devices_vt_option_exist) { mtf::UdevEnvironment udev_environment; boost::program_options::options_description po; mir::options::ProgramOption options; const char *argv[] = {"dummy", "--vt"}; options.parse_arguments(po, 2, argv); udev_environment.add_standard_device("standard-drm-devices"); mir::SharedLibrary platform_lib{mtf::server_platform("graphics-mesa-kms")}; auto probe = platform_lib.load_function(probe_platform); EXPECT_EQ(mg::PlatformPriority::best, probe(options)); } ./tests/unit-tests/graphics/mesa/kms/test_display.cpp0000644000015600001650000006435512676616125023136 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include #include "src/platforms/mesa/server/kms/platform.h" #include "src/platforms/mesa/server/kms/display.h" #include "src/platforms/mesa/server/kms/virtual_terminal.h" #include "src/server/report/logging/display_report.h" #include "mir/logging/logger.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/default_display_configuration_policy.h" #include "mir/renderer/gl/render_target.h" #include "mir/time/steady_clock.h" #include "mir/glib_main_loop.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/advanceable_clock.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/mock_display_report.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/mock_gl_config.h" #include "mir/test/doubles/mock_virtual_terminal.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/test/doubles/mock_event_handler_register.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir_test_framework/udev_environment.h" #include "mir/test/fake_shared.h" #include "mir/test/auto_unblock_thread.h" #include "mir/test/signal.h" #include "mir/test/as_render_target.h" #include #include #include #include #include #include #include namespace mg=mir::graphics; namespace mgm=mir::graphics::mesa; namespace ml=mir::logging; namespace mrl=mir::report::logging; namespace mtd=mir::test::doubles; namespace mtf=mir_test_framework; namespace mr=mir::report; namespace mt=mir::test; namespace { struct MockLogger : public ml::Logger { MOCK_METHOD3(log, void(ml::Severity, const std::string&, const std::string&)); ~MockLogger() noexcept(true) {} }; class MesaDisplayTest : public ::testing::Test { public: MesaDisplayTest() : mock_report{std::make_shared>()}, null_report{mr::null_display_report()} { using namespace testing; ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); mock_egl.provide_egl_extensions(); mock_gl.provide_gles_extensions(); /* * Silence uninteresting calls called when cleaning up resources in * the MockGBM destructor, and which are not handled by NiceMock<>. */ EXPECT_CALL(mock_gbm, gbm_bo_get_device(_)) .Times(AtLeast(0)); EXPECT_CALL(mock_gbm, gbm_device_get_fd(_)) .Times(AtLeast(0)); fake_devices.add_standard_device("standard-drm-devices"); } std::shared_ptr create_platform() { return std::make_shared( mir::report::null_display_report(), std::make_shared(), *std::make_shared(), mgm::BypassOption::allowed); } std::shared_ptr create_display( std::shared_ptr const& platform) { return std::make_shared( platform->drm, platform->gbm, platform->vt, platform->bypass_option(), std::make_shared(), std::make_shared(), null_report); } void setup_post_update_expectations() { using namespace testing; EXPECT_CALL(mock_egl, eglSwapBuffers(mock_egl.fake_egl_display, mock_egl.fake_egl_surface)) .Times(Exactly(2)); EXPECT_CALL(mock_gbm, gbm_surface_lock_front_buffer(mock_gbm.fake_gbm.surface)) .Times(Exactly(2)) .WillOnce(Return(fake.bo1)) .WillOnce(Return(fake.bo2)); EXPECT_CALL(mock_gbm, gbm_bo_get_handle(fake.bo1)) .Times(Exactly(1)) .WillOnce(Return(fake.bo_handle1)); EXPECT_CALL(mock_gbm, gbm_bo_get_handle(fake.bo2)) .Times(Exactly(1)) .WillOnce(Return(fake.bo_handle2)); EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), _, _, _, Pointee(fake.bo_handle1.u32), _, _, _, _)) .Times(Exactly(1)) .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0))); EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), _, _, _, Pointee(fake.bo_handle2.u32), _, _, _, _)) .Times(Exactly(1)) .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id2), Return(0))); } uint32_t get_connected_connector_id() { auto drm_res = mock_drm.fake_drm.resources_ptr(); for (int i = 0; i < drm_res->count_connectors; i++) { auto connector = mock_drm.fake_drm.find_connector(drm_res->connectors[i]); if (connector->connection == DRM_MODE_CONNECTED) return connector->connector_id; } return 0; } uint32_t get_connected_crtc_id() { auto connector_id = get_connected_connector_id(); auto connector = mock_drm.fake_drm.find_connector(connector_id); if (connector) { auto encoder = mock_drm.fake_drm.find_encoder(connector->encoder_id); if (encoder) return encoder->crtc_id; } return 0; } struct FakeData { FakeData() : bo1{reinterpret_cast(0xabcd)}, bo2{reinterpret_cast(0xabce)}, fb_id1{66}, fb_id2{67}, crtc() { bo_handle1.u32 = 0x1234; bo_handle2.u32 = 0x1235; crtc.buffer_id = 88; crtc.crtc_id = 565; } gbm_bo* bo1; gbm_bo* bo2; uint32_t fb_id1; uint32_t fb_id2; gbm_bo_handle bo_handle1; gbm_bo_handle bo_handle2; drmModeCrtc crtc; } fake; ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; std::shared_ptr> const mock_report; std::shared_ptr const null_report; mtf::UdevEnvironment fake_devices; }; } TEST_F(MesaDisplayTest, create_display) { using namespace testing; auto const connector_id = get_connected_connector_id(); auto const crtc_id = get_connected_crtc_id(); /* To display a gbm surface, the MesaDisplay should... */ /* Create a gbm surface to use as the frame buffer */ EXPECT_CALL(mock_gbm, gbm_surface_create(mock_gbm.fake_gbm.device,_,_,_,_)) .Times(Exactly(1)); /* Create an EGL window surface backed by the gbm surface */ EXPECT_CALL(mock_egl, eglCreateWindowSurface(mock_egl.fake_egl_display, mock_egl.fake_configs[0], (EGLNativeWindowType)mock_gbm.fake_gbm.surface, _)) .Times(Exactly(1)); /* Swap the EGL window surface to bring the back buffer to the front */ EXPECT_CALL(mock_egl, eglSwapBuffers(mock_egl.fake_egl_display, mock_egl.fake_egl_surface)) .Times(Exactly(1)); /* Get the gbm_bo object corresponding to the front buffer */ EXPECT_CALL(mock_gbm, gbm_surface_lock_front_buffer(mock_gbm.fake_gbm.surface)) .Times(Exactly(1)) .WillOnce(Return(fake.bo1)); /* Get the DRM buffer handle associated with the gbm_bo */ EXPECT_CALL(mock_gbm, gbm_bo_get_handle(fake.bo1)) .Times(Exactly(1)) .WillOnce(Return(fake.bo_handle1)); /* Create a a DRM FB with the DRM buffer attached */ EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), _, _, _, Pointee(fake.bo_handle1.u32), _, _, _, _)) .Times(Exactly(1)) .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0))); /* Display the DRM FB (first expectation is for cleanup) */ EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_id, Ne(fake.fb_id1), _, _, Pointee(connector_id), _, _)) .Times(AtLeast(0)); EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_id, fake.fb_id1, _, _, Pointee(connector_id), _, _)) .Times(Exactly(1)) .WillOnce(Return(0)); auto display = create_display(create_platform()); } TEST_F(MesaDisplayTest, reset_crtc_on_destruction) { using namespace testing; auto const connector_id = get_connected_connector_id(); auto const crtc_id = get_connected_crtc_id(); uint32_t const fb_id{66}; /* Create DRM FBs */ EXPECT_CALL(mock_drm, drmModeAddFB2(mock_drm.fake_drm.fd(), _, _, _, _, _, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0))); { InSequence s; /* crtc is set */ EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_id, fb_id, _, _, Pointee(connector_id), _, _)) .Times(AtLeast(1)); /* crtc is reset */ EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_id, Ne(fb_id), _, _, Pointee(connector_id), _, _)) .Times(1); } auto display = create_display(create_platform()); } TEST_F(MesaDisplayTest, create_display_drm_failure) { using namespace testing; EXPECT_CALL(mock_drm, open(_,_,_)) .Times(AtLeast(1)) .WillRepeatedly(Return(-1)); EXPECT_CALL(mock_drm, drmClose(_)) .Times(Exactly(0)); EXPECT_THROW( { auto display = create_display(create_platform()); }, std::runtime_error); } TEST_F(MesaDisplayTest, create_display_kms_failure) { using namespace testing; auto platform = create_platform(); Mock::VerifyAndClearExpectations(&mock_drm); EXPECT_CALL(mock_drm, drmModeGetResources(_)) .Times(Exactly(1)) .WillOnce(Return(reinterpret_cast(0))); EXPECT_CALL(mock_drm, drmModeFreeResources(_)) .Times(Exactly(0)); EXPECT_CALL(mock_drm, drmClose(_)) .Times(Exactly(1)); EXPECT_THROW({ auto display = create_display(platform); }, std::runtime_error) << "Expected that c'tor of mgm::Display throws"; } TEST_F(MesaDisplayTest, create_display_gbm_failure) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_create_device(_)) .Times(Exactly(1)) .WillOnce(Return(reinterpret_cast(0))); EXPECT_CALL(mock_gbm, gbm_device_destroy(_)) .Times(Exactly(0)); EXPECT_CALL(mock_drm, drmClose(_)) .Times(Exactly(1)); EXPECT_THROW({ auto platform = create_platform(); }, std::runtime_error) << "Expected c'tor of Platform to throw an exception"; } namespace { ACTION_P(QueuePageFlipEvent, write_drm_fd) { EXPECT_EQ(1, write(write_drm_fd, "a", 1)); } ACTION_P(InvokePageFlipHandler, param) { int const dont_care{0}; char dummy; arg1->page_flip_handler(dont_care, dont_care, dont_care, dont_care, *param); ASSERT_EQ(1, read(arg0, &dummy, 1)); } } TEST_F(MesaDisplayTest, post_update) { using namespace testing; auto const crtc_id = get_connected_crtc_id(); void* user_data{nullptr}; setup_post_update_expectations(); { InSequence s; /* Flip the new FB */ EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), crtc_id, fake.fb_id2, _, _)) .Times(Exactly(1)) .WillOnce(DoAll(QueuePageFlipEvent(mock_drm.fake_drm.write_fd()), SaveArg<4>(&user_data), Return(0))); /* Handle the flip event */ EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _)) .Times(1) .WillOnce(DoAll(InvokePageFlipHandler(&user_data), Return(0))); /* Release last_flipped_bufobj (at destruction time) */ EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo1)) .Times(Exactly(1)); /* Release scheduled_bufobj (at destruction time) */ EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo2)) .Times(Exactly(1)); } auto display = create_display(create_platform()); display->for_each_display_sync_group([](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([](mg::DisplayBuffer& db) { mt::as_render_target(db)->swap_buffers(); }); group.post(); }); } TEST_F(MesaDisplayTest, post_update_flip_failure) { using namespace testing; auto const crtc_id = get_connected_crtc_id(); setup_post_update_expectations(); { InSequence s; /* New FB flip failure */ EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(), crtc_id, fake.fb_id2, _, _)) .Times(Exactly(1)) .WillOnce(Return(-1)); /* Release failed bufobj */ EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo2)) .Times(Exactly(1)); /* Release scheduled_bufobj (at destruction time) */ EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo1)) .Times(Exactly(1)); } EXPECT_THROW( { auto display = create_display(create_platform()); display->for_each_display_sync_group([](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([](mg::DisplayBuffer& db) { mt::as_render_target(db)->swap_buffers(); }); group.post(); }); }, std::runtime_error); } TEST_F(MesaDisplayTest, successful_creation_of_display_reports_successful_setup_of_native_resources) { using namespace ::testing; EXPECT_CALL( *mock_report, report_successful_setup_of_native_resources()).Times(Exactly(1)); EXPECT_CALL( *mock_report, report_successful_egl_make_current_on_construction()).Times(Exactly(1)); EXPECT_CALL( *mock_report, report_successful_egl_buffer_swap_on_construction()).Times(Exactly(1)); EXPECT_CALL( *mock_report, report_successful_drm_mode_set_crtc_on_construction()).Times(Exactly(1)); EXPECT_CALL( *mock_report, report_successful_display_construction()).Times(Exactly(1)); EXPECT_CALL( *mock_report, report_egl_configuration(mock_egl.fake_egl_display,mock_egl.fake_configs[0])).Times(Exactly(1)); auto platform = create_platform(); auto display = std::make_shared( platform->drm, platform->gbm, platform->vt, platform->bypass_option(), std::make_shared(), std::make_shared(), mock_report); } TEST_F(MesaDisplayTest, outputs_correct_string_for_successful_setup_of_native_resources) { using namespace ::testing; auto logger = std::make_shared(); auto reporter = std::make_shared(logger, std::make_shared()); EXPECT_CALL( *logger, log(Eq(ml::Severity::informational), StrEq("Successfully setup native resources."), StrEq("graphics"))).Times(Exactly(1)); reporter->report_successful_setup_of_native_resources(); } TEST_F(MesaDisplayTest, outputs_correct_string_for_successful_egl_make_current_on_construction) { using namespace ::testing; auto logger = std::make_shared(); auto reporter = std::make_shared(logger, std::make_shared()); EXPECT_CALL( *logger, log(Eq(ml::Severity::informational), StrEq("Successfully made egl context current on construction."), StrEq("graphics"))).Times(Exactly(1)); reporter->report_successful_egl_make_current_on_construction(); } TEST_F(MesaDisplayTest, outputs_correct_string_for_successful_egl_buffer_swap_on_construction) { using namespace ::testing; auto logger = std::make_shared(); auto reporter = std::make_shared(logger, std::make_shared()); EXPECT_CALL( *logger, log(Eq(ml::Severity::informational), StrEq("Successfully performed egl buffer swap on construction."), StrEq("graphics"))).Times(Exactly(1)); reporter->report_successful_egl_buffer_swap_on_construction(); } TEST_F(MesaDisplayTest, outputs_correct_string_for_successful_drm_mode_set_crtc_on_construction) { using namespace ::testing; auto logger = std::make_shared(); auto reporter = std::make_shared(logger, std::make_shared()); EXPECT_CALL( *logger, log(Eq(ml::Severity::informational), StrEq("Successfully performed drm mode setup on construction."), StrEq("graphics"))).Times(Exactly(1)); reporter->report_successful_drm_mode_set_crtc_on_construction(); } // Disabled until mesa drm platform and mir platform properly shows support for those extensions TEST_F(MesaDisplayTest, DISABLED_constructor_throws_if_egl_khr_image_pixmap_not_supported) { using namespace ::testing; const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base"; EXPECT_CALL(mock_egl, eglQueryString(_,EGL_EXTENSIONS)) .WillOnce(Return(egl_exts)); EXPECT_THROW( { auto display = create_display(create_platform()); }, std::runtime_error); } TEST_F(MesaDisplayTest, constructor_throws_if_gl_oes_image_not_supported) { using namespace ::testing; const char* gl_exts = "GL_OES_texture_npot GL_OES_blend_func_separate"; EXPECT_CALL(mock_gl, glGetString(GL_EXTENSIONS)) .WillOnce(Return(reinterpret_cast(gl_exts))); EXPECT_THROW( { auto display = create_display(create_platform()); }, std::runtime_error); } TEST_F(MesaDisplayTest, for_each_display_buffer_calls_callback) { using namespace ::testing; auto display = create_display(create_platform()); int callback_count{0}; display->for_each_display_sync_group([&](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([&](mg::DisplayBuffer&) { callback_count++; }); }); EXPECT_NE(0, callback_count); } TEST_F(MesaDisplayTest, constructor_sets_vt_graphics_mode) { using namespace testing; auto mock_vt = std::make_shared(); EXPECT_CALL(*mock_vt, set_graphics_mode()) .Times(1); auto platform = std::make_shared( null_report, mock_vt, *std::make_shared(), mgm::BypassOption::allowed); auto display = create_display(platform); } TEST_F(MesaDisplayTest, pause_drops_drm_master) { using namespace testing; EXPECT_CALL(mock_drm, drmDropMaster(mock_drm.fake_drm.fd())) .Times(1); auto display = create_display(create_platform()); display->pause(); } TEST_F(MesaDisplayTest, resume_sets_drm_master) { using namespace testing; EXPECT_CALL(mock_drm, drmSetMaster(mock_drm.fake_drm.fd())) .Times(1); auto display = create_display(create_platform()); display->resume(); } TEST_F(MesaDisplayTest, set_or_drop_drm_master_failure_throws_and_reports_error) { using namespace testing; EXPECT_CALL(mock_drm, drmDropMaster(_)) .WillOnce(SetErrnoAndReturn(EACCES, -EACCES)); EXPECT_CALL(mock_drm, drmSetMaster(_)) .WillOnce(SetErrnoAndReturn(EPERM, -EPERM)); EXPECT_CALL(*mock_report, report_drm_master_failure(EACCES)); EXPECT_CALL(*mock_report, report_drm_master_failure(EPERM)); auto platform = std::make_shared( mock_report, std::make_shared(), *std::make_shared(), mgm::BypassOption::allowed); auto display = platform->create_display( std::make_shared(), std::make_shared() ); EXPECT_THROW({ display->pause(); }, std::runtime_error); EXPECT_THROW({ display->resume(); }, std::runtime_error); } TEST_F(MesaDisplayTest, configuration_change_registers_video_devices_handler) { using namespace testing; auto display = create_display(create_platform()); mtd::MockEventHandlerRegister mock_register; EXPECT_CALL(mock_register, register_fd_handler_module_ptr(_,_,_)); display->register_configuration_change_handler(mock_register, []{}); } TEST_F(MesaDisplayTest, drm_device_change_event_triggers_handler) { using namespace testing; using namespace std::chrono_literals; auto display = create_display(create_platform()); mir::GLibMainLoop ml{std::make_shared()}; mir::test::Signal done; int const device_add_count{1}; int const device_change_count{10}; int const expected_call_count{device_add_count + device_change_count}; std::atomic call_count{0}; display->register_configuration_change_handler( ml, [&call_count, &ml, &done]() { if (++call_count == expected_call_count) { done.raise(); } }); int const owner{0}; mt::Signal mainloop_started; ml.enqueue(&owner, [&] { mainloop_started.raise(); }); mt::AutoUnblockThread mainLoopThread([&ml]{ml.stop();}, [&ml]{ml.run();}); ASSERT_TRUE(mainloop_started.wait_for(10s)); auto const syspath = fake_devices.add_device("drm", "card2", NULL, {}, {"DEVTYPE", "drm_minor"}); for (int i = 0; i != device_change_count; ++i) { // sleeping between calls to fake_devices hides race conditions std::this_thread::sleep_for(std::chrono::microseconds{500}); fake_devices.emit_device_changed(syspath); } done.wait_for(20s); EXPECT_EQ(expected_call_count, call_count); } TEST_F(MesaDisplayTest, respects_gl_config) { using namespace testing; mtd::MockGLConfig mock_gl_config; EGLint const depth_bits{24}; EGLint const stencil_bits{8}; EXPECT_CALL(mock_gl_config, depth_buffer_bits()) .Times(AtLeast(1)) .WillRepeatedly(Return(depth_bits)); EXPECT_CALL(mock_gl_config, stencil_buffer_bits()) .Times(AtLeast(1)) .WillRepeatedly(Return(stencil_bits)); EXPECT_CALL(mock_egl, eglChooseConfig( _, AllOf(mtd::EGLConfigContainsAttrib(EGL_DEPTH_SIZE, depth_bits), mtd::EGLConfigContainsAttrib(EGL_STENCIL_SIZE, stencil_bits)), _,_,_)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); auto platform = create_platform(); mgm::Display display{ platform->drm, platform->gbm, platform->vt, platform->bypass_option(), std::make_shared(), mir::test::fake_shared(mock_gl_config), null_report}; } TEST_F(MesaDisplayTest, supports_as_low_as_15bit_colour) { // Regression test for LP: #1212753 using namespace testing; mtd::StubGLConfig stub_gl_config; EXPECT_CALL(mock_egl, eglChooseConfig( _, AllOf(mtd::EGLConfigContainsAttrib(EGL_RED_SIZE, 5), mtd::EGLConfigContainsAttrib(EGL_GREEN_SIZE, 5), mtd::EGLConfigContainsAttrib(EGL_BLUE_SIZE, 5), mtd::EGLConfigContainsAttrib(EGL_ALPHA_SIZE, 0)), _,_,_)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); auto platform = create_platform(); mgm::Display display{ platform->drm, platform->gbm, platform->vt, platform->bypass_option(), std::make_shared(), mir::test::fake_shared(stub_gl_config), null_report}; } ./tests/unit-tests/graphics/mesa/kms/test_linux_virtual_terminal.cpp0000644000015600001650000005245512676616125026267 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/platforms/mesa/server/kms/linux_virtual_terminal.h" #include "src/server/report/null_report_factory.h" #include "mir/graphics/event_handler_register.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_display_report.h" #include "mir/test/doubles/mock_event_handler_register.h" #include "mir/test/gmock_fixes.h" #include #include #include "mir/test/gmock_fixes.h" #include #include #include namespace mg = mir::graphics; namespace mgm = mir::graphics::mesa; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mr = mir::report; namespace { class MockVTFileOperations : public mgm::VTFileOperations { public: ~MockVTFileOperations() noexcept {} MOCK_METHOD2(open, int(char const*, int)); MOCK_METHOD1(close, int(int)); MOCK_METHOD3(ioctl, int(int, int, int)); MOCK_METHOD3(ioctl, int(int, int, void*)); MOCK_METHOD3(tcsetattr, int(int, int, const struct termios*)); MOCK_METHOD2(tcgetattr, int(int, struct termios*)); }; class MockPosixProcessOperations : public mgm::PosixProcessOperations { public: ~MockPosixProcessOperations() = default; MOCK_CONST_METHOD0(getpid, pid_t()); MOCK_CONST_METHOD0(getppid, pid_t()); MOCK_CONST_METHOD1(getpgid, pid_t(pid_t)); MOCK_CONST_METHOD1(getsid, pid_t(pid_t)); MOCK_METHOD2(setpgid, int(pid_t, pid_t)); MOCK_METHOD0(setsid, pid_t()); }; // The default return values are appropriate, so // Add a typedef to aid clarity. typedef testing::NiceMock StubPosixProcessOperations; ACTION_TEMPLATE(SetIoctlPointee, HAS_1_TEMPLATE_PARAMS(typename, T), AND_1_VALUE_PARAMS(param)) { *static_cast(arg2) = param; } ACTION_TEMPLATE(SetTcAttrPointee, HAS_1_TEMPLATE_PARAMS(typename, T), AND_1_VALUE_PARAMS(param)) { *static_cast(arg1) = param; } MATCHER_P(ModeUsesSignal, sig, "") { auto vtm = static_cast(arg); return vtm->mode == VT_PROCESS && vtm->relsig == sig && vtm->acqsig == sig; } MATCHER_P(VTModeMatches, mode, "") { auto vtm = static_cast(arg); return vtm->mode == mode.mode && vtm->waitv == mode.waitv && vtm->relsig == mode.relsig && vtm->acqsig == mode.acqsig && vtm->frsig == mode.frsig; } } class LinuxVirtualTerminalTest : public ::testing::Test { public: LinuxVirtualTerminalTest() : fake_vt_fd{5}, fake_kd_mode{KD_TEXT}, fake_vt_mode_auto{VT_AUTO, 0, 0, 0, 0}, fake_vt_mode_process{VT_PROCESS, 0, SIGUSR1, SIGUSR2, 0}, fake_kb_mode{K_RAW}, fake_tc_attr() { } void set_up_expectations_for_current_vt_search(int vt_num) { using namespace testing; int const tmp_fd1{3}; int const tmp_fd2{4}; vt_stat vts = vt_stat(); vts.v_active = vt_num; EXPECT_CALL(mock_fops, open(StrEq("/dev/tty"), _)) .WillOnce(Return(tmp_fd1)); EXPECT_CALL(mock_fops, ioctl(tmp_fd1, VT_GETSTATE, An())) .WillOnce(Return(-1)); EXPECT_CALL(mock_fops, close(tmp_fd1)) .WillOnce(Return(0)); EXPECT_CALL(mock_fops, open(StrEq("/dev/tty0"), _)) .WillOnce(Return(tmp_fd2)); EXPECT_CALL(mock_fops, ioctl(tmp_fd2, VT_GETSTATE, An())) .WillOnce(DoAll(SetIoctlPointee(vts), Return(0))); EXPECT_CALL(mock_fops, close(tmp_fd2)) .WillOnce(Return(0)); } void set_up_expectations_for_vt_setup(int vt_num, bool activate) { set_up_expectations_for_vt_setup(vt_num, activate, fake_vt_mode_auto); } void set_up_expectations_for_vt_setup(int vt_num, bool activate, vt_mode const& vtm) { using namespace testing; std::stringstream ss; ss << "/dev/tty" << vt_num; EXPECT_CALL(mock_fops, open(StrEq(ss.str()), _)) .WillOnce(Return(fake_vt_fd)); if (activate) { EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_ACTIVATE, vt_num)) .WillOnce(Return(0)); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_WAITACTIVE, vt_num)) .WillOnce(Return(0)); } EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, KDGETMODE, An())) .WillOnce(DoAll(SetIoctlPointee(fake_kd_mode), Return(0))); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_GETMODE, An())) .WillOnce(DoAll(SetIoctlPointee(vtm), Return(0))); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, KDGKBMODE, An())) .WillOnce(DoAll(SetIoctlPointee(fake_kb_mode), Return(0))); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, KDSKBMODE, K_OFF)) .WillOnce(Return(0)); EXPECT_CALL(mock_fops, tcgetattr(fake_vt_fd, An())) .WillOnce(DoAll(SetTcAttrPointee(fake_tc_attr), Return(0))); EXPECT_CALL(mock_fops, tcsetattr(fake_vt_fd, TCSANOW, An())) .WillOnce(Return(0)); } void set_up_expectations_for_switch_handler(int sig) { using namespace testing; EXPECT_CALL(mock_event_handler_register, register_signal_handler_module_ptr(ElementsAre(sig), _)) .WillOnce(SaveArg<1>(&sig_handler)); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_SETMODE, MatcherCast(ModeUsesSignal(sig)))); } void set_up_expectations_for_vt_teardown() { set_up_expectations_for_vt_teardown(fake_vt_mode_auto); } void set_up_expectations_for_vt_teardown(vt_mode const& vt_mode) { using namespace testing; set_up_expectations_for_vt_restore(vt_mode); EXPECT_CALL(mock_fops, close(fake_vt_fd)) .WillOnce(Return(0)); } void set_up_expectations_for_vt_restore(vt_mode const& vt_mode) { using namespace testing; EXPECT_CALL(mock_fops, tcsetattr(fake_vt_fd, TCSANOW, An())) .WillOnce(Return(0)); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, KDSKBMODE, fake_kb_mode)) .WillOnce(Return(0)); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, KDSETMODE, fake_kd_mode)) .WillOnce(Return(0)); if (vt_mode.mode == VT_AUTO) { EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_SETMODE, Matcher(VTModeMatches(vt_mode)))) .WillOnce(Return(0)); } else { EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_SETMODE, An())) .Times(0); } } int const fake_vt_fd; int const fake_kd_mode; vt_mode const fake_vt_mode_auto; vt_mode const fake_vt_mode_process; int const fake_kb_mode; struct termios fake_tc_attr; std::function sig_handler; MockVTFileOperations mock_fops; mtd::MockEventHandlerRegister mock_event_handler_register; }; TEST_F(LinuxVirtualTerminalTest, use_provided_vt) { using namespace testing; int const vt_num{7}; InSequence s; set_up_expectations_for_vt_setup(vt_num, true); set_up_expectations_for_vt_teardown(); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); auto null_report = mr::null_display_report(); mgm::LinuxVirtualTerminal vt{fops, std::move(pops), vt_num, null_report}; } TEST_F(LinuxVirtualTerminalTest, sets_up_current_vt) { using namespace testing; int const vt_num{7}; InSequence s; set_up_expectations_for_current_vt_search(vt_num); set_up_expectations_for_vt_setup(vt_num, false); set_up_expectations_for_vt_teardown(); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); auto null_report = mr::null_display_report(); mgm::LinuxVirtualTerminal vt{fops, std::move(pops), 0, null_report}; } TEST_F(LinuxVirtualTerminalTest, failure_to_find_current_vt_throws) { using namespace testing; int const tmp_fd1{3}; InSequence s; EXPECT_CALL(mock_fops, open(StrEq("/dev/tty"), _)) .WillOnce(Return(tmp_fd1)); EXPECT_CALL(mock_fops, ioctl(tmp_fd1, VT_GETSTATE, An())) .WillOnce(Return(-1)); EXPECT_CALL(mock_fops, close(tmp_fd1)) .WillOnce(Return(0)); EXPECT_CALL(mock_fops, open(StrEq("/dev/tty0"), _)) .WillOnce(Return(-1)) .WillOnce(Return(-1)); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); auto null_report = mr::null_display_report(); EXPECT_THROW({ mgm::LinuxVirtualTerminal vt(fops, std::move(pops), 0, null_report); }, std::runtime_error); } TEST_F(LinuxVirtualTerminalTest, does_not_restore_vt_mode_if_vt_process) { using namespace testing; int const vt_num{7}; InSequence s; set_up_expectations_for_current_vt_search(vt_num); set_up_expectations_for_vt_setup(vt_num, false, fake_vt_mode_process); set_up_expectations_for_vt_teardown(fake_vt_mode_process); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); auto null_report = mr::null_display_report(); mgm::LinuxVirtualTerminal vt(fops, std::move(pops), 0, null_report); } TEST_F(LinuxVirtualTerminalTest, sets_graphics_mode) { using namespace testing; int const vt_num{7}; InSequence s; set_up_expectations_for_current_vt_search(vt_num); set_up_expectations_for_vt_setup(vt_num, false); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, KDSETMODE, KD_GRAPHICS)) .WillOnce(Return(0)); set_up_expectations_for_vt_teardown(); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); auto null_report = mr::null_display_report(); mgm::LinuxVirtualTerminal vt(fops, std::move(pops), 0, null_report); vt.set_graphics_mode(); } TEST_F(LinuxVirtualTerminalTest, failure_to_set_graphics_mode_throws) { using namespace testing; int const vt_num{7}; InSequence s; set_up_expectations_for_current_vt_search(vt_num); set_up_expectations_for_vt_setup(vt_num, false); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, KDSETMODE, KD_GRAPHICS)) .WillOnce(Return(-1)); set_up_expectations_for_vt_teardown(); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); auto null_report = mr::null_display_report(); mgm::LinuxVirtualTerminal vt(fops, std::move(pops), 0, null_report); EXPECT_THROW({ vt.set_graphics_mode(); }, std::runtime_error); } TEST_F(LinuxVirtualTerminalTest, uses_sigusr1_for_switch_handling) { using namespace testing; int const vt_num{7}; InSequence s; set_up_expectations_for_current_vt_search(vt_num); set_up_expectations_for_vt_setup(vt_num, false); set_up_expectations_for_switch_handler(SIGUSR1); set_up_expectations_for_vt_teardown(); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); auto null_report = mr::null_display_report(); mgm::LinuxVirtualTerminal vt(fops, std::move(pops), 0, null_report); auto null_handler = [] { return true; }; vt.register_switch_handlers(mock_event_handler_register, null_handler, null_handler); } TEST_F(LinuxVirtualTerminalTest, allows_vt_switch_on_switch_away_handler_success) { using namespace testing; int const vt_num{7}; int const allow_switch{1}; InSequence s; set_up_expectations_for_current_vt_search(vt_num); set_up_expectations_for_vt_setup(vt_num, false); set_up_expectations_for_switch_handler(SIGUSR1); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_RELDISP, allow_switch)); set_up_expectations_for_vt_teardown(); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); auto null_report = mr::null_display_report(); mgm::LinuxVirtualTerminal vt(fops, std::move(pops), 0, null_report); auto succeeding_handler = [] { return true; }; vt.register_switch_handlers(mock_event_handler_register, succeeding_handler, succeeding_handler); /* Fake a VT switch away request */ sig_handler(SIGUSR1); } TEST_F(LinuxVirtualTerminalTest, disallows_vt_switch_on_switch_away_handler_failure) { using namespace testing; int const vt_num{7}; int const disallow_switch{0}; mtd::MockDisplayReport mock_report; InSequence s; set_up_expectations_for_current_vt_search(vt_num); set_up_expectations_for_vt_setup(vt_num, false); set_up_expectations_for_switch_handler(SIGUSR1); /* First switch away attempt */ EXPECT_CALL(mock_report, report_vt_switch_away_failure()); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_RELDISP, MatcherCast(disallow_switch))); /* Second switch away attempt */ EXPECT_CALL(mock_report, report_vt_switch_away_failure()); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_RELDISP, MatcherCast(disallow_switch))); set_up_expectations_for_vt_teardown(); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); mgm::LinuxVirtualTerminal vt(fops, std::move(pops), 0, mt::fake_shared(mock_report)); auto failing_handler = [] { return false; }; vt.register_switch_handlers(mock_event_handler_register, failing_handler, failing_handler); /* Fake a VT switch away request */ sig_handler(SIGUSR1); /* Fake another VT switch away request */ sig_handler(SIGUSR1); } TEST_F(LinuxVirtualTerminalTest, reports_failed_vt_switch_back_attempt) { using namespace testing; int const vt_num{7}; int const allow_switch{1}; mtd::MockDisplayReport mock_report; InSequence s; set_up_expectations_for_current_vt_search(vt_num); set_up_expectations_for_vt_setup(vt_num, false); set_up_expectations_for_switch_handler(SIGUSR1); /* Switch away */ EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_RELDISP, allow_switch)); /* Switch back */ EXPECT_CALL(mock_report, report_vt_switch_back_failure()); EXPECT_CALL(mock_fops, ioctl(fake_vt_fd, VT_RELDISP, VT_ACKACQ)); set_up_expectations_for_vt_teardown(); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); mgm::LinuxVirtualTerminal vt(fops, std::move(pops), 0, mt::fake_shared(mock_report)); auto succeeding_handler = [] { return true; }; auto failing_handler = [] { return false; }; vt.register_switch_handlers(mock_event_handler_register, succeeding_handler, failing_handler); /* Fake a VT switch away request */ sig_handler(SIGUSR1); /* Fake a VT switch back request */ sig_handler(SIGUSR1); } TEST_F(LinuxVirtualTerminalTest, does_not_try_to_reaquire_session_leader) { using namespace testing; int const vt_num{7}; InSequence s; auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr>(new NiceMock()); auto null_report = mr::null_display_report(); pid_t const mockpid{1234}; ON_CALL(*pops, getpid()).WillByDefault(Return(mockpid)); ON_CALL(*pops, getsid(Eq(0))).WillByDefault(Return(mockpid)); ON_CALL(*pops, getsid(Eq(mockpid))).WillByDefault(Return(mockpid)); EXPECT_CALL(*pops, setpgid(_,_)).Times(0); EXPECT_CALL(*pops, setsid()).Times(0); set_up_expectations_for_vt_setup(vt_num, true); set_up_expectations_for_vt_teardown(); mgm::LinuxVirtualTerminal vt{fops, std::move(pops), vt_num, null_report}; } TEST_F(LinuxVirtualTerminalTest, relinquishes_group_leader_before_claiming_session_leader) { using namespace testing; int const vt_num{7}; InSequence s; auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr>(new NiceMock()); auto null_report = mr::null_display_report(); pid_t const mockpid{1234}; pid_t const mock_parent_pid{4567}; ON_CALL(*pops, getpid()).WillByDefault(Return(mockpid)); ON_CALL(*pops, getpgid(Eq(0))).WillByDefault(Return(mockpid)); ON_CALL(*pops, getpgid(Eq(mockpid))).WillByDefault(Return(mockpid)); ON_CALL(*pops, getpgid(Eq(mock_parent_pid))).WillByDefault(Return(mock_parent_pid)); ON_CALL(*pops, getppid()).WillByDefault(Return(mock_parent_pid)); ON_CALL(*pops, getsid(Eq(0))).WillByDefault(Return(1)); ON_CALL(*pops, getsid(Eq(mockpid))).WillByDefault(Return(1)); EXPECT_CALL(*pops, setpgid(Eq(0), Eq(mock_parent_pid))) .Times(1) .WillOnce(Return(0)); EXPECT_CALL(*pops, setsid()) .Times(1) .WillOnce(Return(0)); set_up_expectations_for_vt_setup(vt_num, true); set_up_expectations_for_vt_teardown(); mgm::LinuxVirtualTerminal vt{fops, std::move(pops), vt_num, null_report}; } TEST_F(LinuxVirtualTerminalTest, exception_if_setting_process_group_fails) { using namespace testing; int const vt_num{7}; InSequence s; auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr>(new NiceMock()); auto null_report = mr::null_display_report(); pid_t const mockpid{1234}; pid_t const mock_parent_pid{4567}; ON_CALL(*pops, getpid()).WillByDefault(Return(mockpid)); ON_CALL(*pops, getpgid(Eq(0))).WillByDefault(Return(mockpid)); ON_CALL(*pops, getpgid(Eq(mockpid))).WillByDefault(Return(mockpid)); ON_CALL(*pops, getpgid(Eq(mock_parent_pid))).WillByDefault(Return(mock_parent_pid)); ON_CALL(*pops, getppid()).WillByDefault(Return(mock_parent_pid)); ON_CALL(*pops, getsid(Eq(0))).WillByDefault(Return(1)); ON_CALL(*pops, getsid(Eq(mockpid))).WillByDefault(Return(1)); EXPECT_CALL(*pops, setpgid(Eq(0), Eq(mock_parent_pid))) .Times(1) .WillOnce(Return(-1)); EXPECT_THROW({ mgm::LinuxVirtualTerminal vt(fops, std::move(pops), vt_num, null_report); }, std::runtime_error); } TEST_F(LinuxVirtualTerminalTest, exception_if_becoming_session_leader_fails) { using namespace testing; int const vt_num{7}; InSequence s; auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr>(new NiceMock()); auto null_report = mr::null_display_report(); pid_t const mockpid{1234}; pid_t const mock_parent_pid{4567}; ON_CALL(*pops, getpid()).WillByDefault(Return(mockpid)); ON_CALL(*pops, getpgid(Eq(0))).WillByDefault(Return(mockpid)); ON_CALL(*pops, getpgid(Eq(mockpid))).WillByDefault(Return(mockpid)); ON_CALL(*pops, getpgid(Eq(mock_parent_pid))).WillByDefault(Return(mock_parent_pid)); ON_CALL(*pops, getppid()).WillByDefault(Return(mock_parent_pid)); ON_CALL(*pops, getsid(Eq(0))).WillByDefault(Return(1)); ON_CALL(*pops, getsid(Eq(mockpid))).WillByDefault(Return(1)); EXPECT_CALL(*pops, setpgid(Eq(0), Eq(mock_parent_pid))) .Times(1) .WillOnce(Return(0)); EXPECT_CALL(*pops, setsid()) .Times(1) .WillOnce(Return(-1)); EXPECT_THROW({ mgm::LinuxVirtualTerminal vt(fops, std::move(pops), vt_num, null_report); }, std::runtime_error); } TEST_F(LinuxVirtualTerminalTest, restores_keyboard_and_graphics) { using namespace testing; int const vt_num{7}; InSequence s; set_up_expectations_for_vt_setup(vt_num, true); set_up_expectations_for_vt_restore(fake_vt_mode_auto); auto fops = mt::fake_shared(mock_fops); auto pops = std::unique_ptr(new StubPosixProcessOperations()); auto null_report = mr::null_display_report(); mgm::LinuxVirtualTerminal vt(fops, std::move(pops), vt_num, null_report); vt.restore(); Mock::VerifyAndClearExpectations(&mock_fops); set_up_expectations_for_vt_teardown(); } ./tests/unit-tests/graphics/mesa/kms/test_guest_platform.cpp0000644000015600001650000000706012676616125024512 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/nested_context.h" #include "src/platforms/mesa/server/kms/guest_platform.h" #include "mir/graphics/buffer_properties.h" #include "mir/graphics/platform_ipc_operations.h" #include "mir_toolkit/mesa/platform_operation.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/mock_buffer_ipc_message.h" #include "mir/test/doubles/fd_matcher.h" #include "mir/test/doubles/mock_nested_context.h" #include #include #include namespace mg = mir::graphics; namespace mgm = mir::graphics::mesa; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace geom = mir::geometry; namespace { class MesaGuestPlatformTest : public ::testing::Test { public: MesaGuestPlatformTest() { using namespace testing; MirMesaSetGBMDeviceResponse const response_success{0}; mg::PlatformOperationMessage set_gbm_device_success_msg; set_gbm_device_success_msg.data.resize(sizeof(response_success)); std::memcpy(set_gbm_device_success_msg.data.data(), &response_success, sizeof(response_success)); ON_CALL(mock_nested_context, platform_fd_items()) .WillByDefault(Return(std::vector{mock_drm.fake_drm.fd()})); ON_CALL(mock_nested_context, platform_operation(MirMesaPlatformOperation::set_gbm_device, _)) .WillByDefault(Return(set_gbm_device_success_msg)); } protected: ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; ::testing::NiceMock mock_nested_context; }; } TEST_F(MesaGuestPlatformTest, auth_fd_is_delegated_to_nested_context) { using namespace testing; int const auth_fd{13}; mg::PlatformOperationMessage auth_fd_response{{},{auth_fd}}; EXPECT_CALL(mock_nested_context, platform_operation(MirMesaPlatformOperation::set_gbm_device, _)); EXPECT_CALL(mock_nested_context, platform_operation(MirMesaPlatformOperation::auth_fd, _)) .WillOnce(Return(auth_fd_response)); mgm::GuestPlatform native(mt::fake_shared(mock_nested_context)); auto ipc_ops = native.make_ipc_operations(); ipc_ops->connection_ipc_package(); } TEST_F(MesaGuestPlatformTest, sets_gbm_device_during_initialization) { MirMesaSetGBMDeviceRequest const request{mock_gbm.fake_gbm.device}; mg::PlatformOperationMessage request_msg; request_msg.data.resize(sizeof(request)); std::memcpy(request_msg.data.data(), &request, sizeof(request)); EXPECT_CALL(mock_nested_context, platform_operation(MirMesaPlatformOperation::set_gbm_device, request_msg)); mgm::GuestPlatform native(mt::fake_shared(mock_nested_context)); } ./tests/unit-tests/graphics/mesa/kms/test_bypass.cpp0000644000015600001650000002064112676616125022760 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "src/platforms/mesa/server/kms/bypass.h" #include "mir/test/doubles/mock_display_buffer.h" #include "mir/test/doubles/fake_renderable.h" #include #include namespace mg=mir::graphics; namespace geom=mir::geometry; namespace mgm=mir::graphics::mesa; namespace mtd=mir::test::doubles; struct BypassMatchTest : public testing::Test { geom::Rectangle const primary_monitor{{0, 0},{1920, 1200}}; geom::Rectangle const secondary_monitor{{1920, 0},{1920, 1200}}; }; TEST_F(BypassMatchTest, nothing_matches_nothing) { mg::RenderableList empty_list{}; mgm::BypassMatch matcher(primary_monitor); EXPECT_EQ(empty_list.rend(), std::find_if(empty_list.rbegin(), empty_list.rend(), matcher)); } TEST_F(BypassMatchTest, small_window_not_bypassed) { mgm::BypassMatch matcher(primary_monitor); mg::RenderableList list{ std::make_shared(12, 34, 56, 78) }; EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), matcher)); } TEST_F(BypassMatchTest, single_fullscreen_window_bypassed) { auto window = std::make_shared(0, 0, 1920, 1200); mgm::BypassMatch matcher(primary_monitor); mg::RenderableList list{window}; auto it = std::find_if(list.rbegin(), list.rend(), matcher); EXPECT_NE(list.rend(), it); EXPECT_EQ(window, *it); } TEST_F(BypassMatchTest, translucent_fullscreen_window_not_bypassed) { mgm::BypassMatch matcher(primary_monitor); mg::RenderableList list{ std::make_shared(geom::Rectangle{{0, 0}, {1920, 1200}}, 0.5f) }; EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), matcher)); } TEST_F(BypassMatchTest, shaped_fullscreen_window_not_bypassed) { mgm::BypassMatch matcher(primary_monitor); mg::RenderableList list{ std::make_shared(geom::Rectangle{{0, 0}, {1920, 1200}}, 1.0f, false) }; EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), matcher)); } TEST_F(BypassMatchTest, offset_fullscreen_window_not_bypassed) { mgm::BypassMatch matcher(primary_monitor); mg::RenderableList list{ std::make_shared(10, 50, 1920, 1200) }; EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), matcher)); } TEST_F(BypassMatchTest, obscured_fullscreen_window_not_bypassed) { mgm::BypassMatch matcher(primary_monitor); mg::RenderableList list{ std::make_shared(0, 0, 1920, 1200), std::make_shared(20, 30, 40, 50) }; EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), matcher)); } TEST_F(BypassMatchTest, translucently_obscured_fullscreen_window_not_bypassed) { // Regression test for LP: #1266385 mgm::BypassMatch matcher(primary_monitor); mg::RenderableList list{ std::make_shared(0, 0, 1920, 1200), std::make_shared(geom::Rectangle{{20, 30}, {40, 50}}, 0.5f) }; EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), matcher)); } TEST_F(BypassMatchTest, unobscured_fullscreen_window_bypassed) { mgm::BypassMatch matcher(primary_monitor); auto bypassed = std::make_shared(0, 0, 1920, 1200); mg::RenderableList list{ std::make_shared(20, 30, 40, 50), bypassed }; auto it = std::find_if(list.rbegin(), list.rend(), matcher); EXPECT_NE(list.rend(), it); EXPECT_EQ(bypassed, *it); } TEST_F(BypassMatchTest, unobscured_fullscreen_alpha_window_not_bypassed) { mgm::BypassMatch matcher(primary_monitor); mg::RenderableList list{ std::make_shared(20, 30, 40, 50), std::make_shared(geom::Rectangle{{0, 0}, {1920, 1200}}, 0.9f) }; EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), matcher)); } TEST_F(BypassMatchTest, many_fullscreen_windows_only_bypass_top) { mgm::BypassMatch matcher(primary_monitor); auto bypassed = std::make_shared(0, 0, 1920, 1200); auto fullscreen_not_bypassed = std::make_shared(0, 0, 1920, 1200); mg::RenderableList list{ fullscreen_not_bypassed, std::make_shared(9, 10, 11, 12), fullscreen_not_bypassed, std::make_shared(5, 6, 7, 8), fullscreen_not_bypassed, std::make_shared(1, 2, 3, 4), bypassed }; auto it = std::find_if(list.rbegin(), list.rend(), matcher); EXPECT_NE(list.rend(), it); EXPECT_EQ(bypassed, *it); } TEST_F(BypassMatchTest, many_fullscreen_windows_only_bypass_top_rectangular) { mgm::BypassMatch matcher(primary_monitor); auto bypassed = std::make_shared(0, 0, 1920, 1200); mg::RenderableList list{ std::make_shared(primary_monitor, 0.5f, false), std::make_shared(geom::Rectangle{{9, 10}, {11, 12}}), std::make_shared(primary_monitor, 1.0f, true), std::make_shared(geom::Rectangle{{5, 6}, {7, 8}}), std::make_shared(primary_monitor), std::make_shared(geom::Rectangle{{1, 2}, {3, 4}}), std::make_shared(primary_monitor, 1.0f, false), bypassed }; auto it = std::find_if(list.rbegin(), list.rend(), matcher); EXPECT_NE(list.rend(), it); EXPECT_EQ(bypassed, *it); } TEST_F(BypassMatchTest, nonrectangular_not_bypassable) { mgm::BypassMatch matcher(primary_monitor); auto bypassed = std::make_shared(0, 0, 1920, 1200); auto fullscreen_not_bypassed = std::make_shared(0, 0, 1920, 1200); mg::RenderableList list{ std::make_shared(1, 2, 3, 4), std::make_shared(primary_monitor, 1.0f, false) }; EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), matcher)); } TEST_F(BypassMatchTest, multimonitor_one_bypassed) { mgm::BypassMatch primary_matcher(primary_monitor); mgm::BypassMatch secondary_matcher(secondary_monitor); auto bypassed = std::make_shared(1920, 0, 1920, 1200); mg::RenderableList list{ std::make_shared(20, 30, 40, 50), bypassed }; EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), primary_matcher)); auto it = std::find_if(list.rbegin(), list.rend(), secondary_matcher); EXPECT_NE(list.rend(), it); EXPECT_EQ(bypassed, *it); } TEST_F(BypassMatchTest, dual_bypass) { mgm::BypassMatch primary_matcher(primary_monitor); mgm::BypassMatch secondary_matcher(secondary_monitor); auto primary_bypassed = std::make_shared(0, 0, 1920, 1200); auto secondary_bypassed = std::make_shared(1920, 0, 1920, 1200); mg::RenderableList list{ primary_bypassed, secondary_bypassed }; auto it = std::find_if(list.rbegin(), list.rend(), primary_matcher); EXPECT_NE(list.rend(), it); EXPECT_EQ(primary_bypassed, *it); it = std::find_if(list.rbegin(), list.rend(), secondary_matcher); EXPECT_NE(list.rend(), it); EXPECT_EQ(secondary_bypassed, *it); } TEST_F(BypassMatchTest, multimonitor_oversized_no_bypass) { mgm::BypassMatch primary_matcher(primary_monitor); mgm::BypassMatch secondary_matcher(secondary_monitor); mg::RenderableList list{ std::make_shared(0, 0, 3840, 1200) }; EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), primary_matcher)); EXPECT_EQ(list.rend(), std::find_if(list.rbegin(), list.rend(), secondary_matcher)); } ./tests/unit-tests/graphics/mesa/kms/test_graphics_platform.cpp0000644000015600001650000000565012676616125025166 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Guest * Kevin DuBois */ #include "mir/graphics/platform.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/buffer_properties.h" #include "mir/graphics/platform_ipc_operations.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "mir/options/program_option.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir_test_framework/udev_environment.h" #include "src/platforms/mesa/server/kms/platform.h" #include "mir/logging/dumb_console_logger.h" #include namespace mg = mir::graphics; namespace mgm = mg::mesa; namespace ml = mir::logging; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; namespace mo = mir::options; namespace mtf = mir_test_framework; class GraphicsPlatform : public ::testing::Test { public: GraphicsPlatform() : logger(std::make_shared()) { using namespace testing; ON_CALL(mock_gbm, gbm_bo_get_width(_)) .WillByDefault(Return(320)); ON_CALL(mock_gbm, gbm_bo_get_height(_)) .WillByDefault(Return(240)); // FIXME: This format needs to match Mesa's first supported pixel // format or tests will fail. The coupling is presently loose. ON_CALL(mock_gbm, gbm_bo_get_format(_)) .WillByDefault(Return(GBM_FORMAT_ARGB8888)); fake_devices.add_standard_device("standard-drm-devices"); } std::shared_ptr create_platform() { return std::make_shared( mir::report::null_display_report(), std::make_shared(), *std::make_shared(), mgm::BypassOption::allowed); } std::shared_ptr logger; ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; mtf::UdevEnvironment fake_devices; }; #include "../../test_graphics_platform.h" ./tests/unit-tests/graphics/mesa/kms/test_gbm_buffer.cpp0000644000015600001650000001604712676616125023562 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir_test_framework/udev_environment.h" #include "src/platforms/mesa/server/kms/platform.h" #include "src/platforms/mesa/server/common/gbm_buffer.h" #include "src/platforms/mesa/server/common/buffer_allocator.h" #include "mir/graphics/buffer_properties.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/null_virtual_terminal.h" #include #include #include #include #include namespace mg=mir::graphics; namespace mgm=mir::graphics::mesa; namespace geom=mir::geometry; namespace mtd=mir::test::doubles; namespace mtf=mir_test_framework; class GBMBufferTest : public ::testing::Test { protected: virtual void SetUp() { using namespace testing; fake_devices.add_standard_device("standard-drm-devices"); size = geom::Size{300, 200}; pf = mir_pixel_format_argb_8888; stride = geom::Stride{4 * size.width.as_uint32_t()}; usage = mg::BufferUsage::hardware; buffer_properties = mg::BufferProperties{size, pf, usage}; ON_CALL(mock_gbm, gbm_bo_get_width(_)) .WillByDefault(Return(size.width.as_uint32_t())); ON_CALL(mock_gbm, gbm_bo_get_height(_)) .WillByDefault(Return(size.height.as_uint32_t())); ON_CALL(mock_gbm, gbm_bo_get_format(_)) .WillByDefault(Return(GBM_BO_FORMAT_ARGB8888)); ON_CALL(mock_gbm, gbm_bo_get_stride(_)) .WillByDefault(Return(stride.as_uint32_t())); platform = std::make_shared( mir::report::null_display_report(), std::make_shared(), *std::make_shared(), mgm::BypassOption::allowed); allocator.reset(new mgm::BufferAllocator(platform->gbm->device, mgm::BypassOption::allowed, mgm::BufferImportMethod::gbm_native_pixmap)); } mir::renderer::gl::TextureSource* as_texture_source(std::shared_ptr const& buffer) { return dynamic_cast(buffer->native_buffer_base()); } ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; std::shared_ptr platform; std::unique_ptr allocator; // Defaults MirPixelFormat pf; geom::Size size; geom::Stride stride; mg::BufferUsage usage; mg::BufferProperties buffer_properties; mtf::UdevEnvironment fake_devices; }; TEST_F(GBMBufferTest, dimensions_test) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); auto buffer = allocator->alloc_buffer(buffer_properties); ASSERT_EQ(size, buffer->size()); } TEST_F(GBMBufferTest, buffer_has_expected_pixel_format) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); auto buffer(allocator->alloc_buffer(buffer_properties)); ASSERT_EQ(pf, buffer->pixel_format()); } TEST_F(GBMBufferTest, stride_has_sane_value) { using namespace testing; EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)); EXPECT_CALL(mock_gbm, gbm_bo_destroy(_)); // RGBA 8888 cannot take less than 4 bytes // TODO: is there a *maximum* sane value for stride? geom::Stride minimum(size.width.as_uint32_t() * 4); auto buffer(allocator->alloc_buffer(buffer_properties)); ASSERT_LE(minimum, buffer->stride()); } TEST_F(GBMBufferTest, buffer_native_handle_has_correct_size) { using namespace testing; auto buffer = allocator->alloc_buffer(buffer_properties); auto native_handle = buffer->native_buffer_handle(); EXPECT_EQ(1, native_handle->fd_items); EXPECT_EQ(0, native_handle->data_items); } MATCHER_P(GEMFlinkHandleIs, value, "") { auto flink = reinterpret_cast(arg); return flink->handle == value; } ACTION_P(SetGEMFlinkName, value) { auto flink = reinterpret_cast(arg2); flink->name = value; } TEST_F(GBMBufferTest, buffer_native_handle_contains_correct_data) { using namespace testing; uint32_t prime_fd{0x77}; gbm_bo_handle mock_handle; mock_handle.u32 = 0xdeadbeef; EXPECT_CALL(mock_gbm, gbm_bo_get_handle(_)) .Times(Exactly(1)) .WillOnce(Return(mock_handle)); EXPECT_CALL(mock_drm, drmPrimeHandleToFD(_,mock_handle.u32,_,_)) .Times(Exactly(1)) .WillOnce(DoAll(SetArgPointee<3>(prime_fd), Return(0))); auto buffer = allocator->alloc_buffer(buffer_properties); auto handle = buffer->native_buffer_handle(); EXPECT_EQ(prime_fd, static_cast(handle->fd[0])); EXPECT_EQ(stride.as_uint32_t(), static_cast(handle->stride)); } TEST_F(GBMBufferTest, buffer_creation_throws_on_prime_fd_failure) { using namespace testing; EXPECT_CALL(mock_drm, drmPrimeHandleToFD(_,_,_,_)) .Times(Exactly(1)) .WillOnce(Return(-1)); EXPECT_THROW({ auto buffer = allocator->alloc_buffer(buffer_properties); }, std::runtime_error); } TEST_F(GBMBufferTest, gl_bind_to_texture_egl_image_creation_failed) { using namespace testing; ON_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .WillByDefault(Return(EGL_NO_IMAGE_KHR)); EXPECT_THROW({ auto buffer = allocator->alloc_buffer(buffer_properties); as_texture_source(buffer)->gl_bind_to_texture(); }, std::runtime_error); } TEST_F(GBMBufferTest, gl_bind_to_texture_uses_egl_image) { using namespace testing; { InSequence seq; EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .Times(Exactly(1)); EXPECT_CALL(mock_egl, glEGLImageTargetTexture2DOES(_,mock_egl.fake_egl_image)) .Times(Exactly(1)); EXPECT_CALL(mock_egl, eglDestroyImageKHR(_,mock_egl.fake_egl_image)) .Times(Exactly(1)); } EXPECT_NO_THROW({ auto buffer = allocator->alloc_buffer(buffer_properties); as_texture_source(buffer)->gl_bind_to_texture(); }); } ./tests/unit-tests/graphics/mesa/kms/CMakeLists.txt0000644000015600001650000000301612676616157022456 0ustar jenkinsjenkinsmir_add_wrapped_executable( mir_unit_tests_mesa-kms ${CMAKE_CURRENT_SOURCE_DIR}/test_gbm_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_allocator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_graphics_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_generic.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_multi_monitor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_configuration.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_real_kms_output.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_kms_page_flipper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_linux_virtual_terminal.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_cursor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_guest_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_bypass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_nested_authentication.cpp ${MIR_SERVER_OBJECTS} ${MIR_COMMON_OBJECTS} $ $ ) add_dependencies(mir_unit_tests_mesa-kms GMock) target_link_libraries( mir_unit_tests_mesa-kms mir-test-static mir-test-framework-static mir-test-doubles-static mir-test-doubles-platform-static mirsharedmesaservercommon-static ${DRM_LDFLAGS} ${DRM_LIBRARIES} ) if (MIR_RUN_UNIT_TESTS) mir_discover_tests_with_fd_leak_detection(mir_unit_tests_mesa-kms LD_PRELOAD=libumockdev-preload.so.0 G_SLICE=always-malloc G_DEBUG=gc-friendly) endif (MIR_RUN_UNIT_TESTS) ./tests/unit-tests/graphics/mesa/kms/test_display_buffer.cpp0000644000015600001650000004557012676616125024465 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "mir/test/doubles/null_emergency_cleanup.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "src/platforms/mesa/server/kms/platform.h" #include "src/platforms/mesa/server/kms/display_buffer.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/mock_gbm.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/stub_gbm_native_buffer.h" #include "mir_test_framework/udev_environment.h" #include "mir/test/doubles/fake_renderable.h" #include "mock_kms_output.h" #include #include #include using namespace testing; using namespace mir; using namespace std; using namespace mir::test; using namespace mir::test::doubles; using namespace mir_test_framework; using namespace mir::graphics; using namespace mir::graphics::mesa; using mir::report::null_display_report; class MesaDisplayBufferTest : public Test { public: int const mock_refresh_rate = 60; MesaDisplayBufferTest() : mock_bypassable_buffer{std::make_shared>()} , mock_software_buffer{std::make_shared>()} , fake_bypassable_renderable{ std::make_shared(display_area)} , fake_software_renderable{ std::make_shared(display_area)} , stub_gbm_native_buffer{ std::make_shared(display_area.size)} , stub_shm_native_buffer{ std::make_shared()} , bypassable_list{fake_bypassable_renderable} { ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); mock_egl.provide_egl_extensions(); mock_gl.provide_gles_extensions(); fake_bo = reinterpret_cast(123); ON_CALL(mock_gbm, gbm_surface_lock_front_buffer(_)) .WillByDefault(Return(fake_bo)); fake_handle.u32 = 123; ON_CALL(mock_gbm, gbm_bo_get_handle(_)) .WillByDefault(Return(fake_handle)); ON_CALL(mock_gbm, gbm_bo_get_stride(_)) .WillByDefault(Return(456)); fake_devices.add_standard_device("standard-drm-devices"); mock_kms_output = std::make_shared>(); ON_CALL(*mock_kms_output, set_crtc(_)) .WillByDefault(Return(true)); ON_CALL(*mock_kms_output, schedule_page_flip(_)) .WillByDefault(Return(true)); ON_CALL(*mock_kms_output, max_refresh_rate()) .WillByDefault(Return(mock_refresh_rate)); ON_CALL(*mock_bypassable_buffer, size()) .WillByDefault(Return(display_area.size)); ON_CALL(*mock_bypassable_buffer, native_buffer_handle()) .WillByDefault(Return(stub_gbm_native_buffer)); fake_bypassable_renderable->set_buffer(mock_bypassable_buffer); memset(stub_shm_native_buffer.get(), 0, sizeof(MirNativeBuffer)); stub_shm_native_buffer->flags = 0; // Is not a hardware/GBM buffer ON_CALL(*mock_software_buffer, size()) .WillByDefault(Return(display_area.size)); ON_CALL(*mock_software_buffer, native_buffer_handle()) .WillByDefault(Return(stub_shm_native_buffer)); fake_software_renderable->set_buffer(mock_software_buffer); } protected: int const width{56}; int const height{78}; mir::geometry::Rectangle const display_area{{12,34}, {width,height}}; NiceMock mock_gbm; NiceMock mock_egl; NiceMock mock_gl; NiceMock mock_drm; std::shared_ptr mock_bypassable_buffer; std::shared_ptr mock_software_buffer; std::shared_ptr fake_bypassable_renderable; std::shared_ptr fake_software_renderable; std::shared_ptr stub_gbm_native_buffer; std::shared_ptr stub_shm_native_buffer; gbm_bo* fake_bo; gbm_bo_handle fake_handle; UdevEnvironment fake_devices; std::shared_ptr mock_kms_output; StubGLConfig gl_config; mir::graphics::RenderableList const bypassable_list; std::shared_ptr gbm{std::make_shared()}; std::shared_ptr drm{std::make_shared(helpers::DRMNodeToUse::card)}; }; TEST_F(MesaDisplayBufferTest, unrotated_view_area_is_untouched) { graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); EXPECT_EQ(display_area, db.view_area()); } TEST_F(MesaDisplayBufferTest, bypass_buffer_is_held_for_full_frame) { graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); auto original_count = mock_bypassable_buffer.use_count(); EXPECT_TRUE(db.post_renderables_if_optimizable(bypassable_list)); EXPECT_EQ(original_count+1, mock_bypassable_buffer.use_count()); // Switch back to normal compositing db.make_current(); db.swap_buffers(); db.post(); // Bypass buffer should no longer be held by db EXPECT_EQ(original_count, mock_bypassable_buffer.use_count()); } TEST_F(MesaDisplayBufferTest, predictive_bypass_is_throttled) { graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); for (int frame = 0; frame < 5; ++frame) { ASSERT_TRUE(db.post_renderables_if_optimizable(bypassable_list)); db.post(); // Cast to a simple int type so that test failures are readable int milliseconds_per_frame = 1000 / mock_refresh_rate; ASSERT_THAT(db.recommended_sleep().count(), Ge(milliseconds_per_frame/2)); } } TEST_F(MesaDisplayBufferTest, frames_requiring_gl_are_not_throttled) { graphics::RenderableList non_bypassable_list{ std::make_shared(geometry::Rectangle{{12, 34}, {1, 1}}) }; graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); for (int frame = 0; frame < 5; ++frame) { ASSERT_FALSE(db.post_renderables_if_optimizable(non_bypassable_list)); db.post(); // Cast to a simple int type so that test failures are readable ASSERT_EQ(0, db.recommended_sleep().count()); } } TEST_F(MesaDisplayBufferTest, bypass_buffer_only_referenced_once_by_db) { graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); auto original_count = mock_bypassable_buffer.use_count(); EXPECT_TRUE(db.post_renderables_if_optimizable(bypassable_list)); EXPECT_EQ(original_count+1, mock_bypassable_buffer.use_count()); db.post(); // Bypass buffer still held by DB only one ref above the original EXPECT_EQ(original_count+1, mock_bypassable_buffer.use_count()); } TEST_F(MesaDisplayBufferTest, normal_orientation_with_bypassable_list_can_bypass) { graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); EXPECT_TRUE(db.post_renderables_if_optimizable(bypassable_list)); } TEST_F(MesaDisplayBufferTest, failed_bypass_falls_back_gracefully) { // Regression test for LP: #1398296 EXPECT_CALL(mock_drm, drmModeAddFB2(_, _, _, _, _, _, _, _, _)) .WillOnce(Return(0)) // During the DisplayBuffer constructor .WillOnce(Return(-22)) // Fail first bypass attempt .WillOnce(Return(0)); // Succeed second bypass attempt graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); EXPECT_FALSE(db.post_renderables_if_optimizable(bypassable_list)); // And then we recover. DRM finds enough resources to AddFB ... EXPECT_TRUE(db.post_renderables_if_optimizable(bypassable_list)); } TEST_F(MesaDisplayBufferTest, skips_bypass_because_of_lagging_resize) { // Another regression test for LP: #1398296 auto fullscreen = std::make_shared(display_area); auto nonbypassable = std::make_shared>(); ON_CALL(*nonbypassable, native_buffer_handle()) .WillByDefault(Return(stub_gbm_native_buffer)); ON_CALL(*nonbypassable, size()) .WillByDefault(Return(mir::geometry::Size{12,34})); fullscreen->set_buffer(nonbypassable); graphics::RenderableList list{fullscreen}; graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); EXPECT_FALSE(db.post_renderables_if_optimizable(list)); } TEST_F(MesaDisplayBufferTest, rotated_cannot_bypass) { graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {}, nullptr, display_area, mir_orientation_right, gl_config, mock_egl.fake_egl_context); EXPECT_FALSE(db.post_renderables_if_optimizable(bypassable_list)); } TEST_F(MesaDisplayBufferTest, fullscreen_software_buffer_cannot_bypass) { graphics::RenderableList const list{fake_software_renderable}; // Passes the bypass candidate test: EXPECT_EQ(fake_software_renderable->buffer()->size(), display_area.size); graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); EXPECT_FALSE(db.post_renderables_if_optimizable(list)); } TEST_F(MesaDisplayBufferTest, fullscreen_software_buffer_not_used_as_gbm_bo) { // Also checks it doesn't crash (LP: #1493721) graphics::RenderableList const list{fake_software_renderable}; // Passes the bypass candidate test: EXPECT_EQ(fake_software_renderable->buffer()->size(), display_area.size); graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); // If you find yourself using gbm_ functions on a Shm buffer then you're // asking for a crash (LP: #1493721) ... EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)).Times(0); db.post_renderables_if_optimizable(list); } TEST_F(MesaDisplayBufferTest, orientation_not_implemented_internally) { graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {}, nullptr, display_area, mir_orientation_left, gl_config, mock_egl.fake_egl_context); EXPECT_EQ(mir_orientation_left, db.orientation()); } TEST_F(MesaDisplayBufferTest, normal_rotation_constructs_normal_fb) { EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) .WillOnce(Return((void*)0)); EXPECT_CALL(mock_drm, drmModeAddFB2(_, width, height, _, _, _, _, _, _)) .Times(1); graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); } TEST_F(MesaDisplayBufferTest, left_rotation_constructs_transposed_fb) { EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) .WillOnce(Return((void*)0)); EXPECT_CALL(mock_drm, drmModeAddFB2(_, height, width, _, _, _, _, _, _)) .Times(1); graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {}, nullptr, display_area, mir_orientation_left, gl_config, mock_egl.fake_egl_context); } TEST_F(MesaDisplayBufferTest, inverted_rotation_constructs_normal_fb) { EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) .WillOnce(Return((void*)0)); EXPECT_CALL(mock_drm, drmModeAddFB2(_, width, height, _, _, _, _, _, _)) .Times(1); graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {}, nullptr, display_area, mir_orientation_inverted, gl_config, mock_egl.fake_egl_context); } TEST_F(MesaDisplayBufferTest, right_rotation_constructs_transposed_fb) { EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)) .WillOnce(Return((void*)0)); EXPECT_CALL(mock_drm, drmModeAddFB2(_, height, width, _, _, _, _, _, _)) .Times(1); graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {}, nullptr, display_area, mir_orientation_right, gl_config, mock_egl.fake_egl_context); } TEST_F(MesaDisplayBufferTest, clone_mode_first_flip_flips_but_no_wait) { // Ensure clone mode can do multiple page flips in parallel without // blocking on either (at least till the second post) EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) .Times(2); EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) .Times(0); graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output, mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); db.swap_buffers(); db.post(); } TEST_F(MesaDisplayBufferTest, single_mode_first_post_flips_with_wait) { EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) .Times(1); EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) .Times(1); graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); db.swap_buffers(); db.post(); } TEST_F(MesaDisplayBufferTest, clone_mode_waits_for_page_flip_on_second_flip) { InSequence seq; EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) .Times(0); EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) .Times(2); EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) .Times(2); EXPECT_CALL(*mock_kms_output, schedule_page_flip(_)) .Times(2); EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) .Times(0); graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output, mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); db.swap_buffers(); db.post(); db.swap_buffers(); db.post(); } TEST_F(MesaDisplayBufferTest, skips_bypass_because_of_incompatible_list) { graphics::RenderableList list{ std::make_shared(display_area), std::make_shared(geometry::Rectangle{{12, 34}, {1, 1}}) }; graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); EXPECT_FALSE(db.post_renderables_if_optimizable(list)); } TEST_F(MesaDisplayBufferTest, skips_bypass_because_of_incompatible_bypass_buffer) { auto fullscreen = std::make_shared(display_area); auto nonbypassable = std::make_shared>(); auto nonbypassable_gbm_native_buffer = std::make_shared(display_area.size, false); ON_CALL(*nonbypassable, native_buffer_handle()) .WillByDefault(Return(nonbypassable_gbm_native_buffer)); ON_CALL(*nonbypassable, size()) .WillByDefault(Return(display_area.size)); fullscreen->set_buffer(nonbypassable); graphics::RenderableList list{fullscreen}; graphics::mesa::DisplayBuffer db( graphics::mesa::BypassOption::allowed, drm, gbm, null_display_report(), {mock_kms_output}, nullptr, display_area, mir_orientation_normal, gl_config, mock_egl.fake_egl_context); EXPECT_FALSE(db.post_renderables_if_optimizable(list)); } ./tests/unit-tests/graphics/mesa/kms/test_real_kms_output.cpp0000644000015600001650000002140112676616125024667 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/platforms/mesa/server/kms/real_kms_output.h" #include "src/platforms/mesa/server/kms/page_flipper.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_drm.h" #include #include #include namespace mg = mir::graphics; namespace mgm = mir::graphics::mesa; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace { class NullPageFlipper : public mgm::PageFlipper { public: bool schedule_flip(uint32_t,uint32_t) { return true; } void wait_for_flip(uint32_t) { } }; class MockPageFlipper : public mgm::PageFlipper { public: MOCK_METHOD2(schedule_flip, bool(uint32_t,uint32_t)); MOCK_METHOD1(wait_for_flip, void(uint32_t)); }; class RealKMSOutputTest : public ::testing::Test { public: RealKMSOutputTest() : invalid_id{0}, crtc_ids{10, 11}, encoder_ids{20, 21}, connector_ids{30, 21}, possible_encoder_ids1{encoder_ids[0]}, possible_encoder_ids2{encoder_ids[0], encoder_ids[1]} { } void setup_outputs_connected_crtc() { mtd::FakeDRMResources& resources(mock_drm.fake_drm); uint32_t const possible_crtcs_mask{0x1}; resources.reset(); resources.add_crtc(crtc_ids[0], drmModeModeInfo()); resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask); resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_VGA, DRM_MODE_CONNECTED, encoder_ids[0], modes_empty, possible_encoder_ids1, geom::Size()); resources.prepare(); } void setup_outputs_no_connected_crtc() { mtd::FakeDRMResources& resources(mock_drm.fake_drm); uint32_t const possible_crtcs_mask1{0x1}; uint32_t const possible_crtcs_mask_all{0x3}; resources.reset(); resources.add_crtc(crtc_ids[0], drmModeModeInfo()); resources.add_crtc(crtc_ids[1], drmModeModeInfo()); resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask1); resources.add_encoder(encoder_ids[1], invalid_id, possible_crtcs_mask_all); resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite, DRM_MODE_CONNECTED, invalid_id, modes_empty, possible_encoder_ids2, geom::Size()); resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_DVIA, DRM_MODE_CONNECTED, encoder_ids[0], modes_empty, possible_encoder_ids2, geom::Size()); resources.prepare(); } testing::NiceMock mock_drm; MockPageFlipper mock_page_flipper; NullPageFlipper null_page_flipper; std::vector modes_empty; uint32_t const invalid_id; std::vector const crtc_ids; std::vector const encoder_ids; std::vector const connector_ids; std::vector possible_encoder_ids1; std::vector possible_encoder_ids2; }; } TEST_F(RealKMSOutputTest, construction_queries_connector) { using namespace testing; setup_outputs_connected_crtc(); EXPECT_CALL(mock_drm, drmModeGetConnector(_,connector_ids[0])) .Times(1); mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0], mt::fake_shared(null_page_flipper)}; } TEST_F(RealKMSOutputTest, operations_use_existing_crtc) { using namespace testing; uint32_t const fb_id{67}; setup_outputs_connected_crtc(); { InSequence s; EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], fb_id, _, _, Pointee(connector_ids[0]), _, _)) .Times(1); EXPECT_CALL(mock_page_flipper, schedule_flip(crtc_ids[0], fb_id)) .Times(1) .WillOnce(Return(true)); EXPECT_CALL(mock_page_flipper, wait_for_flip(crtc_ids[0])) .Times(1); EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], Ne(fb_id), _, _, Pointee(connector_ids[0]), _, _)) .Times(1); } mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0], mt::fake_shared(mock_page_flipper)}; EXPECT_TRUE(output.set_crtc(fb_id)); EXPECT_TRUE(output.schedule_page_flip(fb_id)); output.wait_for_page_flip(); } TEST_F(RealKMSOutputTest, operations_use_possible_crtc) { using namespace testing; uint32_t const fb_id{67}; setup_outputs_no_connected_crtc(); { InSequence s; EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[1], fb_id, _, _, Pointee(connector_ids[0]), _, _)) .Times(1); EXPECT_CALL(mock_page_flipper, schedule_flip(crtc_ids[1], fb_id)) .Times(1) .WillOnce(Return(true)); EXPECT_CALL(mock_page_flipper, wait_for_flip(crtc_ids[1])) .Times(1); EXPECT_CALL(mock_drm, drmModeSetCrtc(_, 0, 0, _, _, Pointee(connector_ids[0]), _, _)) .Times(1); } mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0], mt::fake_shared(mock_page_flipper)}; EXPECT_TRUE(output.set_crtc(fb_id)); EXPECT_TRUE(output.schedule_page_flip(fb_id)); output.wait_for_page_flip(); } TEST_F(RealKMSOutputTest, set_crtc_failure_is_handled_gracefully) { using namespace testing; uint32_t const fb_id{67}; setup_outputs_connected_crtc(); { InSequence s; EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], fb_id, _, _, _, _, _)) .Times(1) .WillOnce(Return(1)); EXPECT_CALL(mock_page_flipper, schedule_flip(_, _)) .Times(0); EXPECT_CALL(mock_page_flipper, wait_for_flip(_)) .Times(0); EXPECT_CALL(mock_drm, drmModeSetCrtc(_, _, _, _, _, _, _, _)) .Times(0); } mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0], mt::fake_shared(mock_page_flipper)}; EXPECT_FALSE(output.set_crtc(fb_id)); EXPECT_THROW({ output.schedule_page_flip(fb_id); }, std::runtime_error); EXPECT_THROW({ output.wait_for_page_flip(); }, std::runtime_error); } TEST_F(RealKMSOutputTest, clear_crtc_gets_crtc_if_none_is_current) { using namespace testing; setup_outputs_connected_crtc(); mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0], mt::fake_shared(mock_page_flipper)}; EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], 0, 0, 0, nullptr, 0, nullptr)) .Times(1) .WillOnce(Return(0)); output.clear_crtc(); } TEST_F(RealKMSOutputTest, clear_crtc_does_not_throw_if_no_crtc_is_found) { using namespace testing; mtd::FakeDRMResources& resources(mock_drm.fake_drm); uint32_t const possible_crtcs_mask_empty{0x0}; resources.reset(); resources.add_encoder(encoder_ids[0], invalid_id, possible_crtcs_mask_empty); resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_VGA, DRM_MODE_CONNECTED, encoder_ids[0], modes_empty, possible_encoder_ids1, geom::Size()); resources.prepare(); mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0], mt::fake_shared(mock_page_flipper)}; EXPECT_CALL(mock_drm, drmModeSetCrtc(_, _, 0, 0, 0, nullptr, 0, nullptr)) .Times(0); output.clear_crtc(); } TEST_F(RealKMSOutputTest, clear_crtc_throws_if_drm_call_fails) { using namespace testing; setup_outputs_connected_crtc(); mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0], mt::fake_shared(mock_page_flipper)}; EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], 0, 0, 0, nullptr, 0, nullptr)) .Times(1) .WillOnce(Return(-1)); EXPECT_THROW({ output.clear_crtc(); }, std::runtime_error); } ./tests/unit-tests/graphics/mesa/kms/test_display_generic.cpp0000644000015600001650000000552312676616125024622 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/display.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/gl_context.h" #include "mir/graphics/platform.h" #include "mir/options/program_option.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "src/server/report/null_report_factory.h" #include "mir/graphics/default_display_configuration_policy.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir_test_framework/udev_environment.h" #include "src/platforms/mesa/server/kms/platform.h" #include #include namespace mg = mir::graphics; namespace mgm = mg::mesa; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; class DisplayTestGeneric : public ::testing::Test { public: DisplayTestGeneric() { using namespace testing; ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); mock_egl.provide_egl_extensions(); mock_gl.provide_gles_extensions(); fake_devices.add_standard_device("standard-drm-devices"); } std::shared_ptr create_display() { auto const platform = std::make_shared( mir::report::null_display_report(), std::make_shared(), *std::make_shared(), mgm::BypassOption::allowed); return platform->create_display( std::make_shared(), std::make_shared()); } ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; mtf::UdevEnvironment fake_devices; }; #include "../../test_display.h" ./tests/unit-tests/graphics/mesa/kms/test_cursor.cpp0000644000015600001650000005607512676616125023006 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/platforms/mesa/server/kms/cursor.h" #include "src/platforms/mesa/server/kms/kms_output.h" #include "src/platforms/mesa/server/kms/kms_output_container.h" #include "src/platforms/mesa/server/kms/kms_display_configuration.h" #include "mir/graphics/cursor_image.h" #include #include #include "mir/test/doubles/mock_gbm.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/fake_shared.h" #include "mir_test_framework/temporary_environment_value.h" #include "mock_kms_output.h" #include #include #include #include #include namespace mg = mir::graphics; namespace mgm = mir::graphics::mesa; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mt::doubles; using mt::MockKMSOutput; using namespace ::testing; namespace { struct StubKMSOutputContainer : public mgm::KMSOutputContainer { StubKMSOutputContainer() : outputs{ {10, std::make_shared>()}, {11, std::make_shared>()}, {12, std::make_shared>()}} { } std::shared_ptr get_kms_output_for(uint32_t connector_id) { return outputs[connector_id]; } void for_each_output(std::function functor) const { for (auto const& pair : outputs) functor(*pair.second); } void verify_and_clear_expectations() { for (auto const& pair : outputs) ::testing::Mock::VerifyAndClearExpectations(pair.second.get()); } std::unordered_map>> outputs; }; struct StubKMSDisplayConfiguration : public mgm::KMSDisplayConfiguration { StubKMSDisplayConfiguration() : mgm::KMSDisplayConfiguration(), stub_config{ {{ mg::DisplayConfigurationOutputId{10}, mg::DisplayConfigurationCardId{}, mg::DisplayConfigurationOutputType::vga, {}, { {geom::Size{10, 20}, 59.9}, {geom::Size{200, 100}, 59.9}, }, 1, geom::Size{324, 642}, true, true, geom::Point{0, 0}, 1, mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }, { mg::DisplayConfigurationOutputId{11}, mg::DisplayConfigurationCardId{}, mg::DisplayConfigurationOutputType::vga, {}, { {geom::Size{200, 200}, 59.9}, {geom::Size{100, 200}, 59.9}, }, 0, geom::Size{566, 111}, true, true, geom::Point{100, 50}, 0, mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }, { mg::DisplayConfigurationOutputId{12}, mg::DisplayConfigurationCardId{}, mg::DisplayConfigurationOutputType::vga, {}, { {geom::Size{800, 200}, 59.9}, {geom::Size{100, 200}, 59.9}, }, 0, geom::Size{800, 200}, true, true, geom::Point{666, 0}, 0, mir_pixel_format_invalid, mir_power_mode_on, mir_orientation_right, 1.0f, mir_form_factor_monitor }}} { } void for_each_card(std::function f) const override { stub_config.for_each_card(f); } void for_each_output(std::function f) const override { stub_config.for_each_output(f); } void for_each_output(std::function f) override { stub_config.for_each_output(f); } std::unique_ptr clone() const override { return stub_config.clone(); } bool valid() const override { return stub_config.valid(); } uint32_t get_kms_connector_id(mg::DisplayConfigurationOutputId id) const override { return id.as_value(); } size_t get_kms_mode_index(mg::DisplayConfigurationOutputId, size_t conf_mode_index) const override { return conf_mode_index; } void update() override { } void set_orentation_of_output(mg::DisplayConfigurationOutputId id, MirOrientation orientation) { stub_config.for_each_output( [id,orientation] (mg::UserDisplayConfigurationOutput const& output) { if (output.id == id) output.orientation = orientation; }); } mtd::StubDisplayConfig stub_config; }; struct StubCurrentConfiguration : public mgm::CurrentConfiguration { void with_current_configuration_do( std::function const& exec) { exec(conf); } StubKMSDisplayConfiguration conf; }; struct StubCursorImage : public mg::CursorImage { void const* as_argb_8888() const { return image_data; } geom::Size size() const { return geom::Size{geom::Width{64}, geom::Height{64}}; } geom::Displacement hotspot() const { return geom::Displacement{0, 0}; } static void const* image_data; }; void const* StubCursorImage::image_data = reinterpret_cast(&StubCursorImage::image_data); // Those new cap flags are currently only available in drm/drm.h but not in // libdrm/drm.h nor in xf86drm.h. Additionally drm/drm.h is current c++ unfriendly // So until those headers get cleaned up we duplicate those definitions. #ifndef DRM_CAP_CURSOR_WIDTH #define DRM_CAP_CURSOR_WIDTH 0x8 #define DRM_CAP_CURSOR_HEIGHT 0x9 #endif struct MesaCursorTest : ::testing::Test { struct MockGBM : testing::NiceMock { MockGBM() { using namespace ::testing; const uint32_t default_cursor_size = 64; ON_CALL(*this, gbm_bo_get_width(_)) .WillByDefault(Return(default_cursor_size)); ON_CALL(*this, gbm_bo_get_height(_)) .WillByDefault(Return(default_cursor_size)); } } mock_gbm; size_t const cursor_side{64}; MesaCursorTest() : cursor{mock_gbm.fake_gbm.device, output_container, mt::fake_shared(current_configuration), mt::fake_shared(stub_image)} { using namespace ::testing; ON_CALL(mock_drm, drmGetCap(_, DRM_CAP_CURSOR_WIDTH, _)) .WillByDefault(Invoke([this](int , uint64_t , uint64_t *value) { *value = cursor_side; return 0; })); ON_CALL(mock_drm, drmGetCap(_, DRM_CAP_CURSOR_HEIGHT, _)) .WillByDefault(Invoke([this](int , uint64_t , uint64_t *value) { *value = cursor_side; return 0; })); } testing::NiceMock mock_drm; StubCurrentConfiguration current_configuration; StubCursorImage stub_image; StubKMSOutputContainer output_container; mgm::Cursor cursor; }; struct SinglePixelCursorImage : public StubCursorImage { geom::Size size() const { return small_cursor_size; } void const* as_argb_8888() const { static uint32_t const pixel = 0xffffffff; return &pixel; } size_t const cursor_side{1}; geom::Size const small_cursor_size{cursor_side, cursor_side}; }; } TEST_F(MesaCursorTest, creates_cursor_bo_image) { EXPECT_CALL(mock_gbm, gbm_bo_create(mock_gbm.fake_gbm.device, cursor_side, cursor_side, GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)); mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, std::make_shared(), std::make_shared()}; } TEST_F(MesaCursorTest, queries_received_cursor_size) { using namespace ::testing; EXPECT_CALL(mock_gbm, gbm_bo_get_width(_)); EXPECT_CALL(mock_gbm, gbm_bo_get_height(_)); mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, std::make_shared(), std::make_shared()}; } TEST_F(MesaCursorTest, respects_drm_cap_cursor) { auto const drm_buffer_size = 255; ON_CALL(mock_drm, drmGetCap(_, DRM_CAP_CURSOR_WIDTH, _)) .WillByDefault(Invoke([](int , uint64_t , uint64_t *value) { *value = drm_buffer_size; return 0; })); ON_CALL(mock_drm, drmGetCap(_, DRM_CAP_CURSOR_HEIGHT, _)) .WillByDefault(Invoke([](int , uint64_t , uint64_t *value) { *value = drm_buffer_size; return 0; })); EXPECT_CALL(mock_gbm, gbm_bo_create(_, drm_buffer_size, drm_buffer_size, _, _)); mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, std::make_shared(), std::make_shared()}; } TEST_F(MesaCursorTest, can_force_64x64_cursor) { auto const drm_buffer_size = 255; ON_CALL(mock_drm, drmGetCap(_, DRM_CAP_CURSOR_WIDTH, _)) .WillByDefault(Invoke([](int , uint64_t , uint64_t *value) { *value = drm_buffer_size; return 0; })); ON_CALL(mock_drm, drmGetCap(_, DRM_CAP_CURSOR_HEIGHT, _)) .WillByDefault(Invoke([](int , uint64_t , uint64_t *value) { *value = drm_buffer_size; return 0; })); mir_test_framework::TemporaryEnvironmentValue mir_drm_cursor_64x64{"MIR_DRM_CURSOR_64x64", "on"}; EXPECT_CALL(mock_gbm, gbm_bo_create(_, 64, 64, _, _)); mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, std::make_shared(), std::make_shared()}; } TEST_F(MesaCursorTest, show_cursor_writes_to_bo) { using namespace testing; StubCursorImage image; geom::Size const cursor_size{cursor_side, cursor_side}; size_t const cursor_size_bytes{cursor_side * cursor_side * sizeof(uint32_t)}; EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, StubCursorImage::image_data, cursor_size_bytes)); cursor.show(image); } // When we upload our 1x1 cursor we should upload a single white pixel and then transparency filling a 64x64 buffer. MATCHER_P(ContainsASingleWhitePixel, buffersize, "") { auto pixels = static_cast(arg); if (pixels[0] != 0xffffffff) return false; for (decltype(buffersize) i = 1; i < buffersize; i++) { if (pixels[i] != 0x0) return false; } return true; } TEST_F(MesaCursorTest, show_cursor_pads_missing_data) { using namespace testing; // We expect a full 64x64 pixel write as we will pad missing data with transparency. size_t const height = 64; size_t const width = 64; size_t const stride = width * 4; size_t const buffer_size_bytes{height * stride}; ON_CALL(mock_gbm, gbm_bo_get_stride(_)) .WillByDefault(Return(stride)); EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, ContainsASingleWhitePixel(width*height), buffer_size_bytes)); cursor.show(SinglePixelCursorImage()); } TEST_F(MesaCursorTest, pads_missing_data_when_buffer_size_differs) { using namespace ::testing; size_t const height = 128; size_t const width = 128; size_t const stride = width * 4; size_t const buffer_size_bytes{height * stride}; ON_CALL(mock_gbm, gbm_bo_get_width(mock_gbm.fake_gbm.bo)) .WillByDefault(Return(width)); ON_CALL(mock_gbm, gbm_bo_get_height(mock_gbm.fake_gbm.bo)) .WillByDefault(Return(height)); ON_CALL(mock_gbm, gbm_bo_get_stride(mock_gbm.fake_gbm.bo)) .WillByDefault(Return(stride)); EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, ContainsASingleWhitePixel(width*height), buffer_size_bytes)); mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, std::make_shared(), std::make_shared()}; } TEST_F(MesaCursorTest, throws_when_images_are_too_large) { using namespace testing; struct LargeCursorImage : public StubCursorImage { geom::Size size() const { return large_cursor_size; } size_t const cursor_side{128}; geom::Size const large_cursor_size{cursor_side, cursor_side}; }; EXPECT_THROW({ cursor.show(LargeCursorImage()); }, std::logic_error); } TEST_F(MesaCursorTest, forces_cursor_state_on_construction) { using namespace testing; EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{0,0})); EXPECT_CALL(*output_container.outputs[10], set_cursor(_)); EXPECT_CALL(*output_container.outputs[11], clear_cursor()); EXPECT_CALL(*output_container.outputs[12], clear_cursor()); /* No checking of existing cursor state */ EXPECT_CALL(*output_container.outputs[10], has_cursor()).Times(0); EXPECT_CALL(*output_container.outputs[11], has_cursor()).Times(0); EXPECT_CALL(*output_container.outputs[12], has_cursor()).Times(0); mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container, std::make_shared(), std::make_shared()}; output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, move_to_sets_clears_cursor_if_needed) { using namespace testing; EXPECT_CALL(*output_container.outputs[10], has_cursor()) .WillOnce(Return(false)); EXPECT_CALL(*output_container.outputs[10], set_cursor(_)); EXPECT_CALL(*output_container.outputs[11], has_cursor()) .WillOnce(Return(true)); EXPECT_CALL(*output_container.outputs[11], clear_cursor()); cursor.move_to({10, 10}); output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, move_to_doesnt_set_clear_cursor_if_not_needed) { using namespace testing; EXPECT_CALL(*output_container.outputs[10], has_cursor()) .WillOnce(Return(true)); EXPECT_CALL(*output_container.outputs[10], set_cursor(_)) .Times(0); EXPECT_CALL(*output_container.outputs[11], has_cursor()) .WillOnce(Return(false)); EXPECT_CALL(*output_container.outputs[11], clear_cursor()) .Times(0); cursor.move_to({10, 10}); output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, move_to_moves_cursor_to_right_output) { using namespace testing; EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{10,10})); EXPECT_CALL(*output_container.outputs[11], move_cursor(_)) .Times(0); cursor.move_to({10, 10}); output_container.verify_and_clear_expectations(); EXPECT_CALL(*output_container.outputs[10], move_cursor(_)) .Times(0); EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,100})); cursor.move_to({150, 150}); output_container.verify_and_clear_expectations(); EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75})); EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25})); cursor.move_to({150, 75}); output_container.verify_and_clear_expectations(); EXPECT_CALL(*output_container.outputs[10], move_cursor(_)) .Times(0); EXPECT_CALL(*output_container.outputs[11], move_cursor(_)) .Times(0); cursor.move_to({-1, -1}); output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, moves_properly_to_and_inside_left_rotated_output) { using namespace testing; current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_left); EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{112,100})); EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{150,96})); cursor.move_to({766, 112}); cursor.move_to({770, 150}); output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, moves_properly_to_and_inside_right_rotated_output) { using namespace testing; current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_right); EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{688,100})); EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{650,104})); cursor.move_to({766, 112}); cursor.move_to({770, 150}); output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, moves_properly_to_and_inside_inverted_output) { using namespace testing; current_configuration.conf.set_orentation_of_output(mg::DisplayConfigurationOutputId{12}, mir_orientation_inverted); EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{700,88})); EXPECT_CALL(*output_container.outputs[12], move_cursor(geom::Point{696,50})); cursor.move_to({766, 112}); cursor.move_to({770, 150}); output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, hides_cursor_in_all_outputs) { using namespace testing; EXPECT_CALL(*output_container.outputs[10], clear_cursor()); EXPECT_CALL(*output_container.outputs[11], clear_cursor()); EXPECT_CALL(*output_container.outputs[12], clear_cursor()); cursor.hide(); output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, hidden_cursor_is_not_shown_on_display_when_moved) { using namespace testing; EXPECT_CALL(*output_container.outputs[10], clear_cursor()); EXPECT_CALL(*output_container.outputs[11], clear_cursor()); EXPECT_CALL(*output_container.outputs[12], clear_cursor()); EXPECT_CALL(*output_container.outputs[10], move_cursor(_)).Times(0); EXPECT_CALL(*output_container.outputs[11], move_cursor(_)).Times(0); EXPECT_CALL(*output_container.outputs[12], move_cursor(_)).Times(0); cursor.hide(); cursor.move_to({17, 29}); output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, clears_cursor_on_exit) { using namespace testing; EXPECT_CALL(*output_container.outputs[10], clear_cursor()); EXPECT_CALL(*output_container.outputs[11], clear_cursor()); EXPECT_CALL(*output_container.outputs[12], clear_cursor()); } TEST_F(MesaCursorTest, cursor_is_shown_at_correct_location_after_suspend_resume) { using namespace testing; EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75})); EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25})); EXPECT_CALL(*output_container.outputs[10], clear_cursor()); EXPECT_CALL(*output_container.outputs[11], clear_cursor()); EXPECT_CALL(*output_container.outputs[12], clear_cursor()); cursor.move_to({150, 75}); cursor.suspend(); output_container.verify_and_clear_expectations(); EXPECT_CALL(*output_container.outputs[10], set_cursor(_)); EXPECT_CALL(*output_container.outputs[11], set_cursor(_)); EXPECT_CALL(*output_container.outputs[10], move_cursor(geom::Point{150,75})); EXPECT_CALL(*output_container.outputs[11], move_cursor(geom::Point{50,25})); cursor.resume(); output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, hidden_cursor_is_not_shown_after_suspend_resume) { using namespace testing; EXPECT_CALL(*output_container.outputs[10], clear_cursor()).Times(AnyNumber()); EXPECT_CALL(*output_container.outputs[11], clear_cursor()).Times(AnyNumber()); EXPECT_CALL(*output_container.outputs[12], clear_cursor()).Times(AnyNumber()); cursor.hide(); cursor.suspend(); output_container.verify_and_clear_expectations(); EXPECT_CALL(*output_container.outputs[10], set_cursor(_)).Times(0); EXPECT_CALL(*output_container.outputs[11], set_cursor(_)).Times(0); EXPECT_CALL(*output_container.outputs[12], set_cursor(_)).Times(0); cursor.resume(); output_container.verify_and_clear_expectations(); } TEST_F(MesaCursorTest, show_without_param_places_cursor_on_output_output) { using namespace testing; EXPECT_CALL(*output_container.outputs[10], clear_cursor()); cursor.hide(); output_container.verify_and_clear_expectations(); EXPECT_CALL(*output_container.outputs[10], set_cursor(_)); cursor.show(); } TEST_F(MesaCursorTest, show_cursor_sets_cursor_with_hotspot) { using namespace testing; static geom::Displacement hotspot_displacement{10, 10}; static geom::Point const initial_cursor_location = {0, 0}, cursor_location_1 = {20, 20}, cursor_location_2 = {40, 12}; static geom::Point const initial_buffer_location = initial_cursor_location - hotspot_displacement; static geom::Point const expected_buffer_location_1 = cursor_location_1 - hotspot_displacement; static geom::Point const expected_buffer_location_2 = cursor_location_2 - hotspot_displacement; struct HotspotCursor : public StubCursorImage { geom::Displacement hotspot() const override { return hotspot_displacement; } }; EXPECT_CALL(mock_gbm, gbm_bo_write(_, _, _)).Times(AnyNumber()); EXPECT_CALL(*output_container.outputs[10], set_cursor(_)).Times(AnyNumber()); // When we set the image with the hotspot, first we should see the cursor move from its initial // location, to account for the displacement. Further movement should be offset by the hotspot. { InSequence seq; EXPECT_CALL(*output_container.outputs[10], move_cursor(initial_buffer_location)); EXPECT_CALL(*output_container.outputs[10], move_cursor(expected_buffer_location_1)); EXPECT_CALL(*output_container.outputs[10], move_cursor(expected_buffer_location_2)); } cursor.show(HotspotCursor()); cursor.move_to(cursor_location_1); cursor.move_to(cursor_location_2); } ./tests/unit-tests/graphics/mesa/common/0000755000015600001650000000000012676616160020406 5ustar jenkinsjenkins./tests/unit-tests/graphics/mesa/common/test_ipc_operations.cpp0000644000015600001650000001144212676616125025172 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/mesa/server/common/ipc_operations.h" #include "src/platforms/mesa/server/common/drm_authentication.h" #include "mir/graphics/platform_ipc_package.h" #include "mir/graphics/platform_operation_message.h" #include "mir_toolkit/mesa/platform_operation.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/mock_buffer_ipc_message.h" #include "mir/test/doubles/fd_matcher.h" #include "mir/test/doubles/mock_drm.h" #include #include namespace mg = mir::graphics; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mgm = mir::graphics::mesa; namespace geom = mir::geometry; namespace { struct MockDRMOperations : public mgm::DRMAuthentication { MOCK_METHOD1(auth_magic, void(drm_magic_t incantation)); MOCK_METHOD0(authenticated_fd, mir::Fd()); }; struct IpcOperations : public ::testing::Test { IpcOperations() { using namespace testing; ON_CALL(mock_buffer, native_buffer_handle()) .WillByDefault(Return(mt::fake_shared(native_handle))); ON_CALL(mock_buffer, stride()) .WillByDefault(Return(dummy_stride)); ON_CALL(mock_buffer, size()) .WillByDefault(Return(dummy_size)); native_handle.data_items = 4; native_handle.fd_items = 2; for(auto i=0; i < mir_buffer_package_max; i++) { native_handle.fd[i] = i; native_handle.data[i] = i; } } MockDRMOperations mock_drm_ops; mgm::IpcOperations ipc_ops{mt::fake_shared(mock_drm_ops)}; MirBufferPackage native_handle; testing::NiceMock mock_buffer; geom::Stride dummy_stride{4390}; geom::Size dummy_size{123, 345}; ::testing::NiceMock mock_drm; }; } TEST_F(IpcOperations, packs_buffer_correctly) { mtd::MockBufferIpcMessage mock_buffer_msg; for(auto i = 0; i < native_handle.fd_items; i++) EXPECT_CALL(mock_buffer_msg, pack_fd(mtd::RawFdMatcher(native_handle.fd[i]))); for(auto i = 0; i < native_handle.data_items; i++) EXPECT_CALL(mock_buffer_msg, pack_data(native_handle.data[i])); EXPECT_CALL(mock_buffer_msg, pack_stride(dummy_stride)); EXPECT_CALL(mock_buffer_msg, pack_flags(testing::_)); EXPECT_CALL(mock_buffer_msg, pack_size(dummy_size)); ipc_ops.pack_buffer(mock_buffer_msg, mock_buffer, mg::BufferIpcMsgType::full_msg); ipc_ops.pack_buffer(mock_buffer_msg, mock_buffer, mg::BufferIpcMsgType::update_msg); } TEST_F(IpcOperations, calls_drm_auth_magic_for_auth_magic_operation) { using namespace testing; MirMesaAuthMagicRequest request; request.magic = 0x10111213; EXPECT_CALL(mock_drm_ops, auth_magic(request.magic)); mg::PlatformOperationMessage request_msg; request_msg.data.resize(sizeof(request)); std::memcpy(request_msg.data.data(), &request, sizeof(request)); auto response_msg = ipc_ops.platform_operation( MirMesaPlatformOperation::auth_magic, request_msg); MirMesaAuthMagicResponse response{-1}; ASSERT_THAT(response_msg.data.size(), Eq(sizeof(response))); std::memcpy(&response, response_msg.data.data(), response_msg.data.size()); EXPECT_THAT(response.status, Eq(0)); } TEST_F(IpcOperations, gets_authentication_fd_for_auth_fd_operation) { using namespace testing; mir::Fd stub_fd(fileno(tmpfile())); EXPECT_CALL(mock_drm_ops, authenticated_fd()) .WillOnce(Return(stub_fd)); mg::PlatformOperationMessage request_msg; auto const response_msg = ipc_ops.platform_operation( MirMesaPlatformOperation::auth_fd, request_msg); EXPECT_THAT(response_msg.data, IsEmpty()); EXPECT_THAT(response_msg.fds, ElementsAre(stub_fd)); } TEST_F(IpcOperations, gets_authentication_fd_for_connection_package) { using namespace testing; mir::Fd stub_fd(fileno(tmpfile())); EXPECT_CALL(mock_drm_ops, authenticated_fd()) .WillOnce(Return(stub_fd)); auto connection_package = ipc_ops.connection_ipc_package(); ASSERT_THAT(connection_package->ipc_fds.size(), Eq(1)); EXPECT_THAT(connection_package->ipc_fds[0], mtd::RawFdMatcher(stub_fd)); } ./tests/unit-tests/graphics/mesa/common/test_shm_buffer.cpp0000644000015600001650000001652712676616125024305 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/platforms/mesa/server/common/shm_buffer.h" #include "src/platforms/mesa/server/common/shm_file.h" #include "mir/test/doubles/mock_gl.h" #include #include #include #include namespace mg = mir::graphics; namespace mgm = mir::graphics::mesa; namespace mtd = mir::test::doubles; namespace geom = mir::geometry; using namespace testing; namespace { struct StubShmFile : public mgm::ShmFile { void* base_ptr() const { return fake_mapping; } int fd() const { return fake_fd; } void* const fake_mapping = reinterpret_cast(0x12345678); int const fake_fd = 17; }; struct ShmBufferTest : public testing::Test { ShmBufferTest() : size{150,340}, pixel_format{mir_pixel_format_bgr_888}, stub_shm_file{std::make_shared()}, shm_buffer{stub_shm_file, size, pixel_format} { } geom::Size const size; MirPixelFormat const pixel_format; std::shared_ptr const stub_shm_file; mgm::ShmBuffer shm_buffer; testing::NiceMock mock_gl; }; } TEST_F(ShmBufferTest, has_correct_properties) { size_t const bytes_per_pixel = MIR_BYTES_PER_PIXEL(pixel_format); size_t const expected_stride{bytes_per_pixel * size.width.as_uint32_t()}; EXPECT_EQ(size, shm_buffer.size()); EXPECT_EQ(geom::Stride{expected_stride}, shm_buffer.stride()); EXPECT_EQ(pixel_format, shm_buffer.pixel_format()); } TEST_F(ShmBufferTest, native_buffer_contains_correct_data) { size_t const bytes_per_pixel = MIR_BYTES_PER_PIXEL(pixel_format); size_t const expected_stride{bytes_per_pixel * size.width.as_uint32_t()}; auto native_buffer = shm_buffer.native_buffer_handle(); EXPECT_EQ(1, native_buffer->fd_items); EXPECT_EQ(stub_shm_file->fake_fd, native_buffer->fd[0]); EXPECT_EQ(size.width.as_int(), native_buffer->width); EXPECT_EQ(size.height.as_int(), native_buffer->height); EXPECT_EQ(expected_stride, static_cast(native_buffer->stride)); } TEST_F(ShmBufferTest, cannot_be_used_for_bypass) { EXPECT_FALSE(shm_buffer.native_buffer_handle()->flags & mir_buffer_flag_can_scanout); } TEST_F(ShmBufferTest, cant_upload_bgr_888) { EXPECT_CALL(mock_gl, glTexImage2D(GL_TEXTURE_2D, 0, _, size.width.as_int(), size.height.as_int(), 0, _, _, stub_shm_file->fake_mapping)) .Times(0); mgm::ShmBuffer buf(stub_shm_file, size, mir_pixel_format_bgr_888); buf.gl_bind_to_texture(); } TEST_F(ShmBufferTest, uploads_rgb_888_correctly) { EXPECT_CALL(mock_gl, glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); EXPECT_CALL(mock_gl, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width.as_int(), size.height.as_int(), 0, GL_RGB, GL_UNSIGNED_BYTE, stub_shm_file->fake_mapping)); mgm::ShmBuffer buf(stub_shm_file, size, mir_pixel_format_rgb_888); buf.gl_bind_to_texture(); } TEST_F(ShmBufferTest, uploads_rgb_565_correctly) { EXPECT_CALL(mock_gl, glPixelStorei(GL_UNPACK_ALIGNMENT, AnyOf(Eq(1),Eq(2)))); EXPECT_CALL(mock_gl, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width.as_int(), size.height.as_int(), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, stub_shm_file->fake_mapping)); mgm::ShmBuffer buf(stub_shm_file, size, mir_pixel_format_rgb_565); buf.gl_bind_to_texture(); } TEST_F(ShmBufferTest, uploads_rgba_5551_correctly) { EXPECT_CALL(mock_gl, glPixelStorei(GL_UNPACK_ALIGNMENT, AnyOf(Eq(1),Eq(2)))); EXPECT_CALL(mock_gl, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width.as_int(), size.height.as_int(), 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, stub_shm_file->fake_mapping)); mgm::ShmBuffer buf(stub_shm_file, size, mir_pixel_format_rgba_5551); buf.gl_bind_to_texture(); } TEST_F(ShmBufferTest, uploads_rgba_4444_correctly) { EXPECT_CALL(mock_gl, glPixelStorei(GL_UNPACK_ALIGNMENT, AnyOf(Eq(1),Eq(2)))); EXPECT_CALL(mock_gl, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width.as_int(), size.height.as_int(), 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, stub_shm_file->fake_mapping)); mgm::ShmBuffer buf(stub_shm_file, size, mir_pixel_format_rgba_4444); buf.gl_bind_to_texture(); } TEST_F(ShmBufferTest, uploads_xrgb_8888_correctly) { #if __BYTE_ORDER == __LITTLE_ENDIAN EXPECT_CALL(mock_gl, glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, size.width.as_int(), size.height.as_int(), 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, stub_shm_file->fake_mapping)); #endif mgm::ShmBuffer buf(stub_shm_file, size, mir_pixel_format_xrgb_8888); buf.gl_bind_to_texture(); } TEST_F(ShmBufferTest, uploads_argb_8888_correctly) { #if __BYTE_ORDER == __LITTLE_ENDIAN EXPECT_CALL(mock_gl, glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, size.width.as_int(), size.height.as_int(), 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, stub_shm_file->fake_mapping)); #endif mgm::ShmBuffer buf(stub_shm_file, size, mir_pixel_format_argb_8888); buf.gl_bind_to_texture(); } TEST_F(ShmBufferTest, uploads_xbgr_8888_correctly) { #if __BYTE_ORDER == __LITTLE_ENDIAN EXPECT_CALL(mock_gl, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width.as_int(), size.height.as_int(), 0, GL_RGBA, GL_UNSIGNED_BYTE, stub_shm_file->fake_mapping)); #endif mgm::ShmBuffer buf(stub_shm_file, size, mir_pixel_format_xbgr_8888); buf.gl_bind_to_texture(); } TEST_F(ShmBufferTest, uploads_abgr_8888_correctly) { #if __BYTE_ORDER == __LITTLE_ENDIAN EXPECT_CALL(mock_gl, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width.as_int(), size.height.as_int(), 0, GL_RGBA, GL_UNSIGNED_BYTE, stub_shm_file->fake_mapping)); #endif mgm::ShmBuffer buf(stub_shm_file, size, mir_pixel_format_abgr_8888); buf.gl_bind_to_texture(); } ./tests/unit-tests/graphics/mesa/common/test_anonymous_shm_file.cpp0000644000015600001650000000340712676616125026054 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/platforms/mesa/server/common/anonymous_shm_file.h" #include namespace mgm = mir::graphics::mesa; TEST(AnonymousShmFile, is_created) { size_t const file_size{100}; mgm::AnonymousShmFile shm_file{file_size}; EXPECT_GE(shm_file.fd(), 0); } TEST(AnonymousShmFile, has_correct_size) { size_t const file_size{100}; mgm::AnonymousShmFile shm_file{file_size}; struct stat stat; fstat(shm_file.fd(), &stat); EXPECT_EQ(static_cast(file_size), stat.st_size); } TEST(AnonymousShmFile, writing_to_base_ptr_writes_to_file) { size_t const file_size{100}; mgm::AnonymousShmFile shm_file{file_size}; auto base_ptr = reinterpret_cast(shm_file.base_ptr()); for (size_t i = 0; i < file_size; i++) { base_ptr[i] = i; } std::vector buffer(file_size); EXPECT_EQ(static_cast(file_size), read(shm_file.fd(), buffer.data(), file_size)); for (size_t i = 0; i < file_size; i++) { EXPECT_EQ(base_ptr[i], buffer[i]) << "i=" << i; } } ./tests/unit-tests/graphics/mesa/common/test_drm_helper.cpp0000644000015600001650000000414612676616125024300 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/platforms/mesa/server/common/display_helpers.h" #include "mir/udev/wrapper.h" #include "mir_test_framework/udev_environment.h" #include "mir/test/doubles/mock_drm.h" #include #include #include namespace mgm = mir::graphics::mesa; namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace { MATCHER_P(FlagSet, flag, "") { return (arg & flag); } class DRMHelperTest : public ::testing::Test { public: DRMHelperTest() { fake_devices.add_standard_device("standard-drm-devices"); } protected: ::testing::NiceMock mock_drm; mtf::UdevEnvironment fake_devices; mgm::helpers::DRMHelper drm_helper{mgm::helpers::DRMNodeToUse::card}; }; } TEST_F(DRMHelperTest, closes_drm_fd_on_exec) { using namespace testing; EXPECT_CALL(mock_drm, open(_, FlagSet(O_CLOEXEC), _)); drm_helper.setup(std::make_shared()); } TEST_F(DRMHelperTest, throws_if_drm_auth_magic_fails) { using namespace testing; drm_magic_t const magic{0x10111213}; EXPECT_CALL(mock_drm, drmAuthMagic(mock_drm.fake_drm.fd(), magic)) .WillOnce(Return(-1)); drm_helper.setup(std::make_shared()); EXPECT_THROW({ drm_helper.auth_magic(magic); }, std::runtime_error); } ./tests/unit-tests/graphics/mesa/common/CMakeLists.txt0000644000015600001650000000100312676616157023146 0ustar jenkinsjenkinsadd_library(unit_test_graphics_mesa_common OBJECT ${CMAKE_CURRENT_SOURCE_DIR}/test_anonymous_shm_file.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_shm_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_ipc_operations.cpp ) list(APPEND UNIT_TEST_SOURCES $ ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) list(APPEND UMOCK_UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_drm_helper.cpp ) set(UMOCK_UNIT_TEST_SOURCES ${UMOCK_UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/graphics/mesa/CMakeLists.txt0000644000015600001650000000063712676616157021672 0ustar jenkinsjenkinsinclude_directories( ${PROJECT_SOURCE_DIR}/include/platforms/mesa ${PROJECT_SOURCE_DIR}/src/platforms/mesa/server/common ) add_subdirectory(common/) if (MIR_BUILD_PLATFORM_MESA_KMS) add_subdirectory(kms/) endif() if (MIR_BUILD_PLATFORM_MESA_X11) add_subdirectory(x11/) endif() set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) set(UMOCK_UNIT_TEST_SOURCES ${UMOCK_UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/graphics/mesa/x11/0000755000015600001650000000000012676616160017527 5ustar jenkinsjenkins./tests/unit-tests/graphics/mesa/x11/test_platform.cpp0000644000015600001650000001001712676616125023116 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Cemil Azizoglu */ #include #include #include "mir/options/program_option.h" #include "src/platforms/mesa/server/x11/graphics/platform.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir/test/doubles/mock_x11.h" #include "mir/shared_library.h" #include "mir_test_framework/executable_path.h" #include "mir_test_framework/udev_environment.h" namespace mg = mir::graphics; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { const char probe_platform[] = "probe_graphics_platform"; class X11GraphicsPlatformTest : public ::testing::Test { public: void SetUp() { ::testing::Mock::VerifyAndClearExpectations(&mock_drm); ::testing::Mock::VerifyAndClearExpectations(&mock_gbm); } std::shared_ptr create_platform() { return std::make_shared(std::shared_ptr<::Display>( XOpenDisplay(nullptr), [](::Display* display) { XCloseDisplay(display); }), mir::geometry::Size{1280,1024}); } ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; ::testing::NiceMock mock_x11; }; } TEST_F(X11GraphicsPlatformTest, failure_to_open_x11_display_results_in_an_error) { using namespace ::testing; EXPECT_CALL(mock_x11, XOpenDisplay(_)) .WillRepeatedly(Return(nullptr)); EXPECT_THROW({ create_platform(); }, std::exception); } TEST_F(X11GraphicsPlatformTest, failure_to_open_drm_results_in_an_error) { using namespace ::testing; EXPECT_CALL(mock_drm, open(_,_,_)) .WillRepeatedly(Return(-1)); EXPECT_THROW({ create_platform(); }, std::exception); } TEST_F(X11GraphicsPlatformTest, failure_to_create_gbm_device_results_in_an_error) { using namespace ::testing; EXPECT_CALL(mock_gbm, gbm_create_device(mock_drm.fake_drm.fd())) .WillRepeatedly(Return(nullptr)); EXPECT_THROW({ create_platform(); }, std::exception); } TEST_F(X11GraphicsPlatformTest, probe_returns_unsupported_when_no_drm_udev_devices) { mtf::UdevEnvironment udev_environment; mir::options::ProgramOption options; mir::SharedLibrary platform_lib{mtf::server_platform("server-mesa-x11")}; auto probe = platform_lib.load_function(probe_platform); EXPECT_EQ(mg::PlatformPriority::unsupported, probe(options)); } TEST_F(X11GraphicsPlatformTest, probe_returns_unsupported_when_x_cannot_open_display) { using namespace ::testing; mir::options::ProgramOption options; EXPECT_CALL(mock_x11, XOpenDisplay(_)) .WillRepeatedly(Return(nullptr)); mir::SharedLibrary platform_lib{mtf::server_platform("server-mesa-x11")}; auto probe = platform_lib.load_function(probe_platform); EXPECT_EQ(mg::PlatformPriority::unsupported, probe(options)); } TEST_F(X11GraphicsPlatformTest, probe_returns_supported_when_drm_render_nodes_exist) { mtf::UdevEnvironment udev_environment; mir::options::ProgramOption options; udev_environment.add_standard_device("standard-drm-render-nodes"); mir::SharedLibrary platform_lib{mtf::server_platform("server-mesa-x11")}; auto probe = platform_lib.load_function(probe_platform); EXPECT_EQ(mg::PlatformPriority::supported, probe(options)); } ./tests/unit-tests/graphics/mesa/x11/test_display.cpp0000644000015600001650000000755512676616125022754 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Cemil Azizoglu */ #include #include #include "src/platforms/mesa/server/x11/graphics/display.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_x11.h" namespace mg=mir::graphics; namespace mgx=mg::X; namespace mtd=mir::test::doubles; namespace geom=mir::geometry; namespace { geom::Size const size{1280, 1024}; class X11DisplayTest : public ::testing::Test { public: X11DisplayTest() { using namespace testing; EGLint const client_version = 2; ON_CALL(mock_egl, eglQueryContext(mock_egl.fake_egl_display, mock_egl.fake_egl_context, EGL_CONTEXT_CLIENT_VERSION, _)) .WillByDefault(DoAll(SetArgPointee<3>(client_version), Return(EGL_TRUE))); ON_CALL(mock_egl, eglQuerySurface(mock_egl.fake_egl_display, mock_egl.fake_egl_surface, EGL_WIDTH, _)) .WillByDefault(DoAll(SetArgPointee<3>(size.width.as_int()), Return(EGL_TRUE))); ON_CALL(mock_egl, eglQuerySurface(mock_egl.fake_egl_display, mock_egl.fake_egl_surface, EGL_HEIGHT, _)) .WillByDefault(DoAll(SetArgPointee<3>(size.height.as_int()), Return(EGL_TRUE))); ON_CALL(mock_egl, eglGetConfigAttrib(mock_egl.fake_egl_display, _, _, _)) .WillByDefault(DoAll(SetArgPointee<3>(EGL_WINDOW_BIT), Return(EGL_TRUE))); ON_CALL(mock_x11, XNextEvent(mock_x11.fake_x11.display, _)) .WillByDefault(DoAll(SetArgPointee<1>(mock_x11.fake_x11.expose_event_return), Return(1))); } std::shared_ptr create_display() { return std::make_shared(mock_x11.fake_x11.display, size); } ::testing::NiceMock mock_egl; ::testing::NiceMock mock_x11; }; } TEST_F(X11DisplayTest, creates_display_successfully) { using namespace testing; EXPECT_CALL(mock_egl, eglGetDisplay(mock_x11.fake_x11.display)) .Times(Exactly(1)); EXPECT_CALL(mock_x11, XCreateWindow_wrapper(mock_x11.fake_x11.display,_, size.width.as_int(), size.height.as_int(),_,_,_,_,_,_)) .Times(Exactly(1)); EXPECT_CALL(mock_egl, eglCreateContext(mock_egl.fake_egl_display,_, EGL_NO_CONTEXT,_)) .Times(Exactly(1)); EXPECT_CALL(mock_egl, eglCreateWindowSurface(mock_egl.fake_egl_display,_, mock_x11.fake_x11.window, nullptr)) .Times(Exactly(1)); EXPECT_CALL(mock_x11, XNextEvent(mock_x11.fake_x11.display,_)) .Times(AtLeast(1)); EXPECT_CALL(mock_x11, XMapWindow(mock_x11.fake_x11.display,_)) .Times(Exactly(1)); auto display = create_display(); } ./tests/unit-tests/graphics/mesa/x11/test_guest_platform.cpp0000644000015600001650000000366712676616125024342 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Cemil Azizoglu */ #include "src/platforms/mesa/server/x11/graphics/guest_platform.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include #include namespace mgx = mir::graphics::X; namespace mtd = mir::test::doubles; namespace { class X11GuestPlatformTest : public ::testing::Test { public: void SetUp() { ::testing::Mock::VerifyAndClearExpectations(&mock_drm); ::testing::Mock::VerifyAndClearExpectations(&mock_gbm); } std::shared_ptr create_guest_platform() { return std::make_shared(nullptr); } ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; }; } TEST_F(X11GuestPlatformTest, failure_to_open_drm_results_in_an_error) { using namespace ::testing; EXPECT_CALL(mock_drm, open(_,_,_)) .WillRepeatedly(Return(-1)); EXPECT_THROW({ create_guest_platform(); }, std::exception); } TEST_F(X11GuestPlatformTest, failure_to_create_gbm_device_results_in_an_error) { using namespace ::testing; EXPECT_CALL(mock_gbm, gbm_create_device(mock_drm.fake_drm.fd())) .WillRepeatedly(Return(nullptr)); EXPECT_THROW({ create_guest_platform(); }, std::exception); } ./tests/unit-tests/graphics/mesa/x11/test_graphics_platform.cpp0000644000015600001650000000601212676616125024776 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Guest * Kevin DuBois */ #include "mir/graphics/platform.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/buffer_properties.h" #include "mir/graphics/platform_ipc_operations.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "mir/options/program_option.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir_test_framework/udev_environment.h" #include "src/platforms/mesa/server/x11/graphics/platform.h" #include "mir/test/doubles/mock_x11.h" #include "mir/logging/dumb_console_logger.h" #include namespace mg = mir::graphics; namespace mgm = mg::mesa; namespace ml = mir::logging; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; namespace mo = mir::options; namespace mtf = mir_test_framework; class GraphicsPlatform : public ::testing::Test { public: GraphicsPlatform() : logger(std::make_shared()) { using namespace testing; ON_CALL(mock_gbm, gbm_bo_get_width(_)) .WillByDefault(Return(320)); ON_CALL(mock_gbm, gbm_bo_get_height(_)) .WillByDefault(Return(240)); // FIXME: This format needs to match Mesa's first supported pixel // format or tests will fail. The coupling is presently loose. ON_CALL(mock_gbm, gbm_bo_get_format(_)) .WillByDefault(Return(GBM_FORMAT_ARGB8888)); fake_devices.add_standard_device("standard-drm-render-nodes"); } std::shared_ptr create_platform() { return std::make_shared(std::shared_ptr<::Display>( XOpenDisplay(nullptr), [](::Display* display) { XCloseDisplay(display); }), mir::geometry::Size{1280,1024}); } std::shared_ptr logger; ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; mtf::UdevEnvironment fake_devices; ::testing::NiceMock mock_x11; }; #include "../../test_graphics_platform.h" ./tests/unit-tests/graphics/mesa/x11/CMakeLists.txt0000644000015600001650000000162712676616157022303 0ustar jenkinsjenkinsmir_add_wrapped_executable( mir_unit_tests_mesa-x11 ${CMAKE_CURRENT_SOURCE_DIR}/test_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_graphics_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_guest_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_generic.cpp $ $ $ ) add_dependencies(mir_unit_tests_mesa-x11 GMock) target_link_libraries( mir_unit_tests_mesa-x11 mir-test-static mir-test-framework-static mir-test-doubles-static mir-test-doubles-platform-static mirsharedmesaservercommon-static ) if (MIR_RUN_UNIT_TESTS) mir_discover_tests_with_fd_leak_detection(mir_unit_tests_mesa-x11 LD_PRELOAD=libumockdev-preload.so.0 G_SLICE=always-malloc G_DEBUG=gc-friendly) endif (MIR_RUN_UNIT_TESTS) ./tests/unit-tests/graphics/mesa/x11/test_display_generic.cpp0000644000015600001650000001117012676616125024434 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/display.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/gl_context.h" #include "mir/graphics/platform.h" #include "mir/options/program_option.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "src/server/report/null_report_factory.h" #include "mir/graphics/default_display_configuration_policy.h" #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #include "mir_test_framework/udev_environment.h" #include "src/platforms/mesa/server/x11/graphics/platform.h" #include "mir/test/doubles/mock_x11.h" #include #include namespace mg = mir::graphics; namespace mgm = mg::mesa; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; class DisplayTestGeneric : public ::testing::Test { public: DisplayTestGeneric() { using namespace testing; EGLint const client_version = 2; ON_CALL(mock_egl, eglQueryContext(mock_egl.fake_egl_display, mock_egl.fake_egl_context, EGL_CONTEXT_CLIENT_VERSION, _)) .WillByDefault(DoAll(SetArgPointee<3>(client_version), Return(EGL_TRUE))); ON_CALL(mock_egl, eglQuerySurface(mock_egl.fake_egl_display, mock_egl.fake_egl_surface, EGL_WIDTH, _)) .WillByDefault(DoAll(SetArgPointee<3>(1280), Return(EGL_TRUE))); ON_CALL(mock_egl, eglQuerySurface(mock_egl.fake_egl_display, mock_egl.fake_egl_surface, EGL_HEIGHT, _)) .WillByDefault(DoAll(SetArgPointee<3>(1024), Return(EGL_TRUE))); ON_CALL(mock_egl, eglGetConfigAttrib(mock_egl.fake_egl_display, _, _, _)) .WillByDefault(DoAll(SetArgPointee<3>(EGL_WINDOW_BIT), Return(EGL_TRUE))); ON_CALL(mock_x11, XNextEvent(mock_x11.fake_x11.display, _)) .WillByDefault(DoAll(SetArgPointee<1>(mock_x11.fake_x11.expose_event_return), Return(1))); ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); mock_egl.provide_egl_extensions(); mock_gl.provide_gles_extensions(); fake_devices.add_standard_device("standard-drm-render-nodes"); } std::shared_ptr create_display() { auto const platform = std::make_shared(std::shared_ptr<::Display>( XOpenDisplay(nullptr), [](::Display* display) { XCloseDisplay(display); }), mir::geometry::Size{1280,1024}); return platform->create_display( std::make_shared(), std::make_shared()); } ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; ::testing::NiceMock mock_drm; ::testing::NiceMock mock_gbm; mtf::UdevEnvironment fake_devices; ::testing::NiceMock mock_x11; }; #define MIR_DISABLE_TESTS_ON_X11 #include "../../test_display.h" #undef MIR_DISABLE_TESTS_ON_X11 ./tests/unit-tests/graphics/android/0000755000015600001650000000000012676616160017611 5ustar jenkinsjenkins./tests/unit-tests/graphics/android/hwc_struct_helpers.cpp0000644000015600001650000000531412676616125024230 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include #include "hwc_struct_helpers.h" #include "native_buffer.h" void PrintTo(const hwc_rect_t& rect, ::std::ostream* os) { *os << "( left: " << rect.left << ", top: " << rect.top << ", right " << rect.right << ", bottom: "<< rect.bottom << ")"; } void PrintTo(const hwc_layer_1& layer , ::std::ostream* os) { *os << "compositionType: " << layer.compositionType << std::endl << "\thints: " << layer.hints << std::endl << "\tflags: " << layer.flags << std::endl << "\thandle: " << layer.handle << std::endl << "\ttransform: " << layer.transform << std::endl << "\tblending: " << layer.blending << std::endl << "\tsourceCrop: "; PrintTo(layer.sourceCropi, os); *os << std::endl << "\tdisplayFrame:"; PrintTo(layer.displayFrame, os); *os << std::endl; *os << "\tvisibleRegionScreen.numRects: " << layer.visibleRegionScreen.numRects << std::endl << "\tplaneAlpha: " << layer.planeAlpha << std::endl << "\tacquireFenceFd: " << layer.acquireFenceFd << std::endl << "\treleaseFenceFd: " << layer.releaseFenceFd << std::endl; } void mir::test::fill_hwc_layer( hwc_layer_1_t& layer, hwc_rect_t* visible_rect, mir::geometry::Rectangle const& position, mir::graphics::Buffer const& buffer, int type, int flags) { *visible_rect = {0, 0, buffer.size().width.as_int(), buffer.size().height.as_int()}; layer.compositionType = type; layer.hints = 0; layer.flags = flags; layer.handle = buffer.native_buffer_handle()->handle(); layer.transform = 0; layer.blending = HWC_BLENDING_NONE; layer.sourceCrop = *visible_rect; layer.displayFrame = { position.top_left.x.as_int(), position.top_left.y.as_int(), position.bottom_right().x.as_int(), position.bottom_right().y.as_int() }; layer.visibleRegionScreen = {1, visible_rect}; layer.acquireFenceFd = -1; layer.releaseFenceFd = -1; layer.planeAlpha = std::numeric_limits::max(); } ./tests/unit-tests/graphics/android/test_resource_factory.cpp0000644000015600001650000000655712676616125024750 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/resource_factory.h" #include "src/platforms/android/server/hwc_loggers.h" #include "mir/test/doubles/mock_android_hw.h" #include #include #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mtd=mir::test::doubles; struct ResourceFactoryTest : public ::testing::Test { mtd::HardwareAccessMock hw_access_mock; std::shared_ptr null_report{std::make_shared()}; }; TEST_F(ResourceFactoryTest, fb_native_creation_opens_and_closes_gralloc) { using namespace testing; EXPECT_CALL(hw_access_mock, hw_get_module(StrEq(GRALLOC_HARDWARE_MODULE_ID), _)) .Times(1); mga::ResourceFactory factory; factory.create_fb_native_device(); EXPECT_TRUE(hw_access_mock.open_count_matches_close()); } TEST_F(ResourceFactoryTest, test_device_creation_throws_on_failure) { using namespace testing; mga::ResourceFactory factory; /* failure because of rc */ EXPECT_CALL(hw_access_mock, hw_get_module(StrEq(GRALLOC_HARDWARE_MODULE_ID), _)) .Times(1) .WillOnce(Return(-1)); EXPECT_THROW({ factory.create_fb_native_device(); }, std::runtime_error); /* failure because of nullptr returned */ EXPECT_CALL(hw_access_mock, hw_get_module(StrEq(GRALLOC_HARDWARE_MODULE_ID), _)) .Times(1) .WillOnce(DoAll(SetArgPointee<1>(nullptr),Return(-1))); EXPECT_THROW({ factory.create_fb_native_device(); }, std::runtime_error); } TEST_F(ResourceFactoryTest, hwc_allocation) { using namespace testing; hw_access_mock.mock_hwc_device->common.version = HWC_DEVICE_API_VERSION_1_2; EXPECT_CALL(hw_access_mock, hw_get_module(StrEq(HWC_HARDWARE_MODULE_ID), _)) .Times(1); mga::ResourceFactory factory; auto wrapper_tuple = factory.create_hwc_wrapper(null_report); EXPECT_THAT(std::get<1>(wrapper_tuple), Eq(mga::HwcVersion::hwc12)); std::get<0>(wrapper_tuple).reset(); EXPECT_TRUE(hw_access_mock.open_count_matches_close()); } TEST_F(ResourceFactoryTest, hwc_allocation_failures) { using namespace testing; mtd::FailingHardwareModuleStub failing_hwc_module_stub; EXPECT_CALL(hw_access_mock, hw_get_module(StrEq(HWC_HARDWARE_MODULE_ID), _)) .Times(2) .WillOnce(Return(-1)) .WillOnce(DoAll(SetArgPointee<1>(&failing_hwc_module_stub), Return(0))); mga::ResourceFactory factory; EXPECT_THROW({ factory.create_hwc_wrapper(null_report); }, std::runtime_error); EXPECT_THROW({ factory.create_hwc_wrapper(null_report); }, std::runtime_error); EXPECT_TRUE(hw_access_mock.open_count_matches_close()); } ./tests/unit-tests/graphics/android/test_egl_sync_fence.cpp0000644000015600001650000001231012676616125024315 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include #include "egl_sync_fence.h" #include #include #include namespace mg = mir::graphics; namespace mtd = mir::test::doubles; using namespace testing; struct EglSyncFence : ::testing::Test { NiceMock mock_egl; std::shared_ptr sync_extensions{std::make_shared()}; std::array fake_fences; void* fake_fence0{&fake_fences[0]}; void* fake_fence1{&fake_fences[1]}; }; TEST_F(EglSyncFence, creation_failure_throws) { EXPECT_CALL(mock_egl, eglGetCurrentDisplay()); EXPECT_CALL(mock_egl, eglCreateSyncKHR(mock_egl.fake_egl_display, EGL_SYNC_FENCE_KHR, nullptr)) .WillOnce(Return(EGL_NO_SYNC_KHR)); mg::EGLSyncFence fence(sync_extensions); EXPECT_NO_THROW({ fence.raise(); }); } TEST_F(EglSyncFence, raise_sets_sync_point) { Sequence seq; EXPECT_CALL(mock_egl, eglGetCurrentDisplay()); EXPECT_CALL(mock_egl, eglCreateSyncKHR(mock_egl.fake_egl_display, EGL_SYNC_FENCE_KHR, nullptr)) .WillOnce(Return(fake_fence0)); EXPECT_CALL(mock_egl, eglDestroySyncKHR(mock_egl.fake_egl_display, fake_fence0)) .InSequence(seq); //destructor should call this mg::EGLSyncFence fence(sync_extensions); fence.raise(); } TEST_F(EglSyncFence, can_wait_for_fence) { std::chrono::nanoseconds ns(1456); Sequence seq; EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .InSequence(seq); EXPECT_CALL(mock_egl, eglCreateSyncKHR(mock_egl.fake_egl_display, EGL_SYNC_FENCE_KHR, nullptr)) .InSequence(seq) .WillOnce(Return(fake_fence0)); EXPECT_CALL(mock_egl, eglClientWaitSyncKHR(mock_egl.fake_egl_display, fake_fence0, 0, ns.count())) .InSequence(seq) .WillOnce(Return(EGL_CONDITION_SATISFIED_KHR)); EXPECT_CALL(mock_egl, eglDestroySyncKHR(mock_egl.fake_egl_display, fake_fence0)) .InSequence(seq); mg::EGLSyncFence fence(sync_extensions); fence.raise(); EXPECT_TRUE(fence.wait_for(ns)); } TEST_F(EglSyncFence, can_wait_for_fence_with_timeout) { std::chrono::nanoseconds ns(1456); Sequence seq; EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .InSequence(seq); EXPECT_CALL(mock_egl, eglCreateSyncKHR(mock_egl.fake_egl_display, EGL_SYNC_FENCE_KHR, nullptr)) .InSequence(seq) .WillOnce(Return(fake_fence0)); EXPECT_CALL(mock_egl, eglClientWaitSyncKHR(mock_egl.fake_egl_display, fake_fence0, 0, ns.count())) .InSequence(seq) .WillOnce(Return(EGL_TIMEOUT_EXPIRED_KHR)); EXPECT_CALL(mock_egl, eglDestroySyncKHR(mock_egl.fake_egl_display, fake_fence0)) .InSequence(seq); mg::EGLSyncFence fence(sync_extensions); fence.raise(); EXPECT_FALSE(fence.wait_for(ns)); } TEST_F(EglSyncFence, can_clear_without_waiting) { std::chrono::nanoseconds ns(1456); Sequence seq; EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .InSequence(seq); EXPECT_CALL(mock_egl, eglCreateSyncKHR(mock_egl.fake_egl_display, EGL_SYNC_FENCE_KHR, nullptr)) .InSequence(seq) .WillOnce(Return(fake_fence0)); EXPECT_CALL(mock_egl, eglDestroySyncKHR(mock_egl.fake_egl_display, fake_fence0)) .InSequence(seq); mg::EGLSyncFence fence(sync_extensions); fence.raise(); fence.reset(); Mock::VerifyAndClearExpectations(&mock_egl); } TEST_F(EglSyncFence, repeated_raises_clear_existing_fence) { std::chrono::milliseconds one_ms(1); auto reasonable_value = std::chrono::duration_cast(one_ms); Sequence seq; EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .InSequence(seq); EXPECT_CALL(mock_egl, eglCreateSyncKHR(mock_egl.fake_egl_display, EGL_SYNC_FENCE_KHR, nullptr)) .InSequence(seq) .WillOnce(Return(fake_fence0)); EXPECT_CALL(mock_egl, eglClientWaitSyncKHR(mock_egl.fake_egl_display, fake_fence0, 0, reasonable_value.count())) .InSequence(seq) .WillOnce(Return(EGL_CONDITION_SATISFIED_KHR)); EXPECT_CALL(mock_egl, eglDestroySyncKHR(mock_egl.fake_egl_display, fake_fence0)) .InSequence(seq); EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .InSequence(seq); EXPECT_CALL(mock_egl, eglCreateSyncKHR(mock_egl.fake_egl_display, EGL_SYNC_FENCE_KHR, nullptr)) .InSequence(seq) .WillOnce(Return(fake_fence1)); EXPECT_CALL(mock_egl, eglDestroySyncKHR(mock_egl.fake_egl_display, fake_fence1)) .InSequence(seq); mg::EGLSyncFence fence(sync_extensions); fence.raise(); fence.raise(); } ./tests/unit-tests/graphics/android/test_ipc_operations.cpp0000644000015600001650000000307312676616125024376 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/ipc_operations.h" #include "mir/graphics/platform_operation_message.h" #include namespace mg = mir::graphics; namespace mga = mir::graphics::android; struct IpcOperations : public ::testing::Test { mga::IpcOperations ipc_operations; }; /* ipc packaging tests */ TEST_F(IpcOperations, test_ipc_data_packed_correctly_for_full_ipc) { //android has no valid operations platform specific operations yet, expect throw mg::PlatformOperationMessage message{ std::vector{2,4,8,16,32}, std::vector{fileno(tmpfile()), fileno(tmpfile())} }; EXPECT_THROW({ ipc_operations.platform_operation(0u, message); }, std::invalid_argument); EXPECT_THROW({ ipc_operations.platform_operation(1u, message); }, std::invalid_argument); for (auto fd : message.fds) close(fd); } ./tests/unit-tests/graphics/android/test_sync_fence.cpp0000644000015600001650000000714112676616125023474 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "sync_fence.h" #include "mir/test/doubles/mock_fence.h" #include #include #include #include #include namespace mga = mir::graphics::android; namespace mtd = mir::test::doubles; namespace { struct MockFileOps : public mga::SyncFileOps { MOCK_METHOD3(ioctl, int(int,int,void*)); MOCK_METHOD1(dup, int(int)); MOCK_METHOD1(close, int(int)); }; } class SyncSwTest : public ::testing::Test { protected: virtual void SetUp() { mock_fops = std::make_shared>(); dummy_fd_value = 3; } int dummy_fd_value{3}; int invalid_fd_value{-1}; mir::Fd dummy_fd{std::move(dummy_fd_value)}; mir::Fd invalid_fd{std::move(invalid_fd_value)}; std::shared_ptr mock_fops; }; MATCHER_P(TimeoutMatches, value, std::string("timeout should be: " + testing::PrintToString(value))) { int* timeout = static_cast(arg); if (!timeout) return false; return value == *timeout; } MATCHER_P(MergeMatches, value, std::string("merge should be: " + testing::PrintToString(value))) { auto argument = static_cast(arg); return argument->fd2 == value.fd2; } TEST_F(SyncSwTest, sync_wait) { EXPECT_CALL(*mock_fops, ioctl(dummy_fd_value, SYNC_IOC_WAIT, TimeoutMatches(-1))) .Times(1); mga::SyncFence fence1(mock_fops, std::move(dummy_fd)); fence1.wait(); //will not call ioctl mga::SyncFence fence2(mock_fops, std::move(invalid_fd)); fence2.wait(); } namespace { struct IoctlSetter { IoctlSetter(int fd) : fd(fd) { } int merge_setter(int, int, void* data) { auto b = static_cast(data); b->fence = fd; return 0; } int fd; }; } TEST_F(SyncSwTest, sync_merge_with_valid_fd) { using namespace testing; int dummy_fd2 = 44; int out_fd = 88; IoctlSetter setter(out_fd); struct sync_merge_data expected_data_in { dummy_fd2, "name", 0 }; EXPECT_CALL(*mock_fops, ioctl(dummy_fd_value, static_cast(SYNC_IOC_MERGE), MergeMatches(expected_data_in))) .Times(1) .WillOnce(Invoke(&setter, &IoctlSetter::merge_setter)); mga::SyncFence fence1(mock_fops, std::move(dummy_fd)); fence1.merge_with(dummy_fd2); } TEST_F(SyncSwTest, sync_merge_with_invalid_fd) { using namespace testing; EXPECT_CALL(*mock_fops, ioctl(dummy_fd_value, static_cast(SYNC_IOC_MERGE), _)) .Times(0); mga::SyncFence fence1(mock_fops, std::move(dummy_fd)); fence1.merge_with(invalid_fd_value); } TEST_F(SyncSwTest, copy_dups_fd) { using namespace testing; int fd2 = dummy_fd_value + 1; EXPECT_CALL(*mock_fops, dup(dummy_fd_value)) .Times(1) .WillOnce(Return(fd2));; mga::SyncFence fence(mock_fops, std::move(dummy_fd)); EXPECT_EQ(fd2, fence.copy_native_handle()); } ./tests/unit-tests/graphics/android/test_hwc_fallback_gl_renderer.cpp0000644000015600001650000003130012676616125026322 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/hwc_fallback_gl_renderer.h" #include "mir/graphics/gl_context.h" #include "mir/gl/program_factory.h" #include "mir/gl/primitive.h" #include "mir/gl/texture.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/stub_renderable.h" #include "mir/test/doubles/mock_swapping_gl_context.h" #include "mir/test/doubles/stub_gl_program.h" #include #include #define GLM_FORCE_RADIANS #define GLM_PRECISION_MEDIUMP_FLOAT #include #include #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mgl=mir::gl; namespace mt=mir::test; namespace mtd=mir::test::doubles; namespace geom=mir::geometry; namespace { class MockGLProgramFactory : public mgl::ProgramFactory { public: MOCK_CONST_METHOD2(create_gl_program, std::unique_ptr(std::string const&, std::string const&)); MOCK_CONST_METHOD0(create_texture_cache, std::unique_ptr()); }; class MockContext : public mg::GLContext { public: MOCK_CONST_METHOD0(make_current, void()); MOCK_CONST_METHOD0(release_current, void()); }; struct MockTextureCache : public mgl::TextureCache { MockTextureCache() { ON_CALL(*this, load(testing::_)) .WillByDefault(testing::Return(std::make_shared())); } MOCK_METHOD1(load, std::shared_ptr(mg::Renderable const&)); MOCK_METHOD0(invalidate, void()); MOCK_METHOD0(drop_unused, void()); }; class StubTextureCache : public mgl::TextureCache { std::shared_ptr load(mg::Renderable const&) { return std::make_shared(); } void invalidate() { } void drop_unused() { } }; class HWCFallbackGLRenderer : public ::testing::Test { public: HWCFallbackGLRenderer() { using namespace testing; ON_CALL(mock_gl_program_factory,create_gl_program(_,_)) .WillByDefault(Invoke([](std::string const, std::string const) { return std::unique_ptr(new mtd::StubGLProgram); })); ON_CALL(mock_gl_program_factory,create_texture_cache()) .WillByDefault(Invoke([]() { return std::unique_ptr(new StubTextureCache); })); ON_CALL(mock_gl, glGetShaderiv(_,_,_)) .WillByDefault(SetArgPointee<2>(GL_TRUE)); ON_CALL(mock_gl, glGetProgramiv(_,_,_)) .WillByDefault(SetArgPointee<2>(GL_TRUE)); ON_CALL(mock_gl, glGetUniformLocation(_, StrEq("display_transform"))) .WillByDefault(Return(display_transform_uniform_loc)); ON_CALL(mock_gl, glGetUniformLocation(_, StrEq("tex"))) .WillByDefault(Return(tex_uniform_loc)); ON_CALL(mock_gl, glGetAttribLocation(_, StrEq("position"))) .WillByDefault(Return(position_attr_loc)); ON_CALL(mock_gl, glGetAttribLocation(_, StrEq("texcoord"))) .WillByDefault(Return(texcoord_attr_loc)); ON_CALL(mock_gl, glGenTextures(1,_)) .WillByDefault(SetArgPointee<1>(texid)); } geom::Displacement offset; GLint const display_transform_uniform_loc{1}; GLint const position_attr_loc{2}; GLint const texcoord_attr_loc{3}; GLint const tex_uniform_loc{4}; GLint const texid{5}; size_t const stride{sizeof(mgl::Vertex)}; testing::NiceMock mock_gl_program_factory; testing::NiceMock mock_swapping_context; testing::NiceMock mock_context; testing::NiceMock mock_gl; testing::NiceMock mock_egl; geom::Rectangle dummy_screen_pos{geom::Point{0,0}, geom::Size{500,400}}; }; } TEST_F(HWCFallbackGLRenderer, compiles_and_sets_up_gl_program) { using namespace testing; InSequence seq; EXPECT_CALL(mock_context, make_current()); EXPECT_CALL(mock_gl_program_factory, create_gl_program(_,_)); EXPECT_CALL(mock_gl, glUseProgram(_)); EXPECT_CALL(mock_gl, glGetUniformLocation(_, StrEq("display_transform"))); EXPECT_CALL(mock_gl, glGetAttribLocation(_, StrEq("position"))); EXPECT_CALL(mock_gl, glGetAttribLocation(_, StrEq("texcoord"))); EXPECT_CALL(mock_gl, glGetUniformLocation(_, StrEq("tex"))); EXPECT_CALL(mock_gl, glUniform1i(tex_uniform_loc, 0)); EXPECT_CALL(mock_gl, glUseProgram(0)); EXPECT_CALL(mock_context, release_current()); mga::HWCFallbackGLRenderer glprogram(mock_gl_program_factory, mock_context, dummy_screen_pos); } MATCHER_P(Matches4x4Matrix, value, "matches expected 4x4 matrix") { for(auto i=0; i < 16; i++) EXPECT_THAT(arg[i], testing::FloatEq(value[i])); return !(::testing::Test::HasFailure()); } TEST_F(HWCFallbackGLRenderer, sets_up_orthographic_matrix_based_on_screen_size) { using namespace testing; geom::Size sz{800,600}; geom::Point pt{100,200}; geom::Rectangle screen_pos{pt, sz}; float inv_w = 2.0/sz.width.as_int(); float inv_h = 2.0/sz.height.as_int() * -1.0; float expected_matrix[]{ inv_w, 0.0 , 0.0, 0.0, 0.0 , inv_h , 0.0, 0.0, 0.0 , 0.0 , 1.0, 0.0, -1.0 , 1.0 , 0.0, 1.0 }; EXPECT_CALL(mock_gl, glUniformMatrix4fv( display_transform_uniform_loc, 1, GL_FALSE, Matches4x4Matrix(expected_matrix))); mga::HWCFallbackGLRenderer glprogram(mock_gl_program_factory, mock_context, screen_pos); } struct Vertex { float x; float y; }; Vertex to_vertex(geom::Point const& pos) { return { static_cast(pos.x.as_int()), static_cast(pos.y.as_int()) }; } MATCHER_P2(MatchesVertices, vertices, stride, "matches vertices") { auto arg_vertices = static_cast(arg); for(auto const& vert : vertices) { GLfloat *f = (GLfloat*) arg_vertices; EXPECT_THAT(f[0], testing::FloatEq(vert.x)); EXPECT_THAT(f[1], testing::FloatEq(vert.y)); arg_vertices+=stride; } return !(::testing::Test::HasFailure()); } TEST_F(HWCFallbackGLRenderer, computes_vertex_coordinates_correctly) { using namespace testing; geom::Rectangle rect1{{100,200},{50, 60}}; geom::Rectangle rect2{{150,250},{150, 90}}; mg::RenderableList renderlist{ std::make_shared(rect1), std::make_shared(rect2) }; std::vector expected_vertices1 { to_vertex(rect1.top_left), to_vertex(rect1.bottom_left()), to_vertex(rect1.top_right()), to_vertex(rect1.bottom_right()), }; std::vector expected_vertices2 { to_vertex(rect2.top_left), to_vertex(rect2.bottom_left()), to_vertex(rect2.top_right()), to_vertex(rect2.bottom_right()), }; InSequence seq; EXPECT_CALL(mock_gl, glVertexAttribPointer( position_attr_loc, 3, GL_FLOAT, GL_FALSE, stride, MatchesVertices(expected_vertices1, stride))); EXPECT_CALL(mock_gl, glVertexAttribPointer(_,_,_,_,_,_)) .Times(1); EXPECT_CALL(mock_gl, glVertexAttribPointer( position_attr_loc, 3, GL_FLOAT, GL_FALSE, stride, MatchesVertices(expected_vertices2, stride))); EXPECT_CALL(mock_gl, glVertexAttribPointer(_,_,_,_,_,_)) .Times(1); mga::HWCFallbackGLRenderer glprogram(mock_gl_program_factory, mock_context, dummy_screen_pos); glprogram.render(renderlist, offset, mock_swapping_context); } TEST_F(HWCFallbackGLRenderer, computes_vertex_coordinates_correctly_with_offset) { using namespace testing; geom::Displacement offset{100, 50}; geom::Rectangle rect{{100,200},{50, 60}}; geom::Rectangle offset_rect{{0, 150}, rect.size}; mg::RenderableList renderlist{std::make_shared(rect)}; std::vector expected_vertices { to_vertex(offset_rect.top_left), to_vertex(offset_rect.bottom_left()), to_vertex(offset_rect.top_right()), to_vertex(offset_rect.bottom_right()), }; InSequence seq; EXPECT_CALL(mock_gl, glVertexAttribPointer( position_attr_loc, 3, GL_FLOAT, GL_FALSE, stride, MatchesVertices(expected_vertices, stride))); EXPECT_CALL(mock_gl, glVertexAttribPointer(_,_,_,_,_,_)) .Times(1); mga::HWCFallbackGLRenderer glprogram(mock_gl_program_factory, mock_context, dummy_screen_pos); glprogram.render(renderlist, offset, mock_swapping_context); } TEST_F(HWCFallbackGLRenderer, computes_texture_coordinates_correctly) { using namespace testing; geom::Rectangle rect1{{100,200},{50, 60}}; geom::Rectangle rect2{{150,250},{150, 90}}; mg::RenderableList renderlist{ std::make_shared(rect1), std::make_shared(rect2) }; std::vector expected_texcoord { {0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 0.0f}, {1.0f, 1.0f} }; EXPECT_CALL(mock_gl, glVertexAttribPointer(_,_,_,_,_,_)).Times(AnyNumber()); EXPECT_CALL(mock_gl, glVertexAttribPointer( texcoord_attr_loc, 2, GL_FLOAT, GL_FALSE, stride, MatchesVertices(expected_texcoord, stride))) .Times(2); mga::HWCFallbackGLRenderer glprogram(mock_gl_program_factory, mock_context, dummy_screen_pos); glprogram.render(renderlist, offset, mock_swapping_context); } TEST_F(HWCFallbackGLRenderer, executes_render_in_sequence) { using namespace testing; auto renderable1 = std::make_shared(); auto renderable2 = std::make_shared(); mg::RenderableList renderlist{ renderable1, renderable2 }; std::unique_ptr mock_texture_cache(new MockTextureCache); { InSequence seq; EXPECT_CALL(*mock_texture_cache, load(Ref(*renderable1))); EXPECT_CALL(*mock_texture_cache, load(Ref(*renderable2))); EXPECT_CALL(*mock_texture_cache, drop_unused()); } ON_CALL(mock_gl_program_factory,create_texture_cache()) .WillByDefault(Invoke([&](){ return std::move(mock_texture_cache); })); mga::HWCFallbackGLRenderer glprogram(mock_gl_program_factory, mock_context, dummy_screen_pos); InSequence seq; EXPECT_CALL(mock_gl, glUseProgram(_)); EXPECT_CALL(mock_gl, glClearColor(FloatEq(0.0), FloatEq(0.0), FloatEq(0.0), FloatEq(0.0))); EXPECT_CALL(mock_gl, glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); EXPECT_CALL(mock_gl, glClear(GL_COLOR_BUFFER_BIT)); EXPECT_CALL(mock_gl, glEnableVertexAttribArray(position_attr_loc)); EXPECT_CALL(mock_gl, glEnableVertexAttribArray(texcoord_attr_loc)); EXPECT_CALL(mock_gl, glVertexAttribPointer(position_attr_loc, 3, GL_FLOAT, GL_FALSE, _, _)); EXPECT_CALL(mock_gl, glVertexAttribPointer(texcoord_attr_loc, 2, GL_FLOAT, GL_FALSE, _, _)); EXPECT_CALL(mock_gl, glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); EXPECT_CALL(mock_gl, glVertexAttribPointer(position_attr_loc, 3, GL_FLOAT, GL_FALSE, _, _)); EXPECT_CALL(mock_gl, glVertexAttribPointer(texcoord_attr_loc, 2, GL_FLOAT, GL_FALSE, _, _)); EXPECT_CALL(mock_gl, glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); EXPECT_CALL(mock_gl, glDisableVertexAttribArray(texcoord_attr_loc)); EXPECT_CALL(mock_gl, glDisableVertexAttribArray(position_attr_loc)); EXPECT_CALL(mock_swapping_context, swap_buffers()); EXPECT_CALL(mock_gl, glUseProgram(0)); glprogram.render(renderlist, offset, mock_swapping_context); } TEST_F(HWCFallbackGLRenderer, activates_alpha_per_renderable) { mg::RenderableList renderlist{ std::make_shared(), std::make_shared(), std::make_shared() }; mga::HWCFallbackGLRenderer glprogram(mock_gl_program_factory, mock_context, dummy_screen_pos); testing::InSequence seq; EXPECT_CALL(mock_gl, glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); EXPECT_CALL(mock_gl, glEnable(GL_BLEND)); EXPECT_CALL(mock_gl, glDisable(GL_BLEND)); EXPECT_CALL(mock_gl, glEnable(GL_BLEND)); glprogram.render(renderlist, offset, mock_swapping_context); } ./tests/unit-tests/graphics/android/test_native_buffer.cpp0000644000015600001650000001514212676616125024177 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "android_native_buffer.h" #include "egl_sync_fence.h" #include "mir/test/doubles/mock_fence.h" #include #include namespace mtd=mir::test::doubles; namespace mga=mir::graphics::android; using namespace testing; namespace { struct MockCommandStreamSync : public mir::graphics::CommandStreamSync { MOCK_METHOD0(raise, void()); MOCK_METHOD0(reset, void()); MOCK_METHOD1(wait_for, bool(std::chrono::nanoseconds)); }; } struct NativeBuffer : public testing::Test { NativeBuffer() : a_native_window_buffer(std::make_shared()), mock_fence(std::make_shared>()), fake_fd{48484}, timeout{std::chrono::duration_cast(std::chrono::seconds(2))}, mock_cmdstream_sync{std::make_shared()} { } std::shared_ptr a_native_window_buffer; std::shared_ptr mock_fence; int fake_fd; std::chrono::nanoseconds timeout; std::shared_ptr mock_cmdstream_sync; }; TEST_F(NativeBuffer, extends_lifetime_when_driver_calls_external_refcount_hooks) { auto native_handle_resource = std::make_shared(); auto use_count_before = native_handle_resource.use_count(); ANativeWindowBuffer* driver_reference = nullptr; { auto tmp = new mga::RefCountedNativeBuffer(native_handle_resource); std::shared_ptr buffer(tmp, [](mga::RefCountedNativeBuffer* buffer) { buffer->mir_dereference(); }); driver_reference = buffer.get(); driver_reference->common.incRef(&driver_reference->common); //Mir loses its reference, driver still has a ref } EXPECT_EQ(use_count_before+1, native_handle_resource.use_count()); driver_reference->common.decRef(&driver_reference->common); EXPECT_EQ(use_count_before, native_handle_resource.use_count()); } TEST_F(NativeBuffer, is_not_destroyed_if_driver_loses_reference_while_mir_still_has_reference) { auto native_handle_resource = std::make_shared(); auto use_count_before = native_handle_resource.use_count(); { std::shared_ptr mir_reference; ANativeWindowBuffer* driver_reference = nullptr; { auto tmp = new mga::RefCountedNativeBuffer(native_handle_resource); mir_reference = std::shared_ptr(tmp, [](mga::RefCountedNativeBuffer* buffer) { buffer->mir_dereference(); }); driver_reference = tmp; driver_reference->common.incRef(&driver_reference->common); } //driver loses its reference driver_reference->common.decRef(&driver_reference->common); EXPECT_EQ(use_count_before+1, native_handle_resource.use_count()); } EXPECT_EQ(use_count_before, native_handle_resource.use_count()); } TEST_F(NativeBuffer, does_not_wait_for_read_access_while_being_read) { EXPECT_CALL(*mock_fence, wait()) .Times(0); mga::AndroidNativeBuffer buffer(a_native_window_buffer, mock_cmdstream_sync, mock_fence, mga::BufferAccess::read); buffer.ensure_available_for(mga::BufferAccess::read); } TEST_F(NativeBuffer, waits_for_read_access_while_being_written) { EXPECT_CALL(*mock_fence, wait()) .Times(1); mga::AndroidNativeBuffer buffer(a_native_window_buffer, mock_cmdstream_sync, mock_fence, mga::BufferAccess::write); buffer.ensure_available_for(mga::BufferAccess::read); } TEST_F(NativeBuffer, waits_for_write_access_while_being_read) { EXPECT_CALL(*mock_fence, wait()) .Times(1); mga::AndroidNativeBuffer buffer(a_native_window_buffer, mock_cmdstream_sync, mock_fence, mga::BufferAccess::read); buffer.ensure_available_for(mga::BufferAccess::write); } TEST_F(NativeBuffer, waits_for_write_access_while_being_written) { EXPECT_CALL(*mock_fence, wait()) .Times(1); mga::AndroidNativeBuffer buffer(a_native_window_buffer, mock_cmdstream_sync, mock_fence, mga::BufferAccess::write); buffer.ensure_available_for(mga::BufferAccess::write); } TEST_F(NativeBuffer, merges_existing_fence_with_updated_fence) { EXPECT_CALL(*mock_fence, merge_with(fake_fd)) .Times(1); mga::AndroidNativeBuffer buffer(a_native_window_buffer, mock_cmdstream_sync, mock_fence, mga::BufferAccess::read); buffer.update_usage(fake_fd, mga::BufferAccess::write); } TEST_F(NativeBuffer, waits_depending_on_last_fence_update) { EXPECT_CALL(*mock_fence, wait()) .Times(3); mga::AndroidNativeBuffer buffer(a_native_window_buffer, mock_cmdstream_sync, mock_fence, mga::BufferAccess::read); buffer.ensure_available_for(mga::BufferAccess::write); buffer.ensure_available_for(mga::BufferAccess::read); buffer.update_usage(fake_fd, mga::BufferAccess::write); buffer.ensure_available_for(mga::BufferAccess::write); buffer.ensure_available_for(mga::BufferAccess::read); } TEST_F(NativeBuffer, raises_egl_fence) { mga::AndroidNativeBuffer buffer(a_native_window_buffer, mock_cmdstream_sync, mock_fence, mga::BufferAccess::read); EXPECT_CALL(*mock_cmdstream_sync, raise()); buffer.lock_for_gpu(); } TEST_F(NativeBuffer, clears_egl_fence_successfully) { mga::AndroidNativeBuffer buffer(a_native_window_buffer, mock_cmdstream_sync, mock_fence, mga::BufferAccess::read); EXPECT_CALL(*mock_cmdstream_sync, wait_for(timeout)) .Times(1) .WillOnce(Return(true)); buffer.wait_for_unlock_by_gpu(); } //not really helpful to throw if the timeout fails TEST_F(NativeBuffer, ignores_clears_egl_fence_failure) { mga::AndroidNativeBuffer buffer(a_native_window_buffer, mock_cmdstream_sync, mock_fence, mga::BufferAccess::read); EXPECT_CALL(*mock_cmdstream_sync, wait_for(timeout)) .Times(1) .WillOnce(Return(false)); buffer.wait_for_unlock_by_gpu(); } ./tests/unit-tests/graphics/android/test_graphic_buffer_allocator.cpp0000644000015600001650000001225212676616157026372 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/platforms/android/server/graphic_buffer_allocator.h" #include "src/platforms/android/server/device_quirks.h" #include "src/platforms/android/server/cmdstream_sync_factory.h" #include "mir/test/doubles/mock_android_hw.h" #include "mir/graphics/buffer_properties.h" #include "mir/graphics/buffer.h" #include "native_buffer.h" #include "mir/test/doubles/stub_display_builder.h" #include "mir/test/doubles/stub_cmdstream_sync_factory.h" #include "mir/test/doubles/mock_egl.h" #include #include #include namespace mg = mir::graphics; namespace mga = mir::graphics::android; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; using namespace testing; namespace { struct GraphicBufferAllocator : Test { NiceMock hw_access_mock; NiceMock mock_egl; mga::GraphicBufferAllocator allocator{ std::make_shared(), std::make_shared(mga::PropertiesOps{})}; }; } TEST_F(GraphicBufferAllocator, allocator_accesses_gralloc_module) { EXPECT_CALL(hw_access_mock, hw_get_module(StrEq(GRALLOC_HARDWARE_MODULE_ID), _)) .Times(1); auto quirks = std::make_shared(mga::PropertiesOps{}); mga::GraphicBufferAllocator allocator{std::make_shared(), quirks}; } TEST_F(GraphicBufferAllocator, supported_pixel_formats_contain_common_formats) { auto supported_pixel_formats = allocator.supported_pixel_formats(); auto abgr_8888_count = std::count(supported_pixel_formats.begin(), supported_pixel_formats.end(), mir_pixel_format_abgr_8888); auto xbgr_8888_count = std::count(supported_pixel_formats.begin(), supported_pixel_formats.end(), mir_pixel_format_xbgr_8888); auto bgr_888_count = std::count(supported_pixel_formats.begin(), supported_pixel_formats.end(), mir_pixel_format_bgr_888); auto rgb_888_count = std::count(supported_pixel_formats.begin(), supported_pixel_formats.end(), mir_pixel_format_rgb_888); auto rgb_565_count = std::count(supported_pixel_formats.begin(), supported_pixel_formats.end(), mir_pixel_format_rgb_565); EXPECT_EQ(1, abgr_8888_count); EXPECT_EQ(1, xbgr_8888_count); EXPECT_EQ(0, bgr_888_count); EXPECT_EQ(1, rgb_888_count); EXPECT_EQ(1, rgb_565_count); } TEST_F(GraphicBufferAllocator, supported_pixel_formats_have_sane_default_in_first_position) { auto supported_pixel_formats = allocator.supported_pixel_formats(); ASSERT_FALSE(supported_pixel_formats.empty()); EXPECT_EQ(mir_pixel_format_abgr_8888, supported_pixel_formats[0]); } static unsigned int inc_count{0}; static unsigned int dec_count{0}; void inc_ref(struct android_native_base_t*) { inc_count++; } void dec_ref(struct android_native_base_t*) { dec_count++; } TEST_F(GraphicBufferAllocator, test_buffer_reconstruction_from_MirNativeBuffer) { inc_count = 0; dec_count = 0; unsigned int width {4}; unsigned int height {5}; unsigned int stride {16}; auto anwb = std::make_unique(); anwb->common.incRef = inc_ref; anwb->common.decRef = dec_ref; anwb->width = width; anwb->height = height; anwb->stride = stride; auto buffer = allocator.reconstruct_from(anwb.get(), mir_pixel_format_abgr_8888); ASSERT_THAT(buffer, Ne(nullptr)); EXPECT_THAT(buffer->size(), Eq(geom::Size{width, height})); EXPECT_THAT(buffer->native_buffer_handle()->anwb(), Eq(anwb.get())); EXPECT_THAT(inc_count, Eq(1)); EXPECT_THAT(dec_count, Eq(0)); buffer.reset(); EXPECT_THAT(dec_count, Eq(1)); } TEST_F(GraphicBufferAllocator, throws_if_cannot_share_anwb_ownership) { auto anwb = std::make_unique(); anwb->common.incRef = nullptr; anwb->common.decRef = dec_ref; EXPECT_THROW({ auto buffer = allocator.reconstruct_from(anwb.get(), mir_pixel_format_abgr_8888); }, std::runtime_error); anwb->common.incRef = inc_ref; anwb->common.decRef = nullptr; EXPECT_THROW({ auto buffer = allocator.reconstruct_from(anwb.get(), mir_pixel_format_abgr_8888); }, std::runtime_error); } ./tests/unit-tests/graphics/android/test_buffer_tex_bind.cpp0000644000015600001650000003154012676616125024505 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/buffer.h" #include "mir/graphics/egl_extensions.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_fence.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "mir/test/doubles/mock_android_hw.h" #include #include #include namespace mg = mir::graphics; namespace mga = mir::graphics::android; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; class AndroidBufferBinding : public ::testing::Test { public: virtual void SetUp() { using namespace testing; mock_native_buffer = std::make_shared>(); size = geom::Size{300, 220}; pf = mir_pixel_format_abgr_8888; extensions = std::make_shared(); gralloc = reinterpret_cast(&hw_access_mock.mock_gralloc_module->mock_hw_device); }; geom::Size size; MirPixelFormat pf; testing::NiceMock mock_egl; testing::NiceMock hw_access_mock; std::shared_ptr extensions; std::shared_ptr mock_native_buffer; gralloc_module_t const* gralloc; }; TEST_F(AndroidBufferBinding, buffer_queries_for_display) { using namespace testing; EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_creates_image_on_first_bind) { using namespace testing; EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_only_makes_one_image_per_display) { using namespace testing; EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); buffer.gl_bind_to_texture(); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_anwb_is_bound) { using namespace testing; ANativeWindowBuffer *stub_anwb = reinterpret_cast(0xdeed); EXPECT_CALL(*mock_native_buffer, anwb()) .Times(1) .WillOnce(Return(stub_anwb)); EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,stub_anwb,_)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_makes_new_image_with_new_display) { using namespace testing; EGLDisplay second_fake_display = reinterpret_cast(&second_fake_display); /* return 1st fake display */ EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .Times(Exactly(2)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); /* return 2nd fake display */ EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .Times(Exactly(1)) .WillOnce(Return(second_fake_display)); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_frees_images_it_makes) { using namespace testing; EGLDisplay second_fake_display = reinterpret_cast(&second_fake_display); EXPECT_CALL(mock_egl, eglDestroyImageKHR(_,_)) .Times(Exactly(2)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .Times(Exactly(1)) .WillOnce(Return(second_fake_display)); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_frees_images_it_makes_with_proper_args) { using namespace testing; EGLDisplay first_fake_display = mock_egl.fake_egl_display; EGLImageKHR first_fake_egl_image = reinterpret_cast(&first_fake_egl_image); EGLDisplay second_fake_display = reinterpret_cast(&second_fake_display); EGLImageKHR second_fake_egl_image = reinterpret_cast(&second_fake_egl_image); /* actual expectations */ EXPECT_CALL(mock_egl, eglDestroyImageKHR(first_fake_display, first_fake_egl_image)) .Times(Exactly(1)); EXPECT_CALL(mock_egl, eglDestroyImageKHR(second_fake_display, second_fake_egl_image)) .Times(Exactly(1)); /* manipulate mock to return 1st set */ EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .Times(Exactly(1)) .WillOnce(Return(first_fake_display)); EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .Times(Exactly(1)) .WillOnce(Return((first_fake_egl_image))); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); /* manipulate mock to return 2nd set */ EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .Times(Exactly(1)) .WillOnce(Return(second_fake_display)); EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .Times(Exactly(1)) .WillOnce(Return((second_fake_egl_image))); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_uses_current_display) { using namespace testing; EGLDisplay fake_display = (EGLDisplay) 0x32298; EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .Times(Exactly(1)) .WillOnce(Return(fake_display)); EXPECT_CALL(mock_egl, eglCreateImageKHR(fake_display,_,_,_,_)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_specifies_no_context) { using namespace testing; EXPECT_CALL(mock_egl, eglCreateImageKHR(_, EGL_NO_CONTEXT,_,_,_)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_sets_egl_native_buffer_android) { using namespace testing; EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,EGL_NATIVE_BUFFER_ANDROID,_,_)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_sets_proper_attributes) { using namespace testing; const EGLint* attrs; EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .Times(Exactly(1)) .WillOnce(DoAll(SaveArg<4>(&attrs), Return(mock_egl.fake_egl_image))); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); /* note: this should not segfault. if it does, the attributes were set wrong */ EXPECT_EQ(attrs[0], EGL_IMAGE_PRESERVED_KHR); EXPECT_EQ(attrs[1], EGL_TRUE); EXPECT_EQ(attrs[2], EGL_NONE); } TEST_F(AndroidBufferBinding, buffer_destroys_correct_buffer_with_single_image) { using namespace testing; EGLImageKHR fake_egl_image = (EGLImageKHR) 0x84210; EXPECT_CALL(mock_egl, eglCreateImageKHR(mock_egl.fake_egl_display,_,_,_,_)) .Times(Exactly(1)) .WillOnce(Return((fake_egl_image))); EXPECT_CALL(mock_egl, eglDestroyImageKHR(mock_egl.fake_egl_display, fake_egl_image)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_image_creation_failure_does_not_save) { using namespace testing; EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .Times(Exactly(2)) .WillRepeatedly(Return((EGL_NO_IMAGE_KHR))); EXPECT_CALL(mock_egl, eglDestroyImageKHR(_,_)) .Times(Exactly(0)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); EXPECT_THROW( { buffer.gl_bind_to_texture(); }, std::runtime_error); EXPECT_THROW( { buffer.gl_bind_to_texture(); }, std::runtime_error); } TEST_F(AndroidBufferBinding, buffer_image_creation_failure_throws) { using namespace testing; EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .Times(Exactly(1)) .WillRepeatedly(Return((EGL_NO_IMAGE_KHR))); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); EXPECT_THROW( { buffer.gl_bind_to_texture(); }, std::runtime_error); } /* binding tests */ TEST_F(AndroidBufferBinding, buffer_calls_binding_extension_and_notes_gpu_usage) { using namespace testing; EXPECT_CALL(mock_egl, glEGLImageTargetTexture2DOES(_, _)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, notes_gpu_usage_when_explicity_told) { using namespace testing; EXPECT_CALL(mock_egl, glEGLImageTargetTexture2DOES(_, _)) .Times(0); EXPECT_CALL(*mock_native_buffer, lock_for_gpu()); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.secure_for_render(); } TEST_F(AndroidBufferBinding, buffer_calls_binding_extension_every_time) { using namespace testing; EXPECT_CALL(mock_egl, glEGLImageTargetTexture2DOES(_, _)) .Times(Exactly(3)); EXPECT_CALL(*mock_native_buffer, lock_for_gpu()) .Times(Exactly(3)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); buffer.gl_bind_to_texture(); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_binding_specifies_gl_texture_2d) { using namespace testing; EXPECT_CALL(mock_egl, glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, _)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_binding_uses_right_image) { using namespace testing; EXPECT_CALL(mock_egl, glEGLImageTargetTexture2DOES(_, mock_egl.fake_egl_image)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, buffer_binding_uses_right_image_after_display_swap) { using namespace testing; EGLDisplay second_fake_display = reinterpret_cast(&second_fake_display); EGLImageKHR second_fake_egl_image = reinterpret_cast(&second_fake_egl_image); EXPECT_CALL(mock_egl, glEGLImageTargetTexture2DOES(_, _)) .Times(Exactly(1)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); EXPECT_CALL(mock_egl, glEGLImageTargetTexture2DOES(_, second_fake_egl_image)) .Times(Exactly(1)); EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .Times(Exactly(1)) .WillOnce(Return(second_fake_display)); EXPECT_CALL(mock_egl, eglCreateImageKHR(_,_,_,_,_)) .Times(Exactly(1)) .WillOnce(Return((second_fake_egl_image))); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, gl_bind_to_texture_waits_on_fence) { using namespace testing; EXPECT_CALL(*mock_native_buffer, ensure_available_for(mga::BufferAccess::read)) .Times(1); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); } TEST_F(AndroidBufferBinding, different_egl_contexts_displays_generate_new_eglimages) { using namespace testing; int d1 = 0, d2 = 0, c1 = 0, c2 = 0; EGLDisplay disp1 = reinterpret_cast(&d1); EGLDisplay disp2 = reinterpret_cast(&d2); EGLContext ctxt1 = reinterpret_cast(&c1); EGLContext ctxt2 = reinterpret_cast(&c2); EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .Times(3) .WillOnce(Return(disp1)) .WillOnce(Return(disp1)) .WillOnce(Return(disp2)); EXPECT_CALL(mock_egl, eglGetCurrentContext()) .Times(3) .WillOnce(Return(ctxt1)) .WillRepeatedly(Return(ctxt2)); EXPECT_CALL(mock_egl, eglCreateImageKHR(disp1,_,_,_,_)) .Times(2); EXPECT_CALL(mock_egl, eglCreateImageKHR(disp2,_,_,_,_)) .Times(1); EXPECT_CALL(mock_egl, eglDestroyImageKHR(_,_)) .Times(Exactly(3)); mga::Buffer buffer(gralloc, mock_native_buffer, extensions); buffer.gl_bind_to_texture(); buffer.gl_bind_to_texture(); buffer.gl_bind_to_texture(); } ./tests/unit-tests/graphics/android/hwc_struct_helpers.h0000644000015600001650000001211312676616125023670 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_HWC_STRUCT_HELPERS_H_ #define MIR_TEST_HWC_STRUCT_HELPERS_H_ #include "mir/geometry/rectangle.h" #include "mir/graphics/buffer.h" #include #include namespace mir { namespace test { void fill_hwc_layer( hwc_layer_1_t& layer, hwc_rect_t* visible_rect, mir::geometry::Rectangle const& position, mir::graphics::Buffer const& buffer, int type, int flags); } } void PrintTo(const hwc_rect_t& rect, ::std::ostream* os); void PrintTo(const hwc_layer_1& layer , ::std::ostream* os); MATCHER_P2(MatchesMember, value, str, std::string("layer's " + std::string(str) + " should be: " + testing::PrintToString(value))) { return arg == value; } MATCHER_P2(HWCRectMatchesRect, value, str, std::string("rectangle " + std::string(str) + " should be: " + testing::PrintToString(value))) { hwc_rect_t rect = arg; return ((rect.left == value.left) && (rect.top == value.top) && (rect.right == value.right) && (rect.bottom == value.bottom)); } MATCHER_P2(MatchesRect, value, str, std::string("rectangle " + std::string(str) + " should be: " + testing::PrintToString(value))) { return ((arg.left == value.left) && (arg.top == value.top) && (arg.right == value.right) && (arg.bottom == value.bottom)); } MATCHER_P2(MatchesRectf, value, str, std::string("rectangle " + std::string(str) + " should be: " + testing::PrintToString(value))) { using namespace testing; EXPECT_THAT(arg.left, FloatEq(value.left)); EXPECT_THAT(arg.top, FloatEq(value.top)); EXPECT_THAT(arg.right, FloatEq(value.right)); EXPECT_THAT(arg.bottom, FloatEq(value.bottom)); return !(::testing::Test::HasFailure()); } MATCHER_P(MatchesCommonFields, value, std::string(testing::PrintToString(value))) { EXPECT_THAT(arg.compositionType, MatchesMember(value.compositionType, "compositionType")); EXPECT_THAT(arg.hints, MatchesMember(value.hints, "hints")); EXPECT_THAT(arg.flags, MatchesMember(value.flags, "flags")); EXPECT_THAT(arg.handle, MatchesMember(value.handle, "handle")); EXPECT_THAT(arg.transform, MatchesMember(value.transform, "transform")); EXPECT_THAT(arg.blending, MatchesMember(value.blending, "blending")); EXPECT_THAT(arg.displayFrame, MatchesRect(value.displayFrame, "displayFrame")); EXPECT_THAT(arg.visibleRegionScreen.numRects, MatchesMember(value.visibleRegionScreen.numRects, "visibleRegionScreen.numRects")); EXPECT_THAT(arg.planeAlpha, MatchesMember(value.planeAlpha, "planeAlpha")); EXPECT_THAT(arg.acquireFenceFd, MatchesMember(value.acquireFenceFd, "acquireFenceFd")); EXPECT_THAT(arg.releaseFenceFd, MatchesMember(value.releaseFenceFd, "releaseFenceFd")); return !(::testing::Test::HasFailure()); } MATCHER_P(MatchesLegacyLayer, value, std::string(testing::PrintToString(value)) ) { EXPECT_THAT(arg, MatchesCommonFields(value)); EXPECT_THAT(arg.sourceCropi, MatchesRect(value.sourceCropi, "sourceCrop (int)")); return !(::testing::Test::HasFailure()); } MATCHER_P(MatchesLayer, value, std::string(testing::PrintToString(value)) ) { EXPECT_THAT(arg, MatchesCommonFields(value)); EXPECT_THAT(arg.sourceCropf, MatchesRectf(value.sourceCropf, "sourceCrop (float)")); return !(::testing::Test::HasFailure()); } MATCHER_P(MatchesList, value, std::string("")) { if (arg == nullptr) return (value.empty()); auto const& list = *arg; EXPECT_EQ(list.numHwLayers, value.size()); auto i = 0u; for(auto layer : value) { EXPECT_THAT(list.hwLayers[i++], MatchesLegacyLayer(*layer)); if (::testing::Test::HasFailure()) return false; } return !(::testing::Test::HasFailure()); } MATCHER_P(MatchesPrimaryList, value, std::string("")) { EXPECT_THAT(arg[0], MatchesList(value)); return !(::testing::Test::HasFailure()); } MATCHER_P2(MatchesLists, primary, external, std::string("")) { EXPECT_THAT(arg[0], MatchesList(primary)); EXPECT_THAT(arg[1], MatchesList(external)); return !(::testing::Test::HasFailure()); } MATCHER_P3(MatchesListWithEglFields, value, dpy, sur, std::string("")) { if (arg[0] == nullptr) return (value.empty()); EXPECT_EQ(arg[0]->dpy, dpy); EXPECT_EQ(arg[0]->sur, sur); EXPECT_THAT(arg, MatchesPrimaryList(value)); return !(::testing::Test::HasFailure()); } #endif /* MIR_TEST_HWC_STRUCT_HELPERS_H_ */ ./tests/unit-tests/graphics/android/test_hwc_fb_device.cpp0000644000015600001650000001566412676616125024140 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/hwc_fb_device.h" #include "src/platforms/android/server/hwc_configuration.h" #include "mir/test/doubles/stub_android_native_buffer.h" #include "mir/test/doubles/mock_display_device.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "mir/test/doubles/mock_framebuffer_bundle.h" #include "mir/test/doubles/mock_fb_hal_device.h" #include "mir/test/doubles/stub_renderable.h" #include "mir/test/doubles/stub_swapping_gl_context.h" #include "mir/test/doubles/mock_swapping_gl_context.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/auto_unblock_thread.h" #include "mir/test/doubles/mock_hwc_device_wrapper.h" #include "mir/test/doubles/stub_renderable_list_compositor.h" #include "src/platforms/android/server/hwc_fallback_gl_renderer.h" #include "hwc_struct_helpers.h" #include #include #include #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mtd=mir::test::doubles; namespace geom=mir::geometry; namespace { class HwcFbDevice : public ::testing::Test { protected: virtual void SetUp() { using namespace testing; int width = 88; int height = 4; test_size = geom::Size{width, height}; int fbnum = 558; mock_fb_device = std::make_shared( width, height, HAL_PIXEL_FORMAT_RGBA_8888, fbnum, 11, 12); mock_buffer = std::make_shared>(); mock_hwc_device_wrapper = std::make_shared>(); stub_native_buffer = std::make_shared(test_size); hwc_rect_t region = {0, 0, width, height}; skip_layer.compositionType = HWC_FRAMEBUFFER; skip_layer.hints = 0; skip_layer.flags = HWC_SKIP_LAYER; skip_layer.handle = &stub_native_buffer->native_handle; skip_layer.transform = 0; skip_layer.blending = HWC_BLENDING_NONE; skip_layer.sourceCrop = region; skip_layer.displayFrame = region; skip_layer.visibleRegionScreen = {1, ®ion}; skip_layer.acquireFenceFd = -1; skip_layer.releaseFenceFd = -1; skip_layer.planeAlpha = std::numeric_limits::max(); ON_CALL(*mock_buffer, size()) .WillByDefault(Return(test_size)); ON_CALL(*mock_buffer, native_buffer_handle()) .WillByDefault(Return(stub_native_buffer)); ON_CALL(mock_context, last_rendered_buffer()) .WillByDefault(Return(mock_buffer)); } int fake_dpy = 0; int fake_sur = 0; EGLDisplay dpy{&fake_dpy}; EGLSurface sur{&fake_sur}; testing::NiceMock mock_egl; geom::Size test_size; std::shared_ptr mock_fb_device; std::shared_ptr mock_buffer; std::shared_ptr mock_hwc_device_wrapper; std::shared_ptr stub_native_buffer; mtd::StubSwappingGLContext stub_context; testing::NiceMock mock_context; mtd::StubRenderableListCompositor stub_compositor; mga::DisplayName primary{mga::DisplayName::primary}; mga::LayerList list{std::make_shared(), {}, geom::Displacement{}}; hwc_layer_1_t skip_layer; }; } TEST_F(HwcFbDevice, reports_it_cannot_swap) { mga::HwcFbDevice device(mock_hwc_device_wrapper, mock_fb_device); EXPECT_FALSE(device.can_swap_buffers()); } TEST_F(HwcFbDevice, hwc10_subscribes_to_vsync_events) { using namespace testing; Sequence seq; EXPECT_CALL(*mock_hwc_device_wrapper, subscribe_to_events(_,_,_,_)) .InSequence(seq); EXPECT_CALL(*mock_hwc_device_wrapper, unsubscribe_from_events_(_)) .InSequence(seq); mga::HwcFbDevice device(mock_hwc_device_wrapper, mock_fb_device); } TEST_F(HwcFbDevice, hwc10_rejects_overlays) { using namespace testing; mtd::StubRenderableListCompositor stub_compositor; auto renderable1 = std::make_shared(); auto renderable2 = std::make_shared(); mg::RenderableList renderlist { renderable1, renderable2 }; mga::HwcFbDevice device(mock_hwc_device_wrapper, mock_fb_device); EXPECT_FALSE(device.compatible_renderlist(renderlist)); } TEST_F(HwcFbDevice, hwc10_post) { using namespace testing; std::list expected_list{&skip_layer}; std::function vsync_cb; EXPECT_CALL(*mock_hwc_device_wrapper, subscribe_to_events(_,_,_,_)) .WillOnce(SaveArg<1>(&vsync_cb)); mga::HwcFbDevice device(mock_hwc_device_wrapper, mock_fb_device); Mock::VerifyAndClearExpectations(mock_hwc_device_wrapper.get()); std::atomic vsync_thread_on{true}; mir::test::AutoUnblockThread vsync_thread( [&]{ vsync_thread_on = false; }, [&]{ while(vsync_thread_on) { std::this_thread::sleep_for(std::chrono::microseconds(500)); vsync_cb(mga::DisplayName::primary, std::chrono::nanoseconds(0)); }}); Sequence seq; EXPECT_CALL(*mock_buffer, native_buffer_handle()) .InSequence(seq) .WillOnce(Return(stub_native_buffer)); EXPECT_CALL(*mock_hwc_device_wrapper, prepare(MatchesPrimaryList(expected_list))) .InSequence(seq); EXPECT_CALL(mock_egl, eglGetCurrentDisplay()) .InSequence(seq) .WillOnce(Return(dpy)); EXPECT_CALL(mock_egl, eglGetCurrentSurface(EGL_DRAW)) .InSequence(seq) .WillOnce(Return(sur)); EXPECT_CALL(*mock_hwc_device_wrapper, set(MatchesListWithEglFields(expected_list, dpy, sur))) .InSequence(seq); EXPECT_CALL(*mock_buffer, native_buffer_handle()) .InSequence(seq) .WillOnce(Return(stub_native_buffer)); EXPECT_CALL(*mock_fb_device, post_interface(mock_fb_device.get(), &stub_native_buffer->native_handle)) .InSequence(seq); mga::DisplayContents content{primary, list, geom::Displacement{}, mock_context, stub_compositor}; device.commit({content}); // Predictive bypass not enabled in HwcFbDevice EXPECT_EQ(0, device.recommended_sleep().count()); } ./tests/unit-tests/graphics/android/test_device_detection.cpp0000644000015600001650000002504012676616125024653 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/device_quirks.h" #include "mir/options/program_option.h" #include #include #include namespace mga = mir::graphics::android; namespace bpo = boost::program_options; namespace { struct MockOps : mga::PropertiesWrapper { MOCK_CONST_METHOD3(property_get, int( char const[PROP_NAME_MAX], char[PROP_VALUE_MAX], char const[PROP_VALUE_MAX])); }; } TEST(DeviceDetection, two_buffers_by_default) { using namespace testing; char const default_str[] = ""; char const name_str[] = "anydevice"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&] (char const*, char* value, char const*) { strncpy(value, name_str, PROP_VALUE_MAX); return 0; })); mga::DeviceQuirks quirks(mock_ops); EXPECT_EQ(2u, quirks.num_framebuffers()); } TEST(DeviceDetection, three_buffers_reported_for_mx3) { using namespace testing; char const default_str[] = ""; char const name_str[] = "mx3"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&] (char const*, char* value, char const*) { strncpy(value, name_str, PROP_VALUE_MAX); return 0; })); mga::DeviceQuirks quirks(mock_ops); EXPECT_EQ(3u, quirks.num_framebuffers()); } //LP: 1371619, 1370555 TEST(DeviceDetection, reports_gralloc_can_be_closed_safely_by_default) { using namespace testing; char const default_str[] = ""; char const any_name_str[] = "anydevice"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&](char const*, char* value, char const*) { strncpy(value, any_name_str, PROP_VALUE_MAX); return 0; })); mga::DeviceQuirks quirks(mock_ops); EXPECT_FALSE(quirks.gralloc_cannot_be_closed_safely()); } TEST(DeviceDetection, reports_gralloc_cannot_be_closed_safely_on_krillin) { using namespace testing; char const default_str[] = ""; char const krillin_name_str[] = "krillin"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&](char const*, char* value, char const*) { strncpy(value, krillin_name_str, PROP_VALUE_MAX); return 0; })); mga::DeviceQuirks quirks(mock_ops); EXPECT_TRUE(quirks.gralloc_cannot_be_closed_safely()); } TEST(DeviceDetection, aligns_width_on_vegetahd) { using namespace testing; char const default_str[] = ""; char const vegetahd_name_str[] = "vegetahd"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&](char const*, char* value, char const*) { strncpy(value, vegetahd_name_str, PROP_VALUE_MAX); return 0; })); mga::DeviceQuirks quirks(mock_ops); EXPECT_THAT(quirks.aligned_width(720), Eq(736)); } TEST(DeviceDetection, clears_gl_context_fence_on_manta) { using namespace testing; char const default_str[] = ""; char const manta_name_str[] = "manta"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&](char const*, char* value, char const*) { strncpy(value, manta_name_str, PROP_VALUE_MAX); return 0; })); mga::DeviceQuirks quirks(mock_ops); EXPECT_TRUE(quirks.clear_fb_context_fence()); } TEST(DeviceDetection, clears_gl_context_fence_on_arale) { using namespace testing; char const default_str[] = ""; char const arale_name_str[] = "arale"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&](char const*, char* value, char const*) { strncpy(value, arale_name_str, PROP_VALUE_MAX); return 0; })); mga::DeviceQuirks quirks(mock_ops); EXPECT_TRUE(quirks.clear_fb_context_fence()); } TEST(DeviceDetection, clears_gl_context_fence_on_krillin) { using namespace testing; char const default_str[] = ""; char const krillin_name_str[] = "krillin"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&](char const*, char* value, char const*) { strncpy(value, krillin_name_str, PROP_VALUE_MAX); return 0; })); mga::DeviceQuirks quirks(mock_ops); EXPECT_TRUE(quirks.clear_fb_context_fence()); } TEST(DeviceDetection, does_not_clear_gl_context_fence_on_others) { using namespace testing; char const default_str[] = ""; char const other_name_str[] = "others"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&](char const*, char* value, char const*) { strncpy(value, other_name_str, PROP_VALUE_MAX); return 0; })); mga::DeviceQuirks quirks(mock_ops); EXPECT_FALSE(quirks.clear_fb_context_fence()); } struct DeviceQuirks : testing::Test { void SetUp() { mga::DeviceQuirks::add_options(desc); } void enable_fb_ion_quirk() { int const argc = 2; char const* argv[argc] = { __PRETTY_FUNCTION__, "--fb-ion-heap=false" }; options.parse_arguments(desc, argc, argv); } void disable_num_framebuffers_quirk() { int const argc = 2; char const* argv[argc] = { __PRETTY_FUNCTION__, "--enable-num-framebuffers-quirk=false" }; options.parse_arguments(desc, argc, argv); } void disable_gralloc_cannot_be_closed_safely_quirk() { int const argc = 2; char const* argv[argc] = { __PRETTY_FUNCTION__, "--enable-gralloc-cannot-be-closed-safely-quirk=false" }; options.parse_arguments(desc, argc, argv); } void disable_width_alignment_quirk() { int const argc = 2; char const* argv[argc] = { __PRETTY_FUNCTION__, "--enable-width-alignment-quirk=false" }; options.parse_arguments(desc, argc, argv); } bpo::options_description desc{"Options"}; mir::options::ProgramOption options; }; TEST_F(DeviceQuirks, number_of_framebuffers_quirk_can_be_disabled) { using namespace testing; char const default_str[] = ""; char const name_str[] = "mx3"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&] (char const*, char* value, char const*) { strncpy(value, name_str, PROP_VALUE_MAX); return 0; })); disable_num_framebuffers_quirk(); mga::DeviceQuirks quirks(mock_ops, options); EXPECT_THAT(quirks.num_framebuffers(), Ne(3)); } TEST_F(DeviceQuirks, gralloc_cannot_be_closed_safely_quirk_can_be_disabled) { using namespace testing; char const default_str[] = ""; char const krillin_name_str[] = "krillin"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&](char const*, char* value, char const*) { strncpy(value, krillin_name_str, PROP_VALUE_MAX); return 0; })); disable_gralloc_cannot_be_closed_safely_quirk(); mga::DeviceQuirks quirks(mock_ops, options); EXPECT_FALSE(quirks.gralloc_cannot_be_closed_safely()); } TEST_F(DeviceQuirks, width_alignment_quirk_can_be_disabled) { using namespace testing; char const default_str[] = ""; char const vegetahd_name_str[] = "vegetahd"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&](char const*, char* value, char const*) { strncpy(value, vegetahd_name_str, PROP_VALUE_MAX); return 0; })); disable_width_alignment_quirk(); mga::DeviceQuirks quirks(mock_ops, options); EXPECT_THAT(quirks.aligned_width(720), Eq(720)); } TEST_F(DeviceQuirks, returns_correct_gralloc_bits_with_fb_ion_quirk_for_device) { using namespace testing; char const default_str[] = ""; char const name_str[] = "Aquaris_M10_FHD"; MockOps mock_ops; EXPECT_CALL(mock_ops, property_get(StrEq("ro.product.device"), _, StrEq(default_str))) .Times(1) .WillOnce(Invoke([&](char const*, char* value, char const*) { strncpy(value, name_str, PROP_VALUE_MAX); return 0; })); mga::DeviceQuirks quirks(mock_ops, options); EXPECT_THAT(quirks.fb_gralloc_bits(), testing::Eq(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE)); } TEST_F(DeviceQuirks, returns_correct_gralloc_bits_without_fb_ion_quirk) { MockOps mock_ops; mga::DeviceQuirks quirks(mock_ops, options); EXPECT_THAT(quirks.fb_gralloc_bits(), testing::Eq(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB)); } TEST_F(DeviceQuirks, returns_correct_gralloc_bits_with_fb_ion_quirk) { using namespace testing; MockOps mock_ops; enable_fb_ion_quirk(); mga::DeviceQuirks quirks(mock_ops, options); EXPECT_THAT(quirks.fb_gralloc_bits(), testing::Eq(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE)); } ./tests/unit-tests/graphics/android/test_buffer.cpp0000644000015600001650000001743012676616157022640 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/graphics/egl_extensions.h" #include "src/platforms/android/server/buffer.h" #include "sync_fence.h" #include "native_buffer.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_fence.h" #include "mir/test/doubles/mock_android_hw.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include #include #include #include namespace mg = mir::graphics; namespace mga = mir::graphics::android; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; namespace mt = mir::test; class AndroidBuffer : public ::testing::Test { protected: virtual void SetUp() { using namespace testing; mock_native_buffer = std::make_shared>(); pf = mir_pixel_format_abgr_8888; anwb = mock_native_buffer->anwb(); anwb->width = 44; anwb->height = 45; anwb->stride = anwb->width * MIR_BYTES_PER_PIXEL(pf); anwb->format = HAL_PIXEL_FORMAT_RGBA_8888; size = geom::Size{anwb->width, anwb->height}; extensions = std::make_shared(); } ANativeWindowBuffer *anwb; testing::NiceMock mock_egl; std::shared_ptr mock_native_buffer; MirPixelFormat pf; geom::Size size; std::shared_ptr extensions; testing::NiceMock hw_access_mock; mtd::MockGrallocModule gralloc; }; TEST_F(AndroidBuffer, size_query_test) { using namespace testing; mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); geom::Size expected_size{anwb->width, anwb->height}; EXPECT_EQ(expected_size, buffer.size()); } TEST_F(AndroidBuffer, format_query_test) { using namespace testing; mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); EXPECT_EQ(mir_pixel_format_abgr_8888, buffer.pixel_format()); } TEST_F(AndroidBuffer, returns_native_buffer_times_two) { using namespace testing; int acquire_fake_fence_fd1 = 948; int acquire_fake_fence_fd2 = 954; EXPECT_CALL(*mock_native_buffer, update_usage(acquire_fake_fence_fd1, mga::BufferAccess::write)) .Times(1); EXPECT_CALL(*mock_native_buffer, update_usage(acquire_fake_fence_fd2, mga::BufferAccess::read)) .Times(1); mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); { auto native_resource = buffer.native_buffer_handle(); EXPECT_EQ(mock_native_buffer, native_resource); native_resource->update_usage(acquire_fake_fence_fd1, mga::BufferAccess::write); } { auto native_resource = buffer.native_buffer_handle(); EXPECT_EQ(mock_native_buffer, native_resource); native_resource->update_usage(acquire_fake_fence_fd2, mga::BufferAccess::read); } } TEST_F(AndroidBuffer, queries_native_window_for_stride) { using namespace testing; geom::Stride expected_stride{anwb->stride * MIR_BYTES_PER_PIXEL(pf)}; mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); EXPECT_EQ(expected_stride, buffer.stride()); } TEST_F(AndroidBuffer, write_detects_incorrect_size) { mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); unsigned char pix = {0x32}; EXPECT_THROW({ buffer.write(&pix, sizeof(pix)); }, std::logic_error); } TEST_F(AndroidBuffer, write_throws_on_failed_mapping_indicated_by_rc_code) { using namespace testing; EXPECT_CALL(gralloc, lock_interface(_, _, _, _, _, _, _, _)) .WillOnce(Return(-1)); mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); size_t sz = anwb->height * anwb->width * MIR_BYTES_PER_PIXEL(pf); auto const pixels = std::shared_ptr( static_cast(::operator new(sizeof(unsigned char) *sz))); EXPECT_THROW({ buffer.write(pixels.get(), sz); }, std::runtime_error); } TEST_F(AndroidBuffer, write_throws_on_failed_mapping_indicated_by_nullptr_return) { using namespace testing; EXPECT_CALL(gralloc, lock_interface(_, _, _, _, _, _, _, _)) .WillOnce(DoAll(SetArgPointee<7>(nullptr), Return(0))); mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); size_t sz = anwb->height * anwb->width * MIR_BYTES_PER_PIXEL(pf); auto const pixels = std::shared_ptr( static_cast(::operator new(sizeof(unsigned char) *sz))); EXPECT_THROW({ buffer.write(pixels.get(), sz); }, std::runtime_error); } TEST_F(AndroidBuffer, writes_pixels) { using namespace testing; size_t strided_sz = anwb->height * anwb->stride * MIR_BYTES_PER_PIXEL(pf); size_t sz = anwb->height * anwb->width * MIR_BYTES_PER_PIXEL(pf); auto const mapped_pixels = std::shared_ptr( static_cast(::operator new(sizeof(unsigned char) * strided_sz))); EXPECT_CALL(*mock_native_buffer, ensure_available_for(mga::BufferAccess::write)); EXPECT_CALL(gralloc, lock_interface( &gralloc,_, GRALLOC_USAGE_SW_WRITE_OFTEN, 0, 0, size.width.as_int(), size.height.as_int(), _)) .WillOnce(DoAll(SetArgPointee<7>(mapped_pixels.get()), Return(0))); EXPECT_CALL(gralloc, unlock_interface(_,_)); mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); auto const pixels = std::shared_ptr( static_cast(::operator new(sizeof(unsigned char) *sz))); buffer.write(pixels.get(), sz); Mock::VerifyAndClearExpectations(&gralloc); } TEST_F(AndroidBuffer, read_throws_on_failed_mapping_indicated_by_rc_code) { using namespace testing; EXPECT_CALL(gralloc, lock_interface(_, _, _, _, _, _, _, _)) .WillOnce(Return(-1)); mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); EXPECT_THROW({ buffer.read([](unsigned char const*){}); }, std::runtime_error); } TEST_F(AndroidBuffer, read_throws_on_failed_mapping_indicated_by_nullptr_return) { using namespace testing; EXPECT_CALL(gralloc, lock_interface(_, _, _, _, _, _, _, _)) .WillOnce(DoAll(SetArgPointee<7>(nullptr), Return(0))); mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); EXPECT_THROW({ buffer.read([](unsigned char const*){}); }, std::runtime_error); } TEST_F(AndroidBuffer, reads_pixels) { using namespace testing; size_t strided_sz = anwb->height * anwb->stride * MIR_BYTES_PER_PIXEL(pf); auto const mapped_pixels = std::shared_ptr( static_cast(::operator new(sizeof(unsigned char) * strided_sz))); EXPECT_CALL(*mock_native_buffer, ensure_available_for(mga::BufferAccess::read)); EXPECT_CALL(gralloc, lock_interface( &gralloc,_, GRALLOC_USAGE_SW_READ_OFTEN, 0, 0, size.width.as_int(), size.height.as_int(), _)) .WillOnce(DoAll(SetArgPointee<7>(mapped_pixels.get()), Return(0))); EXPECT_CALL(gralloc, unlock_interface(_,_)); mga::Buffer buffer(&gralloc, mock_native_buffer, extensions); buffer.read([](unsigned char const* pixels) { EXPECT_THAT(pixels, Ne(nullptr)); }); Mock::VerifyAndClearExpectations(&gralloc); } ./tests/unit-tests/graphics/android/test_platform.cpp0000644000015600001650000002457412676616125023215 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/server/report/null_report_factory.h" #include "mir/graphics/platform_ipc_operations.h" #include "mir/options/program_option.h" #include "src/platforms/android/server/platform.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/mock_android_hw.h" #include "mir/test/doubles/mock_buffer_ipc_message.h" #include "mir/test/doubles/mock_display_report.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/stub_display_builder.h" #include "mir/test/doubles/fd_matcher.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir_test_framework/executable_path.h" #include "mir/shared_library.h" #include #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mt=mir::test; namespace mtd=mir::test::doubles; namespace mr=mir::report; namespace geom=mir::geometry; namespace mo=mir::options; namespace mtf=mir_test_framework; static const char probe_platform[] = "probe_graphics_platform"; class PlatformBufferIPCPackaging : public ::testing::Test { protected: virtual void SetUp() { using namespace testing; stub_display_builder = std::make_shared(); stub_display_report = mr::null_display_report(); stride = geom::Stride(300*4); num_ints = 43; num_fds = 55; auto handle_size = sizeof(native_handle_t) + (sizeof(int)*(num_ints + num_fds)); auto native_buffer_raw = (native_handle_t*) ::operator new(handle_size); native_buffer_handle = std::shared_ptr(native_buffer_raw); native_buffer_handle->numInts = num_ints; native_buffer_handle->numFds = num_fds; for (auto i = 0u; i < (num_ints+num_fds); i++) { native_buffer_handle->data[i] = i; } native_buffer = std::make_shared(); mock_buffer = std::make_shared>(); ON_CALL(*native_buffer, handle()) .WillByDefault(Return(native_buffer_handle.get())); ON_CALL(*mock_buffer, native_buffer_handle()) .WillByDefault(Return(native_buffer)); ON_CALL(*mock_buffer, stride()) .WillByDefault(Return(stride)); quirks = std::make_shared(mga::PropertiesOps{}); } std::shared_ptr native_buffer; std::shared_ptr stub_buffer_allocator; std::shared_ptr stub_display_builder; std::shared_ptr mock_buffer; std::shared_ptr native_buffer_handle; std::shared_ptr stub_display_report; std::shared_ptr quirks; geom::Stride stride; unsigned int num_ints, num_fds; }; /* ipc packaging tests */ TEST_F(PlatformBufferIPCPackaging, test_ipc_data_packed_correctly_for_full_ipc_with_fence) { using namespace ::testing; int fake_fence{333}; EXPECT_CALL(*native_buffer, wait_for_unlock_by_gpu()); EXPECT_CALL(*native_buffer, copy_fence()) .WillOnce(Return(fake_fence)); mga::Platform platform(stub_buffer_allocator, stub_display_builder, stub_display_report, mga::OverlayOptimization::enabled, quirks); mtd::MockBufferIpcMessage mock_ipc_msg; int offset = 0; EXPECT_CALL(mock_ipc_msg, pack_data(static_cast(mga::BufferFlag::fenced))); EXPECT_CALL(mock_ipc_msg, pack_fd(mtd::RawFdMatcher(fake_fence))); for (auto i = 0u; i < num_fds; i++) EXPECT_CALL(mock_ipc_msg, pack_fd(mtd::RawFdMatcher(native_buffer_handle->data[offset++]))); for (auto i = 0u; i < num_ints; i++) EXPECT_CALL(mock_ipc_msg, pack_data(native_buffer_handle->data[offset++])); EXPECT_CALL(*mock_buffer, stride()) .WillOnce(Return(stride)); EXPECT_CALL(mock_ipc_msg, pack_stride(stride)) .Times(1); EXPECT_CALL(*mock_buffer, size()) .WillOnce(Return(mir::geometry::Size{123, 456})); EXPECT_CALL(mock_ipc_msg, pack_size(_)) .Times(1); auto ipc_ops = platform.make_ipc_operations(); ipc_ops->pack_buffer(mock_ipc_msg, *mock_buffer, mg::BufferIpcMsgType::full_msg); } TEST_F(PlatformBufferIPCPackaging, test_ipc_data_packed_correctly_for_full_ipc_without_fence) { using namespace ::testing; EXPECT_CALL(*native_buffer, wait_for_unlock_by_gpu()); EXPECT_CALL(*native_buffer, copy_fence()) .WillOnce(Return(-1)); mga::Platform platform(stub_buffer_allocator, stub_display_builder, stub_display_report, mga::OverlayOptimization::enabled, quirks); mtd::MockBufferIpcMessage mock_ipc_msg; int offset = 0; EXPECT_CALL(mock_ipc_msg, pack_data(static_cast(mga::BufferFlag::unfenced))); EXPECT_CALL(mock_ipc_msg, pack_fd(mtd::RawFdMatcher(-1))) .Times(0); for (auto i = 0u; i < num_fds; i++) { EXPECT_CALL(mock_ipc_msg, pack_fd(mtd::RawFdMatcher(native_buffer_handle->data[offset++]))) .Times(1); } for (auto i = 0u; i < num_ints; i++) { EXPECT_CALL(mock_ipc_msg, pack_data(native_buffer_handle->data[offset++])) .Times(1); } EXPECT_CALL(*mock_buffer, stride()) .WillOnce(Return(stride)); EXPECT_CALL(mock_ipc_msg, pack_stride(stride)) .Times(1); EXPECT_CALL(*mock_buffer, size()) .WillOnce(Return(mir::geometry::Size{123, 456})); EXPECT_CALL(mock_ipc_msg, pack_size(_)) .Times(1); auto ipc_ops = platform.make_ipc_operations(); ipc_ops->pack_buffer(mock_ipc_msg, *mock_buffer, mg::BufferIpcMsgType::full_msg); } TEST_F(PlatformBufferIPCPackaging, test_ipc_data_packed_correctly_for_nested) { using namespace ::testing; EXPECT_CALL(*native_buffer, wait_for_unlock_by_gpu()); EXPECT_CALL(*native_buffer, copy_fence()) .WillOnce(Return(-1)); mga::Platform platform(stub_buffer_allocator, stub_display_builder, stub_display_report, mga::OverlayOptimization::enabled, quirks); mtd::MockBufferIpcMessage mock_ipc_msg; int offset = 0; for (auto i = 0u; i < num_fds; i++) { EXPECT_CALL(mock_ipc_msg, pack_fd(mtd::RawFdMatcher(native_buffer_handle->data[offset++]))) .Times(1); } EXPECT_CALL(mock_ipc_msg, pack_data(static_cast(mga::BufferFlag::unfenced))); for (auto i = 0u; i < num_ints; i++) { EXPECT_CALL(mock_ipc_msg, pack_data(native_buffer_handle->data[offset++])) .Times(1); } EXPECT_CALL(*mock_buffer, stride()) .WillOnce(Return(stride)); EXPECT_CALL(mock_ipc_msg, pack_stride(stride)) .Times(1); EXPECT_CALL(*mock_buffer, size()) .WillOnce(Return(mir::geometry::Size{123, 456})); EXPECT_CALL(mock_ipc_msg, pack_size(_)) .Times(1); auto ipc_ops = platform.make_ipc_operations(); ipc_ops->pack_buffer(mock_ipc_msg, *mock_buffer, mg::BufferIpcMsgType::full_msg); } TEST_F(PlatformBufferIPCPackaging, test_ipc_data_packed_correctly_for_partial_ipc) { using namespace ::testing; int fake_fence{33}; mga::Platform platform(stub_buffer_allocator, stub_display_builder, stub_display_report, mga::OverlayOptimization::enabled, quirks); auto ipc_ops = platform.make_ipc_operations(); mtd::MockBufferIpcMessage mock_ipc_msg; Sequence seq; EXPECT_CALL(mock_ipc_msg, pack_data(static_cast(mga::BufferFlag::fenced))) .InSequence(seq); EXPECT_CALL(mock_ipc_msg, pack_fd(mtd::RawFdMatcher(fake_fence))) .InSequence(seq); EXPECT_CALL(mock_ipc_msg, pack_data(static_cast(mga::BufferFlag::unfenced))) .InSequence(seq); EXPECT_CALL(*native_buffer, copy_fence()) .Times(2) .WillOnce(Return(fake_fence)) .WillOnce(Return(-1)); ipc_ops->pack_buffer(mock_ipc_msg, *mock_buffer, mg::BufferIpcMsgType::update_msg); ipc_ops->pack_buffer(mock_ipc_msg, *mock_buffer, mg::BufferIpcMsgType::update_msg); } TEST(AndroidGraphicsPlatform, egl_native_display_is_egl_default_display) { mga::Platform platform( std::make_shared(), std::make_shared(), mr::null_display_report(), mga::OverlayOptimization::enabled, std::make_shared(mga::PropertiesOps{})); EXPECT_EQ(EGL_DEFAULT_DISPLAY, platform.egl_native_display()); } TEST(AndroidGraphicsPlatform, probe_returns_unsupported_when_no_hwaccess) { using namespace testing; NiceMock hwaccess; mir::options::ProgramOption options; ON_CALL(hwaccess, hw_get_module(_,_)).WillByDefault(Return(-1)); mir::SharedLibrary platform_lib{mtf::server_platform("graphics-android")}; auto probe = platform_lib.load_function(probe_platform); EXPECT_EQ(mg::PlatformPriority::unsupported, probe(options)); } TEST(AndroidGraphicsPlatform, probe_returns_best_when_hwaccess_succeeds) { testing::NiceMock hwaccess; mir::options::ProgramOption options; mir::SharedLibrary platform_lib{mtf::server_platform("graphics-android")}; auto probe = platform_lib.load_function(probe_platform); EXPECT_EQ(mg::PlatformPriority::best, probe(options)); } TEST(NestedPlatformCreation, doesnt_access_display_hardware) { using namespace testing; mtd::HardwareAccessMock hwaccess; mtd::MockDisplayReport stub_report; testing::NiceMock mock_egl; EXPECT_CALL(hwaccess, hw_get_module(StrEq(HWC_HARDWARE_MODULE_ID), _)) .Times(0); EXPECT_CALL(hwaccess, hw_get_module(StrEq(GRALLOC_HARDWARE_MODULE_ID), _)) .Times(AtMost(1)); auto platform = create_guest_platform(mt::fake_shared(stub_report), nullptr); } ./tests/unit-tests/graphics/android/test_hwc_configuration.cpp0000644000015600001650000003154112676616125025071 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/hwc_configuration.h" #include "mir/test/doubles/mock_hwc_device_wrapper.h" #include "mir/test/doubles/mock_egl.h" #include #include #include namespace mga = mir::graphics::android; namespace mtd = mir::test::doubles; namespace geom = mir::geometry; struct HwcConfiguration : public testing::Test { testing::NiceMock mock_egl; std::shared_ptr mock_hwc_wrapper{ std::make_shared>()}; mga::DisplayName display{mga::DisplayName::primary}; mga::HwcBlankingControl config{mock_hwc_wrapper}; mga::HwcPowerModeControl power_mode_config{mock_hwc_wrapper}; }; TEST_F(HwcConfiguration, fb_format_selection) { using namespace testing; Mock::VerifyAndClearExpectations(&mock_egl); EGLint const expected_egl_config_attr [] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_FRAMEBUFFER_TARGET_ANDROID, EGL_TRUE, EGL_NONE }; int visual_id = HAL_PIXEL_FORMAT_BGRA_8888; EGLDisplay fake_display = reinterpret_cast(0x11235813); EGLConfig fake_egl_config = reinterpret_cast(0x44); Sequence seq; EXPECT_CALL(mock_egl, eglGetDisplay(EGL_DEFAULT_DISPLAY)) .InSequence(seq) .WillOnce(Return(fake_display)); EXPECT_CALL(mock_egl, eglInitialize(fake_display,_,_)) .InSequence(seq); EXPECT_CALL(mock_egl, eglChooseConfig(fake_display,mtd::AttrMatches(expected_egl_config_attr),_,1,_)) .InSequence(seq) .WillOnce(DoAll(SetArgPointee<2>(fake_egl_config), SetArgPointee<4>(1), Return(EGL_TRUE))); EXPECT_CALL(mock_egl, eglGetConfigAttrib(fake_display, fake_egl_config, EGL_NATIVE_VISUAL_ID, _)) .InSequence(seq) .WillOnce(DoAll(SetArgPointee<3>(visual_id), Return(EGL_TRUE))); EXPECT_CALL(mock_egl, eglTerminate(fake_display)) .InSequence(seq); mga::HwcBlankingControl hwc_config{mock_hwc_wrapper}; EXPECT_EQ(mir_pixel_format_argb_8888, hwc_config.active_config_for(mga::DisplayName::primary).current_format); } //not all hwc implementations give a hint about their framebuffer formats in their configuration. //prefer abgr_8888 if we can't figure things out TEST_F(HwcConfiguration, format_selection_failure_default) { using namespace testing; Mock::VerifyAndClearExpectations(&mock_egl); EGLDisplay fake_display = reinterpret_cast(0x11235813); Sequence seq; EXPECT_CALL(mock_egl, eglGetDisplay(EGL_DEFAULT_DISPLAY)) .InSequence(seq) .WillOnce(Return(fake_display)); EXPECT_CALL(mock_egl, eglInitialize(fake_display,_,_)) .InSequence(seq); EXPECT_CALL(mock_egl, eglChooseConfig(_,_,_,_,_)) .InSequence(seq) .WillOnce(DoAll(SetArgPointee<4>(0), Return(EGL_TRUE))); EXPECT_CALL(mock_egl, eglTerminate(fake_display)) .InSequence(seq); mga::HwcBlankingControl hwc_config{mock_hwc_wrapper}; EXPECT_EQ(mir_pixel_format_abgr_8888, hwc_config.active_config_for(mga::DisplayName::primary).current_format); } TEST_F(HwcConfiguration, turns_screen_on) { testing::InSequence seq; EXPECT_CALL(*mock_hwc_wrapper, display_on(display)); EXPECT_CALL(*mock_hwc_wrapper, vsync_signal_on(display)); config.power_mode(display, mir_power_mode_on); } TEST_F(HwcConfiguration, turns_screen_off_for_off_suspend_and_standby) { testing::InSequence seq; EXPECT_CALL(*mock_hwc_wrapper, vsync_signal_off(display)); EXPECT_CALL(*mock_hwc_wrapper, display_off(display)); EXPECT_CALL(*mock_hwc_wrapper, display_on(display)); EXPECT_CALL(*mock_hwc_wrapper, vsync_signal_on(display)); EXPECT_CALL(*mock_hwc_wrapper, vsync_signal_off(display)); EXPECT_CALL(*mock_hwc_wrapper, display_off(display)); config.power_mode(display, mir_power_mode_off); //HWC version 1.3 and prior do not support anything more than on and off. config.power_mode(display, mir_power_mode_suspend); config.power_mode(display, mir_power_mode_off); config.power_mode(display, mir_power_mode_standby); config.power_mode(display, mir_power_mode_on); config.power_mode(display, mir_power_mode_standby); //translate this into blanking the screen config.power_mode(display, mir_power_mode_suspend); config.power_mode(display, mir_power_mode_standby); } TEST_F(HwcConfiguration, translates_mir_power_mode_to_hwc_power_mode) { testing::InSequence seq; EXPECT_CALL(*mock_hwc_wrapper, power_mode(display, mga::PowerMode::off)); EXPECT_CALL(*mock_hwc_wrapper, power_mode(display, mga::PowerMode::doze_suspend)); EXPECT_CALL(*mock_hwc_wrapper, power_mode(display, mga::PowerMode::doze)); EXPECT_CALL(*mock_hwc_wrapper, power_mode(display, mga::PowerMode::normal)); power_mode_config.power_mode(display, mir_power_mode_off); power_mode_config.power_mode(display, mir_power_mode_suspend); power_mode_config.power_mode(display, mir_power_mode_standby); power_mode_config.power_mode(display, mir_power_mode_on); } TEST_F(HwcConfiguration, queries_connected_primary_display_properties) { using namespace testing; auto android_reported_dpi_x = 390000u; auto android_reported_dpi_y = 400000u; geom::Size px_size {768, 1280}; geom::Size mm_size {50, 81}; std::vector hwc_config {mga::ConfigId{0xA1}, mga::ConfigId{0xBEE}}; std::chrono::milliseconds vrefresh_period {16}; EXPECT_CALL(*mock_hwc_wrapper, display_configs(display)) .WillOnce(Return(hwc_config)); EXPECT_CALL(*mock_hwc_wrapper, display_attributes(display, hwc_config[0], _, _)) .WillOnce(Invoke([&] (mga::DisplayName, mga::ConfigId, uint32_t const* attribute_list, int32_t* values) { int i = 0; while(attribute_list[i] != HWC_DISPLAY_NO_ATTRIBUTE) { switch(attribute_list[i]) { case HWC_DISPLAY_WIDTH: values[i] = px_size.width.as_int(); break; case HWC_DISPLAY_HEIGHT: values[i] = px_size.height.as_int(); break; case HWC_DISPLAY_VSYNC_PERIOD: values[i] = std::chrono::duration_cast(vrefresh_period).count(); break; case HWC_DISPLAY_DPI_X: values[i] = android_reported_dpi_x; break; case HWC_DISPLAY_DPI_Y: values[i] = android_reported_dpi_y; break; default: break; } i++; } //the attribute list should be at least this long, as some qcom drivers always deref attribute_list[5] EXPECT_EQ(5, i); return 0; })); auto vrefresh_hz = 1000.0 / vrefresh_period.count(); auto attribs = config.active_config_for(display); ASSERT_THAT(attribs.modes.size(), Eq(1)); EXPECT_THAT(attribs.modes[0].size, Eq(px_size)); EXPECT_THAT(attribs.modes[0].vrefresh_hz, Eq(vrefresh_hz)); EXPECT_THAT(attribs.physical_size_mm, Eq(mm_size)); EXPECT_TRUE(attribs.connected); EXPECT_TRUE(attribs.used); } //the primary display should not be disconnected, but this is how to tell if the external one is TEST_F(HwcConfiguration, test_hwc_device_display_config_failure_throws) { using namespace testing; ON_CALL(*mock_hwc_wrapper, display_configs(_)) .WillByDefault(Return(std::vector{})); EXPECT_THROW({ config.active_config_for(mga::DisplayName::primary); }, std::runtime_error); auto external_attribs = config.active_config_for(mga::DisplayName::external); EXPECT_THAT(external_attribs.modes.size(), Eq(0)); EXPECT_FALSE(external_attribs.connected); EXPECT_FALSE(external_attribs.used); } //some devices (bq) only report an error later in the display attributes call, make sure to report disconnected on error to this call. TEST_F(HwcConfiguration, display_attributes_failure_indicates_problem_for_primary_disconnect_for_secondary) { using namespace testing; ON_CALL(*mock_hwc_wrapper, display_attributes(_,_,_,_)) .WillByDefault(Return(-22)); EXPECT_THROW({ config.active_config_for(mga::DisplayName::primary); }, std::runtime_error); auto external_attribs = config.active_config_for(mga::DisplayName::external); EXPECT_THAT(external_attribs.modes.size(), Eq(0)); EXPECT_FALSE(external_attribs.connected); EXPECT_FALSE(external_attribs.used); } TEST_F(HwcConfiguration, no_fpe_from_malformed_refresh) { using namespace testing; EXPECT_CALL(*mock_hwc_wrapper, display_attributes(_,_,_,_)) .WillOnce(Invoke([] (mga::DisplayName, mga::ConfigId, uint32_t const* attribute_list, int32_t* values) { int i = 0; while(attribute_list[i] != HWC_DISPLAY_NO_ATTRIBUTE) values[i++] = 0; return 0; })); auto attribs = config.active_config_for(mga::DisplayName::external); EXPECT_THAT(attribs.modes[attribs.current_mode_index].vrefresh_hz, Eq(0.0f)); } TEST_F(HwcConfiguration, no_fpe_from_malformed_dpi) { using namespace testing; EXPECT_CALL(*mock_hwc_wrapper, display_attributes(_,_,_,_)) .WillOnce(Invoke([] (mga::DisplayName, mga::ConfigId, uint32_t const* attribute_list, int32_t* values) { int i = 0; while(attribute_list[i] != HWC_DISPLAY_NO_ATTRIBUTE) values[i++] = 0; return 0; })); auto attribs = config.active_config_for(mga::DisplayName::external); EXPECT_THAT(attribs.physical_size_mm, Eq(geom::Size{0,0})); } TEST_F(HwcConfiguration, subscribes_to_hotplug_and_vsync) { using namespace testing; std::function hotplug_fn([](mga::DisplayName, bool){}); std::function vsync_fn( [](mga::DisplayName, std::chrono::nanoseconds){}); EXPECT_CALL(*mock_hwc_wrapper, subscribe_to_events(_,_,_,_)) .WillOnce(DoAll(SaveArg<1>(&vsync_fn), SaveArg<2>(&hotplug_fn))); EXPECT_CALL(*mock_hwc_wrapper, unsubscribe_from_events_(_)); unsigned int hotplug_call_count{0}; unsigned int vsync_call_count{0}; auto subscription = config.subscribe_to_config_changes( [&]{ hotplug_call_count++; }, [&](mga::DisplayName){ vsync_call_count++; }); hotplug_fn(mga::DisplayName::primary, true); hotplug_fn(mga::DisplayName::primary, true); vsync_fn(mga::DisplayName::primary, std::chrono::nanoseconds(33)); EXPECT_THAT(hotplug_call_count, Eq(2)); EXPECT_THAT(vsync_call_count, Eq(1)); } TEST_F(HwcConfiguration, sets_active_config_when_needed) { using namespace testing; std::vector config_ids{mga::ConfigId{0xA1}, mga::ConfigId{0xBEE}}; mga::DisplayName display_name{mga::DisplayName::primary}; EXPECT_CALL(*mock_hwc_wrapper, display_configs(display)) .WillOnce(Return(config_ids)); EXPECT_CALL(*mock_hwc_wrapper, has_active_config(display_name)) .WillOnce(Return(false)); EXPECT_CALL(*mock_hwc_wrapper, set_active_config(display_name, config_ids[0])); power_mode_config.active_config_for(display_name); } TEST_F(HwcConfiguration, uses_given_active_config_id) { using namespace testing; std::vector config_ids{mga::ConfigId{0xA1}, mga::ConfigId{0xBEE}}; mga::DisplayName display_name{mga::DisplayName::external}; EXPECT_CALL(*mock_hwc_wrapper, display_configs(display_name)) .WillOnce(Return(config_ids)); EXPECT_CALL(*mock_hwc_wrapper, has_active_config(display_name)) .WillOnce(Return(true)); EXPECT_CALL(*mock_hwc_wrapper, active_config_for(display_name)) .WillOnce(Return(config_ids[1])); EXPECT_CALL(*mock_hwc_wrapper, display_attributes(display_name, config_ids[1], _, _)); power_mode_config.active_config_for(display_name); } ./tests/unit-tests/graphics/android/test_display_hotplug.cpp0000644000015600001650000001313012676616125024562 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/display.h" #include "src/platforms/android/server/hal_component_factory.h" #include "src/platforms/android/server/hwc_layerlist.h" #include "mir/gl/default_program_factory.h" #include "src/server/report/null_report_factory.h" #include "mir/glib_main_loop.h" #include "mir/time/steady_clock.h" #include "mir/test/doubles/mock_display_device.h" #include "mir/test/doubles/mock_framebuffer_bundle.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/auto_unblock_thread.h" #include namespace mga=mir::graphics::android; namespace mg=mir::graphics; namespace geom=mir::geometry; namespace mtd=mir::test::doubles; //doesn't use the HW modules, rather tests mainloop integration struct DisplayHotplug : ::testing::Test { struct StubHwcConfig : public mga::HwcConfiguration { void power_mode(mga::DisplayName, MirPowerMode) override {} mg::DisplayConfigurationOutput active_config_for(mga::DisplayName) override { return mtd::StubDisplayConfig({{true,true}}).outputs[0]; } mga::ConfigChangeSubscription subscribe_to_config_changes( std::function const& cb, std::function const&) override { hotplug_fn = cb; return {}; } void simulate_hotplug() { hotplug_fn(); } std::function hotplug_fn{[]{}}; }; struct WrappingConfig : public mga::HwcConfiguration { WrappingConfig(mga::HwcConfiguration& config) : wrapped(config) {} void power_mode(mga::DisplayName d, MirPowerMode m) override { wrapped.power_mode(d, m); } mg::DisplayConfigurationOutput active_config_for(mga::DisplayName d) override { return wrapped.active_config_for(d); } mga::ConfigChangeSubscription subscribe_to_config_changes( std::function const& hotplug, std::function const& vsync) override { return wrapped.subscribe_to_config_changes(hotplug, vsync); } mga::HwcConfiguration& wrapped; }; struct StubOutputBuilder : public mga::DisplayComponentFactory { std::unique_ptr create_framebuffers(mg::DisplayConfigurationOutput const&) override { return std::unique_ptr(new testing::NiceMock()); } std::unique_ptr create_display_device() override { return std::unique_ptr(new testing::NiceMock()); } std::unique_ptr create_hwc_configuration() override { return std::unique_ptr(new WrappingConfig(stub_config)); } std::unique_ptr create_layer_list() override { return std::unique_ptr( new mga::LayerList(std::make_shared(), {}, geom::Displacement{})); } std::unique_ptr create_command_stream_sync() { return nullptr; } std::shared_ptr the_buffer_allocator() { return nullptr; } StubHwcConfig stub_config; }; testing::NiceMock mock_egl; testing::NiceMock mock_gl; mir::GLibMainLoop mainloop{std::make_shared()}; mir::test::AutoUnblockThread loop{ [this]{ mainloop.enqueue(this, [this] { mainloop.stop(); }); }, [this]{ mainloop.run(); }}; std::shared_ptr stub_output_builder{std::make_shared()}; mga::Display display{ stub_output_builder, std::make_shared(), std::make_shared(), mir::report::null_display_report(), mga::OverlayOptimization::enabled}; }; TEST_F(DisplayHotplug, hotplug_generates_mainloop_event) { std::mutex mutex; std::condition_variable cv; int call_count{0}; std::function change_handler{[&] { std::unique_lock lk(mutex); call_count++; cv.notify_all(); }}; std::unique_lock lk(mutex); display.register_configuration_change_handler(mainloop, change_handler); stub_output_builder->stub_config.simulate_hotplug(); EXPECT_TRUE(cv.wait_for(lk, std::chrono::seconds(2), [&]{ return call_count == 1; })); stub_output_builder->stub_config.simulate_hotplug(); EXPECT_TRUE(cv.wait_for(lk, std::chrono::seconds(2), [&]{ return call_count == 2; })); mainloop.unregister_fd_handler(&display); } ./tests/unit-tests/graphics/android/test_pixel_format.cpp0000644000015600001650000000401312676616125024044 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "android_format_conversion-inl.h" #include namespace mga=mir::graphics::android; TEST(PixelFormatConversion, conversion_to_android_test) { EXPECT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mga::to_android_format(mir_pixel_format_abgr_8888)); EXPECT_EQ(HAL_PIXEL_FORMAT_RGBX_8888, mga::to_android_format(mir_pixel_format_xbgr_8888)); EXPECT_EQ(HAL_PIXEL_FORMAT_BGRA_8888, mga::to_android_format(mir_pixel_format_argb_8888)); // Note X to A conversion! We resolve this during compositing. EXPECT_EQ(HAL_PIXEL_FORMAT_BGRA_8888, mga::to_android_format(mir_pixel_format_xrgb_8888)); EXPECT_EQ(HAL_PIXEL_FORMAT_RGB_888, mga::to_android_format(mir_pixel_format_rgb_888)); EXPECT_EQ(0, mga::to_android_format(mir_pixel_format_bgr_888)); EXPECT_EQ(HAL_PIXEL_FORMAT_RGB_565, mga::to_android_format(mir_pixel_format_rgb_565)); } TEST(PixelFormatConversion, conversion_to_mir_test) { EXPECT_EQ(mir_pixel_format_abgr_8888, mga::to_mir_format(HAL_PIXEL_FORMAT_RGBA_8888)); EXPECT_EQ(mir_pixel_format_xbgr_8888, mga::to_mir_format(HAL_PIXEL_FORMAT_RGBX_8888)); EXPECT_EQ(mir_pixel_format_argb_8888, mga::to_mir_format(HAL_PIXEL_FORMAT_BGRA_8888)); EXPECT_EQ(mir_pixel_format_rgb_888, mga::to_mir_format(HAL_PIXEL_FORMAT_RGB_888)); EXPECT_EQ(mir_pixel_format_rgb_565, mga::to_mir_format(HAL_PIXEL_FORMAT_RGB_565)); } ./tests/unit-tests/graphics/android/test_hwc_layerlist.cpp0000644000015600001650000001777212676616125024244 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/stub_renderable.h" #include "mir/test/doubles/stub_buffer.h" #include "src/platforms/android/server/hwc_layerlist.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "hwc_struct_helpers.h" #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mt=mir::test; namespace mtd=mir::test::doubles; namespace geom=mir::geometry; namespace { struct LayerListTest : public testing::Test { LayerListTest() : layer_adapter{std::make_shared()}, buffer1{std::make_shared()}, buffer2{std::make_shared()}, renderables{std::make_shared(buffer1), std::make_shared(buffer2), std::make_shared()} { mt::fill_hwc_layer(fbtarget, &visible_rect, disp_frame, *stub_fb, HWC_FRAMEBUFFER_TARGET, 0); mt::fill_hwc_layer(skip, &visible_rect, disp_frame, *stub_fb, HWC_FRAMEBUFFER, HWC_SKIP_LAYER); } std::shared_ptr layer_adapter; std::shared_ptr buffer1; std::shared_ptr buffer2; mg::RenderableList renderables; geom::Rectangle const disp_frame{{0,0}, {44,22}}; std::shared_ptr stub_fb{ std::make_shared( std::make_shared>(disp_frame.size), disp_frame.size)}; hwc_layer_1_t fbtarget; hwc_layer_1_t skip; hwc_rect_t visible_rect; geom::Displacement offset; }; } TEST_F(LayerListTest, list_defaults) { mga::LayerList layerlist{layer_adapter, {}, offset}; auto list = layerlist.native_list(); EXPECT_EQ(-1, list->retireFenceFd); EXPECT_EQ(HWC_GEOMETRY_CHANGED, list->flags); EXPECT_NE(nullptr, list->dpy); EXPECT_NE(nullptr, list->sur); EXPECT_EQ(std::distance(layerlist.begin(), layerlist.end()), 2); } TEST_F(LayerListTest, list_iterators) { size_t additional_layers = 2; mga::LayerList list(layer_adapter, {}, offset); EXPECT_EQ(std::distance(list.begin(), list.end()), additional_layers); additional_layers = 1; mga::LayerList list2(layer_adapter, renderables, offset); EXPECT_EQ(std::distance(list2.begin(), list2.end()), additional_layers + renderables.size()); mga::LayerList list3(std::make_shared(), renderables, offset); EXPECT_EQ(std::distance(list3.begin(), list3.end()), renderables.size()); } TEST_F(LayerListTest, keeps_track_of_needs_commit) { size_t fb_target_size{1}; mga::LayerList list(layer_adapter, renderables, offset); auto i = 0; for (auto& layer : list) { if (i == 3) EXPECT_FALSE(layer.needs_commit); else EXPECT_TRUE(layer.needs_commit); i++; } mg::RenderableList list2{ std::make_shared(buffer1), std::make_shared(buffer2), std::make_shared() }; list.update_list(list2, offset); i = 0; for (auto& layer : list) { if (i == 3) EXPECT_FALSE(layer.needs_commit); else EXPECT_TRUE(layer.needs_commit); i++; } ASSERT_THAT(list.native_list()->numHwLayers, testing::Eq(list2.size() + fb_target_size)); list.native_list()->hwLayers[2].compositionType = HWC_OVERLAY; list.update_list(list2, offset); i = 0; for (auto& layer : list) { if ((i == 2) || (i == 3)) EXPECT_FALSE(layer.needs_commit); else EXPECT_TRUE(layer.needs_commit); i++; } } TEST_F(LayerListTest, setup_fb_hwc10) { using namespace testing; mga::LayerList list(std::make_shared(), {}, offset); list.setup_fb(stub_fb); auto l = list.native_list(); ASSERT_THAT(l->numHwLayers, Eq(1)); EXPECT_THAT(l->hwLayers[l->numHwLayers-1], MatchesLegacyLayer(skip)); } TEST_F(LayerListTest, setup_fb_without_skip) { using namespace testing; mga::LayerList list(layer_adapter, renderables, offset); list.setup_fb(stub_fb); auto l = list.native_list(); ASSERT_THAT(l->numHwLayers, Eq(1 + renderables.size())); EXPECT_THAT(l->hwLayers[l->numHwLayers-1], MatchesLegacyLayer(fbtarget)); } TEST_F(LayerListTest, setup_fb_with_skip) { using namespace testing; mga::LayerList list(layer_adapter, {}, offset); list.setup_fb(stub_fb); auto l = list.native_list(); ASSERT_THAT(l->numHwLayers, Eq(2)); EXPECT_THAT(l->hwLayers[l->numHwLayers-2], MatchesLegacyLayer(skip)); EXPECT_THAT(l->hwLayers[l->numHwLayers-1], MatchesLegacyLayer(fbtarget)); } TEST_F(LayerListTest, generate_rejected_renderables) { using namespace testing; mga::LayerList list(layer_adapter, renderables, offset); auto l = list.native_list(); ASSERT_THAT(l->numHwLayers, Eq(4)); l->hwLayers[1].compositionType = HWC_OVERLAY; EXPECT_THAT(list.rejected_renderables(), ElementsAre(renderables.front(), renderables.back())); } TEST_F(LayerListTest, swap_not_needed_when_all_layers_overlay) { using namespace testing; mga::LayerList list(layer_adapter, renderables, offset); auto l = list.native_list(); ASSERT_THAT(l->numHwLayers, Eq(4)); for (auto i = 0u; i < 3; i++) l->hwLayers[i].compositionType = HWC_OVERLAY; l->hwLayers[3].compositionType = HWC_FRAMEBUFFER_TARGET; EXPECT_FALSE(list.needs_swapbuffers()); } TEST_F(LayerListTest, swap_needed_when_one_layer_is_gl_rendered) { using namespace testing; mga::LayerList list(layer_adapter, renderables, offset); auto l = list.native_list(); for (auto i = 0u; i < 3; i++) l->hwLayers[i].compositionType = HWC_OVERLAY; l->hwLayers[1].compositionType = HWC_FRAMEBUFFER; EXPECT_TRUE(list.needs_swapbuffers()); } TEST_F(LayerListTest, swap_needed_when_gl_is_forced) { mga::LayerList list(layer_adapter, {}, offset); EXPECT_TRUE(list.needs_swapbuffers()); } TEST_F(LayerListTest, offset_origin_does_not_affect_skip_and_target) { using namespace testing; geom::Displacement offset{199, 299}; mga::LayerList list(layer_adapter, {}, offset); list.setup_fb(stub_fb); auto l = list.native_list(); ASSERT_THAT(l->numHwLayers, Eq(2)); EXPECT_THAT(l->hwLayers[l->numHwLayers-2], MatchesLegacyLayer(skip)); EXPECT_THAT(l->hwLayers[l->numHwLayers-1], MatchesLegacyLayer(fbtarget)); } TEST_F(LayerListTest, list_is_offset_for_nonorigin_displays) { using namespace testing; geom::Displacement offset{199, 299}; geom::Rectangle not_offset_rect{geom::Point{250, 200}, buffer1->size()}; mg::RenderableList renderable_list {std::make_shared(buffer1, not_offset_rect)}; mga::LayerList list(layer_adapter, renderable_list, offset); list.setup_fb(stub_fb); hwc_layer_1 expected_layer; hwc_rect_t visible_rect; geom::Point expected_point{51, -99}; geom::Rectangle expected_rectangle{expected_point, buffer1->size()}; mt::fill_hwc_layer(expected_layer, &visible_rect, expected_rectangle, *buffer1, HWC_FRAMEBUFFER, 0); auto l = list.native_list(); ASSERT_THAT(l->numHwLayers, Eq(2)); EXPECT_THAT(l->hwLayers[l->numHwLayers-2], MatchesLegacyLayer(expected_layer)); EXPECT_THAT(l->hwLayers[l->numHwLayers-1], MatchesLegacyLayer(fbtarget)); } ./tests/unit-tests/graphics/android/test_hwc_wrapper.cpp0000644000015600001650000003151612676616125023704 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/real_hwc_wrapper.h" #include "src/platforms/android/server/hwc_report.h" #include "mir/test/doubles/mock_hwc_composer_device_1.h" #include "mir/test/doubles/mock_hwc_report.h" #include #include namespace mga = mir::graphics::android; namespace mtd = mir::test::doubles; struct HwcWrapper : public ::testing::Test { HwcWrapper() : mock_device(std::make_shared>()), mock_report(std::make_shared>()), virtual_display{nullptr}, external_display{nullptr}, primary_display{nullptr} { } int display_saving_fn( struct hwc_composer_device_1*, size_t sz, hwc_display_contents_1_t** displays) { switch (sz) { case 3: virtual_display = displays[2]; case 2: external_display = displays[1]; case 1: primary_display = displays[0]; default: break; } return 0; } hwc_display_contents_1_t primary_list; hwc_display_contents_1_t external_list; hwc_display_contents_1_t virtual_list; std::array primary_displays{{ &primary_list, nullptr, nullptr}}; std::array both_displays{{ &primary_list, &external_list, nullptr}}; std::shared_ptr const mock_device; std::shared_ptr const mock_report; hwc_display_contents_1_t *virtual_display; hwc_display_contents_1_t *external_display; hwc_display_contents_1_t *primary_display; }; TEST_F(HwcWrapper, submits_correct_prepare_parameters) { using namespace testing; Sequence seq; EXPECT_CALL(*mock_report, report_list_submitted_to_prepare(Ref(primary_displays))) .InSequence(seq); EXPECT_CALL(*mock_device, prepare_interface(mock_device.get(), 1, _)) .InSequence(seq) .WillOnce(Invoke(this, &HwcWrapper::display_saving_fn)); EXPECT_CALL(*mock_report, report_prepare_done(Ref(primary_displays))) .InSequence(seq); mga::RealHwcWrapper wrapper(mock_device, mock_report); wrapper.prepare(primary_displays); EXPECT_EQ(&primary_list, primary_display); EXPECT_EQ(nullptr, virtual_display); EXPECT_EQ(nullptr, external_display); } TEST_F(HwcWrapper, submits_correct_prepare_parameters_with_external_display) { using namespace testing; Sequence seq; EXPECT_CALL(*mock_report, report_list_submitted_to_prepare(Ref(both_displays))) .InSequence(seq); EXPECT_CALL(*mock_device, prepare_interface(mock_device.get(), 2, _)) .InSequence(seq) .WillOnce(Invoke(this, &HwcWrapper::display_saving_fn)); EXPECT_CALL(*mock_report, report_prepare_done(Ref(both_displays))) .InSequence(seq); mga::RealHwcWrapper wrapper(mock_device, mock_report); wrapper.prepare(both_displays); EXPECT_EQ(&primary_list, primary_display); EXPECT_EQ(&external_list, external_display); EXPECT_EQ(nullptr, virtual_display); } TEST_F(HwcWrapper, throws_on_prepare_failure) { using namespace testing; mga::RealHwcWrapper wrapper(mock_device, mock_report); EXPECT_CALL(*mock_device, prepare_interface(mock_device.get(), _, _)) .Times(1) .WillOnce(Return(-1)); EXPECT_THROW({ wrapper.prepare(primary_displays); }, std::runtime_error); } TEST_F(HwcWrapper, submits_correct_set_parameters) { using namespace testing; Sequence seq; EXPECT_CALL(*mock_report, report_set_list(Ref(both_displays))) .InSequence(seq); EXPECT_CALL(*mock_device, set_interface(mock_device.get(), 2, _)) .InSequence(seq) .WillOnce(Invoke(this, &HwcWrapper::display_saving_fn)); EXPECT_CALL(*mock_report, report_set_done(Ref(both_displays))) .InSequence(seq); mga::RealHwcWrapper wrapper(mock_device, mock_report); wrapper.set(both_displays); EXPECT_EQ(&primary_list, primary_display); EXPECT_EQ(&external_list, external_display); EXPECT_EQ(nullptr, virtual_display); } TEST_F(HwcWrapper, throws_on_set_failure) { using namespace testing; mga::RealHwcWrapper wrapper(mock_device, mock_report); EXPECT_CALL(*mock_device, set_interface(mock_device.get(), _, _)) .Times(1) .WillOnce(Return(-1)); EXPECT_THROW({ wrapper.set(primary_displays); }, std::runtime_error); } TEST_F(HwcWrapper, turns_display_on) { using namespace testing; Sequence seq; EXPECT_CALL(*mock_device, blank_interface(mock_device.get(), HWC_DISPLAY_PRIMARY, 0)) .InSequence(seq) .WillOnce(Return(0)); EXPECT_CALL(*mock_report, report_display_on()) .InSequence(seq); EXPECT_CALL(*mock_device, blank_interface(mock_device.get(), HWC_DISPLAY_EXTERNAL, 0)) .InSequence(seq) .WillOnce(Return(-1)); mga::RealHwcWrapper wrapper(mock_device, mock_report); wrapper.display_on(mga::DisplayName::primary); EXPECT_THROW({ wrapper.display_on(mga::DisplayName::external); }, std::runtime_error); } TEST_F(HwcWrapper, turns_display_off) { using namespace testing; Sequence seq; EXPECT_CALL(*mock_device, blank_interface(mock_device.get(), HWC_DISPLAY_PRIMARY, 1)) .InSequence(seq) .WillOnce(Return(0)); EXPECT_CALL(*mock_report, report_display_off()) .InSequence(seq); EXPECT_CALL(*mock_device, blank_interface(mock_device.get(), HWC_DISPLAY_EXTERNAL, 1)) .InSequence(seq) .WillOnce(Return(-1)); mga::RealHwcWrapper wrapper(mock_device, mock_report); wrapper.display_off(mga::DisplayName::primary); EXPECT_THROW({ wrapper.display_off(mga::DisplayName::external); }, std::runtime_error); } TEST_F(HwcWrapper, turns_vsync_on) { using namespace testing; Sequence seq; EXPECT_CALL(*mock_device, eventControl_interface(mock_device.get(), HWC_DISPLAY_EXTERNAL, HWC_EVENT_VSYNC, 1)) .InSequence(seq) .WillOnce(Return(0)); EXPECT_CALL(*mock_report, report_vsync_on()) .InSequence(seq); EXPECT_CALL(*mock_device, eventControl_interface(mock_device.get(), HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 1)) .InSequence(seq) .WillOnce(Return(-1)); mga::RealHwcWrapper wrapper(mock_device, mock_report); wrapper.vsync_signal_on(mga::DisplayName::external); EXPECT_THROW({ wrapper.vsync_signal_on(mga::DisplayName::primary); }, std::runtime_error); } TEST_F(HwcWrapper, turns_vsync_off) { using namespace testing; Sequence seq; EXPECT_CALL(*mock_device, eventControl_interface(mock_device.get(), HWC_DISPLAY_EXTERNAL, HWC_EVENT_VSYNC, 0)) .InSequence(seq) .WillOnce(Return(0)); EXPECT_CALL(*mock_report, report_vsync_off()) .InSequence(seq); EXPECT_CALL(*mock_device, eventControl_interface(mock_device.get(), HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0)) .InSequence(seq) .WillOnce(Return(-1)); mga::RealHwcWrapper wrapper(mock_device, mock_report); wrapper.vsync_signal_off(mga::DisplayName::external); EXPECT_THROW({ wrapper.vsync_signal_off(mga::DisplayName::primary); }, std::runtime_error); } TEST_F(HwcWrapper, accesses_display_config) { using namespace testing; std::array id_array{ 5u, 7u, 10u }; std::vector ids{id_array.size()}; auto array_it = id_array.begin(); for( auto& id : ids ) id = mga::ConfigId{*array_it++}; EXPECT_CALL(*mock_device, getDisplayConfigs_interface( mock_device.get(), HWC_DISPLAY_PRIMARY, _, Pointee(Gt(0)))) .WillOnce(DoAll( SetArrayArgument<2>(id_array.begin(), id_array.end()), SetArgPointee<3>(id_array.size()), Return(0))) .WillOnce(Return(-1)); mga::RealHwcWrapper wrapper(mock_device, mock_report); EXPECT_THAT(wrapper.display_configs(mga::DisplayName::primary), Eq(ids)); EXPECT_THAT(wrapper.display_configs(mga::DisplayName::primary), IsEmpty()); } TEST_F(HwcWrapper, calls_access_functions) { using namespace testing; uint32_t* attributes{nullptr}; int32_t* values{nullptr}; mga::ConfigId hwc_config{3}; int rc{-2}; EXPECT_CALL(*mock_device, getDisplayAttributes_interface( mock_device.get(), HWC_DISPLAY_PRIMARY, hwc_config.as_value(), attributes, values)) .WillOnce(Return(rc)); mga::RealHwcWrapper wrapper(mock_device, mock_report); EXPECT_THAT(wrapper.display_attributes(mga::DisplayName::primary, hwc_config, attributes, values), Eq(rc)); } TEST_F(HwcWrapper, registers_hooks_for_driver) { using namespace testing; mga::HwcCallbacks const* callbacks{nullptr}; EXPECT_CALL(*mock_device, registerProcs_interface(mock_device.get(),_)) .WillOnce(Invoke([&](struct hwc_composer_device_1*, hwc_procs_t const* procs) {callbacks = reinterpret_cast(procs);})); mga::RealHwcWrapper wrapper(mock_device, mock_report); ASSERT_THAT(callbacks, Ne(nullptr)); EXPECT_THAT(callbacks->hooks.invalidate, Ne(nullptr)); EXPECT_THAT(callbacks->hooks.vsync, Ne(nullptr)); EXPECT_THAT(callbacks->hooks.hotplug, Ne(nullptr)); callbacks->hooks.invalidate(&callbacks->hooks); callbacks->hooks.vsync(&callbacks->hooks, 0, 33223); callbacks->hooks.hotplug(&callbacks->hooks, 0, 1); } TEST_F(HwcWrapper, can_dish_out_notifications) { using namespace testing; auto const expected_invalidate_call_count = 8u; auto const expected_vsync_call_count = 5u; auto const expected_hotplug_call_count = 3u; mga::HwcCallbacks const* callbacks{nullptr}; EXPECT_CALL(*mock_device, registerProcs_interface(mock_device.get(),_)) .WillOnce(Invoke([&](struct hwc_composer_device_1*, hwc_procs_t const* procs) {callbacks = reinterpret_cast(procs);})); mga::RealHwcWrapper wrapper(mock_device, mock_report); auto invalidate_call_count = 0u; auto vsync_call_count = 0u; auto hotplug_call_count = 0u; wrapper.subscribe_to_events(this, [&](mga::DisplayName, std::chrono::nanoseconds){vsync_call_count++;}, [&](mga::DisplayName, bool){hotplug_call_count++;}, [&](){invalidate_call_count++;}); for(auto i = 0u; i < expected_invalidate_call_count; i++) callbacks->hooks.invalidate(&callbacks->hooks); for(auto i = 0u; i < expected_vsync_call_count; i++) callbacks->hooks.vsync(&callbacks->hooks, 0, 33223); for(auto i = 0u; i < expected_hotplug_call_count; i++) callbacks->hooks.hotplug(&callbacks->hooks, 0, 1); wrapper.unsubscribe_from_events(this); //should not get these callbacks callbacks->hooks.invalidate(&callbacks->hooks); callbacks->hooks.vsync(&callbacks->hooks, 0, 33223); callbacks->hooks.hotplug(&callbacks->hooks, 0, 1); EXPECT_THAT(vsync_call_count, Eq(expected_vsync_call_count)); EXPECT_THAT(invalidate_call_count, Eq(expected_invalidate_call_count)); EXPECT_THAT(hotplug_call_count, Eq(expected_hotplug_call_count)); } TEST_F(HwcWrapper, callback_calls_hwcvsync_and_can_continue_calling_after_destruction) { using namespace testing; auto call_count = 0u; mga::HwcCallbacks const* callbacks{nullptr}; EXPECT_CALL(*mock_device, registerProcs_interface(mock_device.get(),_)) .WillOnce(Invoke([&](struct hwc_composer_device_1*, hwc_procs_t const* procs) {callbacks = reinterpret_cast(procs);})); { mga::RealHwcWrapper wrapper(mock_device, mock_report); wrapper.subscribe_to_events( this, [&](mga::DisplayName, std::chrono::nanoseconds){ call_count++; }, [](mga::DisplayName, bool){}, []{}); ASSERT_THAT(callbacks, Ne(nullptr)); ASSERT_THAT(callbacks->hooks.vsync, Ne(nullptr)); callbacks->hooks.vsync(&callbacks->hooks, 0, 0); } //some bad drivers call the hooks after we close() the module. //After that point, we don't care, so just make sure there's something to call callbacks->hooks.vsync(&callbacks->hooks, 0, 0); EXPECT_THAT(call_count, Eq(1)); } ./tests/unit-tests/graphics/android/test_display.cpp0000644000015600001650000011520112676616125023022 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/graphics/display_buffer.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/virtual_output.h" #include "mir/logging/logger.h" #include "src/platforms/android/server/display.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/mock_display_report.h" #include "mir/test/doubles/mock_display_device.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/stub_display_buffer.h" #include "mir/test/doubles/stub_display_builder.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/mock_gl_config.h" #include "mir/test/doubles/stub_gl_program_factory.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/stub_renderable.h" #include "mir_native_window.h" #include "mir/test/doubles/stub_driver_interpreter.h" #include #include #include #include #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mtd=mir::test::doubles; namespace geom=mir::geometry; namespace { mg::DisplayConfigurationOutputId const primary_output_id{ mga::as_output_id(mga::DisplayName::primary)}; mg::DisplayConfigurationOutputId const external_output_id{ mga::as_output_id(mga::DisplayName::external)}; } class Display : public ::testing::Test { public: Display() : dummy_display{mock_egl.fake_egl_display}, dummy_context{mock_egl.fake_egl_context}, dummy_config{mock_egl.fake_configs[0]}, null_display_report{mir::report::null_display_report()}, stub_db_factory{std::make_shared()}, stub_gl_config{std::make_shared()}, stub_gl_program_factory{std::make_shared()} { } protected: testing::NiceMock mock_egl; testing::NiceMock mock_gl; EGLDisplay const dummy_display; EGLContext const dummy_context; EGLConfig const dummy_config; std::shared_ptr const null_display_report; std::shared_ptr const stub_db_factory; std::shared_ptr const stub_gl_config; std::shared_ptr const stub_gl_program_factory; }; //egl expectations are hard to tease out, we should unit-test the gl contexts instead TEST_F(Display, creation_creates_egl_resources_properly) { using namespace testing; EGLSurface fake_surface = reinterpret_cast(0x715); EGLint const expected_pbuffer_attr[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; EGLint const expected_context_attr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; EGLContext dummy_context2 = reinterpret_cast(0x4444); //on constrution EXPECT_CALL(mock_egl, eglGetDisplay(EGL_DEFAULT_DISPLAY)) .WillOnce(Return(dummy_display)); EXPECT_CALL(mock_egl, eglInitialize(dummy_display, _, _)) .WillOnce(DoAll(SetArgPointee<1>(1), SetArgPointee<2>(4), Return(EGL_TRUE))); //display context EXPECT_CALL(mock_egl, eglCreateContext( dummy_display, _, EGL_NO_CONTEXT, mtd::AttrMatches(expected_context_attr))) .WillOnce(Return(dummy_context)); EXPECT_CALL(mock_egl, eglCreatePbufferSurface( dummy_display, _, mtd::AttrMatches(expected_pbuffer_attr))) .WillOnce(Return(fake_surface)); //primary display buffer context EXPECT_CALL(mock_egl, eglCreateContext( dummy_display, _, dummy_context, mtd::AttrMatches(expected_context_attr))) .WillOnce(Return(dummy_context2)); EXPECT_CALL(mock_egl, eglCreateWindowSurface( dummy_display, _, Ne(nullptr), NULL)) .WillOnce(Return(fake_surface)); //fallback renderer could make current too EXPECT_CALL(mock_egl, eglMakeCurrent(dummy_display, fake_surface, fake_surface, dummy_context2)) .Times(AtLeast(1)); EXPECT_CALL(mock_egl, eglMakeCurrent(dummy_display, fake_surface, fake_surface, dummy_context)) .Times(AtLeast(1)); //on destruction EXPECT_CALL(mock_egl, eglGetCurrentContext()) .WillRepeatedly(Return(dummy_context)); EXPECT_CALL(mock_egl, eglMakeCurrent(dummy_display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)) .Times(2); EXPECT_CALL(mock_egl, eglDestroySurface(_,_)) .Times(2); EXPECT_CALL(mock_egl, eglDestroyContext(_,_)) .Times(2); EXPECT_CALL(mock_egl, eglTerminate(dummy_display)) .Times(1); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::disabled); } TEST_F(Display, selects_usable_egl_configuration) { using namespace testing; int const incorrect_visual_id = 2; int const correct_visual_id = 1; EGLint const num_cfgs = 45; EGLint const expected_cfg_attr [] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_NONE }; EGLConfig selected_config; EGLConfig cfgs[45]; for(auto i = 0; i < num_cfgs; i++) cfgs[i] = reinterpret_cast(i); int config_to_select_index = 37; EGLConfig correct_config = cfgs[config_to_select_index]; ON_CALL(mock_egl, eglGetConfigAttrib(_, _, EGL_NATIVE_VISUAL_ID, _)) .WillByDefault(DoAll(SetArgPointee<3>(incorrect_visual_id), Return(EGL_TRUE))); ON_CALL(mock_egl, eglGetConfigAttrib(dummy_display, correct_config, EGL_NATIVE_VISUAL_ID,_)) .WillByDefault(DoAll(SetArgPointee<3>(correct_visual_id), Return(EGL_TRUE))); ON_CALL(mock_egl, eglCreateContext(_,_,_,_)) .WillByDefault(DoAll(SaveArg<1>(&selected_config),Return(dummy_context))); auto config_filler = [&] (EGLDisplay, EGLint const*, EGLConfig* out_cfgs, EGLint, EGLint* out_num_cfgs) -> EGLBoolean { memcpy(out_cfgs, cfgs, sizeof(EGLConfig) * num_cfgs); *out_num_cfgs = num_cfgs; return EGL_TRUE; }; EXPECT_CALL(mock_egl, eglGetConfigs(dummy_display, NULL, 0, _)) .Times(1) .WillOnce(DoAll(SetArgPointee<3>(num_cfgs), Return(EGL_TRUE))); EXPECT_CALL(mock_egl, eglChooseConfig(dummy_display, mtd::AttrMatches(expected_cfg_attr),_,num_cfgs,_)) .Times(1) .WillOnce(Invoke(config_filler)); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::disabled); EXPECT_EQ(correct_config, selected_config); } TEST_F(Display, respects_gl_config) { using namespace testing; auto const mock_gl_config = std::make_shared(); EGLint const depth_bits{24}; EGLint const stencil_bits{8}; EXPECT_CALL(*mock_gl_config, depth_buffer_bits()) .WillOnce(Return(depth_bits)); EXPECT_CALL(*mock_gl_config, stencil_buffer_bits()) .WillOnce(Return(stencil_bits)); EXPECT_CALL(mock_egl, eglChooseConfig( _, AllOf(mtd::EGLConfigContainsAttrib(EGL_DEPTH_SIZE, depth_bits), mtd::EGLConfigContainsAttrib(EGL_STENCIL_SIZE, stencil_bits)), _,_,_)); mga::Display display( stub_db_factory, stub_gl_program_factory, mock_gl_config, null_display_report, mga::OverlayOptimization::disabled); } TEST_F(Display, logs_creation_events) { using namespace testing; auto const mock_display_report = std::make_shared(); EXPECT_CALL(*mock_display_report, report_successful_setup_of_native_resources()) .Times(1); EXPECT_CALL(*mock_display_report, report_egl_configuration(_,_)) .Times(1); EXPECT_CALL(*mock_display_report, report_successful_egl_make_current_on_construction()) .Times(1); EXPECT_CALL(*mock_display_report, report_successful_display_construction()) .Times(1); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, mock_display_report, mga::OverlayOptimization::disabled); } TEST_F(Display, throws_on_eglMakeCurrent_failure) { using namespace testing; auto const mock_display_report = std::make_shared>(); EXPECT_CALL(*mock_display_report, report_successful_setup_of_native_resources()) .Times(1); EXPECT_CALL(mock_egl, eglMakeCurrent(dummy_display, _, _, _)) .WillOnce(Return(EGL_TRUE)) .WillOnce(Return(EGL_TRUE)) .WillOnce(Return(EGL_FALSE)); EXPECT_CALL(*mock_display_report, report_successful_egl_make_current_on_construction()) .Times(0); EXPECT_CALL(*mock_display_report, report_successful_display_construction()) .Times(0); EXPECT_THROW({ mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, mock_display_report, mga::OverlayOptimization::disabled); }, std::runtime_error); } TEST_F(Display, logs_error_because_of_surface_creation_failure) { using namespace testing; auto const mock_display_report = std::make_shared>(); EXPECT_CALL(*mock_display_report, report_successful_setup_of_native_resources()) .Times(0); EXPECT_CALL(*mock_display_report, report_successful_egl_make_current_on_construction()) .Times(0); EXPECT_CALL(*mock_display_report, report_successful_display_construction()) .Times(0); EXPECT_CALL(mock_egl, eglCreatePbufferSurface(_,_,_)) .Times(1) .WillOnce(Return(EGL_NO_SURFACE)); EXPECT_THROW({ mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, mock_display_report, mga::OverlayOptimization::disabled); }, std::runtime_error); } TEST_F(Display, turns_on_db_at_construction_and_off_at_destruction) { stub_db_factory->with_next_config([](mtd::MockHwcConfiguration& mock_config) { testing::InSequence seq; EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, mir_power_mode_on)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, mir_power_mode_off)); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::disabled); } TEST_F(Display, first_power_on_is_not_fatal) //lp:1345533 { stub_db_factory->with_next_config([](mtd::MockHwcConfiguration& mock_config) { ON_CALL(mock_config, power_mode(mga::DisplayName::primary, mir_power_mode_on)) .WillByDefault(testing::Throw(std::runtime_error(""))); }); EXPECT_NO_THROW({ mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::disabled);}); } TEST_F(Display, catches_exceptions_when_turning_off_in_destructor) { stub_db_factory->with_next_config([](mtd::MockHwcConfiguration& mock_config) { testing::InSequence seq; EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, mir_power_mode_on)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, mir_power_mode_off)) .WillOnce(testing::Throw(std::runtime_error(""))); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::disabled); } TEST_F(Display, configures_power_modes) { stub_db_factory->with_next_config([](mtd::MockHwcConfiguration& mock_config) { using namespace testing; //external is not connected, so shouldn't adjust its power mode EXPECT_CALL(mock_config, power_mode(mga::DisplayName::external, _)) .Times(0); testing::InSequence seq; EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, mir_power_mode_on)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, mir_power_mode_standby)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, mir_power_mode_off)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, mir_power_mode_suspend)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, mir_power_mode_off)); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::disabled); auto configuration = display.configuration(); configuration->for_each_output([&](mg::UserDisplayConfigurationOutput& output) { //on by default if (output.id == primary_output_id) EXPECT_EQ(output.power_mode, mir_power_mode_on); output.power_mode = mir_power_mode_on; }); display.configure(*configuration); configuration->for_each_output([&](mg::UserDisplayConfigurationOutput& output) { if (output.id == primary_output_id) EXPECT_EQ(output.power_mode, mir_power_mode_on); output.power_mode = mir_power_mode_standby; }); display.configure(*configuration); configuration->for_each_output([&](mg::UserDisplayConfigurationOutput& output) { if (output.id == primary_output_id) EXPECT_EQ(output.power_mode, mir_power_mode_standby); output.power_mode = mir_power_mode_off; }); display.configure(*configuration); configuration->for_each_output([&](mg::UserDisplayConfigurationOutput& output) { if (output.id == primary_output_id) EXPECT_EQ(output.power_mode, mir_power_mode_off); output.power_mode = mir_power_mode_suspend; }); display.configure(*configuration); configuration->for_each_output([&](mg::UserDisplayConfigurationOutput& output) { if (output.id == primary_output_id) EXPECT_EQ(output.power_mode, mir_power_mode_suspend); }); } TEST_F(Display, returns_correct_config_with_one_connected_output_at_start) { using namespace testing; auto origin = geom::Point{0,0}; geom::Size pixel_size{344, 111}; geom::Size physical_size{4230, 2229}; double vrefresh{4442.32}; MirPixelFormat format{mir_pixel_format_abgr_8888}; stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { ON_CALL(mock_config, active_config_for(mga::DisplayName::primary)) .WillByDefault(Return(mtd::StubDisplayConfigurationOutput{ pixel_size, physical_size, format, vrefresh, true})); ON_CALL(mock_config, active_config_for(mga::DisplayName::external)) .WillByDefault(Return(mtd::StubDisplayConfigurationOutput{ pixel_size, physical_size, format, vrefresh, false})); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto config = display.configuration(); std::vector outputs; config->for_each_output([&](mg::DisplayConfigurationOutput const& disp_conf) { outputs.push_back(disp_conf); }); ASSERT_EQ(3u, outputs.size()); ASSERT_EQ(1u, outputs[0].modes.size()); auto& disp_mode = outputs[0].modes[0]; EXPECT_EQ(pixel_size, disp_mode.size); EXPECT_EQ(vrefresh, disp_mode.vrefresh_hz); EXPECT_EQ(primary_output_id, outputs[0].id); EXPECT_EQ(mg::DisplayConfigurationCardId{0}, outputs[0].card_id); EXPECT_TRUE(outputs[0].connected); EXPECT_TRUE(outputs[0].used); EXPECT_EQ(origin, outputs[0].top_left); EXPECT_EQ(0, outputs[0].current_mode_index); EXPECT_EQ(physical_size, outputs[0].physical_size_mm); EXPECT_FALSE(outputs[1].connected); EXPECT_FALSE(outputs[1].used); EXPECT_FALSE(outputs[2].connected); EXPECT_FALSE(outputs[2].used); } TEST_F(Display, returns_correct_config_with_external_and_primary_output_at_start) { using namespace testing; MirPixelFormat format{mir_pixel_format_abgr_8888}; auto origin = geom::Point{0,0}; geom::Size primary_pixel_size{344, 111}, external_pixel_size{75,5}; geom::Size primary_physical_size{4230, 2229}, external_physical_size{1, 22222}; double primary_vrefresh{4442.32}, external_vrefresh{0.00001}; stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { ON_CALL(mock_config, active_config_for(mga::DisplayName::primary)) .WillByDefault(Return(mtd::StubDisplayConfigurationOutput{ primary_pixel_size, primary_physical_size, format, primary_vrefresh, true})); ON_CALL(mock_config, active_config_for(mga::DisplayName::external)) .WillByDefault(Return(mtd::StubDisplayConfigurationOutput{external_output_id, external_pixel_size, external_physical_size, format, external_vrefresh, true})); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto config = display.configuration(); std::vector outputs; config->for_each_output([&](mg::DisplayConfigurationOutput const& disp_conf) { outputs.push_back(disp_conf); }); ASSERT_EQ(3u, outputs.size()); ASSERT_EQ(1u, outputs[0].modes.size()); auto& disp_mode = outputs[0].modes[0]; EXPECT_EQ(primary_pixel_size, disp_mode.size); EXPECT_EQ(primary_vrefresh, disp_mode.vrefresh_hz); EXPECT_EQ(primary_output_id, outputs[0].id); EXPECT_EQ(mg::DisplayConfigurationCardId{0}, outputs[0].card_id); EXPECT_TRUE(outputs[0].connected); EXPECT_TRUE(outputs[0].used); EXPECT_EQ(origin, outputs[0].top_left); EXPECT_EQ(0, outputs[0].current_mode_index); EXPECT_EQ(primary_physical_size, outputs[0].physical_size_mm); EXPECT_EQ(format, outputs[0].current_format); ASSERT_EQ(1, outputs[0].pixel_formats.size()); EXPECT_EQ(format, outputs[0].pixel_formats[0]); ASSERT_EQ(1u, outputs[1].modes.size()); disp_mode = outputs[1].modes[0]; EXPECT_EQ(external_pixel_size, disp_mode.size); EXPECT_EQ(external_vrefresh, disp_mode.vrefresh_hz); EXPECT_EQ(external_output_id, outputs[1].id); EXPECT_EQ(mg::DisplayConfigurationCardId{0}, outputs[1].card_id); EXPECT_TRUE(outputs[1].connected); EXPECT_TRUE(outputs[1].used); EXPECT_EQ(origin, outputs[1].top_left); EXPECT_EQ(0, outputs[1].current_mode_index); EXPECT_EQ(external_physical_size, outputs[1].physical_size_mm); EXPECT_EQ(format, outputs[1].current_format); ASSERT_EQ(1, outputs[1].pixel_formats.size()); EXPECT_EQ(format, outputs[1].pixel_formats[0]); EXPECT_FALSE(outputs[2].connected); EXPECT_FALSE(outputs[2].used); } TEST_F(Display, incorrect_display_configure_throws) { mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto config = display.configuration(); config->for_each_output([](mg::UserDisplayConfigurationOutput const& c){ c.current_format = mir_pixel_format_invalid; }); EXPECT_THROW({ display.configure(*config); }, std::logic_error); config->for_each_output([](mg::UserDisplayConfigurationOutput const& c){ c.current_format = mir_pixel_format_bgr_888; }); EXPECT_THROW({ display.configure(*config); }, std::logic_error); } //TODO: the list does not support fb target rotation yet TEST_F(Display, display_orientation_not_supported) { mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto config = display.configuration(); config->for_each_output([](mg::UserDisplayConfigurationOutput const& c){ c.orientation = mir_orientation_left; }); display.configure(*config); config = display.configuration(); config->for_each_output([](mg::UserDisplayConfigurationOutput const& c){ if (c.id == primary_output_id) EXPECT_EQ(mir_orientation_left, c.orientation); }); } //LP: #1535780 TEST_F(Display, can_configure_orientation) { mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto scale = 4.2f; auto config = display.configuration(); config->for_each_output([&scale](mg::UserDisplayConfigurationOutput const& c){ c.scale = scale; }); display.configure(*config); config = display.configuration(); config->for_each_output([&scale](mg::UserDisplayConfigurationOutput const& c){ if (c.id == primary_output_id) EXPECT_THAT(c.scale, testing::FloatEq(scale)); }); } TEST_F(Display, can_configure_form_factor) { mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto form_factor = mir_form_factor_tablet; auto config = display.configuration(); config->for_each_output([&form_factor](mg::UserDisplayConfigurationOutput const& c){ c.form_factor = form_factor; }); display.configure(*config); config = display.configuration(); config->for_each_output([&form_factor](mg::UserDisplayConfigurationOutput const& c){ if (c.id == primary_output_id) EXPECT_THAT(c.form_factor, testing::Eq(form_factor)); }); } TEST_F(Display, keeps_subscription_to_hotplug) { using namespace testing; std::shared_ptr subscription = std::make_shared(3433); auto use_count_before = subscription.use_count(); stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { EXPECT_CALL(mock_config, subscribe_to_config_changes(_,_)) .WillOnce(Return(subscription)); }); { mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); EXPECT_THAT(subscription.use_count(), Gt(use_count_before)); } EXPECT_THAT(subscription.use_count(), Eq(use_count_before)); } TEST_F(Display, will_requery_display_configuration_after_hotplug) { using namespace testing; std::shared_ptr subscription = std::make_shared(3433); std::function hotplug_fn = []{}; mtd::StubDisplayConfigurationOutput attribs1 { {33, 32}, {31, 35}, mir_pixel_format_abgr_8888, 0.44, true, }; mtd::StubDisplayConfigurationOutput attribs2 { {3, 3}, {1, 5}, mir_pixel_format_abgr_8888, 0.5544, true, }; stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { EXPECT_CALL(mock_config, subscribe_to_config_changes(_,_)) .WillOnce(DoAll(SaveArg<0>(&hotplug_fn), Return(subscription))); EXPECT_CALL(mock_config, active_config_for(mga::DisplayName::primary)) .Times(2) .WillOnce(testing::Return(attribs1)) .WillOnce(testing::Return(attribs2)); EXPECT_CALL(mock_config, active_config_for(mga::DisplayName::external)) .Times(2) .WillOnce(testing::Return(attribs1)) .WillOnce(testing::Return(attribs2)); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto config = display.configuration(); config->for_each_output([&](mg::UserDisplayConfigurationOutput const& c){ if (c.connected) EXPECT_THAT(c.modes[c.current_mode_index].size, Eq(attribs1.modes[attribs1.current_mode_index].size)); }); hotplug_fn(); config = display.configuration(); config = display.configuration(); config->for_each_output([&](mg::UserDisplayConfigurationOutput const& c){ if (c.connected) EXPECT_THAT(c.modes[c.current_mode_index].size, Eq(attribs2.modes[attribs2.current_mode_index].size)); }); } TEST_F(Display, returns_correct_dbs_with_external_and_primary_output_at_start) { using namespace testing; std::function hotplug_fn = []{}; bool external_connected = true; stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { ON_CALL(mock_config, active_config_for(mga::DisplayName::primary)) .WillByDefault(Return(mtd::StubDisplayConfigurationOutput{ primary_output_id, {20,20}, {4,4}, mir_pixel_format_abgr_8888, 50.0f, true})); ON_CALL(mock_config, active_config_for(mga::DisplayName::external)) .WillByDefault(Invoke([&](mga::DisplayName) { return mtd::StubDisplayConfigurationOutput{external_output_id, {20,20}, {4,4}, mir_pixel_format_abgr_8888, 50.0f, external_connected}; })); EXPECT_CALL(mock_config, subscribe_to_config_changes(_,_)) .WillOnce(DoAll(SaveArg<0>(&hotplug_fn), Return(std::make_shared('2')))); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto group_count = 0; auto db_count = 0; auto db_group_counter = [&](mg::DisplaySyncGroup& group) { group_count++; group.for_each_display_buffer([&](mg::DisplayBuffer&) {db_count++;}); }; auto conf = display.configuration(); display.configure(*conf); display.for_each_display_sync_group(db_group_counter); EXPECT_THAT(group_count, Eq(1)); EXPECT_THAT(db_count, Eq(2)); //hotplug external away external_connected = false; hotplug_fn(); conf = display.configuration(); display.configure(*conf); group_count = 0; db_count = 0; display.for_each_display_sync_group(db_group_counter); EXPECT_THAT(group_count, Eq(1)); EXPECT_THAT(db_count, Eq(1)); //hotplug external back external_connected = true; hotplug_fn(); conf = display.configuration(); display.configure(*conf); group_count = 0; db_count = 0; display.for_each_display_sync_group(db_group_counter); EXPECT_THAT(group_count, Eq(1)); EXPECT_THAT(db_count, Eq(2)); } TEST_F(Display, turns_external_display_on_with_hotplug) { using namespace testing; std::function hotplug_fn = []{}; bool external_connected = true; stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { EXPECT_CALL(mock_config, subscribe_to_config_changes(_,_)) .WillOnce(DoAll(SaveArg<0>(&hotplug_fn), Return(std::make_shared('2')))); ON_CALL(mock_config, active_config_for(mga::DisplayName::primary)) .WillByDefault(Return(mtd::StubDisplayConfigurationOutput{ {20,20}, {4,4}, mir_pixel_format_abgr_8888, 50.0f, true})); ON_CALL(mock_config, active_config_for(mga::DisplayName::external)) .WillByDefault(Invoke([&](mga::DisplayName) { return mtd::StubDisplayConfigurationOutput{ {20,20}, {4,4}, mir_pixel_format_abgr_8888, 50.0f, external_connected}; })); InSequence seq; //At construction, we expect external display to be connected EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, _)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::external, mir_power_mode_on)); //Unplugging EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, _)); //Hotplugging external back again EXPECT_CALL(mock_config, power_mode(mga::DisplayName::external, mir_power_mode_on)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, _)); //Expected after destructor EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, _)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::external, mir_power_mode_off)); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); //hotplug external away external_connected = false; hotplug_fn(); auto conf = display.configuration(); display.configure(*conf); //hotplug external back external_connected = true; hotplug_fn(); conf = display.configuration(); display.configure(*conf); } TEST_F(Display, configures_external_display) { using namespace testing; stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { ON_CALL(mock_config, active_config_for(mga::DisplayName::external)) .WillByDefault(Return(mtd::StubDisplayConfigurationOutput{ external_output_id, {0,0}, {0,0}, mir_pixel_format_abgr_8888, 0.0, true})); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::primary, _)) .Times(AnyNumber()); InSequence seq; EXPECT_CALL(mock_config, power_mode(mga::DisplayName::external, mir_power_mode_on)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::external, mir_power_mode_off)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::external, mir_power_mode_suspend)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::external, mir_power_mode_on)); EXPECT_CALL(mock_config, power_mode(mga::DisplayName::external, mir_power_mode_off)); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto configuration = display.configuration(); configuration->for_each_output([&](mg::UserDisplayConfigurationOutput& output) { output.power_mode = mir_power_mode_off; }); display.configure(*configuration); configuration->for_each_output([&](mg::UserDisplayConfigurationOutput& output) { output.power_mode = mir_power_mode_suspend; }); display.configure(*configuration); configuration->for_each_output([&](mg::UserDisplayConfigurationOutput& output) { output.power_mode = mir_power_mode_on; }); display.configure(*configuration); configuration->for_each_output([&](mg::UserDisplayConfigurationOutput& output) { output.power_mode = mir_power_mode_off; }); display.configure(*configuration); } TEST_F(Display, reports_vsync) { using namespace testing; std::function vsync_fn = [](mga::DisplayName){}; auto report = std::make_shared>(); EXPECT_CALL(*report, report_vsync(_)); stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { EXPECT_CALL(mock_config, subscribe_to_config_changes(_,_)) .WillOnce(DoAll(SaveArg<1>(&vsync_fn), Return(std::make_shared('2')))); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, report, mga::OverlayOptimization::enabled); vsync_fn(mga::DisplayName::primary); } TEST_F(Display, reports_correct_card_information) { using namespace testing; mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); int num_cards = 0; display.configuration()->for_each_card( [&](mg::DisplayConfigurationCard const& config) { EXPECT_THAT(config.max_simultaneous_outputs, Eq(3)); num_cards++; }); EXPECT_THAT(num_cards, Eq(1)); } TEST_F(Display, can_configure_positioning_of_dbs) { using namespace testing; auto new_location = geom::Point{493,999}; auto another_new_location = geom::Point{540,221}; mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto config = display.configuration(); config->for_each_output([&](mg::UserDisplayConfigurationOutput& disp_conf) { disp_conf.top_left = new_location; }); display.configure(*config); config = display.configuration(); config->for_each_output([&](mg::UserDisplayConfigurationOutput& disp_conf) { EXPECT_THAT(disp_conf.top_left, Eq(new_location)); disp_conf.top_left = another_new_location; }); config->for_each_output([&](mg::DisplayConfigurationOutput const& disp_conf) { EXPECT_THAT(disp_conf.top_left, Eq(another_new_location)); }); } //test for lp:1471858 TEST_F(Display, applying_orientation_after_hotplug) { using namespace testing; std::function hotplug_fn = []{}; bool external_connected = false; MirOrientation const orientation = mir_orientation_left; stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { ON_CALL(mock_config, active_config_for(mga::DisplayName::primary)) .WillByDefault(Return(mtd::StubDisplayConfigurationOutput{ primary_output_id, {20,20}, {4,4}, mir_pixel_format_abgr_8888, 50.0f, true})); ON_CALL(mock_config, active_config_for(mga::DisplayName::external)) .WillByDefault(Invoke([&](mga::DisplayName) { return mtd::StubDisplayConfigurationOutput{external_output_id, {20,20}, {4,4}, mir_pixel_format_abgr_8888, 50.0f, external_connected}; })); EXPECT_CALL(mock_config, subscribe_to_config_changes(_,_)) .WillOnce(DoAll(SaveArg<0>(&hotplug_fn), Return(std::make_shared('2')))); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); //hotplug external back external_connected = true; hotplug_fn(); auto config = display.configuration(); config->for_each_output([orientation](mg::UserDisplayConfigurationOutput& output) { output.orientation = orientation; }); display.configure(*config); display.for_each_display_sync_group([orientation](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([orientation](mg::DisplayBuffer& db) { EXPECT_THAT(db.orientation(), Eq(orientation)); }); }); } //lp:1484638 TEST_F(Display, display_buffers_respect_overlay_option) { using namespace testing; stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { ON_CALL(mock_config, active_config_for(_)) .WillByDefault(InvokeWithoutArgs([] { return mtd::StubDisplayConfigurationOutput{external_output_id, {20,20}, {4,4}, mir_pixel_format_abgr_8888, 50.0f, true}; })); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::disabled); display.for_each_display_sync_group([](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([](mg::DisplayBuffer& db) { EXPECT_FALSE(db.post_renderables_if_optimizable({std::make_shared()})); }); }); } TEST_F(Display, does_not_remove_dbs_when_enumerating_display_groups) { using namespace testing; std::function hotplug_fn = []{}; bool external_connected = true; stub_db_factory->with_next_config([&](mtd::MockHwcConfiguration& mock_config) { ON_CALL(mock_config, active_config_for(mga::DisplayName::primary)) .WillByDefault(Return(mtd::StubDisplayConfigurationOutput{ primary_output_id, {20,20}, {4,4}, mir_pixel_format_abgr_8888, 50.0f, true})); ON_CALL(mock_config, active_config_for(mga::DisplayName::external)) .WillByDefault(Invoke([&](mga::DisplayName) { return mtd::StubDisplayConfigurationOutput{external_output_id, {20,20}, {4,4}, mir_pixel_format_abgr_8888, 50.0f, external_connected}; })); EXPECT_CALL(mock_config, subscribe_to_config_changes(_,_)) .WillOnce(DoAll(SaveArg<0>(&hotplug_fn), Return(std::make_shared('2')))); }); mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); auto db_count = 0; auto db_group_counter = [&](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([&](mg::DisplayBuffer&) {db_count++;}); }; display.for_each_display_sync_group(db_group_counter); auto const expected_buffer_count = 2; EXPECT_THAT(db_count, Eq(expected_buffer_count)); //hotplug external away external_connected = false; hotplug_fn(); db_count = 0; display.for_each_display_sync_group(db_group_counter); EXPECT_THAT(db_count, Eq(expected_buffer_count)); } TEST_F(Display, enabling_virtual_output_updates_display_configuration) { using namespace testing; mga::Display display( stub_db_factory, stub_gl_program_factory, stub_gl_config, null_display_report, mga::OverlayOptimization::enabled); int const virtual_output_width{1234}; int const virtual_output_height{1345}; auto virtual_output = display.create_virtual_output(virtual_output_width, virtual_output_height); ASSERT_THAT(virtual_output.get(), NotNull()); virtual_output->enable(); bool found_matching_size{false}; display.configuration()->for_each_output([&found_matching_size](mg::DisplayConfigurationOutput const& output) { if(output.extents().size == geom::Size{virtual_output_width, virtual_output_height}) found_matching_size = true; }); EXPECT_TRUE(found_matching_size); } ./tests/unit-tests/graphics/android/test_hwc_layers.cpp0000644000015600001650000003305612676616125023524 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/hwc_layerlist.h" #include "mir/test/doubles/mock_buffer.h" #include "hwc_struct_helpers.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "mir/test/doubles/mock_renderable.h" #include "mir/test/fake_shared.h" #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mt=mir::test; namespace mtd=mir::test::doubles; namespace geom=mir::geometry; namespace { class HWCLayersTest : public ::testing::Test { public: virtual void SetUp() { using namespace testing; native_handle_1 = std::make_shared>(buffer_size); ON_CALL(*mock_buffer, size()) .WillByDefault(Return(buffer_size)); ON_CALL(*mock_buffer, native_buffer_handle()) .WillByDefault(Return(native_handle_1)); list = std::shared_ptr( static_cast( ::operator new(sizeof(hwc_display_contents_1_t) + sizeof(hwc_layer_1_t)))); list_index = 0; hwc_layer = &list->hwLayers[list_index]; type = mga::LayerType::gl_rendered; reset_expected_layer(); } void reset_expected_layer() { memset(&expected_layer, 0, sizeof(expected_layer)); expected_layer.compositionType = HWC_FRAMEBUFFER; expected_layer.hints = 0; expected_layer.flags = 0; expected_layer.handle = native_handle_1->handle(); expected_layer.transform = 0; expected_layer.blending = HWC_BLENDING_NONE; expected_layer.sourceCrop = { 0, 0, buffer_size.width.as_int(), buffer_size.height.as_int(), }; expected_layer.displayFrame = { screen_position.top_left.x.as_int(), screen_position.top_left.y.as_int(), screen_position.bottom_right().x.as_int(), screen_position.bottom_right().y.as_int()}; expected_layer.visibleRegionScreen = {1, ®ion}; expected_layer.acquireFenceFd = -1; expected_layer.releaseFenceFd = -1; expected_layer.planeAlpha = std::numeric_limits::max(); } mga::LayerType type; geom::Size buffer_size{333, 444}; geom::Rectangle screen_position{{9,8},buffer_size}; bool alpha_enabled{false}; std::shared_ptr native_handle_1; std::shared_ptr mock_buffer{std::make_shared>()}; std::shared_ptr list; hwc_layer_1_t* hwc_layer; size_t list_index; hwc_layer_1 expected_layer; hwc_rect_t region{0,0,0,0}; std::shared_ptr layer_adapter{std::make_shared()}; }; } TEST_F(HWCLayersTest, check_if_layer_needs_gl_render) { mga::HWCLayer layer(layer_adapter, list, list_index); hwc_layer->compositionType = HWC_OVERLAY; hwc_layer->flags = 0; EXPECT_FALSE(layer.needs_gl_render()); hwc_layer->compositionType = HWC_FRAMEBUFFER; hwc_layer->flags = HWC_SKIP_LAYER; EXPECT_TRUE(layer.needs_gl_render()); hwc_layer->compositionType = HWC_FRAMEBUFFER; hwc_layer->flags = 0; EXPECT_TRUE(layer.needs_gl_render()); } TEST_F(HWCLayersTest, move_layer_positions) { mga::HWCLayer layer(layer_adapter, list, list_index, type, screen_position, false, mock_buffer); mga::HWCLayer second_layer(std::move(layer)); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); } TEST_F(HWCLayersTest, change_layer_types) { expected_layer.compositionType = HWC_FRAMEBUFFER_TARGET; type = mga::LayerType::framebuffer_target; mga::HWCLayer layer( layer_adapter, list, list_index, mga::LayerType::framebuffer_target, screen_position, alpha_enabled, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); EXPECT_THROW({ layer.setup_layer( mga::LayerType::overlay, screen_position, alpha_enabled, mock_buffer); }, std::logic_error); expected_layer.compositionType = HWC_FRAMEBUFFER; layer.setup_layer(mga::LayerType::gl_rendered, screen_position, alpha_enabled, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); expected_layer.compositionType = HWC_FRAMEBUFFER; expected_layer.flags = HWC_SKIP_LAYER; layer.setup_layer(mga::LayerType::skip, screen_position, alpha_enabled, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); expected_layer.compositionType = HWC_FRAMEBUFFER; expected_layer.flags = 0; layer.setup_layer(mga::LayerType::gl_rendered, screen_position, alpha_enabled, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); } TEST_F(HWCLayersTest, apply_buffer_updates_to_framebuffer_layer) { EXPECT_CALL(*native_handle_1, copy_fence()) .Times(0); hwc_rect_t region = {0,0,buffer_size.width.as_int(), buffer_size.height.as_int()}; expected_layer.handle = native_handle_1->handle(); expected_layer.visibleRegionScreen = {1, ®ion}; expected_layer.sourceCrop = region; expected_layer.acquireFenceFd = -1; expected_layer.releaseFenceFd = -1; mga::HWCLayer layer(layer_adapter, list, list_index, type, screen_position, alpha_enabled, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); } TEST_F(HWCLayersTest, apply_buffer_updates_to_overlay_layers) { int fake_fence = 552; hwc_rect_t region = {0,0,buffer_size.width.as_int(), buffer_size.height.as_int()}; expected_layer.compositionType = HWC_OVERLAY; expected_layer.handle = native_handle_1->handle(); expected_layer.visibleRegionScreen = {1, ®ion}; expected_layer.sourceCrop = region; expected_layer.acquireFenceFd = -1; expected_layer.releaseFenceFd = -1; mga::HWCLayer layer(layer_adapter, list, list_index, type, screen_position, alpha_enabled, mock_buffer); hwc_layer->compositionType = HWC_OVERLAY; EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); //set_acquirefence should set the fence //mir must reset releaseFenceFd to -1 hwc_layer->releaseFenceFd = fake_fence; EXPECT_CALL(*native_handle_1, copy_fence()) .Times(1) .WillOnce(testing::Return(fake_fence)); expected_layer.acquireFenceFd = fake_fence; layer.set_acquirefence(); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); } TEST_F(HWCLayersTest, apply_buffer_updates_to_fbtarget) { int fake_fence = 552; hwc_rect_t region = {0,0,buffer_size.width.as_int(), buffer_size.height.as_int()}; expected_layer.compositionType = HWC_FRAMEBUFFER_TARGET; expected_layer.handle = native_handle_1->handle(); expected_layer.visibleRegionScreen = {1, ®ion}; expected_layer.sourceCrop = region; expected_layer.acquireFenceFd = -1; expected_layer.releaseFenceFd = -1; mga::HWCLayer layer( layer_adapter, list, list_index, mga::LayerType::framebuffer_target, screen_position, alpha_enabled, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); //mir must reset releaseFenceFd to -1 if hwc has changed it hwc_layer->releaseFenceFd = fake_fence; EXPECT_CALL(*native_handle_1, copy_fence()) .Times(1) .WillOnce(testing::Return(fake_fence)); expected_layer.acquireFenceFd = fake_fence; layer.set_acquirefence(); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); //hwc will set this to -1 to acknowledge that its adopted this layer's fence. //multiple sequential updates to the same layer must not set the acquireFenceFds on the calls //after the first. hwc_layer->acquireFenceFd = -1; expected_layer.acquireFenceFd = -1; layer.setup_layer( mga::LayerType::framebuffer_target, screen_position, false, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); } TEST_F(HWCLayersTest, buffer_fence_updates) { int fake_fence = 552; EXPECT_CALL(*native_handle_1, update_usage(fake_fence, mga::BufferAccess::read)) .Times(1); mga::HWCLayer layer( layer_adapter, list, list_index, mga::LayerType::framebuffer_target, screen_position, alpha_enabled, mock_buffer); hwc_layer->releaseFenceFd = fake_fence; layer.release_buffer(); } TEST_F(HWCLayersTest, check_layer_defaults_and_alpha) { using namespace testing; hwc_rect_t crop { 0,0, buffer_size.width.as_int(), buffer_size.height.as_int() }; hwc_rect_t screen_pos { screen_position.top_left.x.as_int(), screen_position.top_left.y.as_int(), screen_position.bottom_right().x.as_int(), screen_position.bottom_right().y.as_int() }; hwc_region_t visible_region {1, &screen_pos}; hwc_layer_1 expected_layer; expected_layer.compositionType = HWC_FRAMEBUFFER; expected_layer.hints = 0; expected_layer.flags = 0; expected_layer.handle = native_handle_1->handle(); expected_layer.transform = 0; expected_layer.blending = HWC_BLENDING_PREMULT; expected_layer.sourceCrop = crop; expected_layer.displayFrame = screen_pos; expected_layer.visibleRegionScreen = visible_region; expected_layer.acquireFenceFd = -1; expected_layer.releaseFenceFd = -1; expected_layer.planeAlpha = std::numeric_limits::max(); mga::HWCLayer layer(layer_adapter, list, list_index); layer.setup_layer( mga::LayerType::gl_rendered, screen_position, true, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); expected_layer.blending = HWC_BLENDING_NONE; layer.setup_layer( mga::LayerType::gl_rendered, screen_position, false, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); } //HWC 1.3 and later started using floats for the sourceCrop field, and ints for the displayFrame TEST_F(HWCLayersTest, has_float_sourcecrop) { hwc_frect_t crop { 0.0f, 0.0f, buffer_size.width.as_float(), buffer_size.height.as_float() }; hwc_rect_t screen_pos { screen_position.top_left.x.as_int(), screen_position.top_left.y.as_int(), screen_position.bottom_right().x.as_int(), screen_position.bottom_right().y.as_int() }; hwc_region_t visible_region {1, &screen_pos}; hwc_layer_1 expected_layer; expected_layer.compositionType = HWC_FRAMEBUFFER; expected_layer.hints = 0; expected_layer.flags = 0; expected_layer.handle = native_handle_1->handle(); expected_layer.transform = 0; expected_layer.blending = HWC_BLENDING_PREMULT; expected_layer.sourceCropf = crop; expected_layer.displayFrame = screen_pos; expected_layer.visibleRegionScreen = visible_region; expected_layer.acquireFenceFd = -1; expected_layer.releaseFenceFd = -1; expected_layer.planeAlpha = std::numeric_limits::max(); mga::HWCLayer layer(std::make_shared(), list, list_index); layer.setup_layer( mga::LayerType::gl_rendered, screen_position, true, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLayer(expected_layer)); } TEST_F(HWCLayersTest, list_can_be_offset_for_nonorigin_displays) { using namespace testing; geom::Point offset{199, 299}; hwc_rect_t crop { 0,0, buffer_size.width.as_int(), buffer_size.height.as_int() }; hwc_rect_t screen_pos { screen_position.top_left.x.as_int(), screen_position.top_left.y.as_int(), screen_position.bottom_right().x.as_int(), screen_position.bottom_right().y.as_int() }; hwc_region_t visible_region {1, &screen_pos}; hwc_layer_1 expected_layer; expected_layer.compositionType = HWC_FRAMEBUFFER; expected_layer.hints = 0; expected_layer.flags = 0; expected_layer.handle = native_handle_1->handle(); expected_layer.transform = 0; expected_layer.blending = HWC_BLENDING_PREMULT; expected_layer.sourceCrop = crop; expected_layer.displayFrame = screen_pos; expected_layer.visibleRegionScreen = visible_region; expected_layer.acquireFenceFd = -1; expected_layer.releaseFenceFd = -1; expected_layer.planeAlpha = std::numeric_limits::max(); mga::HWCLayer layer(layer_adapter, list, list_index); layer.setup_layer( mga::LayerType::gl_rendered, screen_position, true, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); expected_layer.blending = HWC_BLENDING_NONE; layer.setup_layer( mga::LayerType::gl_rendered, screen_position, false, mock_buffer); EXPECT_THAT(*hwc_layer, MatchesLegacyLayer(expected_layer)); } ./tests/unit-tests/graphics/android/test_display_group.cpp0000644000015600001650000001112212676616125024233 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/configurable_display_buffer.h" #include "src/platforms/android/server/display_group.h" #include "src/platforms/android/server/display_device_exceptions.h" #include "mir/test/doubles/mock_display_device.h" #include "mir/test/doubles/stub_renderable_list_compositor.h" #include "mir/test/doubles/stub_swapping_gl_context.h" #include "mir/test/fake_shared.h" #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mtd=mir::test::doubles; namespace mt=mir::test; namespace { struct StubConfigurableDB : mga::ConfigurableDisplayBuffer, mg::NativeDisplayBuffer { mir::geometry::Rectangle view_area() const override { return {}; } bool post_renderables_if_optimizable(mg::RenderableList const&) override { return false; } MirOrientation orientation() const override { return mir_orientation_normal; } mg::NativeDisplayBuffer* native_display_buffer() override { return this; } void configure(MirPowerMode, MirOrientation, mir::geometry::Displacement) override {} mga::DisplayContents contents() override { return mga::DisplayContents{mga::DisplayName::primary, list, offset, context, compositor}; } MirPowerMode power_mode() const override { return mir_power_mode_on; } mga::LayerList mutable list{std::make_shared(), {}, offset}; mtd::StubRenderableListCompositor mutable compositor; mtd::StubSwappingGLContext mutable context; mir::geometry::Displacement offset; }; } TEST(DisplayGroup, db_additions_and_removals) { using namespace testing; NiceMock mock_device; InSequence seq; EXPECT_CALL(mock_device, commit(SizeIs(1))); EXPECT_CALL(mock_device, commit(SizeIs(2))); EXPECT_CALL(mock_device, commit(SizeIs(2))); EXPECT_CALL(mock_device, commit(SizeIs(1))); mga::DisplayGroup group(mt::fake_shared(mock_device), std::unique_ptr{new StubConfigurableDB}); group.post(); //hwc api does not allow removing primary EXPECT_THROW({ group.remove(mga::DisplayName::primary); }, std::logic_error); EXPECT_FALSE(group.display_present(mga::DisplayName::external)); group.add(mga::DisplayName::external, std::unique_ptr(new StubConfigurableDB)); EXPECT_TRUE(group.display_present(mga::DisplayName::external)); group.post(); //can replace primary or external group.add(mga::DisplayName::primary, std::unique_ptr(new StubConfigurableDB)); group.add(mga::DisplayName::external, std::unique_ptr(new StubConfigurableDB)); group.post(); group.remove(mga::DisplayName::external); group.remove(mga::DisplayName::external); group.post(); } //lp: 1474891, 1498550: If the driver is processing the external display in set, //and it gets a hotplug event removing the external display, set() will throw, which we should ignore TEST(DisplayGroup, group_ignores_throws_during_hotplug) { using namespace testing; NiceMock mock_device; mga::DisplayGroup group(mt::fake_shared(mock_device), std::make_unique()); EXPECT_CALL(mock_device, commit(_)) .WillOnce(Throw(mga::DisplayDisconnectedException(""))) .WillOnce(Throw(std::runtime_error(""))); EXPECT_NO_THROW({group.post();}); EXPECT_THROW({ group.post(); }, std::runtime_error); } TEST(DisplayGroup, calls_error_handler_on_external_display_error) { using namespace testing; NiceMock mock_device; bool error_handler_called{false}; mga::DisplayGroup group(mt::fake_shared(mock_device), std::make_unique(), [&error_handler_called] { error_handler_called = true; }); EXPECT_CALL(mock_device, commit(_)) .WillOnce(Throw(mga::ExternalDisplayError(""))); EXPECT_NO_THROW({group.post();}); EXPECT_TRUE(error_handler_called); } ./tests/unit-tests/graphics/android/test_gralloc.cpp0000644000015600001650000002111412676616157023004 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/gralloc_module.h" #include "src/platforms/android/server/device_quirks.h" #include "src/platforms/android/server/cmdstream_sync_factory.h" #include "mir/options/program_option.h" #include "native_buffer.h" #include "mir/test/doubles/mock_android_alloc_device.h" #include "mir/test/doubles/mock_egl.h" #include #include #include namespace mg = mir::graphics; namespace mga = mir::graphics::android; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; using namespace testing; struct Gralloc : Test { Gralloc(): mock_alloc_device(std::make_shared>()), gralloc(std::make_shared( mock_alloc_device, sync_factory, std::make_shared(mga::PropertiesOps{}))), pf(mir_pixel_format_abgr_8888), size{300, 200} { } mtd::MockEGL mock_egl; std::shared_ptr sync_factory{std::make_shared()}; std::shared_ptr mock_alloc_device; std::shared_ptr gralloc; MirPixelFormat pf; geom::Size size; mg::BufferUsage usage = mg::BufferUsage::hardware; int const fb_usage_flags {GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB}; int const fb_usage_flags_broken_device {GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE}; int const hw_usage_flags {GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER}; int const sw_usage_flags {GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE}; }; TEST_F(Gralloc, resource_type_test_fail_ret) { EXPECT_CALL(*mock_alloc_device, alloc_interface(_,_,_,_,_,_,_)) .WillOnce(DoAll( SetArgPointee<5>(mock_alloc_device->buffer_handle), SetArgPointee<6>(size.width.as_uint32_t()*4), Return(-1))); EXPECT_THROW({ gralloc->alloc_buffer(size, pf, usage); }, std::runtime_error); } TEST_F(Gralloc, resource_type_test_fail_stride) { EXPECT_CALL(*mock_alloc_device, alloc_interface(_,_,_,_,_,_,_)) .WillOnce(DoAll( SetArgPointee<5>(mock_alloc_device->buffer_handle), SetArgPointee<6>(0), Return(0))); EXPECT_THROW({ gralloc->alloc_buffer(size, pf, usage); }, std::runtime_error); } TEST_F(Gralloc, resource_type_test_fail_null_handle) { EXPECT_CALL(*mock_alloc_device, alloc_interface(_,_,_,_,_,_,_)) .WillOnce(DoAll( SetArgPointee<5>(nullptr), SetArgPointee<6>(size.width.as_uint32_t()*4), Return(0))); EXPECT_THROW({ gralloc->alloc_buffer(size, pf, usage); }, std::runtime_error); } TEST_F(Gralloc, resource_type_test_proper_alloc_is_used) { EXPECT_CALL(*mock_alloc_device, alloc_interface(mock_alloc_device.get(),_,_,_,_,_,_)); EXPECT_CALL(*mock_alloc_device, free_interface(mock_alloc_device.get(),_)); gralloc->alloc_buffer(size, pf, usage); } TEST_F(Gralloc, resource_type_test_deleter_deletes_correct_handle) { EXPECT_CALL(*mock_alloc_device, alloc_interface(_,_,_,_,_,_,_)) .WillOnce(DoAll( SetArgPointee<5>(mock_alloc_device->buffer_handle), SetArgPointee<6>(size.width.as_uint32_t()*4), Return(0))); EXPECT_CALL(*mock_alloc_device, free_interface(_,mock_alloc_device->buffer_handle)); gralloc->alloc_buffer(size, pf, usage); } TEST_F(Gralloc, adaptor_gralloc_format_conversion_abgr8888) { EXPECT_CALL(*mock_alloc_device, alloc_interface(_,_,_,HAL_PIXEL_FORMAT_RGBA_8888,_,_,_)); EXPECT_CALL(*mock_alloc_device, free_interface(_,_)); gralloc->alloc_buffer(size, pf, usage); } TEST_F(Gralloc, adaptor_gralloc_dimension_conversion) { int w = size.width.as_uint32_t(); int h = size.height.as_uint32_t(); EXPECT_CALL(*mock_alloc_device, alloc_interface(_,w,h,_,_,_,_)); EXPECT_CALL(*mock_alloc_device, free_interface(_,_)); gralloc->alloc_buffer(size, pf, usage ); } TEST_F(Gralloc, adaptor_gralloc_hw_usage_conversion) { EXPECT_CALL(*mock_alloc_device, alloc_interface(_,_,_,_,hw_usage_flags,_,_)); EXPECT_CALL(*mock_alloc_device, free_interface(_,_) ); gralloc->alloc_buffer(size, pf, usage ); } TEST_F(Gralloc, adaptor_gralloc_sw_usage_conversion) { EXPECT_CALL(*mock_alloc_device, alloc_interface(_,_,_,_,sw_usage_flags,_,_)); EXPECT_CALL(*mock_alloc_device, free_interface(_,_)); gralloc->alloc_buffer(size, pf, mg::BufferUsage::software); } TEST_F(Gralloc, adaptor_gralloc_usage_conversion_fb_gles) { using namespace testing; EXPECT_CALL(*mock_alloc_device, alloc_interface(_,_,_,_,fb_usage_flags,_,_)); EXPECT_CALL(*mock_alloc_device, free_interface(_,_)); gralloc->alloc_framebuffer(size, pf); } TEST_F(Gralloc, adaptor_gralloc_usage_conversion_fb_gles_with_quirk) { using namespace testing; mir::options::ProgramOption options; boost::program_options::options_description description; description.add_options()("fb-ion-heap", boost::program_options::value()->default_value(true), ""); std::array args { "progname", "--fb-ion-heap", "false"}; options.parse_arguments(description, args.size(), args.data()); auto quirks = std::make_shared(mga::PropertiesOps{}, options); gralloc = std::make_shared(mock_alloc_device, sync_factory, quirks); EXPECT_CALL(*mock_alloc_device, alloc_interface(_,_,_,_,fb_usage_flags_broken_device,_,_)); EXPECT_CALL(*mock_alloc_device, free_interface(_,_)); gralloc->alloc_framebuffer(size, pf); } TEST_F(Gralloc, handle_size_is_correct) { auto native_handle = gralloc->alloc_buffer(size, pf, usage); auto anwb = native_handle->anwb(); EXPECT_EQ(static_cast(size.width.as_uint32_t()), anwb->width); EXPECT_EQ(static_cast(size.height.as_uint32_t()), anwb->height); EXPECT_EQ(static_cast(mock_alloc_device->fake_stride), anwb->stride); } TEST_F(Gralloc, handle_buffer_pf_is_converted_to_android_abgr_8888) { auto native_handle = gralloc->alloc_buffer(size, pf, usage); auto anwb = native_handle->anwb(); EXPECT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, anwb->format); } TEST_F(Gralloc, handle_buffer_pf_is_converted_to_android_xbgr_8888) { auto native_handle = gralloc->alloc_buffer(size, mir_pixel_format_xbgr_8888, usage); auto anwb = native_handle->anwb(); EXPECT_EQ(HAL_PIXEL_FORMAT_RGBX_8888, anwb->format); } TEST_F(Gralloc, handle_buffer_usage_is_converted_to_android_use_hw) { auto native_handle = gralloc->alloc_buffer(size, pf, usage); auto anwb = native_handle->anwb(); EXPECT_EQ(hw_usage_flags, anwb->usage); } TEST_F(Gralloc, handle_buffer_usage_is_converted_to_android_use_fb) { auto native_handle = gralloc->alloc_framebuffer(size, pf); auto anwb = native_handle->anwb(); EXPECT_EQ(fb_usage_flags, anwb->usage); } TEST_F(Gralloc, handle_has_strong_reference_for_c_drivers) { auto native_handle = gralloc->alloc_buffer(size, pf, usage); auto anwb = native_handle->anwb(); ASSERT_NE(nullptr, anwb->common.incRef); ASSERT_NE(nullptr, anwb->common.decRef); anwb->common.incRef(&anwb->common); anwb->common.decRef(&anwb->common); } TEST_F(Gralloc, handle_has_right_magic) { int magic = ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r'); /* magic value shared by JB and ICS */ auto native_handle = gralloc->alloc_buffer(size, pf, usage); auto anwb = native_handle->anwb(); EXPECT_EQ(magic, anwb->common.magic); } TEST_F(Gralloc, handle_has_version) { int version = sizeof(ANativeWindowBuffer); /* version value shared by JB and ICS */ auto native_handle = gralloc->alloc_buffer(size, pf, usage); auto anwb = native_handle->anwb(); EXPECT_EQ(version, anwb->common.version); } ./tests/unit-tests/graphics/android/test_graphics_platform.cpp0000644000015600001650000000416612676616125025070 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Guest * Kevin DuBois */ #include "mir/graphics/platform.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/buffer_properties.h" #include "mir/graphics/platform_ipc_operations.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "mir/options/program_option.h" #include "mir/test/doubles/mock_android_hw.h" #include "mir/logging/dumb_console_logger.h" #include namespace mg = mir::graphics; namespace mgm = mg::mesa; namespace ml = mir::logging; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; namespace mo = mir::options; class GraphicsPlatform : public ::testing::Test { public: GraphicsPlatform() : logger(std::make_shared()) { } std::shared_ptr create_platform() { return create_host_platform( std::make_shared(), std::make_shared(), mir::report::null_display_report()); } std::shared_ptr logger; ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; ::testing::NiceMock hw_access_mock; }; #include "../test_graphics_platform.h" ./tests/unit-tests/graphics/android/test_hwc_logger.cpp0000644000015600001650000002714212676616125023503 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/hwc_loggers.h" #include #include #include #include namespace mga=mir::graphics::android; namespace { struct HwcLogger : public ::testing::Test { void fill_display_list(hwc_display_contents_1_t* display_list) { display_list->numHwLayers = num_layers; display_list->hwLayers[0].compositionType = HWC_OVERLAY; display_list->hwLayers[0].flags = 0; display_list->hwLayers[0].handle = &native_handle1; display_list->hwLayers[0].transform = HWC_TRANSFORM_ROT_90; display_list->hwLayers[0].blending = HWC_BLENDING_NONE; display_list->hwLayers[0].displayFrame = {1, 1, 2, 1}; display_list->hwLayers[0].sourceCrop = {3, 2, 5, 3}; display_list->hwLayers[0].acquireFenceFd = fake_fence[0]; display_list->hwLayers[0].releaseFenceFd = fake_fence[1]; display_list->hwLayers[1].compositionType = HWC_FRAMEBUFFER; display_list->hwLayers[1].flags = 0; display_list->hwLayers[1].handle = &native_handle2; display_list->hwLayers[1].transform = HWC_TRANSFORM_ROT_180; display_list->hwLayers[1].blending = HWC_BLENDING_PREMULT; display_list->hwLayers[1].displayFrame = {8, 5, 13, 8}; display_list->hwLayers[1].sourceCrop = {21, 13, 34, 21}; display_list->hwLayers[1].acquireFenceFd = fake_fence[2]; display_list->hwLayers[1].releaseFenceFd = fake_fence[3]; display_list->hwLayers[2].compositionType = HWC_FRAMEBUFFER; display_list->hwLayers[2].flags = HWC_SKIP_LAYER; display_list->hwLayers[2].handle = &native_handle3; display_list->hwLayers[2].transform = HWC_TRANSFORM_ROT_270; display_list->hwLayers[2].blending = HWC_BLENDING_COVERAGE; display_list->hwLayers[2].displayFrame = {55, 34, 89, 55}; display_list->hwLayers[2].sourceCrop = {144, 89, 233, 144}; display_list->hwLayers[2].acquireFenceFd = fake_fence[4]; display_list->hwLayers[2].releaseFenceFd = fake_fence[5]; display_list->hwLayers[3].compositionType = HWC_FRAMEBUFFER_TARGET; display_list->hwLayers[3].flags = 0; display_list->hwLayers[3].handle = &native_handle4; display_list->hwLayers[3].transform = 0; display_list->hwLayers[3].blending = HWC_BLENDING_NONE; display_list->hwLayers[3].displayFrame = {377, 233, 610, 337}; display_list->hwLayers[3].sourceCrop = {987, 610, 1597, 987}; display_list->hwLayers[3].acquireFenceFd = fake_fence[6]; display_list->hwLayers[3].releaseFenceFd = fake_fence[7]; } HwcLogger() : num_layers{4}, primary_list{std::shared_ptr( static_cast( ::operator new(sizeof(hwc_display_contents_1_t) + (num_layers * sizeof(hwc_layer_1_t)))))}, external_list{std::shared_ptr( static_cast( ::operator new(sizeof(hwc_display_contents_1_t) + (num_layers * sizeof(hwc_layer_1_t)))))} { default_cout_buffer = std::cout.rdbuf(); std::cout.rdbuf(test_stream.rdbuf()); fill_display_list(primary_list.get()); fill_display_list(external_list.get()); display_list[HWC_DISPLAY_PRIMARY] = primary_list.get(); display_list[HWC_DISPLAY_EXTERNAL] = external_list.get(); display_list[HWC_DISPLAY_VIRTUAL] = nullptr; }; virtual ~HwcLogger() { std::cout.rdbuf(default_cout_buffer); } decltype(std::cout.rdbuf()) default_cout_buffer; std::ostringstream test_stream; size_t const num_layers; std::shared_ptr const primary_list; std::shared_ptr const external_list; std::array display_list; std::array const fake_fence{ {4,5,6,7,8,9,10,11} }; native_handle_t native_handle1; native_handle_t native_handle2; native_handle_t native_handle3; native_handle_t native_handle4; }; } TEST_F(HwcLogger, report_pre_prepare) { std::stringstream str; str << "before prepare():" << std::endl << " # | display | Type | pos {l,t,r,b} | crop {l,t,r,b} | transform | blending | " << std::endl << " 0 | primary | OVERLAY | { 1, 1, 2, 1} | { 3, 2, 5, 3} | ROT_90 | NONE | " << std::endl << " 1 | primary | GL_RENDER | { 8, 5, 13, 8} | { 21, 13, 34, 21} | ROT_180 | PREMULT | " << std::endl << " 2 | primary | FORCE_GL | { 55, 34, 89, 55} | { 144, 89, 233, 144} | ROT_270 | COVERAGE | " << std::endl << " 3 | primary | FB_TARGET | { 377, 233, 610, 337} | { 987, 610,1597, 987} | NONE | NONE | " << std::endl << " 0 | external | OVERLAY | { 1, 1, 2, 1} | { 3, 2, 5, 3} | ROT_90 | NONE | " << std::endl << " 1 | external | GL_RENDER | { 8, 5, 13, 8} | { 21, 13, 34, 21} | ROT_180 | PREMULT | " << std::endl << " 2 | external | FORCE_GL | { 55, 34, 89, 55} | { 144, 89, 233, 144} | ROT_270 | COVERAGE | " << std::endl << " 3 | external | FB_TARGET | { 377, 233, 610, 337} | { 987, 610,1597, 987} | NONE | NONE | " << std::endl; mga::HwcFormattedLogger logger; logger.set_version(mga::HwcVersion::hwc12); logger.report_list_submitted_to_prepare(display_list); EXPECT_EQ(str.str(), test_stream.str()); } TEST_F(HwcLogger, report_post_prepare) { std::stringstream str; str << "after prepare():" << std::endl << " # | display | Type | " << std::endl << " 0 | primary | OVERLAY | " << std::endl << " 1 | primary | GL_RENDER | " << std::endl << " 2 | primary | FORCE_GL | " << std::endl << " 3 | primary | FB_TARGET | " << std::endl << " 0 | external | OVERLAY | " << std::endl << " 1 | external | GL_RENDER | " << std::endl << " 2 | external | FORCE_GL | " << std::endl << " 3 | external | FB_TARGET | " << std::endl; mga::HwcFormattedLogger logger; logger.report_prepare_done(display_list); EXPECT_EQ(str.str(), test_stream.str()); } TEST_F(HwcLogger, report_set) { std::stringstream str; str << "set list():" << std::endl << " # | display | Type | handle | acquireFenceFd" << std::endl << " 0 | primary | OVERLAY | " << &native_handle1 << " | " << fake_fence[0] << std::endl << " 1 | primary | GL_RENDER | " << &native_handle2 << " | " << fake_fence[2] << std::endl << " 2 | primary | FORCE_GL | " << &native_handle3 << " | " << fake_fence[4] << std::endl << " 3 | primary | FB_TARGET | " << &native_handle4 << " | " << fake_fence[6] << std::endl << " 0 | external | OVERLAY | " << &native_handle1 << " | " << fake_fence[0] << std::endl << " 1 | external | GL_RENDER | " << &native_handle2 << " | " << fake_fence[2] << std::endl << " 2 | external | FORCE_GL | " << &native_handle3 << " | " << fake_fence[4] << std::endl << " 3 | external | FB_TARGET | " << &native_handle4 << " | " << fake_fence[6] << std::endl; mga::HwcFormattedLogger logger; logger.report_set_list(display_list); EXPECT_EQ(str.str(), test_stream.str()); } TEST_F(HwcLogger, report_post_set) { std::stringstream str; str << "after set():" << std::endl << " # | display | releaseFenceFd" << std::endl << " 0 | primary | " << fake_fence[1] << std::endl << " 1 | primary | " << fake_fence[3] << std::endl << " 2 | primary | " << fake_fence[5] << std::endl << " 3 | primary | " << fake_fence[7] << std::endl << " 0 | external | " << fake_fence[1] << std::endl << " 1 | external | " << fake_fence[3] << std::endl << " 2 | external | " << fake_fence[5] << std::endl << " 3 | external | " << fake_fence[7] << std::endl; mga::HwcFormattedLogger logger; logger.report_set_done(display_list); EXPECT_EQ(str.str(), test_stream.str()); } TEST_F(HwcLogger, report_optimization) { std::stringstream enabled_str; std::stringstream disabled_str; enabled_str << "HWC overlay optimizations are ON" << std::endl; disabled_str << "HWC overlay optimizations are OFF" << std::endl; mga::HwcFormattedLogger logger; logger.report_overlay_optimization(mga::OverlayOptimization::enabled); EXPECT_EQ(enabled_str.str(), test_stream.str()); test_stream.str(""); test_stream.clear(); logger.report_overlay_optimization(mga::OverlayOptimization::disabled); EXPECT_EQ(disabled_str.str(), test_stream.str()); } TEST_F(HwcLogger, report_vsync_on) { std::stringstream str; str << "HWC: vsync signal on" << std::endl; mga::HwcFormattedLogger logger; logger.report_vsync_on(); EXPECT_EQ(str.str(), test_stream.str()); } TEST_F(HwcLogger, report_vsync_off) { std::stringstream str; str << "HWC: vsync signal off" << std::endl; mga::HwcFormattedLogger logger; logger.report_vsync_off(); EXPECT_EQ(str.str(), test_stream.str()); } TEST_F(HwcLogger, report_display_off) { std::stringstream str; str << "HWC: display off" << std::endl; mga::HwcFormattedLogger logger; logger.report_display_off(); EXPECT_EQ(str.str(), test_stream.str()); } TEST_F(HwcLogger, report_display_on) { std::stringstream str; str << "HWC: display on" << std::endl; mga::HwcFormattedLogger logger; logger.report_display_on(); EXPECT_EQ(str.str(), test_stream.str()); } TEST_F(HwcLogger, report_legacy_fb) { std::stringstream str; str << "Legacy FB module" << std::endl; mga::HwcFormattedLogger logger; logger.report_legacy_fb_module(); EXPECT_EQ(str.str(), test_stream.str()); } TEST_F(HwcLogger, report_hwc_version) { std::stringstream str; str << "HWC version 1.0" << std::endl << "HWC version 1.1" << std::endl << "HWC version 1.2" << std::endl << "HWC version 1.3" << std::endl << "HWC version 1.4" << std::endl; mga::HwcFormattedLogger logger; logger.report_hwc_version(mga::HwcVersion::hwc10); logger.report_hwc_version(mga::HwcVersion::hwc11); logger.report_hwc_version(mga::HwcVersion::hwc12); logger.report_hwc_version(mga::HwcVersion::hwc13); logger.report_hwc_version(mga::HwcVersion::hwc14); EXPECT_EQ(str.str(), test_stream.str()); } TEST_F(HwcLogger, report_power_mode) { std::stringstream str; str << "HWC: power mode: off" << std::endl << "HWC: power mode: doze" << std::endl << "HWC: power mode: doze(suspend)" << std::endl << "HWC: power mode: on(normal)" << std::endl; mga::HwcFormattedLogger logger; logger.report_power_mode(mga::PowerMode::off); logger.report_power_mode(mga::PowerMode::doze); logger.report_power_mode(mga::PowerMode::doze_suspend); logger.report_power_mode(mga::PowerMode::normal); EXPECT_EQ(str.str(), test_stream.str()); } ./tests/unit-tests/graphics/android/test_egl_sync_extensions.cpp0000644000015600001650000000444012676616125025441 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #include "egl_sync_extensions.h" #include "mir/test/doubles/mock_egl.h" #include #include #include namespace mg = mir::graphics; namespace mtd = mir::test::doubles; using namespace testing; typedef mtd::MockEGL::generic_function_pointer_t func_ptr_t; class EGLSyncExtensions : public ::testing::Test { protected: virtual void SetUp() { } testing::NiceMock mock_egl; }; TEST_F(EGLSyncExtensions, constructor_throws_if_egl_create_sync_not_supported) { ON_CALL(mock_egl, eglGetProcAddress(StrEq("eglCreateSyncKHR"))) .WillByDefault(Return(reinterpret_cast(0))); EXPECT_THROW({ mg::EGLSyncExtensions extensions; }, std::runtime_error); } TEST_F(EGLSyncExtensions, constructor_throws_if_egl_destroy_sync_not_supported) { ON_CALL(mock_egl, eglGetProcAddress(StrEq("eglDestroySyncKHR"))) .WillByDefault(Return(reinterpret_cast(0))); EXPECT_THROW({ mg::EGLSyncExtensions extensions; }, std::runtime_error); } TEST_F(EGLSyncExtensions, constructor_throws_if_egl_wait_sync_not_supported) { ON_CALL(mock_egl, eglGetProcAddress(StrEq("eglClientWaitSyncKHR"))) .WillByDefault(Return(reinterpret_cast(0))); EXPECT_THROW({ mg::EGLSyncExtensions extensions; }, std::runtime_error); } TEST_F(EGLSyncExtensions, sync_success_has_sane_function_hooks) { mg::EGLSyncExtensions extensions; EXPECT_NE(nullptr, extensions.eglCreateSyncKHR); EXPECT_NE(nullptr, extensions.eglDestroySyncKHR); EXPECT_NE(nullptr, extensions.eglClientWaitSyncKHR); } ./tests/unit-tests/graphics/android/test_hwc_device.cpp0000644000015600001650000007664612676616125023500 0ustar jenkinsjenkins/** * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "sync_fence.h" #include "src/platforms/android/server/framebuffer_bundle.h" #include "src/platforms/android/server/hwc_device.h" #include "src/platforms/android/server/hwc_layerlist.h" #include "src/platforms/android/server/gl_context.h" #include "src/platforms/android/server/hwc_configuration.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "mir/test/doubles/stub_renderable.h" #include "mir/test/doubles/mock_framebuffer_bundle.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/mock_hwc_device_wrapper.h" #include "mir/test/fake_shared.h" #include "hwc_struct_helpers.h" #include "mir/test/doubles/mock_swapping_gl_context.h" #include "mir/test/doubles/stub_swapping_gl_context.h" #include "mir/test/doubles/stub_renderable_list_compositor.h" #include "mir/test/doubles/mock_renderable_list_compositor.h" #include "mir/test/doubles/mock_renderable.h" #include "mir/test/doubles/stub_renderable.h" #include #include #include #include #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mtd=mir::test::doubles; namespace geom=mir::geometry; namespace mt=mir::test; namespace { struct MockFileOps : public mga::SyncFileOps { MOCK_METHOD3(ioctl, int(int,int,void*)); MOCK_METHOD1(dup, int(int)); MOCK_METHOD1(close, int(int)); }; struct HwcDevice : public ::testing::Test { HwcDevice() : mock_native_buffer1(std::make_shared>(size1)), mock_native_buffer2(std::make_shared>(size2)), mock_native_buffer3(std::make_shared>(size3)), stub_buffer1(std::make_shared(mock_native_buffer1, size1)), stub_buffer2(std::make_shared(mock_native_buffer2, size2)), stub_fb_buffer(std::make_shared(mock_native_buffer3, size3)), stub_renderable1(std::make_shared(stub_buffer1, position1)), stub_renderable2(std::make_shared(stub_buffer2, position2)), mock_device(std::make_shared>()), stub_context{stub_fb_buffer}, renderlist({stub_renderable1, stub_renderable2}), layer_adapter{std::make_shared()} { mt::fill_hwc_layer(layer, &comp_rect, position1, *stub_buffer1, HWC_FRAMEBUFFER, 0); mt::fill_hwc_layer(layer2, &comp2_rect, position2, *stub_buffer2, HWC_FRAMEBUFFER, 0); mt::fill_hwc_layer(target_layer, &target_rect, fb_position, *stub_fb_buffer, HWC_FRAMEBUFFER_TARGET, 0); mt::fill_hwc_layer(skip_layer, &skip_rect, fb_position, *stub_fb_buffer, HWC_FRAMEBUFFER, HWC_SKIP_LAYER); set_all_layers_to_overlay = [&](std::array const& contents) { for(auto i = 0u; i < contents[0]->numHwLayers - 1; i++) //-1 because the last layer is the target contents[0]->hwLayers[i].compositionType = HWC_OVERLAY; }; reject_all_layers = [](std::array const&){}; } hwc_rect_t skip_rect; hwc_rect_t target_rect; hwc_rect_t comp_rect; hwc_rect_t comp2_rect; hwc_layer_1_t skip_layer; hwc_layer_1_t target_layer; hwc_layer_1_t layer; hwc_layer_1_t layer2; geom::Size const size1{111, 222}; geom::Size const size2{333, 444}; geom::Size const size3{555, 666}; geom::Rectangle const position1{{44,1},size1}; geom::Rectangle const position2{{92,293},size2}; geom::Rectangle const fb_position{{0,0},size3}; mtd::StubRenderableListCompositor stub_compositor; std::function const& contents)> set_all_layers_to_overlay; std::function const& contents)> reject_all_layers; std::shared_ptr const mock_native_buffer1; std::shared_ptr const mock_native_buffer2; std::shared_ptr const mock_native_buffer3; std::shared_ptr const stub_buffer1; std::shared_ptr const stub_buffer2; std::shared_ptr const stub_fb_buffer; std::shared_ptr const stub_renderable1; std::shared_ptr const stub_renderable2; std::shared_ptr const mock_device; mtd::StubSwappingGLContext stub_context; mg::RenderableList renderlist; std::shared_ptr const layer_adapter; mga::DisplayName primary{mga::DisplayName::primary}; geom::Displacement offset; }; } TEST_F(HwcDevice, reports_it_can_swap) { mga::HwcDevice device(mock_device); EXPECT_TRUE(device.can_swap_buffers()); } TEST_F(HwcDevice, prepares_a_skip_and_target_layer_by_default) { using namespace testing; std::list expected_list { &skip_layer, &target_layer }; EXPECT_CALL(*mock_device, prepare(MatchesPrimaryList(expected_list))) .Times(1); mga::LayerList list(layer_adapter, {}, geom::Displacement{0,0}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; mga::HwcDevice device(mock_device); device.commit({content}); } TEST_F(HwcDevice, calls_backup_compositor_when_overlay_rejected) { using namespace testing; mtd::MockRenderableListCompositor mock_compositor; mg::RenderableList expected_renderable_list({ stub_renderable2 }); std::list expected_prepare_list { &layer, &layer2, &target_layer }; Sequence seq; EXPECT_CALL(*mock_device, prepare(MatchesPrimaryList(expected_prepare_list))) .InSequence(seq) .WillOnce(Invoke([&](std::array const& contents) { ASSERT_EQ(contents[0]->numHwLayers, 3); contents[0]->hwLayers[0].compositionType = HWC_OVERLAY; contents[0]->hwLayers[1].compositionType = HWC_FRAMEBUFFER; contents[0]->hwLayers[2].compositionType = HWC_FRAMEBUFFER_TARGET; })); EXPECT_CALL(mock_compositor, render(expected_renderable_list, offset, Ref(stub_context))) .InSequence(seq); mga::LayerList list(layer_adapter, renderlist, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, mock_compositor}; mga::HwcDevice device(mock_device); device.commit({content}); } TEST_F(HwcDevice, does_not_swap_buffers_when_no_renderables) { using namespace testing; mtd::MockRenderableListCompositor mock_compositor; mtd::MockSwappingGLContext mock_context; ON_CALL(mock_context, last_rendered_buffer()) .WillByDefault(Return(stub_fb_buffer)); EXPECT_CALL(mock_compositor, render(_,_,_)) .Times(0); EXPECT_CALL(mock_context, swap_buffers()) .Times(0); mga::LayerList list(layer_adapter, {}, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, mock_context, mock_compositor}; mga::HwcDevice device(mock_device); device.commit({content}); } TEST_F(HwcDevice, resets_layers_when_prepare_gl_called) { using namespace testing; std::list expected_list1 { &layer, &layer2, &target_layer }; std::list expected_list2 { &skip_layer, &target_layer }; Sequence seq; EXPECT_CALL(*mock_device, prepare(MatchesPrimaryList(expected_list1))) .InSequence(seq); EXPECT_CALL(*mock_device, prepare(MatchesPrimaryList(expected_list2))) .InSequence(seq); mga::HwcDevice device(mock_device); mga::LayerList list(layer_adapter, renderlist, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); list.update_list({}, geom::Displacement{}); device.commit({content}); } TEST_F(HwcDevice, sets_and_updates_fences) { using namespace testing; int fb_release_fence = 94; int hwc_retire_fence = ::open("/dev/null", 0); int* list_retire_fence = nullptr; auto set_fences_fn = [&](std::array const& contents) { ASSERT_EQ(contents[0]->numHwLayers, 2); contents[0]->hwLayers[1].releaseFenceFd = fb_release_fence; contents[0]->retireFenceFd = hwc_retire_fence; list_retire_fence = &contents[0]->retireFenceFd; }; std::list expected_list { &skip_layer, &target_layer }; Sequence seq; EXPECT_CALL(*mock_device, set(MatchesPrimaryList(expected_list))) .InSequence(seq) .WillOnce(Invoke(set_fences_fn)); EXPECT_CALL(*mock_native_buffer3, update_usage(fb_release_fence, mga::BufferAccess::read)) .InSequence(seq); mga::HwcDevice device(mock_device); mga::LayerList list(layer_adapter, {}, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); //check that the retire fence is closed bool retire_fence_was_closed{fcntl(hwc_retire_fence, F_GETFD) == -1}; EXPECT_TRUE(retire_fence_was_closed); if (!retire_fence_was_closed) close(hwc_retire_fence); } TEST_F(HwcDevice, commits_correct_list_with_rejected_renderables) { using namespace testing; int fb_acquire_fence = 80; int fb_release_fence = 383; auto set_fences_fn = [&](std::array const& contents) { ASSERT_EQ(contents[0]->numHwLayers, 2); contents[0]->hwLayers[1].releaseFenceFd = fb_release_fence; contents[0]->retireFenceFd = -1; }; layer.acquireFenceFd = -1; target_layer.acquireFenceFd = fb_acquire_fence; std::list expected_list { &layer, &target_layer }; EXPECT_CALL(*mock_native_buffer1, copy_fence()) .Times(0); EXPECT_CALL(*mock_native_buffer1, update_usage(_,_)) .Times(0); Sequence seq; EXPECT_CALL(*mock_device, prepare(_)) .InSequence(seq) .WillOnce(Invoke(reject_all_layers)); EXPECT_CALL(*mock_native_buffer3, copy_fence()) .InSequence(seq) .WillOnce(Return(fb_acquire_fence)); EXPECT_CALL(*mock_device, set(MatchesPrimaryList(expected_list))) .InSequence(seq) .WillOnce(Invoke(set_fences_fn)); EXPECT_CALL(*mock_native_buffer3, update_usage(fb_release_fence, mga::BufferAccess::read)) .InSequence(seq); mga::HwcDevice device(mock_device); mga::LayerList list(layer_adapter, {stub_renderable1}, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); } TEST_F(HwcDevice, commits_correct_list_when_all_accepted_as_overlays) { using namespace testing; int overlay_acquire_fence1 = 80; int overlay_acquire_fence2 = 81; int release_fence1 = 381; int release_fence2 = 382; auto set_fences_fn = [&](std::array const& contents) { ASSERT_EQ(contents[0]->numHwLayers, 3); contents[0]->hwLayers[0].releaseFenceFd = release_fence1; contents[0]->hwLayers[1].releaseFenceFd = release_fence2; contents[0]->retireFenceFd = -1; }; layer.compositionType = HWC_OVERLAY; layer.acquireFenceFd = overlay_acquire_fence1; layer2.compositionType = HWC_OVERLAY; layer2.acquireFenceFd = overlay_acquire_fence2; //all layers are overlays, so we don't copy the fence. lp: #1331769 target_layer.acquireFenceFd = -1; std::list expected_list { &layer, &layer2, &target_layer }; EXPECT_CALL(*mock_native_buffer3, copy_fence()) .Times(0); int invalid{-1}; EXPECT_CALL(*mock_native_buffer3, update_usage(invalid,_)) .Times(1); Sequence seq; EXPECT_CALL(*mock_device, prepare(_)) .InSequence(seq) .WillOnce(Invoke(set_all_layers_to_overlay)); //copy all fb fences for OVERLAY or FRAMEBUFFER_TARGET in preparation of set EXPECT_CALL(*mock_native_buffer1, copy_fence()) .InSequence(seq) .WillOnce(Return(overlay_acquire_fence1)); EXPECT_CALL(*mock_native_buffer2, copy_fence()) .InSequence(seq) .WillOnce(Return(overlay_acquire_fence2)); EXPECT_CALL(*mock_device, set(MatchesPrimaryList(expected_list))) .InSequence(seq) .WillOnce(Invoke(set_fences_fn)); EXPECT_CALL(*mock_native_buffer1, update_usage(release_fence1, mga::BufferAccess::read)) .InSequence(seq); EXPECT_CALL(*mock_native_buffer2, update_usage(release_fence2, mga::BufferAccess::read)) .InSequence(seq); mga::HwcDevice device(mock_device); mga::LayerList list(layer_adapter, renderlist, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); } TEST_F(HwcDevice, submits_every_time_if_at_least_one_layer_is_gl_rendered) { using namespace testing; mga::HwcDevice device(mock_device); ON_CALL(*mock_device, prepare(_)) .WillByDefault(Invoke([&](std::array const& contents) { ASSERT_EQ(contents[0]->numHwLayers, 3); contents[0]->hwLayers[0].compositionType = HWC_OVERLAY; contents[0]->hwLayers[1].compositionType = HWC_FRAMEBUFFER; contents[0]->hwLayers[2].compositionType = HWC_FRAMEBUFFER_TARGET; })); EXPECT_CALL(*mock_device, set(_)) .Times(2); mga::LayerList list(layer_adapter, renderlist, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); list.update_list(renderlist, geom::Displacement{}); device.commit({content}); } TEST_F(HwcDevice, resets_composition_type_with_prepare) //lp:1314399 { using namespace testing; mg::RenderableList renderlist({stub_renderable1}); mg::RenderableList renderlist2({stub_renderable2}); mga::HwcDevice device(mock_device); std::list expected_list1 { &layer, &target_layer }; std::list expected_list2 { &layer2, &target_layer }; Sequence seq; EXPECT_CALL(*mock_device, prepare(MatchesPrimaryList(expected_list1))) .InSequence(seq) .WillOnce(Invoke(set_all_layers_to_overlay)); EXPECT_CALL(*mock_device, prepare(MatchesPrimaryList(expected_list2))) .InSequence(seq); mga::LayerList list(layer_adapter, renderlist, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); list.update_list(renderlist2, geom::Displacement{}); device.commit({content}); } //note: HWC models overlay layer buffers as owned by the display hardware until a subsequent set. TEST_F(HwcDevice, owns_overlay_buffers_until_next_set) { using namespace testing; EXPECT_CALL(*mock_device, prepare(_)) .WillOnce(Invoke(set_all_layers_to_overlay)) .WillOnce(Return()); mga::HwcDevice device(mock_device); auto use_count_before = stub_buffer1.use_count(); mga::LayerList list(layer_adapter, {stub_renderable1}, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); EXPECT_THAT(stub_buffer1.use_count(), Gt(use_count_before)); list.update_list({stub_renderable1}, geom::Displacement{}); device.commit({content}); EXPECT_THAT(stub_buffer1.use_count(), Eq(use_count_before)); } TEST_F(HwcDevice, overlays_are_throttled_per_predictive_bypass) { using namespace testing; EXPECT_CALL(*mock_device, prepare(_)) .WillRepeatedly(Invoke(set_all_layers_to_overlay)); mga::HwcDevice device(mock_device); mga::LayerList list(layer_adapter, {stub_renderable1}, {0,0}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; for (int frame = 0; frame < 5; ++frame) { device.commit({content}); ASSERT_THAT(device.recommended_sleep().count(), Ge(8)); } } TEST_F(HwcDevice, compositing_disables_predictive_bypass) { using namespace testing; NiceMock mock_context; ON_CALL(mock_context, last_rendered_buffer()) .WillByDefault(Return(stub_fb_buffer)); mga::LayerList list(layer_adapter, {}, geom::Displacement{}); mtd::MockRenderableListCompositor mock_compositor; mga::DisplayContents content{primary, list, offset, mock_context, mock_compositor}; mga::HwcDevice device(mock_device); device.commit({content}); for (int frame = 0; frame < 5; ++frame) { device.commit({content}); ASSERT_EQ(0, device.recommended_sleep().count()); } } TEST_F(HwcDevice, does_not_set_acquirefences_when_it_has_set_them_previously_without_update) { using namespace testing; int acquire_fence1 = 39303; int acquire_fence2 = 39302; int acquire_fence3 = 39301; int release_fence1 = 381; int release_fence2 = 382; auto native_buffer = std::make_shared>(size1); auto updated_buffer = std::make_shared(native_buffer, size1); auto set_fences_fn = [&](std::array const& contents) { ASSERT_EQ(contents[0]->numHwLayers, 3); contents[0]->hwLayers[0].releaseFenceFd = release_fence1; contents[0]->hwLayers[1].releaseFenceFd = release_fence2; contents[0]->retireFenceFd = -1; }; mg::RenderableList renderlist{ stub_renderable1, stub_renderable2 }; layer.acquireFenceFd = acquire_fence1; layer2.acquireFenceFd = acquire_fence2; layer.compositionType = HWC_OVERLAY; layer2.compositionType = HWC_OVERLAY; std::list expected_list1 { &layer, &layer2, &target_layer }; hwc_rect_t update_layer_rect; hwc_layer_1_t update_layer; hwc_rect_t nofence_rect; hwc_layer_1_t nofence_layer; mt::fill_hwc_layer(update_layer, &update_layer_rect, position1, *updated_buffer, HWC_OVERLAY, 0); mt::fill_hwc_layer(nofence_layer, &nofence_rect, position2, *stub_buffer2, HWC_OVERLAY, 0); update_layer.acquireFenceFd = acquire_fence3; nofence_layer.acquireFenceFd = -1; std::list expected_list2 { &update_layer, &nofence_layer, &target_layer }; Sequence seq; EXPECT_CALL(*mock_device, prepare(_)) .InSequence(seq) .WillOnce(Invoke(set_all_layers_to_overlay)); EXPECT_CALL(*mock_native_buffer1, copy_fence()) .InSequence(seq) .WillOnce(Return(acquire_fence1)); EXPECT_CALL(*mock_native_buffer2, copy_fence()) .InSequence(seq) .WillOnce(Return(acquire_fence2)); EXPECT_CALL(*mock_device, set(MatchesPrimaryList(expected_list1))) .InSequence(seq) .WillOnce(Invoke(set_fences_fn)); EXPECT_CALL(*mock_device, prepare(_)) .InSequence(seq) .WillOnce(Invoke(set_all_layers_to_overlay)); EXPECT_CALL(*native_buffer, copy_fence()) .InSequence(seq) .WillOnce(Return(acquire_fence3)); EXPECT_CALL(*mock_device, set(MatchesPrimaryList(expected_list2))) .InSequence(seq) .WillOnce(Invoke(set_fences_fn)); mga::HwcDevice device(mock_device); mga::LayerList list(layer_adapter, renderlist, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); //set only the 2nd layer to a new buffer. the first buffer has the same buffer, and would //still be onscreen if this wasn't against a mock stub_renderable1->set_buffer(updated_buffer); list.update_list(renderlist, geom::Displacement{}); device.commit({content}); } TEST_F(HwcDevice, does_not_own_framebuffer_buffers_past_set) { using namespace testing; EXPECT_CALL(*mock_device, prepare(_)) .WillOnce(Invoke([&](std::array const& contents) { ASSERT_THAT(contents[0]->numHwLayers, Ge(2)); contents[0]->hwLayers[0].compositionType = HWC_FRAMEBUFFER; contents[0]->hwLayers[1].compositionType = HWC_FRAMEBUFFER_TARGET; })); mga::HwcDevice device(mock_device); auto use_count_before = stub_buffer1.use_count(); mga::LayerList list(layer_adapter, {stub_renderable1}, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); EXPECT_THAT(stub_buffer1.use_count(), Eq(use_count_before)); } TEST_F(HwcDevice, rejects_empty_list) { mga::HwcDevice device(mock_device); mg::RenderableList renderlist{}; EXPECT_FALSE(device.compatible_renderlist(renderlist)); } //TODO: we could accept a 90 degree transform TEST_F(HwcDevice, rejects_list_containing_transformed) { mga::HwcDevice device(mock_device); auto renderable = std::make_shared(); mg::RenderableList renderlist{renderable}; EXPECT_FALSE(device.compatible_renderlist(renderlist)); } //TODO: support plane alpha for hwc 1.2 and later TEST_F(HwcDevice, rejects_list_containing_plane_alpha) { mga::HwcDevice device(mock_device); mg::RenderableList renderlist{std::make_shared()}; EXPECT_FALSE(device.compatible_renderlist(renderlist)); } TEST_F(HwcDevice, does_not_own_overlay_buffers_after_screen_off) { using namespace testing; EXPECT_CALL(*mock_device, prepare(_)) .WillOnce(Invoke(set_all_layers_to_overlay)); mga::HwcDevice device(mock_device); auto use_count_before = stub_buffer1.use_count(); mga::LayerList list(layer_adapter, {stub_renderable1}, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); EXPECT_THAT(stub_buffer1.use_count(), Gt(use_count_before)); device.content_cleared(); EXPECT_THAT(stub_buffer1.use_count(), Eq(use_count_before)); } TEST_F(HwcDevice, tracks_hwc_owned_fences_even_across_list_changes) { using namespace testing; hwc_layer_1_t layer3; mt::fill_hwc_layer(layer2, &comp_rect, position1, *stub_buffer1, HWC_FRAMEBUFFER, 0); mt::fill_hwc_layer(layer3, &comp2_rect, position2, *stub_buffer2, HWC_FRAMEBUFFER, 0); int acquire_fence1 = 39303; int acquire_fence2 = 393044; int release_fence1 = 39304; int release_fence2 = 123; int release_fence3 = 136; mg::RenderableList renderlist1{ stub_renderable1 }; mg::RenderableList renderlist2{ stub_renderable1, stub_renderable2 }; layer.acquireFenceFd = acquire_fence1; layer.compositionType = HWC_OVERLAY; std::list expected_list1 { &layer, &target_layer }; auto set_fences = [&](std::array const& contents) { ASSERT_THAT(contents[0], testing::NotNull()); ASSERT_EQ(contents[0]->numHwLayers, 2); contents[0]->hwLayers[0].releaseFenceFd = release_fence1; contents[0]->retireFenceFd = -1; }; auto set_fences2 = [&](std::array const& contents) { ASSERT_THAT(contents[0], testing::NotNull()); ASSERT_EQ(contents[0]->numHwLayers, 3); contents[0]->hwLayers[0].releaseFenceFd = release_fence2; contents[0]->hwLayers[1].releaseFenceFd = release_fence3; contents[0]->retireFenceFd = -1; }; layer2.acquireFenceFd = -1; layer2.compositionType = HWC_OVERLAY; layer3.acquireFenceFd = acquire_fence2; layer3.compositionType = HWC_OVERLAY; std::list expected_list2 { &layer2, &layer3, &target_layer }; Sequence seq; // first post EXPECT_CALL(*mock_device, prepare(_)) .InSequence(seq) .WillOnce(Invoke(set_all_layers_to_overlay)); EXPECT_CALL(*mock_native_buffer1, copy_fence()) .InSequence(seq) .WillOnce(Return(acquire_fence1)); EXPECT_CALL(*mock_device, set(MatchesPrimaryList(expected_list1))) .InSequence(seq) .WillOnce(Invoke(set_fences)); EXPECT_CALL(*mock_native_buffer1, update_usage(release_fence1, mga::BufferAccess::read)) .InSequence(seq); //end first post, second post EXPECT_CALL(*mock_device, prepare(_)) .InSequence(seq) .WillOnce(Invoke(set_all_layers_to_overlay)); //note that only the 2nd buffer should have its fence copied EXPECT_CALL(*mock_native_buffer2, copy_fence()) .InSequence(seq) .WillOnce(Return(acquire_fence2)); EXPECT_CALL(*mock_device, set(MatchesPrimaryList(expected_list2))) .InSequence(seq) .WillOnce(Invoke(set_fences2)); EXPECT_CALL(*mock_native_buffer1, update_usage(release_fence2, mga::BufferAccess::read)) .InSequence(seq); EXPECT_CALL(*mock_native_buffer2, update_usage(release_fence3, mga::BufferAccess::read)) .InSequence(seq); //end second post mga::HwcDevice device(mock_device); mga::LayerList list(layer_adapter, renderlist1, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); list.update_list(renderlist2, geom::Displacement{}); device.commit({content}); } TEST_F(HwcDevice, tracks_hwc_owned_fences_across_list_rearrange) { using namespace testing; hwc_layer_1_t layer3; hwc_layer_1_t layer4; mt::fill_hwc_layer(layer3, &comp2_rect, position2, *stub_buffer2, HWC_FRAMEBUFFER, 0); mt::fill_hwc_layer(layer4, &comp_rect, position1, *stub_buffer1, HWC_FRAMEBUFFER, 0); int acquire_fence1 = 39303; int acquire_fence2 = 393044; int release_fence1 = 39304; int release_fence2 = 123; int release_fence3 = 136; int release_fence4 = 1344; mg::RenderableList renderlist{ stub_renderable1, stub_renderable2 }; //the renderable ptr or the buffer ptr could change, while still referencing the same buffer_handle_t mg::RenderableList renderlist2{ std::make_shared( std::make_shared(mock_native_buffer2, size2), position2), std::make_shared( std::make_shared(mock_native_buffer1, size1), position1), }; layer.acquireFenceFd = acquire_fence1; layer.compositionType = HWC_OVERLAY; layer2.acquireFenceFd = acquire_fence2; layer2.compositionType = HWC_OVERLAY; std::list expected_list1 { &layer, &layer2, &target_layer }; auto set_fences = [&](std::array const& contents) { ASSERT_EQ(contents[0]->numHwLayers, 3); contents[0]->hwLayers[0].releaseFenceFd = release_fence1; contents[0]->hwLayers[1].releaseFenceFd = release_fence2; contents[0]->retireFenceFd = -1; }; auto set_fences2 = [&](std::array const& contents) { ASSERT_EQ(contents[0]->numHwLayers, 3); contents[0]->hwLayers[0].releaseFenceFd = release_fence3; contents[0]->hwLayers[1].releaseFenceFd = release_fence4; contents[0]->retireFenceFd = -1; }; layer3.acquireFenceFd = -1; layer3.compositionType = HWC_OVERLAY; layer4.acquireFenceFd = -1; layer4.compositionType = HWC_OVERLAY; std::list expected_list2 { &layer3, &layer4, &target_layer }; Sequence seq; // first post EXPECT_CALL(*mock_device, prepare(_)) .InSequence(seq) .WillOnce(Invoke(set_all_layers_to_overlay)); EXPECT_CALL(*mock_native_buffer1, copy_fence()) .InSequence(seq) .WillOnce(Return(acquire_fence1)); EXPECT_CALL(*mock_native_buffer2, copy_fence()) .InSequence(seq) .WillOnce(Return(acquire_fence2)); EXPECT_CALL(*mock_device, set(MatchesPrimaryList(expected_list1))) .InSequence(seq) .WillOnce(Invoke(set_fences)); EXPECT_CALL(*mock_native_buffer1, update_usage(release_fence1, mga::BufferAccess::read)) .InSequence(seq); EXPECT_CALL(*mock_native_buffer2, update_usage(release_fence2, mga::BufferAccess::read)) .InSequence(seq); //end first post, second post EXPECT_CALL(*mock_device, prepare(_)) .InSequence(seq) .WillOnce(Invoke(set_all_layers_to_overlay)); //note that the buffers just flipped position, no acquire fence copying. EXPECT_CALL(*mock_device, set(MatchesPrimaryList(expected_list2))) .InSequence(seq) .WillOnce(Invoke(set_fences2)); EXPECT_CALL(*mock_native_buffer2, update_usage(release_fence3, mga::BufferAccess::read)) .InSequence(seq); EXPECT_CALL(*mock_native_buffer1, update_usage(release_fence4, mga::BufferAccess::read)) .InSequence(seq); //end second post mga::HwcDevice device(mock_device); mga::LayerList list(layer_adapter, renderlist, geom::Displacement{}); mga::DisplayContents content{primary, list, offset, stub_context, stub_compositor}; device.commit({content}); list.update_list(renderlist2, geom::Displacement{}); device.commit({content}); } TEST_F(HwcDevice, commits_external_list_with_both_force_gl) { using namespace testing; testing::NiceMock mock_context1; testing::NiceMock mock_context2; ON_CALL(mock_context1, last_rendered_buffer()) .WillByDefault(Return(stub_fb_buffer)); ON_CALL(mock_context2, last_rendered_buffer()) .WillByDefault(Return(stub_fb_buffer)); std::list expected_list { &skip_layer, &target_layer }; InSequence seq; EXPECT_CALL(*mock_device, prepare(MatchesLists(expected_list, expected_list))); EXPECT_CALL(*mock_device, set(MatchesLists(expected_list, expected_list))); mga::LayerList primary_list(layer_adapter, {}, geom::Displacement{}); mga::LayerList external_list(layer_adapter, {}, geom::Displacement{}); mga::HwcDevice device(mock_device); mga::DisplayContents primary_content{ primary, primary_list, offset, mock_context1, stub_compositor}; mga::DisplayContents external_content{ mga::DisplayName::external, external_list, offset, mock_context2, stub_compositor}; device.commit({primary_content, external_content}); } ./tests/unit-tests/graphics/android/test_interpreter_buffer_cache.cpp0000644000015600001650000000742412676616125026403 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/interpreter_cache.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "mir/test/doubles/stub_android_native_buffer.h" #include #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mtd=mir::test::doubles; struct InterpreterResourceTest : public ::testing::Test { void SetUp() { stub_buffer1 = std::make_shared(); stub_buffer2 = std::make_shared(); stub_buffer3 = std::make_shared(); native_buffer1 = std::make_shared>(); native_buffer2 = std::make_shared(); native_buffer3 = std::make_shared(); } std::shared_ptr stub_buffer1; std::shared_ptr stub_buffer2; std::shared_ptr stub_buffer3; std::shared_ptr native_buffer1; std::shared_ptr native_buffer2; std::shared_ptr native_buffer3; }; TEST_F(InterpreterResourceTest, deposit_buffer) { mga::InterpreterCache cache; cache.store_buffer(stub_buffer1, native_buffer1); auto test_buffer = cache.retrieve_buffer(native_buffer1->anwb()); EXPECT_EQ(stub_buffer1, test_buffer); } TEST_F(InterpreterResourceTest, deposit_many_buffers) { mga::InterpreterCache cache; cache.store_buffer(stub_buffer1, native_buffer1); cache.store_buffer(stub_buffer2, native_buffer2); cache.store_buffer(stub_buffer3, native_buffer3); EXPECT_EQ(stub_buffer3, cache.retrieve_buffer(native_buffer3->anwb())); EXPECT_EQ(stub_buffer1, cache.retrieve_buffer(native_buffer1->anwb())); EXPECT_EQ(stub_buffer2, cache.retrieve_buffer(native_buffer2->anwb())); } TEST_F(InterpreterResourceTest, deposit_buffer_has_ownership) { mga::InterpreterCache cache; auto native_use_count_before = native_buffer1.use_count(); auto use_count_before = stub_buffer1.use_count(); cache.store_buffer(stub_buffer1, native_buffer1); EXPECT_EQ(native_use_count_before+1, native_buffer1.use_count()); EXPECT_EQ(use_count_before+1, stub_buffer1.use_count()); cache.retrieve_buffer(native_buffer1->anwb()); EXPECT_EQ(native_use_count_before, native_buffer1.use_count()); EXPECT_EQ(use_count_before, stub_buffer1.use_count()); } TEST_F(InterpreterResourceTest, update_usage_for) { int fence_fd = 44; mga::InterpreterCache cache; EXPECT_CALL(*native_buffer1, update_usage(fence_fd, mga::BufferAccess::write)) .Times(1); cache.store_buffer(stub_buffer1, native_buffer1); cache.update_native_fence(native_buffer1->anwb(), fence_fd); EXPECT_THROW({ cache.update_native_fence(nullptr, fence_fd); }, std::runtime_error); } TEST_F(InterpreterResourceTest, retreive_buffer_with_bad_key_throws) { mga::InterpreterCache cache; EXPECT_THROW({ cache.retrieve_buffer(native_buffer1->anwb()); }, std::runtime_error); } ./tests/unit-tests/graphics/android/test_output_builder.cpp0000644000015600001650000001610712676616157024435 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/hal_component_factory.h" #include "android_format_conversion-inl.h" #include "src/platforms/android/server/resource_factory.h" #include "src/platforms/android/server/hwc_loggers.h" #include "src/platforms/android/server/hwc_configuration.h" #include "src/platforms/android/server/device_quirks.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/mock_display_report.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_android_hw.h" #include "mir/test/doubles/mock_fb_hal_device.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "mir/test/doubles/mock_hwc_report.h" #include "mir/test/doubles/mock_hwc_device_wrapper.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/stub_gl_program_factory.h" #include "mir/test/doubles/stub_display_configuration.h" #include #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mt=mir::test; namespace mtd=mir::test::doubles; namespace geom=mir::geometry; namespace { struct MockResourceFactory: public mga::DisplayResourceFactory { ~MockResourceFactory() noexcept {} MockResourceFactory() { using namespace testing; ON_CALL(*this, create_hwc_wrapper(_)) .WillByDefault(Return(std::make_tuple(nullptr, mga::HwcVersion::hwc10))); ON_CALL(*this, create_fb_native_device()).WillByDefault(Return(nullptr)); } MOCK_CONST_METHOD1(create_hwc_wrapper, std::tuple, mga::HwcVersion>(std::shared_ptr const&)); MOCK_CONST_METHOD0(create_fb_native_device, std::shared_ptr()); }; class HalComponentFactory : public ::testing::Test { public: void SetUp() { using namespace testing; quirks = std::make_shared(mga::PropertiesOps{}); mock_resource_factory = std::make_shared>(); mock_wrapper = std::make_shared>(); ON_CALL(*mock_resource_factory, create_hwc_wrapper(_)) .WillByDefault(Return(std::make_tuple(mock_wrapper, mga::HwcVersion::hwc11))); ON_CALL(*mock_resource_factory, create_fb_native_device()) .WillByDefault(Return(mt::fake_shared(fb_hal_mock))); } testing::NiceMock mock_egl; testing::NiceMock mock_gl; testing::NiceMock hw_access_mock; testing::NiceMock fb_hal_mock; std::shared_ptr mock_resource_factory; testing::NiceMock mock_display_report; std::shared_ptr mock_hwc_report{ std::make_shared>()}; std::shared_ptr mock_wrapper; std::shared_ptr quirks; }; } TEST_F(HalComponentFactory, builds_hwc_version_10) { using namespace testing; EXPECT_CALL(*mock_resource_factory, create_hwc_wrapper(_)) .WillOnce(Return(std::make_tuple(mock_wrapper, mga::HwcVersion::hwc10))); EXPECT_CALL(*mock_resource_factory, create_fb_native_device()); EXPECT_CALL(*mock_hwc_report, report_hwc_version(mga::HwcVersion::hwc10)); mga::HalComponentFactory factory( mock_resource_factory, mock_hwc_report, quirks); factory.create_display_device(); } TEST_F(HalComponentFactory, builds_hwc_version_11_and_later) { using namespace testing; EXPECT_CALL(*mock_resource_factory, create_hwc_wrapper(_)) .WillOnce(Return(std::make_tuple(mock_wrapper, mga::HwcVersion::hwc11))); EXPECT_CALL(*mock_hwc_report, report_hwc_version(mga::HwcVersion::hwc11)); mga::HalComponentFactory factory( mock_resource_factory, mock_hwc_report, quirks); factory.create_display_device(); } TEST_F(HalComponentFactory, allocates_correct_hwc_configuration) { using namespace testing; EXPECT_CALL(*mock_resource_factory, create_hwc_wrapper(_)) .WillOnce(Return(std::make_tuple(mock_wrapper, mga::HwcVersion::hwc14))); mga::HalComponentFactory factory( mock_resource_factory, mock_hwc_report, quirks); auto hwc_config = factory.create_hwc_configuration(); EXPECT_THAT(dynamic_cast(hwc_config.get()), Ne(nullptr)); } TEST_F(HalComponentFactory, hwc_failure_falls_back_to_fb) { using namespace testing; EXPECT_CALL(*mock_resource_factory, create_hwc_wrapper(_)) .WillOnce(Throw(std::runtime_error(""))); EXPECT_CALL(*mock_resource_factory, create_fb_native_device()); EXPECT_CALL(*mock_hwc_report, report_legacy_fb_module()); mga::HalComponentFactory factory( mock_resource_factory, mock_hwc_report, quirks); factory.create_display_device(); } TEST_F(HalComponentFactory, hwc_and_fb_failure_fatal) { using namespace testing; EXPECT_CALL(*mock_resource_factory, create_hwc_wrapper(_)) .WillOnce(Throw(std::runtime_error(""))); EXPECT_CALL(*mock_resource_factory, create_fb_native_device()) .WillOnce(Throw(std::runtime_error(""))); EXPECT_THROW({ mga::HalComponentFactory factory( mock_resource_factory, mock_hwc_report, quirks); }, std::runtime_error); } //some drivers incorrectly report 0 buffers available. request 2 fbs in this case. TEST_F(HalComponentFactory, determine_fbnum_always_reports_2_minimum) { using namespace testing; EXPECT_CALL(*mock_resource_factory, create_hwc_wrapper(_)) .WillOnce(Throw(std::runtime_error(""))); EXPECT_CALL(*mock_resource_factory, create_fb_native_device()) .WillOnce(Return(std::make_shared( 0, 0, mir_pixel_format_abgr_8888, 0, 0.0, 0.0))); mga::HalComponentFactory factory( mock_resource_factory, mock_hwc_report, quirks); auto fbs = factory.create_framebuffers(mtd::StubDisplayConfig(1).outputs[0]); std::vector buffer_list; for(auto i = 0u; i < 10u; i++) buffer_list.push_back(fbs->buffer_for_render()->id()); std::sort(buffer_list.begin(), buffer_list.end()); buffer_list.erase(std::unique(buffer_list.begin(), buffer_list.end()), buffer_list.end()); EXPECT_THAT(buffer_list.size(), Eq(2)); } ./tests/unit-tests/graphics/android/CMakeLists.txt0000644000015600001650000000357612676616157022372 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/hwc_struct_helpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_sync_fence.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_tex_bind.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_generic.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_group.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_gralloc.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_graphic_buffer_allocator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_hwc_device.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_hwc_fb_device.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_ipc_operations.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_resource_factory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_framebuffers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_fb_device.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_hwc_layers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_hwc_layerlist.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_hwc_logger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_server_interpreter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_pixel_format.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_graphics_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_interpreter_buffer_cache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_native_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_output_builder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_device_detection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_hwc_wrapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_hwc_fallback_gl_renderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_hwc_configuration.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_hotplug.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_egl_sync_extensions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_egl_sync_fence.cpp $ ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/graphics/android/test_display_buffer.cpp0000644000015600001650000003044212676616125024356 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/display_buffer.h" #include "src/platforms/android/server/gl_context.h" #include "android_format_conversion-inl.h" #include "mir/test/doubles/mock_display_device.h" #include "mir/test/doubles/mock_display_report.h" #include "mir/test/doubles/stub_renderable.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir_native_window.h" #include "mir/test/doubles/stub_driver_interpreter.h" #include "mir/test/doubles/stub_display_buffer.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/mock_framebuffer_bundle.h" #include "mir/test/doubles/stub_gl_program_factory.h" #include namespace geom=mir::geometry; namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mtd=mir::test::doubles; namespace { struct DisplayBuffer : public ::testing::Test { testing::NiceMock mock_egl; testing::NiceMock mock_gl; mtd::StubGLProgramFactory stub_program_factory; int visual_id{5}; EGLConfig dummy_config{mock_egl.fake_configs[0]}; EGLDisplay dummy_display{mock_egl.fake_egl_display}; EGLContext dummy_context{mock_egl.fake_egl_context}; mtd::StubGLConfig stub_gl_config; testing::NiceMock mock_display_report; std::shared_ptr gl_context{ std::make_shared( mga::to_mir_format(mock_egl.fake_visual_id), stub_gl_config, mock_display_report)}; std::shared_ptr stub_buffer{ std::make_shared>()}; std::shared_ptr native_window{ std::make_shared( std::make_shared())}; std::shared_ptr mock_display_device{ std::make_shared>()}; geom::Size const display_size{433,232}; double const refresh_rate{60.0}; geom::Displacement top_left{0,0}; std::unique_ptr list{ new mga::LayerList(std::make_shared(), {}, top_left)}; std::shared_ptr mock_fb_bundle{ std::make_shared>(display_size)}; MirOrientation orientation{mir_orientation_normal}; mga::DisplayBuffer db{ mga::DisplayName::primary, std::unique_ptr( new mga::LayerList(std::make_shared(), {}, top_left)), mock_fb_bundle, mock_display_device, native_window, *gl_context, stub_program_factory, orientation, top_left, mga::OverlayOptimization::enabled}; }; } TEST_F(DisplayBuffer, posts_overlay_list_returns_display_device_decision) { using namespace testing; mg::RenderableList renderlist{ std::make_shared(), std::make_shared()}; EXPECT_CALL(*mock_display_device, compatible_renderlist(Ref(renderlist))) .Times(2) .WillOnce(Return(true)) .WillOnce(Return(false)); EXPECT_TRUE(db.post_renderables_if_optimizable(renderlist)); EXPECT_FALSE(db.post_renderables_if_optimizable(renderlist)); } TEST_F(DisplayBuffer, defaults_to_normal_orientation) { EXPECT_EQ(mir_orientation_normal, db.orientation()); } TEST_F(DisplayBuffer, rotation_transposes_dimensions_and_reports_correctly) { geom::Size const transposed{display_size.height.as_int(), display_size.width.as_int()}; EXPECT_EQ(display_size, db.view_area().size); EXPECT_EQ(db.orientation(), mir_orientation_normal); db.configure(mir_power_mode_on, mir_orientation_inverted, top_left); EXPECT_EQ(display_size, db.view_area().size); EXPECT_EQ(db.orientation(), mir_orientation_inverted); db.configure(mir_power_mode_on, mir_orientation_left, top_left); EXPECT_EQ(transposed, db.view_area().size); EXPECT_EQ(db.orientation(), mir_orientation_left); db.configure(mir_power_mode_on, mir_orientation_right, top_left); EXPECT_EQ(transposed, db.view_area().size); EXPECT_EQ(db.orientation(), mir_orientation_right); } TEST_F(DisplayBuffer, reports_correct_size) { auto view_area = db.view_area(); geom::Point origin_pt{geom::X{0}, geom::Y{0}}; EXPECT_EQ(display_size, view_area.size); EXPECT_EQ(origin_pt, view_area.top_left); } TEST_F(DisplayBuffer, creates_egl_context_from_shared_context) { testing::Mock::VerifyAndClearExpectations(&mock_egl); using namespace testing; EGLint const expected_attr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; EXPECT_CALL(mock_egl, eglCreateContext( dummy_display, _, dummy_context, mtd::AttrMatches(expected_attr))) .Times(1) .WillOnce(Return(mock_egl.fake_egl_context)); EXPECT_CALL(mock_egl, eglCreateWindowSurface( dummy_display, _, native_window.get(), NULL)) .Times(1) .WillOnce(Return(mock_egl.fake_egl_surface)); EXPECT_CALL(mock_egl, eglDestroySurface(dummy_display, mock_egl.fake_egl_surface)) .Times(AtLeast(1)); EXPECT_CALL(mock_egl, eglDestroyContext(dummy_display, mock_egl.fake_egl_context)) .Times(AtLeast(1)); { mga::DisplayBuffer db{ mga::DisplayName::primary, std::unique_ptr( new mga::LayerList(std::make_shared(), {}, top_left)), mock_fb_bundle, mock_display_device, native_window, *gl_context, stub_program_factory, orientation, top_left, mga::OverlayOptimization::enabled}; } testing::Mock::VerifyAndClearExpectations(&mock_egl); } TEST_F(DisplayBuffer, fails_on_egl_resource_creation) { using namespace testing; EXPECT_CALL(mock_egl, eglCreateContext(_,_,_,_)) .Times(2) .WillOnce(Return(EGL_NO_CONTEXT)) .WillOnce(Return(mock_egl.fake_egl_context)); EXPECT_CALL(mock_egl, eglCreateWindowSurface(_,_,_,_)) .Times(1) .WillOnce(Return(EGL_NO_SURFACE)); EXPECT_THROW({ mga::DisplayBuffer db( mga::DisplayName::primary, std::unique_ptr( new mga::LayerList(std::make_shared(), {}, top_left)), mock_fb_bundle, mock_display_device, native_window, *gl_context, stub_program_factory, orientation, top_left, mga::OverlayOptimization::enabled); }, std::runtime_error); EXPECT_THROW({ mga::DisplayBuffer db( mga::DisplayName::primary, std::unique_ptr( new mga::LayerList(std::make_shared(), {}, top_left)), mock_fb_bundle, mock_display_device, native_window, *gl_context, stub_program_factory, orientation, top_left, mga::OverlayOptimization::enabled); }, std::runtime_error); } TEST_F(DisplayBuffer, can_make_current) { using namespace testing; EXPECT_CALL(mock_egl, eglMakeCurrent( dummy_display, mock_egl.fake_egl_surface, mock_egl.fake_egl_surface, dummy_context)) .Times(2) .WillOnce(testing::Return(EGL_TRUE)) .WillOnce(testing::Return(EGL_FALSE)); db.make_current(); EXPECT_THROW({ db.make_current(); }, std::runtime_error); } TEST_F(DisplayBuffer, release_current) { EXPECT_CALL(mock_egl, eglMakeCurrent(dummy_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); db.release_current(); } //In HWC 1.0 notably we cannot eglSwapBuffers on the fb context. TEST_F(DisplayBuffer, swaps_when_allowed) { using namespace testing; EXPECT_CALL(*mock_display_device, can_swap_buffers()) .Times(2) .WillOnce(Return(true)) .WillOnce(Return(false)); EXPECT_CALL(mock_egl, eglSwapBuffers(dummy_display, mock_egl.fake_egl_surface)) .Times(1); db.swap_buffers(); db.swap_buffers(); } TEST_F(DisplayBuffer, notifies_list_that_content_is_cleared) { EXPECT_CALL(*mock_display_device, content_cleared()) .Times(3); db.configure(mir_power_mode_off, mir_orientation_normal, top_left); db.configure(mir_power_mode_suspend, mir_orientation_normal, top_left); db.configure(mir_power_mode_standby, mir_orientation_normal, top_left); db.configure(mir_power_mode_on, mir_orientation_normal, top_left); } TEST_F(DisplayBuffer, reject_list_if_option_disabled) { using namespace testing; ON_CALL(*mock_display_device, compatible_renderlist(_)) .WillByDefault(Return(true)); mg::RenderableList renderlist{std::make_shared()}; mga::DisplayBuffer db( mga::DisplayName::primary, std::unique_ptr( new mga::LayerList(std::make_shared(), {}, top_left)), mock_fb_bundle, mock_display_device, native_window, *gl_context, stub_program_factory, orientation, top_left, mga::OverlayOptimization::disabled); EXPECT_FALSE(db.post_renderables_if_optimizable(renderlist)); } TEST_F(DisplayBuffer, rejects_commit_if_list_doesnt_need_commit) { using namespace testing; auto buffer1 = std::make_shared(); auto buffer2 = std::make_shared(); auto buffer3 = std::make_shared(); ON_CALL(*mock_display_device, compatible_renderlist(_)) .WillByDefault(Return(true)); auto set_to_overlays = [](mga::LayerList& list) { auto native_list = list.native_list(); for (auto i = 0u; i < native_list->numHwLayers; i++) { if (native_list->hwLayers[i].compositionType == HWC_FRAMEBUFFER) native_list->hwLayers[i].compositionType = HWC_OVERLAY; } }; mg::RenderableList renderlist{buffer1, buffer2}; EXPECT_TRUE(db.post_renderables_if_optimizable(renderlist)); set_to_overlays(db.contents().list); EXPECT_FALSE(db.post_renderables_if_optimizable(renderlist)); renderlist = mg::RenderableList{buffer2, buffer1}; //ordering changed EXPECT_TRUE(db.post_renderables_if_optimizable(renderlist)); set_to_overlays(db.contents().list); EXPECT_FALSE(db.post_renderables_if_optimizable(renderlist)); renderlist = mg::RenderableList{buffer3, buffer1}; //buffer changed EXPECT_TRUE(db.post_renderables_if_optimizable(renderlist)); set_to_overlays(db.contents().list); EXPECT_FALSE(db.post_renderables_if_optimizable(renderlist)); } TEST_F(DisplayBuffer, reports_position_correctly) { using namespace testing; geom::Point origin; geom::Displacement offset{100, 100}; EXPECT_THAT(db.view_area().top_left, Eq(origin)); db.configure(mir_power_mode_on, orientation, offset); EXPECT_THAT(db.view_area().top_left, Eq(geom::Point{origin + offset})); } //lp: #1485070. Could alternitvely rotate all the renderables, once rotation is supported TEST_F(DisplayBuffer, rejects_lists_if_db_is_rotated) { ON_CALL(*mock_display_device, compatible_renderlist(testing::_)) .WillByDefault(testing::Return(true)); mg::RenderableList const renderlist{ std::make_shared(), std::make_shared()}; db.configure(mir_power_mode_on, mir_orientation_inverted, geom::Displacement{0,0}); EXPECT_FALSE(db.post_renderables_if_optimizable(renderlist)); db.configure(mir_power_mode_on, mir_orientation_normal, geom::Displacement{0,0}); EXPECT_TRUE(db.post_renderables_if_optimizable(renderlist)); } ./tests/unit-tests/graphics/android/test_framebuffers.cpp0000644000015600001650000000643612676616157024042 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/framebuffers.h" #include "src/platforms/android/server/graphic_buffer_allocator.h" #include "src/platforms/android/server/cmdstream_sync_factory.h" #include "src/platforms/android/server/device_quirks.h" #include "mir/test/doubles/mock_android_hw.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/mock_egl.h" #include #include #include #include #include namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace mtd=mir::test::doubles; namespace geom=mir::geometry; using namespace testing; namespace { struct Framebuffers : Test { NiceMock mock_egl; NiceMock hw_access_mock; mga::GraphicBufferAllocator allocator{ std::make_shared(), std::make_shared(mga::PropertiesOps{})}; MirPixelFormat format{mir_pixel_format_abgr_8888}; geom::Size display_size{180, 222}; }; } TEST_F(Framebuffers, framebuffers_have_correct_size) { mga::Framebuffers framebuffers(allocator, display_size, format, 2u); EXPECT_EQ(display_size, framebuffers.fb_size()); } TEST_F(Framebuffers, last_rendered_returns_valid) { mga::Framebuffers framebuffers(allocator, display_size, format, 2u); auto first_buffer = framebuffers.buffer_for_render(); auto first_buffer_ptr = first_buffer.get(); EXPECT_NE(first_buffer, framebuffers.last_rendered_buffer()); first_buffer.reset(); EXPECT_EQ(first_buffer_ptr, framebuffers.last_rendered_buffer().get()); } TEST_F(Framebuffers, last_rendered_is_first_returned_from_driver) { mga::Framebuffers framebuffers(allocator, display_size, format, 2u); auto buffer1 = framebuffers.buffer_for_render().get(); EXPECT_EQ(buffer1, framebuffers.last_rendered_buffer().get()); auto buffer2 = framebuffers.buffer_for_render().get(); EXPECT_EQ(buffer2, framebuffers.last_rendered_buffer().get()); } TEST_F(Framebuffers, no_rendering_returns_same_buffer) { mga::Framebuffers framebuffers(allocator, display_size, format, 2u); framebuffers.buffer_for_render().get(); auto buffer = framebuffers.last_rendered_buffer(); EXPECT_EQ(buffer, framebuffers.last_rendered_buffer()); } TEST_F(Framebuffers, three_buffers_for_hwc) { mga::Framebuffers framebuffers(allocator, display_size, format, 3u); auto buffer1 = framebuffers.buffer_for_render().get(); framebuffers.buffer_for_render(); framebuffers.buffer_for_render(); auto buffer4 = framebuffers.buffer_for_render().get(); EXPECT_EQ(buffer1, buffer4); } ./tests/unit-tests/graphics/android/test_fb_device.cpp0000644000015600001650000001604212676616125023266 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/mock_fb_hal_device.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/stub_android_native_buffer.h" #include "src/platforms/android/server/fb_device.h" #include "src/platforms/android/server/hwc_fallback_gl_renderer.h" #include "src/platforms/android/server/hwc_layerlist.h" #include "mir/test/doubles/mock_framebuffer_bundle.h" #include "mir/test/doubles/mock_android_hw.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_renderable.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "mir/test/doubles/mock_swapping_gl_context.h" #include "mir/test/doubles/stub_renderable_list_compositor.h" #include "mir/test/doubles/mock_renderable_list_compositor.h" #include #include namespace mg=mir::graphics; namespace mtd=mir::test::doubles; namespace mga=mir::graphics::android; namespace geom=mir::geometry; namespace mt=mir::test; using namespace testing; struct FBDevice : public ::testing::Test { virtual void SetUp() { fbnum = 4; format = HAL_PIXEL_FORMAT_RGBA_8888; fb_hal_mock = std::make_shared>( display_size.width.as_int(), display_size.height.as_int(), format, fbnum, dpi_x, dpi_y); mock_buffer = std::make_shared>(); native_buffer = std::make_shared(); ON_CALL(*mock_buffer, native_buffer_handle()) .WillByDefault(Return(native_buffer)); ON_CALL(mock_context, last_rendered_buffer()) .WillByDefault(Return(mock_buffer)); } float dpi_x{160.0f}; float dpi_y{150.0f}; unsigned int format, fbnum; geom::Size display_size{413, 516}; std::shared_ptr fb_hal_mock; std::shared_ptr mock_buffer; std::shared_ptr native_buffer; mtd::HardwareAccessMock hw_access_mock; testing::NiceMock mock_context; mga::LayerList list{std::make_shared(), {}, geom::Displacement{}}; mtd::StubRenderableListCompositor stub_compositor; mga::DisplayName primary{mga::DisplayName::primary}; }; TEST_F(FBDevice, reports_it_can_swap) { mga::FBDevice fbdev(fb_hal_mock); EXPECT_TRUE(fbdev.can_swap_buffers()); } TEST_F(FBDevice, rejects_renderables) { mg::RenderableList renderlist { std::make_shared(), std::make_shared() }; mga::FBDevice fbdev(fb_hal_mock); EXPECT_FALSE(fbdev.compatible_renderlist(renderlist)); } TEST_F(FBDevice, commits_frame) { EXPECT_CALL(*fb_hal_mock, post_interface(fb_hal_mock.get(), native_buffer->handle())) .Times(2) .WillOnce(Return(-1)) .WillOnce(Return(0)); EXPECT_CALL(mock_context, swap_buffers()) .Times(0); mga::FBDevice fbdev(fb_hal_mock); mga::DisplayContents content{primary, list, geom::Displacement{}, mock_context, stub_compositor}; EXPECT_THROW({ fbdev.commit({content}); }, std::runtime_error); fbdev.commit({content}); // Predictive bypass not enabled in FBDevice EXPECT_EQ(0, fbdev.recommended_sleep().count()); } //not all fb devices provide a swap interval hook. make sure we don't explode if thats the case TEST_F(FBDevice, does_not_segfault_if_null_swapinterval_hook) { fb_hal_mock->setSwapInterval = nullptr; mga::FbControl fb_control(fb_hal_mock); } TEST_F(FBDevice, can_screen_on_off) { Sequence seq; EXPECT_CALL(*fb_hal_mock, setSwapInterval_interface(fb_hal_mock.get(), 1)) .Times(1); EXPECT_CALL(*fb_hal_mock, enableScreen_interface(_,0)) .InSequence(seq); EXPECT_CALL(*fb_hal_mock, enableScreen_interface(_,0)) .InSequence(seq); EXPECT_CALL(*fb_hal_mock, enableScreen_interface(_,0)) .InSequence(seq); EXPECT_CALL(*fb_hal_mock, enableScreen_interface(_,1)) .InSequence(seq); mga::FbControl fb_control(fb_hal_mock); fb_control.power_mode(mga::DisplayName::primary, mir_power_mode_standby); fb_control.power_mode(mga::DisplayName::primary, mir_power_mode_suspend); fb_control.power_mode(mga::DisplayName::primary, mir_power_mode_off); fb_control.power_mode(mga::DisplayName::primary, mir_power_mode_on); EXPECT_THROW({ fb_control.power_mode(mga::DisplayName::external, mir_power_mode_on); }, std::runtime_error); } TEST_F(FBDevice, bundle_from_fb) { mga::FbControl fb_control(fb_hal_mock); auto attribs = fb_control.active_config_for(mga::DisplayName::primary); EXPECT_EQ(display_size, attribs.modes[attribs.current_mode_index].size); EXPECT_EQ(mir_pixel_format_abgr_8888, attribs.current_format); } //LP: #1517597 TEST_F(FBDevice, reports_dpi) { float mm_per_inch = 25.4; geom::Size physical_size_mm = { display_size.width.as_int() / dpi_x * mm_per_inch , display_size.height.as_int() / dpi_y * mm_per_inch }; mga::FbControl fb_control(fb_hal_mock); auto attribs = fb_control.active_config_for(mga::DisplayName::primary); EXPECT_EQ(physical_size_mm, attribs.physical_size_mm); } TEST_F(FBDevice, reports_0_when_0_dpi) { geom::Size physical_size_mm = { 0, 0 }; fb_hal_mock = std::make_shared>( display_size.width.as_int(), display_size.height.as_int(), format, fbnum, 0.0f, 0.0f); mga::FbControl fb_control(fb_hal_mock); auto attribs = fb_control.active_config_for(mga::DisplayName::primary); EXPECT_EQ(physical_size_mm, attribs.physical_size_mm); } TEST_F(FBDevice, supports_primary_display) { mga::FbControl fb_control(fb_hal_mock); auto const primary = fb_control.active_config_for(mga::DisplayName::primary); EXPECT_TRUE(primary.connected); EXPECT_TRUE(primary.used); } TEST_F(FBDevice, does_not_support_external_display) { mga::FbControl fb_control(fb_hal_mock); auto const external = fb_control.active_config_for(mga::DisplayName::external); EXPECT_FALSE(external.connected); EXPECT_FALSE(external.used); } TEST_F(FBDevice, assigns_different_output_ids_to_displays) { mga::FbControl fb_control(fb_hal_mock); auto const primary = fb_control.active_config_for(mga::DisplayName::primary); auto const external = fb_control.active_config_for(mga::DisplayName::external); EXPECT_NE(primary.id, external.id); } ./tests/unit-tests/graphics/android/test_display_generic.cpp0000644000015600001650000000503412676616125024520 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/display.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/gl_context.h" #include "mir/graphics/platform.h" #include "mir/options/program_option.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/test/doubles/null_virtual_terminal.h" #include "src/server/report/null_report_factory.h" #include "mir/graphics/default_display_configuration_policy.h" #include "mir/test/doubles/mock_android_hw.h" #include "mir/test/doubles/mock_display_device.h" #include #include namespace mg = mir::graphics; namespace mgm = mg::mesa; namespace mtd = mir::test::doubles; class DisplayTestGeneric : public ::testing::Test { public: DisplayTestGeneric() { using namespace testing; ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), SetArgPointee<4>(1), Return(EGL_TRUE))); mock_egl.provide_egl_extensions(); mock_gl.provide_gles_extensions(); } std::shared_ptr create_display() { auto const platform = create_host_platform( std::make_shared(), std::make_shared(), mir::report::null_display_report()); return platform->create_display( std::make_shared(), std::make_shared()); } ::testing::NiceMock mock_egl; ::testing::NiceMock mock_gl; ::testing::NiceMock hw_access_mock; }; #include "../test_display.h" ./tests/unit-tests/graphics/android/test_server_interpreter.cpp0000644000015600001650000001610612676616125025312 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/android/server/server_render_window.h" #include "mir/test/doubles/mock_buffer.h" #include "mir/test/doubles/mock_fence.h" #include "mir/test/doubles/mock_interpreter_resource_cache.h" #include "mir/test/doubles/mock_framebuffer_bundle.h" #include "mir/test/doubles/stub_android_native_buffer.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "mir_toolkit/common.h" #include #include #include #include namespace mt=mir::test; namespace mtd=mir::test::doubles; namespace geom=mir::geometry; namespace mg=mir::graphics; namespace mga=mir::graphics::android; namespace { //krillin and arale need to clear their fences before hwc commit. struct StubPropertiesWrapper : mga::PropertiesWrapper { StubPropertiesWrapper(bool should_clear_fence) : name(should_clear_fence ? "arale" : "otherdevice") { } int property_get(char const* key, char* value, char const* default_value) const override { if (strncmp(key, "ro.product.device", PROP_VALUE_MAX) == 0) strncpy(value, name.c_str(), name.size()); else strncpy(value, default_value, PROP_VALUE_MAX); return 0; } std::string name; }; struct ServerRenderWindow : public ::testing::Test { std::shared_ptr mock_buffer{std::make_shared>()}; std::shared_ptr mock_cache{ std::make_shared>()}; std::shared_ptr mock_fb_bundle{ std::make_shared>()}; MirPixelFormat format{mir_pixel_format_abgr_8888}; StubPropertiesWrapper wrapper{false}; mga::DeviceQuirks quirks{wrapper}; mga::ServerRenderWindow render_window{mock_fb_bundle, format, mock_cache, quirks}; }; } TEST_F(ServerRenderWindow, returns_buffer_on_request) { using namespace testing; auto stub_buffer = std::make_shared(); EXPECT_CALL(*mock_fb_bundle, buffer_for_render()) .WillOnce(Return(mock_buffer)); EXPECT_CALL(*mock_buffer, native_buffer_handle()) .WillOnce(Return(stub_buffer)); std::shared_ptr tmp = mock_buffer; std::shared_ptr tmp2 = stub_buffer; EXPECT_CALL(*mock_cache, store_buffer(tmp, tmp2)); auto rc_buffer = render_window.driver_requests_buffer(); EXPECT_EQ(stub_buffer.get(), rc_buffer); } TEST_F(ServerRenderWindow, updates_fences_and_returns_buffer_on_queue) { using namespace testing; int fake_fence = 488; auto stub_buffer = std::make_shared(); EXPECT_CALL(*mock_fb_bundle, buffer_for_render()) .WillOnce(Return(mock_buffer)); EXPECT_CALL(*mock_buffer, native_buffer_handle()) .WillOnce(Return(stub_buffer)); render_window.driver_requests_buffer(); Mock::VerifyAndClearExpectations(mock_fb_bundle.get()); std::shared_ptr buf1 = mock_buffer; EXPECT_CALL(*mock_cache, update_native_fence(stub_buffer->anwb(), fake_fence)); EXPECT_CALL(*mock_cache, retrieve_buffer(stub_buffer->anwb())) .WillOnce(Return(mock_buffer)); render_window.driver_returns_buffer(stub_buffer->anwb(), fake_fence); Mock::VerifyAndClearExpectations(mock_fb_bundle.get()); } TEST_F(ServerRenderWindow, clears_fence_when_quirk_present) { using namespace testing; StubPropertiesWrapper wrapper{true}; mga::DeviceQuirks quirks{wrapper}; mga::ServerRenderWindow render_window{mock_fb_bundle, format, mock_cache, quirks}; int fake_fence = 488; auto stub_buffer = std::make_shared(); EXPECT_CALL(*mock_fb_bundle, buffer_for_render()) .WillOnce(Return(mock_buffer)); EXPECT_CALL(*mock_buffer, native_buffer_handle()) .WillOnce(Return(stub_buffer)); render_window.driver_requests_buffer(); Mock::VerifyAndClearExpectations(mock_fb_bundle.get()); std::shared_ptr buf1 = mock_buffer; EXPECT_CALL(*mock_cache, update_native_fence(stub_buffer->anwb(), fake_fence)) .Times(0); EXPECT_CALL(*mock_cache, retrieve_buffer(stub_buffer->anwb())) .WillOnce(Return(mock_buffer)); render_window.driver_returns_buffer(stub_buffer->anwb(), fake_fence); Mock::VerifyAndClearExpectations(mock_fb_bundle.get()); } TEST_F(ServerRenderWindow, returns_format) { EXPECT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, render_window.driver_requests_info(NATIVE_WINDOW_FORMAT)); } TEST_F(ServerRenderWindow, returns_usage_bits_for_fb) { using namespace testing; auto bits = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB; EXPECT_THAT(render_window.driver_requests_info(NATIVE_WINDOW_CONSUMER_USAGE_BITS), Eq(bits)); } TEST_F(ServerRenderWindow, returns_different_format_if_format_changes) { render_window.dispatch_driver_request_format(HAL_PIXEL_FORMAT_RGBX_8888); EXPECT_EQ(HAL_PIXEL_FORMAT_RGBX_8888, render_window.driver_requests_info(NATIVE_WINDOW_FORMAT)); } TEST_F(ServerRenderWindow, returns_sensible_size_values_without_size_having_been_set) { using namespace testing; geom::Size test_size{4, 5}; EXPECT_CALL(*mock_fb_bundle, fb_size()) .Times(4) .WillRepeatedly(Return(test_size)); unsigned int rc_width = render_window.driver_requests_info(NATIVE_WINDOW_DEFAULT_WIDTH); unsigned int rc_height = render_window.driver_requests_info(NATIVE_WINDOW_DEFAULT_HEIGHT); EXPECT_EQ(test_size.width.as_uint32_t(), rc_width); EXPECT_EQ(test_size.height.as_uint32_t(), rc_height); rc_width = render_window.driver_requests_info(NATIVE_WINDOW_WIDTH); rc_height = render_window.driver_requests_info(NATIVE_WINDOW_HEIGHT); EXPECT_EQ(test_size.width.as_uint32_t(), rc_width); EXPECT_EQ(test_size.height.as_uint32_t(), rc_height); } TEST_F(ServerRenderWindow, returns_no_transform_when_asked_for_hint) { EXPECT_EQ(0, render_window.driver_requests_info(NATIVE_WINDOW_TRANSFORM_HINT)); } TEST_F(ServerRenderWindow, reports_framebuffer_concrete_type) { EXPECT_EQ(NATIVE_WINDOW_FRAMEBUFFER, render_window.driver_requests_info(NATIVE_WINDOW_CONCRETE_TYPE)); } TEST_F(ServerRenderWindow, throws_on_driver_unknown_inquiry) { EXPECT_THROW({ render_window.driver_requests_info(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND); }, std::runtime_error); } ./tests/unit-tests/graphics/test_graphics_platform.h0000644000015600001650000000363312676616125023113 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Cemil Azizoglu */ #ifndef TEST_GRAPHICS_PLATFORM_H_ #define TEST_GRAPHICS_PLATFORM_H_ TEST_F(GraphicsPlatform, buffer_allocator_creation) { using namespace testing; EXPECT_NO_THROW ( auto platform = create_platform(); auto allocator = platform->create_buffer_allocator(); EXPECT_TRUE(allocator.get()); ); } TEST_F(GraphicsPlatform, buffer_creation) { auto platform = create_platform(); auto allocator = platform->create_buffer_allocator(); auto supported_pixel_formats = allocator->supported_pixel_formats(); ASSERT_NE(0u, supported_pixel_formats.size()); geom::Size size{320, 240}; MirPixelFormat const pf{supported_pixel_formats[0]}; mg::BufferUsage usage{mg::BufferUsage::hardware}; mg::BufferProperties buffer_properties{size, pf, usage}; auto buffer = allocator->alloc_buffer(buffer_properties); ASSERT_TRUE(buffer.get() != NULL); EXPECT_EQ(buffer->size(), size); EXPECT_EQ(buffer->pixel_format(), pf); } TEST_F(GraphicsPlatform, connection_ipc_package) { auto platform = create_platform(); auto ipc_ops = platform->make_ipc_operations(); auto pkg = ipc_ops->connection_ipc_package(); ASSERT_TRUE(pkg.get() != NULL); } #endif // TEST_GRAPHICS_PLATFORM_H_ ./tests/unit-tests/graphics/test_default_display_configuration_policy.cpp0000644000015600001650000002663712676616125027432 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/default_display_configuration_policy.h" #include "mir/graphics/display_configuration.h" #include "mir/geometry/displacement.h" #include "mir/test/doubles/stub_display_configuration.h" #include #include using namespace mir::graphics; using namespace mir::geometry; namespace { class StubDisplayConfiguration : public mir::test::doubles::StubDisplayConfig { public: using mir::test::doubles::StubDisplayConfig::StubDisplayConfig; StubDisplayConfiguration( size_t max_simultaneous_outputs, std::vector const& outputs) : StubDisplayConfig{ {{DisplayConfigurationCardId{1}, max_simultaneous_outputs}}, outputs} { } }; } DisplayConfigurationOutput default_output(DisplayConfigurationOutputId id) { return { id, DisplayConfigurationCardId{1}, DisplayConfigurationOutputType::vga, {mir_pixel_format_abgr_8888}, { {Size{523, 555}, 60.0} }, 0, Size{324, 642}, true, false, Point{X{123}, Y{343}}, 0, mir_pixel_format_abgr_8888, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }; } DisplayConfigurationOutput connected_with_modes() { DisplayConfigurationOutput output = default_output(DisplayConfigurationOutputId{10}) ; output.modes = { {Size{123, 111}, 59.9}, {Size{123, 111}, 59.9}, {Size{123, 111}, 59.9} }; output.preferred_mode_index = 2; output.current_mode_index = 1; return output; } DisplayConfigurationOutput connected_without_modes() { DisplayConfigurationOutput output = default_output(DisplayConfigurationOutputId{11}); output.pixel_formats = {}; output.modes = {}; output.current_format = mir_pixel_format_invalid; output.current_mode_index = std::numeric_limits::max(); return output; } DisplayConfigurationOutput connected_with_single_mode() { return default_output(DisplayConfigurationOutputId{12}); } DisplayConfigurationOutput not_connected() { DisplayConfigurationOutput output = default_output(DisplayConfigurationOutputId{13}); output.connected = false; output.current_mode_index = 1; return output; } DisplayConfigurationOutput connected_with_rgba_and_xrgb() { DisplayConfigurationOutput output = default_output(DisplayConfigurationOutputId{14}); output.pixel_formats = {mir_pixel_format_argb_8888, mir_pixel_format_xrgb_8888}; return output; } DisplayConfigurationOutput connected_with_xrgb_bgr() { DisplayConfigurationOutput output = default_output(DisplayConfigurationOutputId{15}); output.pixel_formats = {mir_pixel_format_xrgb_8888, mir_pixel_format_bgr_888}; output.current_format = mir_pixel_format_bgr_888; return output; } StubDisplayConfiguration create_default_configuration(size_t max_outputs = 4) { return StubDisplayConfiguration { max_outputs, { connected_with_modes(), connected_without_modes(), connected_with_single_mode(), not_connected(), } }; } TEST(CloneDisplayConfigurationPolicyTest, uses_all_connected_valid_outputs) { using namespace ::testing; CloneDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration()}; policy.apply_to(conf); conf.for_each_output([&conf](DisplayConfigurationOutput const& output) { if (output.connected && output.modes.size() > 0) { EXPECT_TRUE(output.used); EXPECT_EQ(Point(), output.top_left); EXPECT_EQ(output.preferred_mode_index, output.current_mode_index); } else { EXPECT_FALSE(output.used); } }); } TEST(CloneDisplayConfigurationPolicyTest, default_policy_is_power_mode_on) { using namespace ::testing; CloneDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration()}; policy.apply_to(conf); conf.for_each_output([](DisplayConfigurationOutput const& output) { EXPECT_EQ(mir_power_mode_on, output.power_mode); }); } TEST(CloneDisplayConfigurationPolicyTest, default_orientation_is_normal) { using namespace ::testing; CloneDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration()}; conf.for_each_output([&conf](DisplayConfigurationOutput const& output) { EXPECT_EQ(mir_orientation_normal, output.orientation); }); } TEST(CloneDisplayConfigurationPolicyTest, does_not_enable_more_outputs_than_supported) { using namespace ::testing; size_t const max_simultaneous_outputs{1}; CloneDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration(max_simultaneous_outputs)}; policy.apply_to(conf); size_t used_count{0}; conf.for_each_output([&used_count](DisplayConfigurationOutput const& output) { if (output.used) ++used_count; }); EXPECT_GE(max_simultaneous_outputs, used_count); } TEST(CloneDisplayConfigurationPolicyTest, prefer_opaque_over_alpha) { using namespace ::testing; CloneDisplayConfigurationPolicy policy; StubDisplayConfiguration pick_xrgb{ { connected_with_rgba_and_xrgb() } }; policy.apply_to(pick_xrgb); pick_xrgb.for_each_output([](DisplayConfigurationOutput const& output) { EXPECT_EQ(mir_pixel_format_xrgb_8888, output.current_format); }); } TEST(CloneDisplayConfigurationPolicyTest, preserve_opaque_selection) { using namespace ::testing; CloneDisplayConfigurationPolicy policy; StubDisplayConfiguration keep_bgr{ { connected_with_xrgb_bgr() } }; policy.apply_to(keep_bgr); keep_bgr.for_each_output([](DisplayConfigurationOutput const& output) { EXPECT_EQ(mir_pixel_format_bgr_888, output.current_format); }); } TEST(CloneDisplayConfigurationPolicyTest, accept_transparency_when_only_option) { using namespace ::testing; CloneDisplayConfigurationPolicy policy; StubDisplayConfiguration pick_rgba{ { default_output(DisplayConfigurationOutputId{15}) } }; policy.apply_to(pick_rgba); pick_rgba.for_each_output([](DisplayConfigurationOutput const& output) { EXPECT_EQ(mir_pixel_format_abgr_8888, output.current_format); }); } TEST(SingleDisplayConfigurationPolicyTest, uses_first_of_connected_valid_outputs) { using namespace ::testing; SingleDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration()}; policy.apply_to(conf); bool is_first{true}; conf.for_each_output([&conf, &is_first](DisplayConfigurationOutput const& output) { if (output.connected && output.modes.size() > 0 && is_first) { EXPECT_TRUE(output.used); EXPECT_EQ(Point(), output.top_left); EXPECT_EQ(output.preferred_mode_index, output.current_mode_index); is_first = false; } else { EXPECT_FALSE(output.used); } }); } TEST(SingleDisplayConfigurationPolicyTest, default_policy_is_power_mode_on) { using namespace ::testing; SingleDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration()}; policy.apply_to(conf); bool is_first{true}; conf.for_each_output([&is_first](DisplayConfigurationOutput const& output) { if (output.connected && output.modes.size() > 0 && is_first) { EXPECT_EQ(mir_power_mode_on, output.power_mode); is_first = false; } }); } TEST(SingleDisplayConfigurationPolicyTest, default_orientation_is_normal) { using namespace ::testing; SingleDisplayConfigurationPolicy policy; auto conf = create_default_configuration(); //StubDisplayConfiguration conf{create_default_configuration()}; conf.for_each_output([&conf](DisplayConfigurationOutput const& output) { EXPECT_EQ(mir_orientation_normal, output.orientation); }); } TEST(SingleDisplayConfigurationPolicyTest, does_not_enable_more_outputs_than_supported) { using namespace ::testing; size_t const max_simultaneous_outputs{1}; SingleDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration(max_simultaneous_outputs)}; policy.apply_to(conf); size_t used_count{0}; conf.for_each_output([&used_count](DisplayConfigurationOutput const& output) { if (output.used) ++used_count; }); EXPECT_GE(max_simultaneous_outputs, used_count); } TEST(SideBySideDisplayConfigurationPolicyTest, uses_all_connected_valid_outputs) { using namespace ::testing; SideBySideDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration()}; policy.apply_to(conf); Point expected_position; conf.for_each_output([&](DisplayConfigurationOutput const& output) { if (output.connected && output.modes.size() > 0) { EXPECT_TRUE(output.used); EXPECT_EQ(expected_position, output.top_left); EXPECT_EQ(output.preferred_mode_index, output.current_mode_index); expected_position += as_displacement(output.extents().size).dx; } else { EXPECT_FALSE(output.used); } }); } TEST(SideBySideDisplayConfigurationPolicyTest, default_policy_is_power_mode_on) { using namespace ::testing; SideBySideDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration()}; policy.apply_to(conf); conf.for_each_output([](DisplayConfigurationOutput const& output) { if (output.connected && output.modes.size() > 0) { EXPECT_EQ(mir_power_mode_on, output.power_mode); } }); } TEST(SideBySideDisplayConfigurationPolicyTest, default_orientation_is_normal) { using namespace ::testing; SideBySideDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration()}; conf.for_each_output([&conf](DisplayConfigurationOutput const& output) { EXPECT_EQ(mir_orientation_normal, output.orientation); }); } TEST(SideBySideDisplayConfigurationPolicyTest, does_not_enable_more_outputs_than_supported) { using namespace ::testing; size_t const max_simultaneous_outputs{1}; SideBySideDisplayConfigurationPolicy policy; StubDisplayConfiguration conf{create_default_configuration(max_simultaneous_outputs)}; policy.apply_to(conf); size_t used_count{0}; conf.for_each_output([&used_count](DisplayConfigurationOutput const& output) { if (output.used) ++used_count; }); EXPECT_GE(max_simultaneous_outputs, used_count); } ./tests/unit-tests/graphics/test_buffer_properties.cpp0000644000015600001650000000606212676616125023466 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/buffer_properties.h" #include #include namespace mg = mir::graphics; namespace geom = mir::geometry; TEST(buffer_properties, default_create) { geom::Size size; MirPixelFormat pixel_format{mir_pixel_format_invalid}; mg::BufferUsage usage{mg::BufferUsage::undefined}; mg::BufferProperties prop; EXPECT_EQ(size, prop.size); EXPECT_EQ(pixel_format, prop.format); EXPECT_EQ(usage, prop.usage); } TEST(buffer_properties, custom_create) { geom::Size size{66, 166}; MirPixelFormat pixel_format{mir_pixel_format_abgr_8888}; mg::BufferUsage usage{mg::BufferUsage::hardware}; mg::BufferProperties prop{size, pixel_format, usage}; EXPECT_EQ(size, prop.size); EXPECT_EQ(pixel_format, prop.format); EXPECT_EQ(usage, prop.usage); } TEST(buffer_properties, equal_properties_test_equal) { geom::Size size{66, 166}; MirPixelFormat pixel_format{mir_pixel_format_abgr_8888}; mg::BufferUsage usage{mg::BufferUsage::hardware}; mg::BufferProperties prop0{size, pixel_format, usage}; mg::BufferProperties prop1{size, pixel_format, usage}; EXPECT_EQ(prop0, prop0); EXPECT_EQ(prop1, prop1); EXPECT_EQ(prop0, prop1); EXPECT_EQ(prop1, prop0); } TEST(buffer_properties, unequal_properties_test_unequal) { geom::Size size[2] = { {geom::Width{66}, geom::Height{166}}, {geom::Width{67}, geom::Height{166}} }; MirPixelFormat pixel_format[2] = { mir_pixel_format_abgr_8888, mir_pixel_format_bgr_888 }; mg::BufferUsage usage[2] = { mg::BufferUsage::hardware, mg::BufferUsage::software }; mg::BufferProperties prop000{size[0], pixel_format[0], usage[0]}; /* This approach doesn't really scale, but it's good enough for now */ for (int s = 0; s < 2; s++) { for (int p = 0; p < 2; p++) { for (int u = 0; u < 2; u++) { mg::BufferProperties prop{size[s], pixel_format[p], usage[u]}; if (s != 0 || p != 0 || u != 0) { EXPECT_NE(prop000, prop); EXPECT_NE(prop, prop000); } else { EXPECT_EQ(prop000, prop); EXPECT_EQ(prop, prop000); } } } } } ./tests/unit-tests/graphics/test_surfaceless_egl_context.cpp0000644000015600001650000001722612676616125024657 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Christopher James Halse Rogers */ #include "mir/graphics/surfaceless_egl_context.h" #include "mir/test/doubles/mock_egl.h" #include #include #include #include namespace mg = mir::graphics; namespace mtd = mir::test::doubles; class SurfacelessEGLContextTest : public ::testing::Test { public: EGLDisplay const fake_display{reinterpret_cast(0xfffaaafa)}; EGLSurface const fake_surface{reinterpret_cast(0xdeadbeef)}; EGLContext const fake_context{reinterpret_cast(0xfaafbaaf)}; protected: virtual void SetUp() { using namespace testing; ON_CALL(mock_egl, eglCreatePbufferSurface(_,_,_)) .WillByDefault(Return(fake_surface)); ON_CALL(mock_egl, eglCreateContext(_,_,_,_)) .WillByDefault(Return(fake_context)); ON_CALL(mock_egl, eglGetCurrentContext()) .WillByDefault(Return(EGL_NO_CONTEXT)); } testing::NiceMock mock_egl; }; namespace { MATCHER(ConfigAttribContainsPBufferFlag, "") { EGLint surface_type = 0; bool found_surface_type = false; std::list pretty_surface; for (int i = 0; arg[i] != EGL_NONE; ++i) { if (arg[i] == EGL_SURFACE_TYPE) { surface_type = arg[i+1]; found_surface_type = true; } } if (found_surface_type) { if (surface_type == EGL_DONT_CARE) { pretty_surface.push_back("EGL_DONT_CARE"); } else { if (surface_type & EGL_MULTISAMPLE_RESOLVE_BOX_BIT) { pretty_surface.push_back("EGL_MULTISAMPLE_RESOLVE_BOX_BIT"); } if (surface_type & EGL_PBUFFER_BIT) { pretty_surface.push_back("EGL_PBUFFER_BIT"); } if (surface_type & EGL_PIXMAP_BIT) { pretty_surface.push_back("EGL_PIXMAP_BIT"); } if (surface_type & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) { pretty_surface.push_back("EGL_SWAP_BEHAVIOR_PRESERVED_BIT"); } if (surface_type & EGL_VG_ALPHA_FORMAT_PRE_BIT) { pretty_surface.push_back("EGL_VG_ALPHA_FORMAT_PRE_BIT"); } if (surface_type & EGL_VG_COLORSPACE_LINEAR_BIT) { pretty_surface.push_back("EGL_VG_COLORSPACE_LINEAR_BIT"); } if (surface_type & EGL_WINDOW_BIT) { pretty_surface.push_back("EGL_WINDOW_BIT"); } } std::string pretty_result = pretty_surface.back(); pretty_surface.pop_back(); for (auto& pretty : pretty_surface) { pretty_result += " | " + pretty; } *result_listener << "surface type is "<< pretty_result; } else { *result_listener << "no surface type set"; } return found_surface_type && (surface_type != EGL_DONT_CARE) && (surface_type & EGL_PBUFFER_BIT); } } TEST_F(SurfacelessEGLContextTest, UsesPBufferContainingAttribsListByDefault) { using namespace testing; ON_CALL(mock_egl, eglQueryString(_,_)) .WillByDefault(Return("")); EXPECT_CALL(mock_egl, eglChooseConfig(_, ConfigAttribContainsPBufferFlag(), _,_,_)) .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE))); mg::SurfacelessEGLContext ctx_noattrib(fake_display, EGL_NO_CONTEXT); } TEST_F(SurfacelessEGLContextTest, KeepsPBufferInAttribsList) { using namespace testing; const EGLint attribs_with_pbuffer[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT | EGL_WINDOW_BIT, EGL_NONE }; ON_CALL(mock_egl, eglQueryString(_,_)) .WillByDefault(Return("")); EXPECT_CALL(mock_egl, eglChooseConfig(_, ConfigAttribContainsPBufferFlag(), _,_,_)) .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE))); mg::SurfacelessEGLContext ctx_attribs_with_pbuffer(fake_display, attribs_with_pbuffer, EGL_NO_CONTEXT); } TEST_F(SurfacelessEGLContextTest, AddsPBufferToAttribList) { using namespace testing; const EGLint attribs_without_surface_type[] = { EGL_ALPHA_SIZE, 8, EGL_CLIENT_APIS, EGL_OPENGL_ES2_BIT, EGL_NONE }; ON_CALL(mock_egl, eglQueryString(_,_)) .WillByDefault(Return("")); EXPECT_CALL(mock_egl, eglChooseConfig(_, ConfigAttribContainsPBufferFlag(), _,_,_)) .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE))); mg::SurfacelessEGLContext ctx_attribs_without_surface_type(fake_display, attribs_without_surface_type, EGL_NO_CONTEXT); } TEST_F(SurfacelessEGLContextTest, AddsPBufferWhenNonPBufferSurfaceTypeRequested) { using namespace testing; const EGLint attribs_without_pbuffer[] = { EGL_SURFACE_TYPE, EGL_DONT_CARE, EGL_ALPHA_SIZE, 8, EGL_CLIENT_APIS, EGL_OPENGL_ES2_BIT, EGL_NONE }; ON_CALL(mock_egl, eglQueryString(_,_)) .WillByDefault(Return("")); EXPECT_CALL(mock_egl, eglChooseConfig(_, ConfigAttribContainsPBufferFlag(), _,_,_)) .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE))); mg::SurfacelessEGLContext ctx_attribs_without_pbuffer(fake_display, attribs_without_pbuffer, EGL_NO_CONTEXT); } TEST_F(SurfacelessEGLContextTest, DoesNotRequestPBufferWithNoAttrib) { using namespace testing; ON_CALL(mock_egl, eglQueryString(_,_)) .WillByDefault(Return("EGL_KHR_surfaceless_context")); EXPECT_CALL(mock_egl, eglChooseConfig(_, Not(ConfigAttribContainsPBufferFlag()), _,_,_)) .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE))); mg::SurfacelessEGLContext ctx_noattrib(fake_display, EGL_NO_CONTEXT); } TEST_F(SurfacelessEGLContextTest, DoesNotAddPBufferToAttribList) { using namespace testing; const EGLint attribs_without_surface_type[] = { EGL_ALPHA_SIZE, 8, EGL_CLIENT_APIS, EGL_OPENGL_ES2_BIT, EGL_NONE }; ON_CALL(mock_egl, eglQueryString(_,_)) .WillByDefault(Return("EGL_KHR_surfaceless_context")); EXPECT_CALL(mock_egl, eglChooseConfig(_, Not(ConfigAttribContainsPBufferFlag()), _,_,_)) .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE))); mg::SurfacelessEGLContext ctx_attribs_without_surface_type(fake_display, attribs_without_surface_type, EGL_NO_CONTEXT); } TEST_F(SurfacelessEGLContextTest, DoesNotAddPBufferToSurfaceTypeRequest) { using namespace testing; const EGLint attribs_with_surface_type[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_ALPHA_SIZE, 8, EGL_CLIENT_APIS, EGL_OPENGL_ES2_BIT, EGL_NONE }; ON_CALL(mock_egl, eglQueryString(_,_)) .WillByDefault(Return("EGL_KHR_surfaceless_context")); EXPECT_CALL(mock_egl, eglChooseConfig(_, Not(ConfigAttribContainsPBufferFlag()), _,_,_)) .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE))); mg::SurfacelessEGLContext ctx_attribs_with_surface_type(fake_display, attribs_with_surface_type, EGL_NO_CONTEXT); } ./tests/unit-tests/graphics/test_overlapping_output_grouping.cpp0000644000015600001650000002264112676616125025622 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/overlapping_output_grouping.h" #include "mir/graphics/display_configuration.h" #include "mir/geometry/rectangle.h" #include "mir/geometry/rectangles.h" #include #include #include namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { class StubDisplayConfiguration : public mg::DisplayConfiguration { public: struct OutputInfo { OutputInfo( geom::Rectangle const& rect, bool connected, bool used, MirOrientation orientation, MirPowerMode power_mode = mir_power_mode_on) : rect(rect), connected{connected}, used{used}, orientation{orientation}, power_mode{power_mode} { } geom::Rectangle rect; bool connected; bool used; MirOrientation orientation; MirPowerMode power_mode; }; StubDisplayConfiguration(std::vector const& info) : outputs{info} { } void for_each_card(std::function f) const { mg::DisplayConfigurationCard card { mg::DisplayConfigurationCardId{1}, outputs.size() }; f(card); } void for_each_output(std::function f) const { uint32_t i = 1; for (auto const& info : outputs) { std::vector modes(i); modes[i - 1] = {info.rect.size, 59.9}; mg::DisplayConfigurationOutput output { mg::DisplayConfigurationOutputId(i), mg::DisplayConfigurationCardId{1}, mg::DisplayConfigurationOutputType::svideo, {}, modes, i - 1, {100, 100}, info.connected, info.used, info.rect.top_left, i - 1, mir_pixel_format_invalid, info.power_mode, info.orientation, 1.0f, mir_form_factor_monitor }; f(output); i++; } } void for_each_output(std::function) override { } std::unique_ptr clone() const override { return {}; } std::vector outputs; }; class OverlappingOutputGroupingTest : public testing::Test { public: void check_groupings(std::vector const& info, std::vector const& expected_groups) { StubDisplayConfiguration conf{info}; mg::OverlappingOutputGrouping grouping{conf}; std::vector> grouping_results; grouping.for_each_group( [&](mg::OverlappingOutputGroup const& group) { std::vector outputs; group.for_each_output([&](mg::DisplayConfigurationOutput const& output) { outputs.push_back(output); }); grouping_results.push_back(outputs); }); auto expected_groups_copy = expected_groups; EXPECT_EQ(expected_groups.size(), grouping_results.size()); for (auto const& v : grouping_results) { geom::Rectangles rects; for (auto const& output : v) rects.add(output.extents()); auto iter = std::find(expected_groups_copy.begin(), expected_groups_copy.end(), rects); EXPECT_TRUE(iter != expected_groups_copy.end()) << "Failed to find " << rects; expected_groups_copy.erase(iter); } } }; } TEST_F(OverlappingOutputGroupingTest, ignores_invalid_outputs) { std::vector info { {{{0,0}, {100, 100}}, false, false, mir_orientation_normal}, {{{0,0}, {100, 100}}, true, false, mir_orientation_normal}, {{{0,0}, {100, 100}}, false, true, mir_orientation_normal} }; std::vector expected_groups; check_groupings(info, expected_groups); } TEST_F(OverlappingOutputGroupingTest, distinct_outputs) { std::vector info { {{{0,0}, {100, 100}}, true, true, mir_orientation_normal}, {{{100,0}, {100, 100}}, true, true, mir_orientation_normal}, {{{0,100}, {100, 100}}, true, true, mir_orientation_normal} }; std::vector expected_groups { geom::Rectangles{{{0,0}, {100, 100}}}, geom::Rectangles{{{100,0}, {100, 100}}}, geom::Rectangles{{{0,100}, {100, 100}}}, }; check_groupings(info, expected_groups); } TEST_F(OverlappingOutputGroupingTest, rotated_output) { std::vector info { {{{0,0}, {100,200}}, true, true, mir_orientation_left}, }; std::vector expected_groups { geom::Rectangles{{{0,0}, {200,100}}}, }; check_groupings(info, expected_groups); } TEST_F(OverlappingOutputGroupingTest, rotated_outputs) { std::vector info { {{{0,0}, {100,200}}, true, true, mir_orientation_normal}, {{{1000,0}, {300,400}}, true, true, mir_orientation_left}, {{{2000,0}, {500,600}}, true, true, mir_orientation_right}, {{{3000,0}, {700,800}}, true, true, mir_orientation_inverted}, }; std::vector expected_groups { geom::Rectangles{{{0,0}, {100,200}}}, geom::Rectangles{{{1000,0}, {400,300}}}, geom::Rectangles{{{2000,0}, {600,500}}}, geom::Rectangles{{{3000,0}, {700,800}}}, }; check_groupings(info, expected_groups); } TEST_F(OverlappingOutputGroupingTest, rotation_creates_overlap) { std::vector info { {{{0,0}, {100,200}}, true, true, mir_orientation_left}, {{{100,0}, {100,200}}, true, true, mir_orientation_left}, }; std::vector expected_groups { geom::Rectangles{ {{0,0}, {200,100}}, {{100,0}, {200,100}} } }; check_groupings(info, expected_groups); } TEST_F(OverlappingOutputGroupingTest, different_orientation_prevents_grouping) { std::vector info { {{{0,0}, {100,200}}, true, true, mir_orientation_left}, {{{100,0}, {100,200}}, true, true, mir_orientation_normal}, }; std::vector expected_groups { geom::Rectangles{ {{0,0}, {200,100}} }, geom::Rectangles{ {{100,0}, {100,200}} }, }; check_groupings(info, expected_groups); } TEST_F(OverlappingOutputGroupingTest, overlapping_outputs) { std::vector info { {{{0,0}, {100, 100}}, true, true, mir_orientation_normal}, {{{0,0}, {50, 50}}, true, true, mir_orientation_normal}, {{{100,0}, {100, 100}}, true, true, mir_orientation_normal}, {{{101,0}, {100, 100}}, true, true, mir_orientation_normal} }; std::vector expected_groups { geom::Rectangles { {{0,0}, {100, 100}}, {{0,0}, {50, 50}} }, geom::Rectangles { {{100,0}, {100, 100}}, {{101,0}, {100, 100}} } }; check_groupings(info, expected_groups); } TEST_F(OverlappingOutputGroupingTest, multiply_overlapping_outputs) { std::vector info { {{{0,0}, {100, 100}}, true, true, mir_orientation_normal}, {{{150,150}, {50, 50}}, true, true, mir_orientation_normal}, {{{90,90}, {85, 85}}, true, true, mir_orientation_normal}, {{{50,50}, {100, 100}}, true, true, mir_orientation_normal} }; std::vector expected_groups { geom::Rectangles { {{0,0}, {100, 100}}, {{150,150}, {50, 50}}, {{90,90}, {85, 85}}, {{50,50}, {100, 100}} }, }; check_groupings(info, expected_groups); } TEST_F(OverlappingOutputGroupingTest, ignores_outputs_with_power_mode_not_on) { std::vector info { {{{0,0}, {100, 100}}, true, true, mir_orientation_normal, mir_power_mode_off}, {{{0,0}, {100, 100}}, true, true, mir_orientation_normal, mir_power_mode_standby}, {{{0,0}, {100, 100}}, true, true, mir_orientation_normal, mir_power_mode_suspend} }; std::vector expected_groups; check_groupings(info, expected_groups); } ./tests/unit-tests/graphics/test_platform_prober.cpp0000644000015600001650000002011512676616125023131 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include #include "mir/graphics/platform.h" #include "mir/graphics/platform_probe.h" #include "mir/options/program_option.h" #include "mir/raii.h" #ifdef MIR_BUILD_PLATFORM_MESA_KMS #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" #endif #ifdef MIR_BUILD_PLATFORM_ANDROID #include "mir/test/doubles/mock_android_hw.h" #endif #include "mir_test_framework/udev_environment.h" #include "mir_test_framework/executable_path.h" namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { const char describe_module[] = "describe_graphics_module"; std::vector> available_platforms() { std::vector> modules; #ifdef MIR_BUILD_PLATFORM_MESA_KMS modules.push_back(std::make_shared(mtf::server_platform("graphics-mesa-kms"))); #endif #ifdef MIR_BUILD_PLATFORM_ANDROID modules.push_back(std::make_shared(mtf::server_platform("graphics-android"))); #endif return modules; } void add_dummy_platform(std::vector>& modules) { modules.insert(modules.begin(), std::make_shared(mtf::server_platform("graphics-dummy.so"))); } std::shared_ptr ensure_android_probing_fails() { #ifdef MIR_BUILD_PLATFORM_ANDROID using namespace testing; auto mock_android = std::make_shared>(); ON_CALL(*mock_android, hw_get_module(_, _)) .WillByDefault(Return(-1)); return mock_android; #else return std::shared_ptr{}; #endif } std::shared_ptr ensure_mesa_probing_fails() { return std::make_shared(); } std::shared_ptr ensure_mesa_probing_succeeds() { auto udev = std::make_shared(); udev->add_standard_device("standard-drm-devices"); return udev; } std::shared_ptr ensure_android_probing_succeeds() { #ifdef MIR_BUILD_PLATFORM_ANDROID using namespace testing; auto mock_android = std::make_shared>(); ON_CALL(*mock_android, hw_get_module(_, _)) .WillByDefault(Return(0)); return mock_android; #else return std::shared_ptr{}; #endif } class ServerPlatformProbeMockDRM : public ::testing::Test { #if defined(MIR_BUILD_PLATFORM_MESA_KMS) || defined(MIR_BUILD_PLATFORM_MESA_X11) public: ::testing::NiceMock mock_drm; #endif }; } TEST(ServerPlatformProbe, ConstructingWithNoModulesIsAnError) { std::vector> empty_modules; mir::options::ProgramOption options; EXPECT_THROW(mir::graphics::module_for_device(empty_modules, options), std::runtime_error); } #ifdef MIR_BUILD_PLATFORM_MESA_KMS TEST_F(ServerPlatformProbeMockDRM, LoadsMesaPlatformWhenDrmMasterCanBeAcquired) { using namespace testing; mir::options::ProgramOption options; auto block_android = ensure_android_probing_fails(); auto fake_mesa = ensure_mesa_probing_succeeds(); auto modules = available_platforms(); auto module = mir::graphics::module_for_device(modules, options); ASSERT_NE(nullptr, module); auto descriptor = module->load_function(describe_module); auto description = descriptor(); EXPECT_THAT(description->name, HasSubstr("mesa-kms")); } //LP: #1526225, LP: #1526505, LP: #1515558, LP: #1526209 TEST_F(ServerPlatformProbeMockDRM, returns_kms_platform_when_nested) { using namespace testing; ON_CALL(mock_drm, drmSetMaster(_)) .WillByDefault(Return(-1)); mir::options::ProgramOption options; boost::program_options::options_description desc(""); desc.add_options() ("host-socket", boost::program_options::value(), "Host socket filename"); std::array args {{ "./aserver", "--host-socket", "/dev/null" }}; options.parse_arguments(desc, args.size(), args.data()); auto block_android = ensure_android_probing_fails(); auto block_mesa = ensure_mesa_probing_succeeds(); auto modules = available_platforms(); auto module = mir::graphics::module_for_device(modules, options); ASSERT_NE(nullptr, module); auto descriptor = module->load_function(describe_module); auto description = descriptor(); EXPECT_THAT(description->name, HasSubstr("mesa-kms")); } #endif #ifdef MIR_BUILD_PLATFORM_ANDROID TEST(ServerPlatformProbe, LoadsAndroidPlatformWhenHwaccessSucceeds) { using namespace testing; mir::options::ProgramOption options; auto block_mesa = ensure_mesa_probing_fails(); auto fake_android = ensure_android_probing_succeeds(); auto modules = available_platforms(); auto module = mir::graphics::module_for_device(modules, options); ASSERT_NE(nullptr, module); auto descriptor = module->load_function(describe_module); auto description = descriptor(); EXPECT_THAT(description->name, HasSubstr("android")); } #endif TEST(ServerPlatformProbe, ThrowsExceptionWhenNothingProbesSuccessfully) { using namespace testing; mir::options::ProgramOption options; auto block_android = ensure_android_probing_fails(); auto block_mesa = ensure_mesa_probing_fails(); EXPECT_THROW(mir::graphics::module_for_device(available_platforms(), options), std::runtime_error); } TEST(ServerPlatformProbe, LoadsSupportedModuleWhenNoBestModule) { using namespace testing; mir::options::ProgramOption options; auto block_android = ensure_android_probing_fails(); auto block_mesa = ensure_mesa_probing_fails(); auto modules = available_platforms(); add_dummy_platform(modules); auto module = mir::graphics::module_for_device(modules, options); ASSERT_NE(nullptr, module); auto descriptor = module->load_function(describe_module); auto description = descriptor(); EXPECT_THAT(description->name, HasSubstr("dummy")); } #if defined(MIR_BUILD_PLATFORM_MESA_KMS) || defined(MIR_BUILD_PLATFORM_MESA_X11) || defined(MIR_BUILD_PLATFORM_ANDROID) TEST_F(ServerPlatformProbeMockDRM, LoadsMesaOrAndroidInPreferenceToDummy) { using namespace testing; mir::options::ProgramOption options; auto ensure_mesa = ensure_mesa_probing_succeeds(); auto ensure_android = ensure_android_probing_succeeds(); auto modules = available_platforms(); add_dummy_platform(modules); auto module = mir::graphics::module_for_device(modules, options); ASSERT_NE(nullptr, module); auto descriptor = module->load_function(describe_module); auto description = descriptor(); EXPECT_THAT(description->name, Not(HasSubstr("dummy"))); } #endif TEST_F(ServerPlatformProbeMockDRM, IgnoresNonPlatformModules) { using namespace testing; mir::options::ProgramOption options; auto ensure_mesa = ensure_mesa_probing_succeeds(); auto ensure_android = ensure_android_probing_succeeds(); auto modules = available_platforms(); add_dummy_platform(modules); // NOTE: We want to load something that doesn't link with libmirplatform, // due to protobuf throwing a screaming hissy fit if it gets loaded twice. modules.push_back(std::make_shared(mtf::client_platform("dummy.so"))); auto module = mir::graphics::module_for_device(modules, options); EXPECT_NE(nullptr, module); } ./tests/unit-tests/graphics/CMakeLists.txt0000644000015600001650000000220012676616157020731 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_display_configuration.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_egl_extensions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_egl_error.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_default_display_configuration_policy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_id.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_properties.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_pixel_format_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_surfaceless_egl_context.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_overlapping_output_grouping.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_software_cursor.cpp ) list(APPEND UMOCK_UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_platform_prober.cpp ) add_subdirectory(nested/) add_subdirectory(offscreen/) add_subdirectory(egl_mock/) if (MIR_TEST_PLATFORM STREQUAL "android") add_subdirectory(android/) endif() if (MIR_TEST_PLATFORM STREQUAL "mesa-kms" OR MIR_TEST_PLATFORM STREQUAL "mesa-x11") add_subdirectory(mesa/) endif() set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) set(UMOCK_UNIT_TEST_SOURCES ${UMOCK_UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/graphics/test_buffer_id.cpp0000644000015600001650000000245312676616125021666 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/graphics/buffer_id.h" #include "mir/test/doubles/stub_buffer.h" #include #include namespace mg = mir::graphics; TEST(unique_generator, generate_unique) { using mir::test::doubles::StubBuffer; int const ids = 542; std::vector generated_ids; for (auto i=0; i < ids; i++) generated_ids.push_back(StubBuffer().id()); while (!generated_ids.empty()) { mg::BufferID test_id = generated_ids.back(); generated_ids.pop_back(); for (auto id : generated_ids) { EXPECT_NE(id, test_id); } } } ./tests/unit-tests/client/0000755000015600001650000000000012676616160015647 5ustar jenkinsjenkins./tests/unit-tests/client/test_mir_screencast.cpp0000644000015600001650000002054512676616125022422 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/client/mir_screencast.h" #include "src/client/rpc/mir_display_server.h" #include "mir/client_buffer_factory.h" #include "mir/client_platform.h" #include "mir/test/doubles/mock_client_buffer_stream.h" #include "mir/test/doubles/null_client_buffer.h" #include "mir/test/doubles/stub_display_server.h" #include "mir/test/fake_shared.h" #include namespace mcl = mir::client; namespace mclr = mir::client::rpc; namespace mp = mir::protobuf; namespace mt = mir::test; namespace mtd = mt::doubles; namespace google { namespace protobuf { class RpcController; } } namespace { struct MockProtobufServer : mclr::DisplayServer { MockProtobufServer() : mclr::DisplayServer(nullptr) {} MOCK_METHOD3(create_screencast, void(mp::ScreencastParameters const* /*request*/, mp::Screencast* /*response*/, google::protobuf::Closure* /*done*/)); MOCK_METHOD3(release_screencast, void(mp::ScreencastId const* /*request*/, mp::Void* /*response*/, google::protobuf::Closure* /*done*/)); }; class StubProtobufServer : public mclr::DisplayServer { public: StubProtobufServer() : mclr::DisplayServer(nullptr) {} void create_screencast( mp::ScreencastParameters const* /*request*/, mp::Screencast* response, google::protobuf::Closure* done) override { if (server_thread.joinable()) server_thread.join(); server_thread = std::thread{ [response, done, this] { response->clear_error(); done->Run(); }}; } void release_screencast( mp::ScreencastId const* /*request*/, mp::Void* /*response*/, google::protobuf::Closure* done) override { if (server_thread.joinable()) server_thread.join(); server_thread = std::thread{[done, this] { done->Run(); }}; } ~StubProtobufServer() { if (server_thread.joinable()) server_thread.join(); } private: std::thread server_thread; }; MATCHER_P(WithOutputId, value, "") { return arg->output_id() == value; } MATCHER_P3(WithParams, region, size, pixel_format, "") { return arg->width() == size.width.as_uint32_t() && arg->height() == size.height.as_uint32_t() && arg->region().left() == region.top_left.x.as_int() && arg->region().top() == region.top_left.y.as_int() && arg->region().width() == region.size.width.as_uint32_t() && arg->region().height() == region.size.height.as_uint32_t() && arg->pixel_format() == pixel_format; } MATCHER_P(WithScreencastId, value, "") { return arg->value() == value; } ACTION_P(SetCreateScreencastId, screencast_id) { arg1->clear_error(); arg1->mutable_screencast_id()->set_value(screencast_id); } ACTION(SetCreateError) { arg1->set_error("Test error"); } ACTION(RunClosure) { arg2->Run(); } struct MockCallback { MOCK_METHOD2(call, void(void*, void*)); }; void mock_callback_func(MirScreencast* screencast, void* context) { auto mock_cb = static_cast(context); mock_cb->call(screencast, context); } void null_callback_func(MirScreencast*, void*) { } class MirScreencastTest : public testing::Test { public: MirScreencastTest() : default_size{1, 1}, default_region{{0, 0}, {1, 1}}, default_pixel_format{mir_pixel_format_xbgr_8888} { } testing::NiceMock mock_server; StubProtobufServer stub_server; mir::geometry::Size default_size; mir::geometry::Rectangle default_region; MirPixelFormat default_pixel_format; mtd::MockClientBufferStream mock_bs; }; } TEST_F(MirScreencastTest, creates_screencast_on_construction) { using namespace testing; EXPECT_CALL(mock_server, create_screencast(WithParams(default_region, default_size, default_pixel_format),_,_)) .WillOnce(RunClosure()); MirScreencast screencast{ default_region, default_size, default_pixel_format, mock_server, nullptr, null_callback_func, nullptr}; } TEST_F(MirScreencastTest, releases_screencast_on_release) { using namespace testing; uint32_t const screencast_id{77}; InSequence seq; EXPECT_CALL(mock_server, create_screencast(WithParams(default_region, default_size, default_pixel_format),_,_)) .WillOnce(DoAll(SetCreateScreencastId(screencast_id), RunClosure())); EXPECT_CALL(mock_server, release_screencast(WithScreencastId(screencast_id),_,_)) .WillOnce(RunClosure()); MirScreencast screencast{ default_region, default_size, default_pixel_format, mock_server, nullptr, null_callback_func, nullptr}; screencast.release(null_callback_func, nullptr); } TEST_F(MirScreencastTest, executes_callback_on_creation) { using namespace testing; MockCallback mock_cb; EXPECT_CALL(mock_cb, call(_, &mock_cb)); MirScreencast screencast{ default_region, default_size, default_pixel_format, stub_server, nullptr, mock_callback_func, &mock_cb}; screencast.creation_wait_handle()->wait_for_all(); } TEST_F(MirScreencastTest, executes_callback_on_release) { using namespace testing; MirScreencast screencast{ default_region, default_size, default_pixel_format, stub_server, nullptr, null_callback_func, nullptr}; screencast.creation_wait_handle()->wait_for_all(); MockCallback mock_cb; EXPECT_CALL(mock_cb, call(&screencast, &mock_cb)); auto wh = screencast.release(mock_callback_func, &mock_cb); wh->wait_for_all(); } TEST_F(MirScreencastTest, construction_throws_on_invalid_params) { mir::geometry::Size const invalid_size{0, 0}; mir::geometry::Rectangle const invalid_region{{0, 0}, {0, 0}}; EXPECT_THROW({ MirScreencast screencast( default_region, invalid_size, default_pixel_format, stub_server, nullptr, null_callback_func, nullptr); }, std::runtime_error); EXPECT_THROW({ MirScreencast screencast( invalid_region, default_size, default_pixel_format, stub_server, nullptr, null_callback_func, nullptr); }, std::runtime_error); EXPECT_THROW({ MirScreencast screencast( default_region, default_size, mir_pixel_format_invalid, stub_server, nullptr, null_callback_func, nullptr); }, std::runtime_error); } TEST_F(MirScreencastTest, is_invalid_if_server_create_screencast_fails) { using namespace testing; EXPECT_CALL(mock_server, create_screencast(_,_,_)) .WillOnce(DoAll(SetCreateError(), RunClosure())); MirScreencast screencast{ default_region, default_size, default_pixel_format, mock_server, nullptr, null_callback_func, nullptr}; screencast.creation_wait_handle()->wait_for_all(); EXPECT_FALSE(screencast.valid()); } TEST_F(MirScreencastTest, calls_callback_on_creation_failure) { using namespace testing; MockCallback mock_cb; EXPECT_CALL(mock_server, create_screencast(_,_,_)) .WillOnce(DoAll(SetCreateError(), RunClosure())); EXPECT_CALL(mock_cb, call(_,&mock_cb)); MirScreencast screencast{ default_region, default_size, default_pixel_format, mock_server, nullptr, mock_callback_func, &mock_cb}; screencast.creation_wait_handle()->wait_for_all(); EXPECT_FALSE(screencast.valid()); } ./tests/unit-tests/client/test_client_mir_surface.cpp0000644000015600001650000004316712676616157023270 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ // TODO: There's a lot to suggest (usage of real connection most prevalent) // that this is perhaps a set of integration tests. But moving it there conflicts // with test_mirsurface.cpp. Client MirSurface testing probably needs to be reviewed #include "mir_protobuf.pb.h" #include "mir_toolkit/mir_client_library.h" #include "mir/client_buffer.h" #include "mir/client_buffer_factory.h" #include "mir/client_platform.h" #include "mir/client_platform_factory.h" #include "src/client/mir_surface.h" #include "src/client/mir_connection.h" #include "src/client/default_connection_configuration.h" #include "src/client/rpc/null_rpc_report.h" #include "src/client/rpc/mir_display_server.h" #include "src/client/rpc/mir_basic_rpc_channel.h" #include "src/client/connection_surface_map.h" #include "mir/dispatch/dispatchable.h" #include "mir/dispatch/threaded_dispatcher.h" #include "mir/events/event_builders.h" #include "mir/frontend/connector.h" #include "mir/input/input_platform.h" #include "mir/test/test_protobuf_server.h" #include "mir/test/stub_server_tool.h" #include "mir/test/gmock_fixes.h" #include "mir/test/fake_shared.h" #include "mir/test/pipe.h" #include "mir/test/signal.h" #include "mir/test/doubles/stub_client_buffer.h" #include "mir/test/test_dispatchable.h" #include #include "mir/test/doubles/stub_client_buffer_factory.h" #include "mir/test/doubles/stub_buffer_stream.h" #include "mir/test/doubles/mock_client_buffer_stream.h" #include "mir_test_framework/stub_client_platform_factory.h" #include #include #include #include namespace mcl = mir::client; namespace mclr = mir::client::rpc; namespace mircv = mir::input::receiver; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd= mir::test::doubles; namespace { struct MockServerPackageGenerator : public mt::StubServerTool { MockServerPackageGenerator() : server_package(), width_sent(891), height_sent(458), pf_sent(mir_pixel_format_abgr_8888), stride_sent(66), input_fd(open("/dev/null", O_APPEND)), global_buffer_id(0) { } ~MockServerPackageGenerator() { close(input_fd); close_server_package_fds(); } void create_surface( mir::protobuf::SurfaceParameters const* request, mir::protobuf::Surface* response, google::protobuf::Closure* done) override { create_surface_response(response); { std::lock_guard lock(guard); surf_name = request->surface_name(); } done->Run(); } void release_surface( const mir::protobuf::SurfaceId*, mir::protobuf::Void*, google::protobuf::Closure* done) { done->Run(); } void exchange_buffer( mir::protobuf::BufferRequest const* /*request*/, mir::protobuf::Buffer* response, google::protobuf::Closure* done) override { create_buffer_response(response); done->Run(); } void modify_surface( const mir::protobuf::SurfaceModifications*, mir::protobuf::Void*, google::protobuf::Closure* done) { done->Run(); } MirBufferPackage server_package; int width_sent; int height_sent; int pf_sent; int stride_sent; static std::map sent_surface_attributes; void generate_unique_buffer() { global_buffer_id++; close_server_package_fds(); int num_fd = 2, num_data = 8; server_package.fd_items = num_fd; for (auto i = 0; i < num_fd; i++) { server_package.fd[i] = open("/dev/null", O_APPEND); } server_package.data_items = num_data; for (auto i = 0; i < num_data; i++) { server_package.data[i] = (global_buffer_id + i) * 2; } server_package.stride = stride_sent; server_package.width = width_sent; server_package.height = height_sent; } void create_buffer_response(mir::protobuf::Buffer* response) { generate_unique_buffer(); response->set_buffer_id(global_buffer_id); /* assemble buffers */ response->set_fds_on_side_channel(server_package.fd_items); for (int i=0; i< server_package.data_items; i++) { response->add_data(server_package.data[i]); } for (int i=0; i< server_package.fd_items; i++) { response->add_fd(server_package.fd[i]); } response->set_stride(server_package.stride); response->set_width(server_package.width); response->set_height(server_package.height); } void create_surface_response(mir::protobuf::Surface* response) { unsigned int const id = 2; response->set_fds_on_side_channel(1); response->mutable_id()->set_value(id); response->set_width(width_sent); response->set_height(height_sent); response->set_pixel_format(pf_sent); response->add_fd(input_fd); for (auto const& kv : sent_surface_attributes) { auto setting = response->add_attributes(); setting->mutable_surfaceid()->set_value(id); setting->set_attrib(kv.first); setting->set_ivalue(kv.second); } create_buffer_response(response->mutable_buffer()); } void close_server_package_fds() { for (int i = 0; i < server_package.fd_items; i++) close(server_package.fd[i]); } int input_fd; int global_buffer_id; }; std::map MockServerPackageGenerator::sent_surface_attributes = { { mir_surface_attrib_type, mir_surface_type_normal }, { mir_surface_attrib_state, mir_surface_state_restored }, { mir_surface_attrib_swapinterval, 1 }, { mir_surface_attrib_focus, mir_surface_focused }, { mir_surface_attrib_dpi, 19 }, { mir_surface_attrib_visibility, mir_surface_visibility_exposed }, { mir_surface_attrib_preferred_orientation, mir_orientation_mode_any } }; struct StubClientInputPlatform : public mircv::InputPlatform { std::shared_ptr create_input_receiver(int /* fd */, std::shared_ptr const&, std::function const& /* callback */) { return std::shared_ptr(); } }; struct MockClientInputPlatform : public mircv::InputPlatform { MOCK_METHOD3(create_input_receiver, std::shared_ptr(int, std::shared_ptr const&, std::function const&)); }; class TestConnectionConfiguration : public mcl::DefaultConnectionConfiguration { public: TestConnectionConfiguration() : DefaultConnectionConfiguration("./test_socket_surface") { } std::shared_ptr the_rpc_report() override { return std::make_shared(); } std::shared_ptr the_client_platform_factory() override { return std::make_shared(); } }; struct FakeRpcChannel : public mir::client::rpc::MirBasicRpcChannel { void call_method( std::string const&, google::protobuf::MessageLite const*, google::protobuf::MessageLite*, google::protobuf::Closure* closure) override { delete closure; } }; void null_connected_callback(MirConnection* /*connection*/, void * /*client_context*/) { } void null_event_callback(MirSurface*, MirEvent const*, void*) { } void null_lifecycle_callback(MirConnection*, MirLifecycleState, void*) { } struct MirClientSurfaceTest : public testing::Test { MirClientSurfaceTest() { mock_server_tool->create_surface_response(&surface_proto); ON_CALL(*stub_buffer_stream, swap_interval()).WillByDefault(testing::Return(1)); start_test_server(); connect_to_test_server(); } ~MirClientSurfaceTest() { // Clear the lifecycle callback in order not to get SIGHUP by the // default lifecycle handler during connection teardown connection->register_lifecycle_event_callback(null_lifecycle_callback, nullptr); } void start_test_server() { // In case an earlier test left a stray file std::remove("./test_socket_surface"); test_server = std::make_shared("./test_socket_surface", mock_server_tool); test_server->comm->start(); } void connect_to_test_server() { mir::protobuf::ConnectParameters connect_parameters; connect_parameters.set_application_name("test"); TestConnectionConfiguration conf; surface_map = conf.the_surface_map(); connection = std::make_shared(conf); MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest", null_connected_callback, 0); wait_handle->wait_for_all(); client_comm_channel = std::make_shared(conf.the_rpc_channel()); } std::shared_ptr create_surface_with(mclr::DisplayServer& server_stub) { return std::make_shared( connection.get(), server_stub, nullptr, stub_buffer_stream, input_platform, spec, surface_proto, wh); } std::shared_ptr create_surface_with( mclr::DisplayServer& server_stub, std::shared_ptr const& buffer_stream) { return std::make_shared( connection.get(), server_stub, nullptr, buffer_stream, input_platform, spec, surface_proto, wh); } std::shared_ptr create_and_wait_for_surface_with( mclr::DisplayServer& server_stub) { auto surface = create_surface_with(server_stub); return surface; } std::shared_ptr create_and_wait_for_surface_with( mclr::DisplayServer& server_stub, std::shared_ptr const& buffer_stream) { auto surface = create_surface_with(server_stub, buffer_stream); return surface; } std::shared_ptr connection; MirSurfaceSpec const spec{nullptr, 33, 45, mir_pixel_format_abgr_8888}; std::shared_ptr stub_buffer_stream{std::make_shared()}; std::shared_ptr const input_platform = std::make_shared(); std::shared_ptr const mock_server_tool = std::make_shared(); std::shared_ptr surface_map; std::shared_ptr test_server; std::shared_ptr client_comm_channel; mir::protobuf::Surface surface_proto; std::shared_ptr const wh{std::make_shared()}; std::chrono::milliseconds const pause_time{10}; }; } TEST_F(MirClientSurfaceTest, attributes_set_on_surface_creation) { using namespace testing; auto surface = create_and_wait_for_surface_with(*client_comm_channel); for (int i = 0; i < mir_surface_attribs; i++) { EXPECT_EQ(MockServerPackageGenerator::sent_surface_attributes[i], surface->attrib(static_cast(i))); } } TEST_F(MirClientSurfaceTest, creates_input_thread_with_input_dispatcher_when_delegate_specified) { using namespace ::testing; auto dispatched = std::make_shared(); auto mock_input_dispatcher = std::make_shared([dispatched]() { dispatched->raise(); }); auto mock_input_platform = std::make_shared(); EXPECT_CALL(*mock_input_platform, create_input_receiver(_, _, _)).Times(1) .WillOnce(Return(mock_input_dispatcher)); MirSurface surface{connection.get(), *client_comm_channel, nullptr, stub_buffer_stream, mock_input_platform, spec, surface_proto, wh}; surface.set_event_handler(null_event_callback, nullptr); mock_input_dispatcher->trigger(); EXPECT_TRUE(dispatched->wait_for(std::chrono::seconds{5})); } TEST_F(MirClientSurfaceTest, replacing_delegate_with_nullptr_prevents_further_dispatch) { using namespace ::testing; auto dispatched = std::make_shared(); auto mock_input_dispatcher = std::make_shared([dispatched]() { dispatched->raise(); }); auto mock_input_platform = std::make_shared(); EXPECT_CALL(*mock_input_platform, create_input_receiver(_, _, _)).Times(1) .WillOnce(Return(mock_input_dispatcher)); MirSurface surface{connection.get(), *client_comm_channel, nullptr, stub_buffer_stream, mock_input_platform, spec, surface_proto, wh}; surface.set_event_handler(null_event_callback, nullptr); // Should now not get dispatched. surface.set_event_handler(nullptr, nullptr); mock_input_dispatcher->trigger(); EXPECT_FALSE(dispatched->wait_for(std::chrono::seconds{1})); } TEST_F(MirClientSurfaceTest, does_not_create_input_dispatcher_when_no_delegate_specified) { using namespace ::testing; auto mock_input_platform = std::make_shared(); EXPECT_CALL(*mock_input_platform, create_input_receiver(_, _, _)).Times(0); MirSurface surface{connection.get(), *client_comm_channel, nullptr, stub_buffer_stream, mock_input_platform, spec, surface_proto, wh}; } TEST_F(MirClientSurfaceTest, valid_surface_is_valid) { auto const surface = create_and_wait_for_surface_with(*client_comm_channel); EXPECT_TRUE(MirSurface::is_valid(surface.get())); } TEST_F(MirClientSurfaceTest, configure_cursor_wait_handle_really_blocks) { using namespace testing; FakeRpcChannel fake_channel; mclr::DisplayServer unresponsive_server{mt::fake_shared(fake_channel)}; auto const surface = create_surface_with(unresponsive_server); auto cursor_config = mir_cursor_configuration_from_name(mir_default_cursor_name); auto cursor_wait_handle = surface->configure_cursor(cursor_config); auto expected_end = std::chrono::steady_clock::now() + pause_time; cursor_wait_handle->wait_for_pending(pause_time); EXPECT_GE(std::chrono::steady_clock::now(), expected_end); mir_cursor_configuration_destroy(cursor_config); } TEST_F(MirClientSurfaceTest, configure_wait_handle_really_blocks) { using namespace testing; FakeRpcChannel fake_channel; mclr::DisplayServer unresponsive_server{mt::fake_shared(fake_channel)}; auto const surface = create_surface_with(unresponsive_server); auto configure_wait_handle = surface->configure(mir_surface_attrib_dpi, 100); auto expected_end = std::chrono::steady_clock::now() + pause_time; configure_wait_handle->wait_for_pending(pause_time); EXPECT_GE(std::chrono::steady_clock::now(), expected_end); } TEST_F(MirClientSurfaceTest, resizes_streams_and_calls_callback_if_no_customized_streams) { using namespace testing; auto mock_stream = std::make_shared(); auto mock_input_platform = std::make_shared>(); ON_CALL(*mock_input_platform, create_input_receiver(_,_,_)) .WillByDefault(Return(std::make_shared([]{}))); ON_CALL(*mock_stream, rpc_id()).WillByDefault(Return(mir::frontend::BufferStreamId(2))); geom::Size size(120, 124); EXPECT_CALL(*mock_stream, set_size(size)); auto ev = mir::events::make_event(mir::frontend::SurfaceId(2), size); MirSurface surface{connection.get(), *client_comm_channel, nullptr, mock_stream, mock_input_platform, spec, surface_proto, wh}; surface.handle_event(*ev); surface_map->erase(mir::frontend::BufferStreamId(2)); } TEST_F(MirClientSurfaceTest, resizes_streams_and_calls_callback_if_customized_streams) { using namespace testing; auto mock_stream = std::make_shared>(); auto mock_input_platform = std::make_shared>(); ON_CALL(*mock_stream, rpc_id()).WillByDefault(Return(mir::frontend::BufferStreamId(2))); ON_CALL(*mock_input_platform, create_input_receiver(_,_,_)) .WillByDefault(Return(std::make_shared([]{}))); geom::Size size(120, 124); EXPECT_CALL(*mock_stream, set_size(size)).Times(0); auto ev = mir::events::make_event(mir::frontend::SurfaceId(2), size); MirSurface surface{connection.get(), *client_comm_channel, nullptr, mock_stream, mock_input_platform, spec, surface_proto, wh}; MirSurfaceSpec spec; std::vector info = { ContentInfo{ geom::Displacement{0,0}, 2, geom::Size{1,1}} }; spec.streams = info; surface.modify(spec)->wait_for_all(); surface.handle_event(*ev); surface_map->erase(mir::frontend::BufferStreamId(2)); } ./tests/unit-tests/client/test_client.cpp0000644000015600001650000000402112676616125020666 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir_toolkit/mir_client_library.h" #include #include #include TEST(MirClientTest, mir_connection_is_valid_handles_invalid_pointers) { using namespace testing; MirConnection *null_pointer = NULL; double stack_variable; MirConnection *not_a_mir_connection_on_the_stack = reinterpret_cast(&stack_variable); auto heap_variable = std::make_shared(); MirConnection *not_a_mir_connection_on_the_heap = reinterpret_cast(heap_variable.get()); ASSERT_FALSE(mir_connection_is_valid(null_pointer)); ASSERT_FALSE(mir_connection_is_valid(not_a_mir_connection_on_the_stack)); ASSERT_FALSE(mir_connection_is_valid(not_a_mir_connection_on_the_heap)); } TEST(MirClientTest, mir_surface_is_valid_handles_invalid_pointers) { MirSurface* null_pointer = NULL; double stack_variable; MirSurface* not_a_mir_surface_on_the_stack = reinterpret_cast(&stack_variable); auto heap_variable = std::make_shared(); MirSurface* not_a_mir_surface_on_the_heap = reinterpret_cast(heap_variable.get()); ASSERT_FALSE(mir_surface_is_valid(null_pointer)); ASSERT_FALSE(mir_surface_is_valid(not_a_mir_surface_on_the_stack)); ASSERT_FALSE(mir_surface_is_valid(not_a_mir_surface_on_the_heap)); } ./tests/unit-tests/client/test_mir_connection.cpp0000644000015600001650000010246312676616157022434 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/client/mir_connection.h" #include "src/client/default_connection_configuration.h" #include "src/client/rpc/mir_basic_rpc_channel.h" #include "src/client/display_configuration.h" #include "src/client/mir_surface.h" #include "src/client/buffer_factory.h" #include "src/client/presentation_chain.h" #include "mir/client_platform.h" #include "mir/client_platform_factory.h" #include "mir/client_buffer_factory.h" #include "mir/raii.h" #include "mir/dispatch/dispatchable.h" #include "mir/events/event_builders.h" #include "mir/geometry/rectangle.h" #include "mir_toolkit/mir_presentation_chain.h" #include "src/server/frontend/resource_cache.h" /* needed by test_server.h */ #include "mir/test/test_protobuf_server.h" #include "mir/test/stub_server_tool.h" #include "mir/test/doubles/stub_client_buffer_factory.h" #include "mir_protobuf.pb.h" #include #include #include #include "mir/test/gmock_fixes.h" namespace mcl = mir::client; namespace mclr = mir::client::rpc; namespace mf = mir::frontend; namespace mp = mir::protobuf; namespace mev = mir::events; namespace md = mir::dispatch; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; using namespace testing; namespace { struct BufferStreamCallback { static void created(MirBufferStream* stream, void *client_context) { auto const context = reinterpret_cast(client_context); context->invoked = true; context->resulting_stream = stream; } bool invoked = false; MirBufferStream* resulting_stream = nullptr; }; struct PresentationChainCallback { static void created(MirPresentationChain* c, void *client_context) { auto const context = reinterpret_cast(client_context); context->invoked = true; context->resulting_chain = c; } bool invoked = false; MirPresentationChain* resulting_chain = nullptr; }; struct MockAsyncBufferFactory : mcl::AsyncBufferFactory { MOCK_METHOD1(generate_buffer, std::unique_ptr(mp::Buffer const&)); MOCK_METHOD7(expect_buffer, void( std::shared_ptr const& native_buffer_factory, MirPresentationChain* chain, geom::Size size, MirPixelFormat format, MirBufferUsage usage, mir_buffer_callback cb, void* cb_context)); }; struct MockRpcChannel : public mir::client::rpc::MirBasicRpcChannel, public mir::dispatch::Dispatchable { MockRpcChannel() : pollable_fd{eventfd(0, EFD_CLOEXEC)} { ON_CALL(*this, watch_fd()).WillByDefault(testing::Return(pollable_fd)); } virtual void call_method(std::string const& name, google::protobuf::MessageLite const* parameters, google::protobuf::MessageLite* response, google::protobuf::Closure* complete) { if (name == "connect") { static_cast(response)->clear_error(); connect(static_cast(parameters), static_cast(response)); } else if (name == "configure_display") { configure_display_sent(static_cast(parameters)); } else if (name == "platform_operation") { platform_operation(static_cast(parameters), static_cast(response)); } else if (name == "create_surface") { auto response_message = static_cast(response); response_message->mutable_id()->set_value(33); response_message->mutable_buffer_stream()->mutable_id()->set_value(33); } else if (name == "create_buffer_stream") { auto response_message = static_cast(response); on_buffer_stream_create(*response_message, complete); } else if (name == "release_buffer_stream") { auto const request_message = static_cast(parameters); buffer_stream_release(request_message); } else if (name == "allocate_buffers") { auto const request_message = static_cast(parameters); allocate_buffers(request_message); } else if (name == "release_buffers") { auto const request_message = static_cast(parameters); release_buffers(request_message); } complete->Run(); } MOCK_METHOD2(on_buffer_stream_create, void(mp::BufferStream&, google::protobuf::Closure* complete)); MOCK_METHOD2(connect, void(mp::ConnectParameters const*,mp::Connection*)); MOCK_METHOD1(configure_display_sent, void(mp::DisplayConfiguration const*)); MOCK_METHOD2(platform_operation, void(mp::PlatformOperationMessage const*, mp::PlatformOperationMessage*)); MOCK_METHOD1(buffer_stream_release, void(mp::BufferStreamId const*)); MOCK_METHOD1(allocate_buffers, void(mp::BufferAllocation const*)); MOCK_METHOD1(release_buffers, void(mp::BufferRelease const*)); MOCK_CONST_METHOD0(watch_fd, mir::Fd()); MOCK_METHOD1(dispatch, bool(md::FdEvents)); MOCK_CONST_METHOD0(relevant_events, md::FdEvents()); private: mir::Fd pollable_fd; }; struct MockClientPlatform : public mcl::ClientPlatform { MockClientPlatform() { using namespace testing; auto native_display = std::make_shared(); *native_display = reinterpret_cast(0x0); ON_CALL(*this, create_egl_native_display()) .WillByDefault(Return(native_display)); ON_CALL(*this, create_buffer_factory()) .WillByDefault(Return(std::make_shared())); ON_CALL(*this, create_egl_native_window(_)) .WillByDefault(Return(std::shared_ptr())); ON_CALL(*this, platform_operation(_)) .WillByDefault(Return(nullptr)); } void set_client_context(mcl::ClientContext* ctx) { client_context = ctx; } void populate(MirPlatformPackage& pkg) const override { client_context->populate_server_package(pkg); } MOCK_CONST_METHOD1(convert_native_buffer, MirNativeBuffer*(mir::graphics::NativeBuffer*)); MOCK_CONST_METHOD0(platform_type, MirPlatformType()); MOCK_METHOD1(platform_operation, MirPlatformMessage*(MirPlatformMessage const*)); MOCK_METHOD0(create_buffer_factory, std::shared_ptr()); MOCK_METHOD1(create_egl_native_window, std::shared_ptr(mcl::EGLNativeSurface*)); MOCK_METHOD0(create_egl_native_display, std::shared_ptr()); MOCK_CONST_METHOD2(get_egl_pixel_format, MirPixelFormat(EGLDisplay, EGLConfig)); mcl::ClientContext* client_context = nullptr; }; struct StubClientPlatformFactory : public mcl::ClientPlatformFactory { StubClientPlatformFactory(std::shared_ptr const& platform) : platform{platform} { } std::shared_ptr create_client_platform(mcl::ClientContext*) { return platform; } std::shared_ptr platform; }; void connected_callback(MirConnection* /*connection*/, void * /*client_context*/) { } class TestConnectionConfiguration : public mcl::DefaultConnectionConfiguration { public: TestConnectionConfiguration( std::shared_ptr const& platform, std::shared_ptr const& channel, std::shared_ptr const& factory) : DefaultConnectionConfiguration(""), disp_config(std::make_shared()), platform{platform}, channel{channel}, factory{factory} { } std::shared_ptr the_rpc_channel() override { return channel; } std::shared_ptr the_client_platform_factory() override { return std::make_shared(platform); } std::shared_ptr the_display_configuration() override { return disp_config; } std::shared_ptr the_buffer_factory() override { return factory; } private: std::shared_ptr disp_config; std::shared_ptr const platform; std::shared_ptr const channel; std::shared_ptr const factory; }; MATCHER_P(BufferAllocationMatches, val, "") { return ((arg->id().value() == val.id().value()) && (arg->buffer_requests_size() == 1) && (val.buffer_requests_size() == 1) && (arg->buffer_requests(0).width() == val.buffer_requests(0).width()) && (arg->buffer_requests(0).height() == val.buffer_requests(0).height()) && (arg->buffer_requests(0).pixel_format() == val.buffer_requests(0).pixel_format()) && (arg->buffer_requests(0).buffer_usage() == val.buffer_requests(0).buffer_usage())); } MATCHER_P(BufferReleaseMatches, val, "") { return ((arg->id().value() == val.id().value()) && (arg->buffers_size() == 1) && (val.buffers_size() == 1) && (arg->buffers(0).buffer_id() == val.buffers(0).buffer_id())); } } struct MirConnectionTest : public testing::Test { MirConnectionTest() : mock_platform{std::make_shared>()}, mock_channel{std::make_shared>()}, mock_buffer_allocator{std::make_shared>()}, conf{mock_platform, mock_channel, mock_buffer_allocator}, connection{std::make_shared(conf)} { mock_platform->set_client_context(connection.get()); } std::shared_ptr> const mock_platform; std::shared_ptr> const mock_channel; std::shared_ptr> const mock_buffer_allocator; TestConnectionConfiguration conf; std::shared_ptr const connection; }; TEST_F(MirConnectionTest, returns_correct_egl_native_display) { using namespace testing; EGLNativeDisplayType native_display_raw = reinterpret_cast(0xabcdef); auto native_display = std::make_shared(); *native_display = native_display_raw; EXPECT_CALL(*mock_platform, create_egl_native_display()) .WillOnce(Return(native_display)); MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest", connected_callback, 0); wait_handle->wait_for_all(); EGLNativeDisplayType connection_native_display = connection->egl_native_display(); ASSERT_EQ(native_display_raw, connection_native_display); } namespace { std::vector const supported_output_formats{ mir_pixel_format_abgr_8888, mir_pixel_format_xbgr_8888 }; unsigned int const number_of_outputs = 4; geom::Rectangle rects[number_of_outputs] = { geom::Rectangle{geom::Point(1,2), geom::Size(14,15)}, geom::Rectangle{geom::Point(3,4), geom::Size(12,13)}, geom::Rectangle{geom::Point(5,6), geom::Size(10,11)}, geom::Rectangle{geom::Point(7,8), geom::Size(9,10)}, }; void fill_display_configuration(mp::ConnectParameters const*, mp::Connection* response) { auto protobuf_config = response->mutable_display_configuration(); for (auto i = 0u; i < number_of_outputs; i++) { auto output = protobuf_config->add_display_output(); output->set_output_id(i); auto const& rect = rects[i]; output->set_position_x(rect.top_left.x.as_uint32_t()); output->set_position_y(rect.top_left.y.as_uint32_t()); auto mode = output->add_mode(); mode->set_horizontal_resolution(rect.size.width.as_uint32_t()); mode->set_vertical_resolution(rect.size.height.as_uint32_t()); for (auto pf : supported_output_formats) output->add_pixel_format(static_cast(pf)); } } std::vector const supported_surface_formats{ mir_pixel_format_argb_8888, mir_pixel_format_bgr_888 }; void fill_surface_pixel_formats(mp::ConnectParameters const*, mp::Connection* response) { for (auto pf : supported_surface_formats) response->add_surface_pixel_format(static_cast(pf)); } } TEST_F(MirConnectionTest, populates_display_output_correctly_on_startup) { using namespace testing; EXPECT_CALL(*mock_channel, connect(_,_)) .WillOnce(Invoke(fill_display_configuration)); MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest", connected_callback, 0); wait_handle->wait_for_all(); auto configuration = connection->create_copy_of_display_config(); ASSERT_EQ(number_of_outputs, configuration->num_outputs); for(auto i=0u; i < number_of_outputs; i++) { auto output = configuration->outputs[i]; auto rect = rects[i]; ASSERT_EQ(1u, output.num_modes); ASSERT_NE(nullptr, output.modes); EXPECT_EQ(rect.size.width.as_uint32_t(), output.modes[0].horizontal_resolution); EXPECT_EQ(rect.size.height.as_uint32_t(), output.modes[0].vertical_resolution); EXPECT_EQ(output.position_x, static_cast(rect.top_left.x.as_uint32_t())); EXPECT_EQ(output.position_y, static_cast(rect.top_left.y.as_uint32_t())); ASSERT_EQ(supported_output_formats.size(), static_cast(output.num_output_formats)); for (size_t i = 0; i < supported_output_formats.size(); ++i) { EXPECT_EQ(supported_output_formats[i], output.output_formats[i]); } } mcl::delete_config_storage(configuration); } TEST_F(MirConnectionTest, user_tries_to_configure_incorrectly) { using namespace testing; EXPECT_CALL(*mock_channel, connect(_,_)) .WillOnce(Invoke(fill_display_configuration)); MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest", connected_callback, 0); wait_handle->wait_for_all(); auto configuration = connection->create_copy_of_display_config(); EXPECT_GT(configuration->num_outputs, 0u); auto proper_num_outputs = configuration->num_outputs; auto proper_outputs = configuration->outputs; auto proper_output_id = configuration->outputs[0].output_id; //user lies about num_outputs configuration->num_outputs = 0; EXPECT_EQ(nullptr, connection->configure_display(configuration)); configuration->num_outputs = proper_num_outputs + 1; EXPECT_EQ(nullptr, connection->configure_display(configuration)); configuration->num_outputs = proper_num_outputs; //user sends nullptr for outputs configuration->outputs = nullptr; EXPECT_EQ(nullptr, connection->configure_display(configuration)); configuration->outputs = proper_outputs; //user makes up own id configuration->outputs[0].output_id = 4944949; EXPECT_EQ(nullptr, connection->configure_display(configuration)); configuration->outputs[0].output_id = proper_output_id; //user tries to set nonsense mode on a connected output configuration->outputs[0].current_mode++; configuration->outputs[0].connected = 1; EXPECT_EQ(nullptr, connection->configure_display(configuration)); mcl::delete_config_storage(configuration); } TEST_F(MirConnectionTest, display_configuration_validation_succeeds_for_invalid_mode_in_disconnected_output) { using namespace testing; EXPECT_CALL(*mock_channel, connect(_,_)) .WillOnce(Invoke(fill_display_configuration)); MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest", connected_callback, 0); wait_handle->wait_for_all(); auto configuration = connection->create_copy_of_display_config(); EXPECT_GT(configuration->num_outputs, 0u); configuration->outputs[0].current_mode++; configuration->outputs[0].connected = 0; EXPECT_NE(nullptr, connection->configure_display(configuration)); mcl::delete_config_storage(configuration); } TEST_F(MirConnectionTest, display_configuration_validation_uses_updated_configuration) { using namespace testing; EXPECT_CALL(*mock_channel, connect(_,_)) .WillOnce(Invoke(fill_display_configuration)); MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest", connected_callback, 0); wait_handle->wait_for_all(); auto old_configuration = connection->create_copy_of_display_config(); /* Update the configuration */ uint32_t const output1_id{11}; uint32_t const output2_id{12}; mp::DisplayConfiguration protobuf_config; auto output1 = protobuf_config.add_display_output(); output1->set_output_id(output1_id); auto output2 = protobuf_config.add_display_output(); output2->set_output_id(output2_id); auto display_config = conf.the_display_configuration(); display_config->set_configuration(protobuf_config); /* Check that the old config cannot be validated */ EXPECT_EQ(nullptr, connection->configure_display(old_configuration)); /* Check that the new config can be validated */ auto configuration = connection->create_copy_of_display_config(); EXPECT_NE(nullptr, connection->configure_display(configuration)); mcl::delete_config_storage(old_configuration); mcl::delete_config_storage(configuration); } TEST_F(MirConnectionTest, populates_pfs_correctly) { using namespace testing; EXPECT_CALL(*mock_channel, connect(_,_)) .WillOnce(Invoke(fill_surface_pixel_formats)); MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest", connected_callback, 0); wait_handle->wait_for_all(); unsigned int const formats_size = 5; unsigned int valid_formats = 0; MirPixelFormat formats[formats_size]; connection->available_surface_formats(&formats[0], formats_size, valid_formats); ASSERT_EQ(supported_surface_formats.size(), valid_formats); for (auto i=0u; i < valid_formats; i++) { EXPECT_EQ(supported_surface_formats[i], formats[i]) << "i=" << i; } } TEST_F(MirConnectionTest, valid_display_configure_sent) { using namespace testing; EXPECT_CALL(*mock_channel, connect(_,_)) .WillOnce(Invoke(fill_display_configuration)); MirDisplayOutput output; output.output_id = 0; output.current_mode = 0; output.current_format = mir_pixel_format_xbgr_8888; output.used = 0; output.position_x = 4; output.position_y = 6; output.connected = 0; MirDisplayConfiguration user_config{1, &output, 0, nullptr}; auto verify_display_change = [&](mp::DisplayConfiguration const* config) { ASSERT_NE(nullptr, config); ASSERT_EQ(1, config->display_output_size()); auto const& disp1 = config->display_output(0); EXPECT_TRUE(disp1.has_output_id()); EXPECT_EQ(output.output_id, disp1.output_id()); EXPECT_TRUE(disp1.has_used()); EXPECT_EQ(output.used, disp1.used()); EXPECT_TRUE(disp1.has_current_mode()); EXPECT_EQ(output.current_mode, disp1.current_mode()); EXPECT_TRUE(disp1.has_current_format()); EXPECT_EQ(output.current_format, disp1.current_format()); EXPECT_TRUE(disp1.has_position_x()); EXPECT_EQ(output.position_x, disp1.position_x()); EXPECT_TRUE(disp1.has_position_y()); EXPECT_EQ(output.position_y, disp1.position_y()); }; EXPECT_CALL(*mock_channel, configure_display_sent(_)) .Times(1) .WillOnce(Invoke(verify_display_change)); MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest", connected_callback, 0); wait_handle->wait_for_all(); auto config_wait_handle = connection->configure_display(&user_config); config_wait_handle->wait_for_all(); } static MirSurface *surface; static void surface_callback(MirSurface* surf, void*) { surface = surf; } static bool unfocused_received; static void surface_event_callback(MirSurface *, MirEvent const *ev, void *) { if (mir_event_type_surface != mir_event_get_type(ev)) return; auto surface_ev = mir_event_get_surface_event(ev); if (mir_surface_attrib_focus != mir_surface_event_get_attribute(surface_ev)) return; if (mir_surface_unfocused != mir_surface_event_get_attribute_value(surface_ev)) return; unfocused_received = true; } TEST_F(MirConnectionTest, focused_window_synthesises_unfocus_event_on_release) { using namespace testing; MirSurfaceSpec params{nullptr, 640, 480, mir_pixel_format_abgr_8888}; params.surface_name = __PRETTY_FUNCTION__; unfocused_received = false; MirWaitHandle *wait_handle = connection->connect("MirClientSurfaceTest", &connected_callback, nullptr); wait_handle->wait_for_all(); wait_handle = connection->create_surface(params, &surface_callback, nullptr); wait_handle->wait_for_all(); surface->handle_event(*mev::make_event(mf::SurfaceId{surface->id()}, mir_surface_attrib_focus, mir_surface_focused)); surface->set_event_handler(&surface_event_callback, nullptr); wait_handle = connection->release_surface(surface, &surface_callback, nullptr); wait_handle->wait_for_all(); wait_handle = connection->disconnect(); wait_handle->wait_for_all(); EXPECT_TRUE(unfocused_received); } TEST_F(MirConnectionTest, unfocused_window_does_not_synthesise_unfocus_event_on_release) { using namespace testing; MirSurfaceSpec params{nullptr, 640, 480, mir_pixel_format_abgr_8888}; params.surface_name = __PRETTY_FUNCTION__; unfocused_received = false; MirWaitHandle *wait_handle = connection->connect("MirClientSurfaceTest", &connected_callback, nullptr); wait_handle->wait_for_all(); wait_handle = connection->create_surface(params, &surface_callback, nullptr); wait_handle->wait_for_all(); surface->handle_event(*mev::make_event(mf::SurfaceId{surface->id()}, mir_surface_attrib_focus, mir_surface_unfocused)); surface->set_event_handler(&surface_event_callback, nullptr); wait_handle = connection->release_surface(surface, &surface_callback, nullptr); wait_handle->wait_for_all(); wait_handle = connection->disconnect(); wait_handle->wait_for_all(); EXPECT_FALSE(unfocused_received); } namespace { void assign_response(MirConnection*, MirPlatformMessage* response, void* context) { auto response_ptr = static_cast(context); *response_ptr = response; } ACTION(CopyRequestToResponse) { *arg1 = *arg0; } } TEST_F(MirConnectionTest, uses_client_platform_for_platform_operation) { using namespace testing; unsigned int const opcode{42}; auto const request = mir::raii::deleter_for( mir_platform_message_create(opcode), &mir_platform_message_release); auto const response = mir::raii::deleter_for( mir_platform_message_create(opcode), &mir_platform_message_release); EXPECT_CALL(*mock_platform, platform_operation(request.get())) .WillOnce(Return(response.get())); EXPECT_CALL(*mock_channel, platform_operation(_,_)) .Times(0); auto connect_wh = connection->connect("MirClientSurfaceTest", &connected_callback, nullptr); mir_wait_for(connect_wh); MirPlatformMessage* returned_response{nullptr}; auto op_wh = connection->platform_operation( request.get(), assign_response, &returned_response); mir_wait_for(op_wh); EXPECT_THAT(returned_response, Eq(response.get())); } TEST_F(MirConnectionTest, contacts_server_if_client_platform_cannot_handle_platform_operation) { using namespace testing; unsigned int const opcode{42}; auto const request = mir::raii::deleter_for( mir_platform_message_create(opcode), &mir_platform_message_release); EXPECT_CALL(*mock_platform, platform_operation(_)) .WillOnce(Return(nullptr)); EXPECT_CALL(*mock_channel, platform_operation(_,_)) .WillOnce(CopyRequestToResponse()); auto connect_wh = connection->connect("MirClientSurfaceTest", &connected_callback, nullptr); mir_wait_for(connect_wh); MirPlatformMessage* returned_response{nullptr}; auto op_wh = connection->platform_operation( request.get(), assign_response, &returned_response); mir_wait_for(op_wh); EXPECT_THAT(mir_platform_message_get_opcode(returned_response), Eq(opcode)); mir_platform_message_release(returned_response); } TEST_F(MirConnectionTest, wait_handle_is_signalled_during_stream_creation_error) { using namespace testing; EXPECT_CALL(*mock_channel, on_buffer_stream_create(_,_)) .WillOnce(Invoke([](mp::BufferStream& bs, google::protobuf::Closure*){ bs.set_error("danger will robertson"); })); EXPECT_FALSE(connection->create_client_buffer_stream( 2, 2, mir_pixel_format_abgr_8888, mir_buffer_usage_hardware, nullptr, nullptr)->is_pending()); } TEST_F(MirConnectionTest, wait_handle_is_signalled_during_creation_exception) { using namespace testing; EXPECT_CALL(*mock_channel, on_buffer_stream_create(_,_)) .WillOnce(DoAll( Invoke([](mp::BufferStream&, google::protobuf::Closure* c){ c->Run(); }), Throw(std::runtime_error("pay no attention to the man behind the curtain")))); auto wh = connection->create_client_buffer_stream( 2, 2, mir_pixel_format_abgr_8888, mir_buffer_usage_hardware, nullptr, nullptr); ASSERT_THAT(wh, Ne(nullptr)); EXPECT_FALSE(wh->is_pending()); } TEST_F(MirConnectionTest, callback_is_still_invoked_after_creation_error_and_error_stream_created) { using namespace testing; BufferStreamCallback callback; std::string error_msg = "danger will robertson"; EXPECT_CALL(*mock_channel, on_buffer_stream_create(_,_)) .WillOnce(Invoke([&](mp::BufferStream& bs, google::protobuf::Closure*) { bs.set_error(error_msg); })); connection->create_client_buffer_stream( 2, 2, mir_pixel_format_abgr_8888, mir_buffer_usage_hardware, &BufferStreamCallback::created, &callback); EXPECT_TRUE(callback.invoked); ASSERT_TRUE(callback.resulting_stream); EXPECT_THAT(mir_buffer_stream_get_error_message(callback.resulting_stream), StrEq("Error processing buffer stream response: " + error_msg)); } TEST_F(MirConnectionTest, callback_is_still_invoked_after_creation_exception_and_error_stream_created) { using namespace testing; BufferStreamCallback callback; EXPECT_CALL(*mock_channel, on_buffer_stream_create(_,_)) .WillOnce(DoAll( Invoke([](mp::BufferStream&, google::protobuf::Closure* c){ c->Run(); }), Throw(std::runtime_error("pay no attention to the man behind the curtain")))); connection->create_client_buffer_stream( 2, 2, mir_pixel_format_abgr_8888, mir_buffer_usage_hardware, &BufferStreamCallback::created, &callback); EXPECT_TRUE(callback.invoked); ASSERT_TRUE(callback.resulting_stream); EXPECT_THAT(mir_buffer_stream_get_error_message(callback.resulting_stream), StrEq("Error processing buffer stream response: no ID in response (disconnected?)")); } TEST_F(MirConnectionTest, create_wait_handle_really_blocks) { using namespace testing; std::chrono::milliseconds const pause_time{10}; struct FakeRpcChannel : public MockRpcChannel { void call_method( std::string const&, google::protobuf::MessageLite const*, google::protobuf::MessageLite*, google::protobuf::Closure* closure) override { delete closure; } }; TestConnectionConfiguration conf{ mock_platform, std::make_shared>(), mock_buffer_allocator }; MirConnection connection(conf); MirSurfaceSpec const spec{&connection, 33, 45, mir_pixel_format_abgr_8888}; auto wait_handle = connection.create_surface(spec, nullptr, nullptr); auto expected_end = std::chrono::steady_clock::now() + pause_time; wait_handle->wait_for_pending(pause_time); EXPECT_GE(std::chrono::steady_clock::now(), expected_end); } TEST_F(MirConnectionTest, callback_is_invoked_after_chain_creation_error) { using namespace testing; PresentationChainCallback callback; std::string error_msg = "danger will robertson"; EXPECT_CALL(*mock_channel, on_buffer_stream_create(_,_)) .WillOnce(Invoke([&](mp::BufferStream& bs, google::protobuf::Closure*) { bs.set_error(error_msg); })); connection->create_presentation_chain( &PresentationChainCallback::created, &callback); EXPECT_TRUE(callback.invoked); ASSERT_TRUE(callback.resulting_chain); EXPECT_THAT(mir_presentation_chain_get_error_message(callback.resulting_chain), StrEq("Error creating MirPresentationChain: " + error_msg)); } TEST_F(MirConnectionTest, callback_is_still_invoked_after_creation_exception_and_error_chain_created) { using namespace testing; PresentationChainCallback callback; EXPECT_CALL(*mock_channel, on_buffer_stream_create(_,_)) .WillOnce(DoAll( Invoke([](mp::BufferStream&, google::protobuf::Closure* c){ c->Run(); }), Throw(std::runtime_error("pay no attention to the man behind the curtain")))); connection->create_presentation_chain( &PresentationChainCallback::created, &callback); EXPECT_TRUE(callback.invoked); ASSERT_TRUE(callback.resulting_chain); EXPECT_THAT(mir_presentation_chain_get_error_message(callback.resulting_chain), StrEq("Error creating MirPresentationChain: no ID in response")); } namespace { MATCHER_P(ReleaseRequestHasId, val, "") { return arg->value() == val.value(); } } TEST_F(MirConnectionTest, release_chain_calls_server) { using namespace testing; connection->connect("MirClientSurfaceTest", connected_callback, nullptr)->wait_for_all(); EXPECT_CALL(*mock_channel, on_buffer_stream_create(_,_)) .WillOnce(Invoke([](mp::BufferStream& stream, google::protobuf::Closure*) { stream.mutable_id()->set_value(0); })); PresentationChainCallback callback; connection->create_presentation_chain( &PresentationChainCallback::created, &callback); EXPECT_TRUE(callback.invoked); ASSERT_TRUE(callback.resulting_chain); mp::BufferStreamId expected_request; expected_request.set_value( static_cast(callback.resulting_chain)->rpc_id()); EXPECT_CALL(*mock_channel, buffer_stream_release(ReleaseRequestHasId(expected_request))) .Times(0); connection->release_presentation_chain(callback.resulting_chain); } TEST_F(MirConnectionTest, release_error_chain_doesnt_call_server) { PresentationChainCallback callback; connection->create_presentation_chain( &PresentationChainCallback::created, &callback); EXPECT_TRUE(callback.invoked); ASSERT_TRUE(callback.resulting_chain); mp::BufferStreamId expected_request; expected_request.set_value( static_cast(callback.resulting_chain)->rpc_id()); EXPECT_CALL(*mock_channel, buffer_stream_release(ReleaseRequestHasId(expected_request))) .Times(0); connection->release_presentation_chain(callback.resulting_chain); } TEST_F(MirConnectionTest, can_alloc_buffer_from_connection) { connection->connect("MirClientSurfaceTest", connected_callback, 0)->wait_for_all(); geom::Size size { 32, 11 }; auto format = mir_pixel_format_abgr_8888; auto usage = mir_buffer_usage_software; mp::BufferAllocation mp_alloc; mp_alloc.mutable_id()->set_value(-1); auto params = mp_alloc.add_buffer_requests(); params->set_width(size.width.as_int()); params->set_height(size.height.as_int()); params->set_buffer_usage(usage); params->set_pixel_format(format); EXPECT_CALL(*mock_channel, allocate_buffers(BufferAllocationMatches(mp_alloc))); EXPECT_CALL(*mock_buffer_allocator, expect_buffer(_, nullptr, size, format, usage, nullptr, nullptr)); connection->allocate_buffer(size, format, usage, nullptr, nullptr); } TEST_F(MirConnectionTest, can_release_buffer_from_connection) { int buffer_id = 1320; mp::BufferRelease release_msg; auto released_buffer = release_msg.add_buffers(); released_buffer->set_buffer_id(buffer_id); EXPECT_CALL(*mock_channel, release_buffers(BufferReleaseMatches(release_msg))); connection->release_buffer(buffer_id); } ./tests/unit-tests/client/test_screencast_stream.cpp0000644000015600001650000001156412676616125023127 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/client/screencast_stream.h" #include "mir/client_platform.h" #include "mir/test/doubles/null_client_buffer.h" #include "mir/test/doubles/mock_client_buffer_factory.h" #include "mir/test/doubles/mock_protobuf_server.h" #include "mir/test/doubles/stub_client_buffer_factory.h" #include "mir_test_framework/stub_client_platform_factory.h" #include "mir/test/doubles/null_logger.h" #include "mir/test/fake_shared.h" #include "mir_toolkit/mir_client_library.h" #include namespace mp = mir::protobuf; namespace ml = mir::logging; namespace mg = mir::graphics; namespace mcl = mir::client; namespace mclr = mir::client::rpc; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mt::doubles; using namespace testing; namespace { MirBufferPackage a_buffer_package() { MirBufferPackage bp; bp.fd_items = 1; bp.fd[0] = 16; bp.data_items = 2; bp.data[0] = 100; bp.data[1] = 234; bp.stride = 768; bp.width = 90; bp.height = 30; return bp; } std::atomic unique_buffer_id{1}; void fill_protobuf_buffer_from_package(mp::Buffer* mb, MirBufferPackage const& buffer_package) { mb->set_buffer_id(unique_buffer_id++); mb->set_fds_on_side_channel(buffer_package.fd_items); for (int i=0; iadd_data(buffer_package.data[i]); for (int i=0; iadd_fd(buffer_package.fd[i]); mb->set_stride(buffer_package.stride); mb->set_width(buffer_package.width); mb->set_height(buffer_package.height); } struct ScreencastStream : Test { ScreencastStream() { ON_CALL(mock_factory, create_buffer(_,_,_)) .WillByDefault(Return(std::make_shared())); } mp::BufferStream a_protobuf_buffer_stream(MirPixelFormat format, MirBufferUsage usage, MirBufferPackage const& package) { mp::BufferStream protobuf_bs; mp::BufferStreamId bs_id; bs_id.set_value(1); *protobuf_bs.mutable_id() = bs_id; protobuf_bs.set_pixel_format(format); protobuf_bs.set_buffer_usage(usage); fill_protobuf_buffer_from_package(protobuf_bs.mutable_buffer(), package); return protobuf_bs; } testing::NiceMock mock_factory; mtd::StubClientBufferFactory stub_factory; testing::NiceMock mock_protobuf_server; MirPixelFormat const default_pixel_format = mir_pixel_format_argb_8888; MirBufferUsage const default_buffer_usage = mir_buffer_usage_hardware; MirBufferPackage buffer_package = a_buffer_package(); geom::Size size{buffer_package.width, buffer_package.height}; mp::BufferStream response = a_protobuf_buffer_stream( default_pixel_format, default_buffer_usage, buffer_package); }; } TEST_F(ScreencastStream, requests_screencast_buffer_when_next_buffer_called) { EXPECT_CALL(mock_protobuf_server, screencast_buffer(_,_,_)) .WillOnce(mtd::RunProtobufClosure()); mcl::ScreencastStream stream( nullptr, mock_protobuf_server, std::make_shared(nullptr), response); auto wh = stream.next_buffer([]{}); ASSERT_THAT(wh, NotNull()); EXPECT_FALSE(wh->is_pending()); } TEST_F(ScreencastStream, advances_current_buffer) { int id0 = 33; int id1 = 34; EXPECT_CALL(mock_protobuf_server, screencast_buffer(_,_,_)) .Times(2) .WillOnce(Invoke( [&](mp::ScreencastId const*, mp::Buffer* b, google::protobuf::Closure* c) { b->set_buffer_id(id0); c->Run(); })) .WillOnce(Invoke( [&](mp::ScreencastId const*, mp::Buffer* b, google::protobuf::Closure* c) { b->set_buffer_id(id1); c->Run(); })); mcl::ScreencastStream stream( nullptr, mock_protobuf_server, std::make_shared(nullptr), response); auto wh = stream.next_buffer([]{}); wh->wait_for_all(); EXPECT_THAT(stream.get_current_buffer_id(), Eq(id0)); wh = stream.next_buffer([]{}); wh->wait_for_all(); EXPECT_THAT(stream.get_current_buffer_id(), Eq(id1)); } ./tests/unit-tests/client/test_buffer_vault.cpp0000644000015600001650000003771512676616157022121 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_toolkit/mir_client_library.h" #include "src/client/client_buffer_depository.h" #include "src/client/buffer_vault.h" #include "mir/client_buffer_factory.h" #include "mir/aging_buffer.h" #include "mir_toolkit/common.h" #include "mir/geometry/size.h" #include "mir/graphics/buffer_properties.h" #include "mir/test/fake_shared.h" #include "mir_protobuf.pb.h" #include "mir/test/doubles/mock_client_buffer.h" #include #include #include #include namespace geom = mir::geometry; namespace mcl = mir::client; namespace mt = mir::test; namespace mg = mir::graphics; namespace mp = mir::protobuf; namespace mtd = mir::test::doubles; using namespace testing; namespace { struct MockClientBufferFactory : public mcl::ClientBufferFactory { MockClientBufferFactory() { ON_CALL(*this, create_buffer(_,_,_)) .WillByDefault(Invoke([]( std::shared_ptr const&, geom::Size size, MirPixelFormat) { auto buffer = std::make_shared>(); ON_CALL(*buffer, size()) .WillByDefault(Return(size)); return buffer; })); } MOCK_METHOD3(create_buffer, std::shared_ptr( std::shared_ptr const&, geom::Size, MirPixelFormat)); }; struct MockServerRequests : mcl::ServerBufferRequests { MOCK_METHOD3(allocate_buffer, void(geom::Size size, MirPixelFormat format, int usage)); MOCK_METHOD1(free_buffer, void(int)); MOCK_METHOD2(submit_buffer, void(int, mcl::ClientBuffer&)); MOCK_METHOD0(disconnected, void()); }; struct BufferVault : public testing::Test { BufferVault() { package.set_width(size.width.as_int()); package.set_height(size.height.as_int()); package2.set_width(size.width.as_int()); package2.set_height(size.height.as_int()); package3.set_width(size.width.as_int()); package3.set_height(size.height.as_int()); package.set_buffer_id(1); package2.set_buffer_id(2); package3.set_buffer_id(3); } unsigned int initial_nbuffers {3}; geom::Size size{271, 314}; MirPixelFormat format{mir_pixel_format_abgr_8888}; int usage{0}; mg::BufferProperties initial_properties{ geom::Size{271,314}, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware}; NiceMock mock_factory; NiceMock mock_requests; mp::Buffer package; mp::Buffer package2; mp::Buffer package3; }; struct StartedBufferVault : BufferVault { StartedBufferVault() { vault.wire_transfer_inbound(package); vault.wire_transfer_inbound(package2); vault.wire_transfer_inbound(package3); } mcl::BufferVault vault{ mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers}; }; } TEST_F(BufferVault, creates_all_buffers_on_start) { EXPECT_CALL(mock_requests, allocate_buffer(size, format, usage)) .Times(initial_nbuffers); mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); } TEST_F(BufferVault, frees_the_buffers_we_actually_got) { EXPECT_CALL(mock_requests, free_buffer(package.buffer_id())); EXPECT_CALL(mock_requests, free_buffer(package2.buffer_id())); mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); vault.wire_transfer_inbound(package); vault.wire_transfer_inbound(package2); } TEST_F(BufferVault, creates_buffer_on_first_insertion) { EXPECT_CALL(mock_factory, create_buffer(_,initial_properties.size,initial_properties.format)); mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); vault.wire_transfer_inbound(package); } TEST_F(BufferVault, updates_buffer_on_subsequent_insertions) { auto mock_buffer = std::make_shared>(); EXPECT_CALL(*mock_buffer, update_from(_)); ON_CALL(*mock_buffer, size()) .WillByDefault(Return(size)); ON_CALL(mock_factory, create_buffer(_,_,_)) .WillByDefault(Return(mock_buffer)); mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); vault.wire_transfer_inbound(package); auto b = vault.withdraw().get().buffer; vault.deposit(b); vault.wire_transfer_outbound(b); vault.wire_transfer_inbound(package); } TEST_F(BufferVault, withdrawing_and_never_filling_up_will_timeout) { using namespace std::literals::chrono_literals; mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); auto buffer_future = vault.withdraw(); ASSERT_TRUE(buffer_future.valid()); EXPECT_THAT(buffer_future.wait_for(20ms), Eq(std::future_status::timeout)); } TEST_F(StartedBufferVault, withdrawing_gives_a_valid_future) { auto buffer_future = vault.withdraw(); ASSERT_TRUE(buffer_future.valid()); EXPECT_THAT(buffer_future.get().buffer, Ne(nullptr));; } TEST_F(StartedBufferVault, can_deposit_buffer) { auto buffer = vault.withdraw().get().buffer; EXPECT_CALL(mock_requests, submit_buffer(_,Ref(*buffer))); vault.deposit(buffer); vault.wire_transfer_outbound(buffer); } TEST_F(StartedBufferVault, cant_transfer_if_not_in_acct) { auto buffer = vault.withdraw().get().buffer; EXPECT_THROW({ vault.wire_transfer_outbound(buffer); }, std::logic_error); } TEST_F(StartedBufferVault, depositing_external_buffer_throws) { auto buffer = std::make_shared>(); ON_CALL(*buffer, size()) .WillByDefault(Return(size)); EXPECT_THROW({ vault.deposit(buffer); }, std::logic_error); } TEST_F(StartedBufferVault, attempt_to_redeposit_throws) { auto buffer = vault.withdraw().get().buffer; vault.deposit(buffer); vault.wire_transfer_outbound(buffer); EXPECT_THROW({ vault.deposit(buffer); }, std::logic_error); } TEST_F(BufferVault, can_transfer_again_when_we_get_the_buffer) { mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); EXPECT_CALL(mock_factory, create_buffer(_,initial_properties.size,initial_properties.format)) .Times(Exactly(1)); vault.wire_transfer_inbound(package); auto buffer = vault.withdraw().get().buffer; vault.deposit(buffer); vault.wire_transfer_outbound(buffer); //should just activate, not create the buffer vault.wire_transfer_inbound(package); auto buffer2 = vault.withdraw().get().buffer; EXPECT_THAT(buffer, Eq(buffer2)); } TEST_F(StartedBufferVault, multiple_draws_get_different_buffer) { auto buffer1 = vault.withdraw().get(); auto buffer2 = vault.withdraw().get(); EXPECT_THAT(buffer1.buffer, Ne(buffer2.buffer)); } TEST_F(BufferVault, multiple_withdrawals_during_wait_period_get_differing_buffers) { mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); auto f_buffer1 = vault.withdraw(); auto f_buffer2 = vault.withdraw(); vault.wire_transfer_inbound(package); vault.wire_transfer_inbound(package2); auto buffer1 = f_buffer1.get(); auto buffer2 = f_buffer2.get(); EXPECT_THAT(buffer1.buffer, Ne(buffer2.buffer)); } TEST_F(BufferVault, destruction_signals_futures) { using namespace std::literals::chrono_literals; mcl::NoTLSFuture fbuffer; { mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); fbuffer = vault.withdraw(); } EXPECT_THAT(fbuffer.wait_for(5s), Ne(std::future_status::timeout)); } TEST_F(BufferVault, ages_buffer_on_deposit) { auto mock_buffer = std::make_shared>(); ON_CALL(*mock_buffer, size()) .WillByDefault(Return(size)); EXPECT_CALL(*mock_buffer, increment_age()); ON_CALL(mock_factory, create_buffer(_,_,_)) .WillByDefault(Return(mock_buffer)); mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); vault.wire_transfer_inbound(package); vault.deposit(vault.withdraw().get().buffer); } TEST_F(BufferVault, marks_as_submitted_on_transfer) { auto mock_buffer = std::make_shared>(); ON_CALL(*mock_buffer, size()) .WillByDefault(Return(size)); EXPECT_CALL(*mock_buffer, mark_as_submitted()); ON_CALL(mock_factory, create_buffer(_,_,_)) .WillByDefault(Return(mock_buffer)); mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); vault.wire_transfer_inbound(package); auto buffer = vault.withdraw().get().buffer; vault.deposit(buffer); vault.wire_transfer_outbound(buffer); } //handy for android's cancelbuffer TEST_F(StartedBufferVault, can_withdraw_and_deposit) { auto a_few_times = 5u; std::vector> buffers(a_few_times); for (auto i = 0u; i < a_few_times; i++) { buffers[i] = vault.withdraw().get().buffer; vault.deposit(buffers[i]); } EXPECT_THAT(buffers, Each(buffers[0])); } TEST_F(StartedBufferVault, reallocates_incoming_buffers_of_incorrect_size_with_immediate_response) { mp::Buffer package4; geom::Size new_size{80, 100}; EXPECT_CALL(mock_requests, free_buffer(package.buffer_id())); EXPECT_CALL(mock_requests, allocate_buffer(new_size,_,_)) .WillOnce(Invoke( [&, this](geom::Size sz, MirPixelFormat, int) { package4.set_width(sz.width.as_int()); package4.set_height(sz.height.as_int()); package4.set_buffer_id(4); vault.wire_transfer_inbound(package4); })); vault.set_size(new_size); vault.wire_transfer_inbound(package); Mock::VerifyAndClearExpectations(&mock_requests); } TEST_F(StartedBufferVault, reallocates_incoming_buffers_of_incorrect_size_with_delayed_response) { geom::Size new_size{80, 100}; mp::Buffer package4; package4.set_width(new_size.width.as_int()); package4.set_height(new_size.height.as_int()); package4.set_buffer_id(4); EXPECT_CALL(mock_requests, free_buffer(package.buffer_id())); EXPECT_CALL(mock_requests, allocate_buffer(new_size,_,_)); vault.set_size(new_size); vault.wire_transfer_inbound(package); vault.wire_transfer_inbound(package4); EXPECT_THAT(vault.withdraw().get().buffer->size(), Eq(new_size)); Mock::VerifyAndClearExpectations(&mock_requests); } TEST_F(StartedBufferVault, withdraw_gives_only_newly_sized_buffers_after_resize) { mp::Buffer package4; geom::Size new_size{80, 100}; package4.set_width(new_size.width.as_int()); package4.set_height(new_size.height.as_int()); package4.set_buffer_id(4); vault.set_size(new_size); vault.wire_transfer_inbound(package4); EXPECT_THAT(vault.withdraw().get().buffer->size(), Eq(new_size)); Mock::VerifyAndClearExpectations(&mock_requests); } TEST_F(StartedBufferVault, simply_setting_size_triggers_no_server_interations) { EXPECT_CALL(mock_requests, free_buffer(_)).Times(0); EXPECT_CALL(mock_requests, allocate_buffer(_,_,_)).Times(0); auto const cycles = 30u; geom::Size new_size(80, 100); for(auto i = 0u; i < cycles; i++) { new_size = geom::Size(geom::Width(i), new_size.height); vault.set_size(new_size); } Mock::VerifyAndClearExpectations(&mock_requests); } TEST_F(StartedBufferVault, scaling_resizes_buffers_right_away) { mp::Buffer package4; float scale = 2.0f; geom::Size new_size = size * scale; package4.set_width(new_size.width.as_int()); package4.set_height(new_size.height.as_int()); package4.set_buffer_id(4); EXPECT_CALL(mock_requests, allocate_buffer(_,_,_)) .WillOnce(InvokeWithoutArgs( [&]{vault.wire_transfer_inbound(package4);})); auto b1 = vault.withdraw().get(); auto b2 = vault.withdraw().get(); vault.set_scale(scale); auto b3 = vault.withdraw().get().buffer; EXPECT_THAT(b3->size(), Eq(new_size)); } TEST_F(BufferVault, waiting_threads_give_error_if_disconnected) { mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); auto future = vault.withdraw(); vault.disconnected(); EXPECT_THROW({ future.get(); }, std::exception); } TEST_F(BufferVault, makes_sure_rpc_calls_exceptions_are_caught_in_destructor) { EXPECT_CALL(mock_requests, free_buffer(_)) .Times(1) .WillOnce(Throw(std::runtime_error(""))); EXPECT_NO_THROW({ mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); vault.wire_transfer_inbound(package); }); } TEST_F(StartedBufferVault, skips_free_buffer_rpc_calls_if_disconnected) { EXPECT_CALL(mock_requests, free_buffer(_)) .Times(0); vault.disconnected(); } TEST_F(StartedBufferVault, buffer_count_remains_the_same_after_scaling) { std::array buffers; float scale = 2.0f; geom::Size new_size = size * scale; int i = initial_nbuffers; for (auto& buffer : buffers) { buffer.set_width(new_size.width.as_int()); buffer.set_height(new_size.height.as_int()); buffer.set_buffer_id(i++); } //make sure we alloc 3 new ones and free 3 old ones EXPECT_CALL(mock_requests, allocate_buffer(_,_,_)) .Times(initial_nbuffers) .WillOnce(InvokeWithoutArgs( [&]{vault.wire_transfer_inbound(buffers[0]);})) .WillOnce(InvokeWithoutArgs( [&]{vault.wire_transfer_inbound(buffers[1]);})) .WillOnce(InvokeWithoutArgs( [&]{vault.wire_transfer_inbound(buffers[2]);})); EXPECT_CALL(mock_requests, free_buffer(_)) .Times(initial_nbuffers); auto buffer = vault.withdraw().get().buffer; vault.set_scale(scale); vault.deposit(buffer); vault.wire_transfer_outbound(buffer); vault.wire_transfer_inbound(package); for(auto i = 0; i < 100; i++) { auto b = vault.withdraw().get().buffer; EXPECT_THAT(b->size(), Eq(new_size)); vault.deposit(b); vault.wire_transfer_outbound(b); vault.wire_transfer_inbound(buffers[(i+1)%3]); } Mock::VerifyAndClearExpectations(&mock_requests); } TEST_F(BufferVault, rescale_before_initial_buffers_are_serviced_frees_initial_buffers) { mcl::BufferVault vault(mt::fake_shared(mock_factory), mt::fake_shared(mock_requests), size, format, usage, initial_nbuffers); vault.set_scale(2.0); EXPECT_CALL(mock_requests, free_buffer(_)) .Times(initial_nbuffers); EXPECT_CALL(mock_requests, allocate_buffer(_,_,_)) .Times(initial_nbuffers); vault.wire_transfer_inbound(package); vault.wire_transfer_inbound(package2); vault.wire_transfer_inbound(package3); } ./tests/unit-tests/client/test_client_buffer_depository.cpp0000644000015600001650000003445312676616157024521 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_toolkit/mir_client_library.h" #include "src/client/client_buffer_depository.h" #include "mir/client_buffer_factory.h" #include "mir/aging_buffer.h" #include "mir_toolkit/common.h" #include "mir/geometry/size.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_client_buffer.h" #include #include #include namespace geom=mir::geometry; namespace mcl=mir::client; namespace mtd = mir::test::doubles; struct MockClientBufferFactory : public mcl::ClientBufferFactory { MockClientBufferFactory() : alloc_count(0), free_count(0) { using namespace testing; ON_CALL(*this, create_buffer(_,_,_)) .WillByDefault(InvokeWithoutArgs([this]() { alloc_count++; auto buffer = std::shared_ptr( new NiceMock(), [this](mcl::ClientBuffer* buffer) { free_count++; delete buffer; }); std::weak_ptr wb (buffer); ON_CALL(*buffer, mark_as_submitted()) .WillByDefault(Invoke([wb]{ if (auto buffer = wb.lock()) buffer->age_ = 1; })); ON_CALL(*buffer, increment_age()) .WillByDefault(Invoke([wb]{ if (auto buffer = wb.lock()) if (buffer->age_) buffer->age_++; })); ON_CALL(*buffer, age()) .WillByDefault(Invoke([wb]{ if (auto buffer = wb.lock()) return buffer->age_; return 0; })); if (alloc_count == 1) first_allocated_buffer = buffer; return buffer; })); } ~MockClientBufferFactory() { EXPECT_THAT(alloc_count, testing::Eq(free_count)); } MOCK_METHOD3(create_buffer, std::shared_ptr(std::shared_ptr const&, geom::Size, MirPixelFormat)); bool first_buffer_allocated_and_then_freed() { if ((alloc_count >= 1) && (first_allocated_buffer.lock() == nullptr)) return true; return false; } int alloc_count; int free_count; std::weak_ptr first_allocated_buffer; }; struct ClientBufferDepository : public testing::Test { void SetUp() { width = geom::Width(12); height =geom::Height(14); pf = mir_pixel_format_abgr_8888; size = geom::Size{width, height}; package = std::make_shared(); mock_factory = std::make_shared>(); } geom::Width width; geom::Height height; MirPixelFormat pf; geom::Size size; std::shared_ptr package; std::shared_ptr mock_factory; }; MATCHER_P(SizeMatches, value, "") { return value == arg; } TEST_F(ClientBufferDepository, sets_width_and_height) { using namespace testing; mcl::ClientBufferDepository depository{mock_factory, 3}; EXPECT_CALL(*mock_factory, create_buffer(_,size,pf)) .Times(1); depository.deposit_package(package, 8, size, pf); } TEST_F(ClientBufferDepository, changes_current_buffer_on_new_deposit) { using namespace testing; auto package2 = std::make_shared(); mcl::ClientBufferDepository depository{mock_factory, 3}; depository.deposit_package(package, 8, size, pf); auto buffer1 = depository.current_buffer(); depository.deposit_package(package2, 9, size, pf); auto buffer2 = depository.current_buffer(); EXPECT_NE(buffer1, buffer2); } TEST_F(ClientBufferDepository, sets_buffer_age_to_zero_for_new_buffer) { using namespace testing; mcl::ClientBufferDepository depository{mock_factory, 3}; depository.deposit_package(package, 1, size, pf); auto buffer1 = depository.current_buffer(); EXPECT_EQ(0u, buffer1->age()); } TEST_F(ClientBufferDepository, has_age_1_for_just_sumbitted_buffer) { using namespace testing; mcl::ClientBufferDepository depository{mock_factory, 3}; auto package2 = std::make_shared(); depository.deposit_package(package, 1, size, pf); auto buffer1 = depository.current_buffer(); ASSERT_EQ(0u, buffer1->age()); // Deposit new package, implicitly marking previous buffer as submitted depository.deposit_package(package2, 2, size, pf); EXPECT_EQ(1u, buffer1->age()); } TEST_F(ClientBufferDepository, ages_other_buffers_on_new_submission) { using namespace testing; mcl::ClientBufferDepository depository{mock_factory, 3}; depository.deposit_package(package, 1, size, pf); auto buffer1 = depository.current_buffer(); EXPECT_EQ(0u, buffer1->age()); auto package2 = std::make_shared(); depository.deposit_package(package2, 2, size, pf); auto buffer2 = depository.current_buffer(); EXPECT_EQ(1u, buffer1->age()); EXPECT_EQ(0u, buffer2->age()); auto package3 = std::make_shared(); depository.deposit_package(package3, 3, size, pf); auto buffer3 = depository.current_buffer(); EXPECT_EQ(2u, buffer1->age()); EXPECT_EQ(1u, buffer2->age()); EXPECT_EQ(0u, buffer3->age()); } TEST_F(ClientBufferDepository, reaches_steady_state_age_for_double_buffering) { using namespace testing; mcl::ClientBufferDepository depository{mock_factory, 3}; depository.deposit_package(package, 1, size, pf); auto buffer1 = depository.current_buffer(); EXPECT_EQ(0u, buffer1->age()); auto package2 = std::make_shared(); depository.deposit_package(package2, 2, size, pf); auto buffer2 = depository.current_buffer(); EXPECT_EQ(1u, buffer1->age()); EXPECT_EQ(0u, buffer2->age()); depository.deposit_package(package, 1, size, pf); EXPECT_EQ(2u, buffer1->age()); EXPECT_EQ(1u, buffer2->age()); depository.deposit_package(package2, 2, size, pf); EXPECT_EQ(1u, buffer1->age()); EXPECT_EQ(2u, buffer2->age()); } TEST_F(ClientBufferDepository, reaches_steady_state_age_when_triple_buffering) { using namespace testing; mcl::ClientBufferDepository depository{mock_factory, 3}; depository.deposit_package(package, 1, size, pf); auto buffer1 = depository.current_buffer(); auto package2 = std::make_shared(); depository.deposit_package(package2, 2, size, pf); auto buffer2 = depository.current_buffer(); auto package3 = std::make_shared(); depository.deposit_package(package3, 3, size, pf); auto buffer3 = depository.current_buffer(); EXPECT_EQ(2u, buffer1->age()); EXPECT_EQ(1u, buffer2->age()); EXPECT_EQ(0u, buffer3->age()); depository.deposit_package(package, 1, size, pf); EXPECT_EQ(3u, buffer1->age()); EXPECT_EQ(2u, buffer2->age()); EXPECT_EQ(1u, buffer3->age()); depository.deposit_package(package2, 2, size, pf); EXPECT_EQ(1u, buffer1->age()); EXPECT_EQ(3u, buffer2->age()); EXPECT_EQ(2u, buffer3->age()); depository.deposit_package(package3, 3, size, pf); EXPECT_EQ(2u, buffer1->age()); EXPECT_EQ(1u, buffer2->age()); EXPECT_EQ(3u, buffer3->age()); } TEST_F(ClientBufferDepository, destroys_old_buffers) { using namespace testing; const int num_buffers = 3; mcl::ClientBufferDepository depository{mock_factory, num_buffers}; const int num_packages = 4; std::shared_ptr packages[num_packages]; depository.deposit_package(packages[0], 1, size, pf); depository.deposit_package(packages[1], 2, size, pf); depository.deposit_package(packages[2], 3, size, pf); // We've deposited three different buffers now; the fourth should trigger the destruction // of the first buffer. EXPECT_THAT(mock_factory->free_count, Eq(0)); depository.deposit_package(packages[3], 4, size, pf); EXPECT_THAT(mock_factory->free_count, Eq(1)); EXPECT_TRUE(mock_factory->first_buffer_allocated_and_then_freed()); } TEST_F(ClientBufferDepository, implicitly_submits_current_buffer_on_deposit) { using namespace testing; const int num_buffers = 3; mcl::ClientBufferDepository depository{mock_factory, num_buffers}; auto package1 = std::make_shared(); auto package2 = std::make_shared(); depository.deposit_package(package1, 1, size, pf); EXPECT_CALL(*static_cast(depository.current_buffer().get()), mark_as_submitted()); depository.deposit_package(package2, 2, size, pf); } TEST_F(ClientBufferDepository, frees_buffers_after_reaching_capacity) { using namespace testing; std::shared_ptr depository; std::shared_ptr packages[10]; for (int num_buffers = 2; num_buffers < 10; ++num_buffers) { depository = std::make_shared(mock_factory, num_buffers); mock_factory->free_count = 0; mock_factory->alloc_count = 0; int i; for (i = 0; i < num_buffers ; ++i) depository->deposit_package(packages[i], i + 1, size, pf); // Next deposit should destroy a buffer EXPECT_THAT(mock_factory->free_count, Eq(0)); depository->deposit_package(packages[i], i+1, size, pf); EXPECT_THAT(mock_factory->free_count, Eq(1)); EXPECT_TRUE(mock_factory->first_buffer_allocated_and_then_freed()); } } TEST_F(ClientBufferDepository, caches_recently_seen_buffer) { using namespace testing; mcl::ClientBufferDepository depository{mock_factory, 3}; auto package1 = std::make_shared(); auto package2 = std::make_shared(); auto package3 = std::make_shared(); NiceMock mock_buffer; Sequence seq; EXPECT_CALL(*mock_factory, create_buffer(Ref(package1),_,_)) .InSequence(seq) .WillOnce(Return(mir::test::fake_shared(mock_buffer))); EXPECT_CALL(mock_buffer, update_from(Ref(*package2))) .InSequence(seq); EXPECT_CALL(mock_buffer, update_from(Ref(*package3))) .InSequence(seq); depository.deposit_package(package1, 8, size, pf); depository.deposit_package(package2, 8, size, pf); depository.deposit_package(package3, 8, size, pf); } TEST_F(ClientBufferDepository, creates_new_buffer_for_different_id) { using namespace testing; mcl::ClientBufferDepository depository{mock_factory, 3}; auto package1 = std::make_shared(); auto package2 = std::make_shared(); EXPECT_CALL(*mock_factory, create_buffer(_,_,_)) .Times(2); depository.deposit_package(package1, 8, size, pf); depository.deposit_package(package2, 9, size, pf); } TEST_F(ClientBufferDepository, keeps_last_2_buffers_regardless_of_age) { using namespace testing; mcl::ClientBufferDepository depository{mock_factory, 2}; EXPECT_CALL(*mock_factory, create_buffer(_,_,_)).Times(2); depository.deposit_package(package, 8, size, pf); depository.deposit_package(package, 9, size, pf); depository.deposit_package(package, 9, size, pf); depository.deposit_package(package, 9, size, pf); depository.deposit_package(package, 8, size, pf); } TEST_F(ClientBufferDepository, keeps_last_3_buffers_regardless_of_age) { using namespace testing; mcl::ClientBufferDepository depository{mock_factory, 3}; EXPECT_CALL(*mock_factory, create_buffer(_,_,_)).Times(3); depository.deposit_package(package, 8, size, pf); depository.deposit_package(package, 9, size, pf); depository.deposit_package(package, 10, size, pf); depository.deposit_package(package, 9, size, pf); depository.deposit_package(package, 10, size, pf); depository.deposit_package(package, 9, size, pf); depository.deposit_package(package, 10, size, pf); depository.deposit_package(package, 8, size, pf); } TEST_F(ClientBufferDepository, can_decrease_cache_size) { using namespace testing; int initial_size{3}; int changed_size{2}; mcl::ClientBufferDepository depository{mock_factory, initial_size}; EXPECT_CALL(*mock_factory, create_buffer(_,_,_)) .Times(4); depository.deposit_package(package, 8, size, pf); depository.deposit_package(package, 9, size, pf); depository.deposit_package(package, 10, size, pf); depository.deposit_package(package, 9, size, pf); depository.deposit_package(package, 10, size, pf); depository.set_max_buffers(changed_size); //8 should be kicked out depository.deposit_package(package, 8, size, pf); } TEST_F(ClientBufferDepository, can_increase_cache_size) { using namespace testing; int initial_size{3}; int changed_size{4}; mcl::ClientBufferDepository depository{mock_factory, initial_size}; EXPECT_CALL(*mock_factory, create_buffer(_,_,_)) .Times(4); depository.deposit_package(package, 8, size, pf); depository.deposit_package(package, 9, size, pf); depository.deposit_package(package, 10, size, pf); depository.deposit_package(package, 9, size, pf); depository.deposit_package(package, 10, size, pf); depository.set_max_buffers(changed_size); depository.deposit_package(package, 7, size, pf); depository.deposit_package(package, 8, size, pf); } TEST_F(ClientBufferDepository, cannot_have_zero_size) { int initial_size{3}; EXPECT_THROW({ mcl::ClientBufferDepository depository(mock_factory, 0); }, std::logic_error); mcl::ClientBufferDepository depository{mock_factory, initial_size}; EXPECT_THROW({ depository.set_max_buffers(0); }, std::logic_error); } ./tests/unit-tests/client/test_mir_buffer.cpp0000644000015600001650000001510012676616157021535 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/mock_client_buffer.h" #include "src/client/buffer.h" #include "mir_protobuf.pb.h" #include namespace mcl = mir::client; namespace mtd = mir::test::doubles; namespace geom = mir::geometry; using namespace testing; namespace { void buffer_callback(MirPresentationChain*, MirBuffer*, void* call_count_context) { if (call_count_context) (*static_cast(call_count_context))++; } struct MirBufferTest : Test { MirBufferTest() { ON_CALL(*mock_client_buffer, size()) .WillByDefault(Return(geom::Size{width, height})); ON_CALL(*mock_client_buffer, pixel_format()) .WillByDefault(Return(format)); } MirGraphicsRegion region; int buffer_id { 32 }; geom::Width width { 190 }; geom::Height height { 119 }; geom::Stride stride { 2211 }; MirBufferUsage usage { mir_buffer_usage_hardware }; MirPixelFormat format { mir_pixel_format_abgr_8888 }; std::shared_ptr vaddr { std::make_shared('\0') }; mir_buffer_callback cb { buffer_callback }; std::shared_ptr const mock_client_buffer { std::make_shared>() }; std::chrono::nanoseconds timeout { 101 }; MirBufferPackage update_message; }; } TEST_F(MirBufferTest, fills_region_with_correct_info_when_securing) { auto region = std::make_shared( mcl::MemoryRegion{width, height, stride, format, vaddr}); EXPECT_CALL(*mock_client_buffer, secure_for_cpu_write()) .WillOnce(Return(region)); mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); auto out_region = buffer.map_region(); EXPECT_THAT(out_region.width, Eq(width.as_int())); EXPECT_THAT(out_region.height, Eq(height.as_int())); EXPECT_THAT(out_region.stride, Eq(stride.as_int())); EXPECT_THAT(out_region.pixel_format, Eq(format)); EXPECT_THAT(out_region.vaddr, Eq(vaddr.get())); } TEST_F(MirBufferTest, releases_buffer_refcount_implicitly_on_submit) { auto region = std::make_shared( mcl::MemoryRegion{width, height, stride, format, vaddr}); EXPECT_CALL(*mock_client_buffer, secure_for_cpu_write()) .WillOnce(Return(region)); mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); auto use_count_before = region.use_count(); buffer.map_region(); EXPECT_THAT(use_count_before, Lt(region.use_count())); buffer.submitted(); EXPECT_THAT(use_count_before, Eq(region.use_count())); } TEST_F(MirBufferTest, returns_correct_native_buffer) { int fake { 4321 }; EXPECT_CALL(*mock_client_buffer, as_mir_native_buffer()) .WillOnce(Return(reinterpret_cast(&fake))); mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); EXPECT_THAT(buffer.as_mir_native_buffer(), Eq(reinterpret_cast(&fake))); } TEST_F(MirBufferTest, sets_client_buffers_fence) { int fakefence { 19 }; MirNativeFence* fence = reinterpret_cast(&fakefence); auto access = MirBufferAccess::mir_read_write; EXPECT_CALL(*mock_client_buffer, set_fence(fence, access)); mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); buffer.set_fence(fence, access); } TEST_F(MirBufferTest, gets_fence_from_client_buffer) { int fakefence { 19 }; MirNativeFence* fence = reinterpret_cast(&fakefence); EXPECT_CALL(*mock_client_buffer, get_fence()) .WillOnce(Return(fence)); mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); EXPECT_THAT(fence, Eq(buffer.get_fence())); } TEST_F(MirBufferTest, waits_for_proper_access) { int fakefence { 19 }; MirNativeFence* fence = reinterpret_cast(&fakefence); auto current_access = MirBufferAccess::mir_read; auto needed_access = MirBufferAccess::mir_read_write; EXPECT_CALL(*mock_client_buffer, set_fence(fence, current_access)); EXPECT_CALL(*mock_client_buffer, wait_fence(needed_access, timeout)) .WillOnce(Return(true)); mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); buffer.set_fence(fence, current_access); EXPECT_TRUE(buffer.wait_fence(needed_access, timeout)); } TEST_F(MirBufferTest, callback_called_when_available_from_creation) { int call_count = 0; mcl::Buffer buffer(cb, &call_count, buffer_id, mock_client_buffer, nullptr, usage); EXPECT_THAT(call_count, Eq(1)); } TEST_F(MirBufferTest, callback_called_when_available_from_server_return) { int call_count = 0; mcl::Buffer buffer(cb, &call_count, buffer_id, mock_client_buffer, nullptr, usage); buffer.submitted(); buffer.received(update_message); EXPECT_THAT(call_count, Eq(2)); } TEST_F(MirBufferTest, updates_package_when_server_returns) { EXPECT_CALL(*mock_client_buffer, update_from(Ref(update_message))); mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); buffer.submitted(); buffer.received(update_message); } TEST_F(MirBufferTest, submitting_unowned_buffer_throws) { mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); buffer.submitted(); EXPECT_THROW({ buffer.submitted(); }, std::logic_error); } TEST_F(MirBufferTest, returns_correct_format) { mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); EXPECT_THAT(buffer.pixel_format(), Eq(format)); } TEST_F(MirBufferTest, returns_correct_usage) { mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); EXPECT_THAT(buffer.buffer_usage(), Eq(usage)); } TEST_F(MirBufferTest, returns_correct_size) { mcl::Buffer buffer(cb, nullptr, buffer_id, mock_client_buffer, nullptr, usage); EXPECT_THAT(buffer.size().height, Eq(height)); EXPECT_THAT(buffer.size().width, Eq(width)); } ./tests/unit-tests/client/test_client_display_conf.cpp0000644000015600001650000000514012676616125023423 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/client/display_configuration.h" #include "mir/test/display_config_matchers.h" #include "gtest/gtest.h" namespace mp = mir::protobuf; namespace mcl = mir::client; namespace mt = mir::test; namespace { void fill(mp::DisplayCard* out) { out->set_card_id(7); out->set_max_simultaneous_outputs(3); } void fill(mp::DisplayOutput* out) { out->add_pixel_format(mir_pixel_format_xrgb_8888); out->set_current_format(mir_pixel_format_argb_8888); auto mode = out->add_mode(); mode->set_horizontal_resolution(4); mode->set_vertical_resolution(558); mode->set_refresh_rate(4.33f); out->set_current_mode(0); out->set_position_x(5); out->set_position_y(6); out->set_card_id(7); out->set_output_id(8); out->set_connected(1); out->set_used(1); out->set_physical_width_mm(11); out->set_physical_height_mm(12); out->set_orientation(90); } } TEST(TestDisplayConfiguration, configuration_storage) { mp::DisplayConfiguration protobuf_config; fill(protobuf_config.add_display_output()); fill(protobuf_config.add_display_output()); fill(protobuf_config.add_display_card()); fill(protobuf_config.add_display_card()); fill(protobuf_config.add_display_card()); mcl::DisplayConfiguration internal_config; internal_config.update_configuration(protobuf_config); MirDisplayConfiguration *info; info = internal_config.copy_to_client(); EXPECT_THAT(*info, mt::DisplayConfigMatches(protobuf_config)); mcl::delete_config_storage(info); int called_count = 0u; internal_config.set_display_change_handler([&]() { called_count++; }); mp::DisplayConfiguration new_result; fill(new_result.add_display_output()); internal_config.update_configuration(new_result); info = internal_config.copy_to_client(); EXPECT_THAT(*info, mt::DisplayConfigMatches(new_result)); EXPECT_EQ(1u, called_count); mcl::delete_config_storage(info); } ./tests/unit-tests/client/test_client_platform.cpp0000644000015600001650000001416612676616157022612 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/client_platform.h" #include "mir/egl_native_surface.h" #include "mir/test/doubles/mock_client_context.h" #include "mir/test/doubles/mock_egl_native_surface.h" #include "mir_test_framework/executable_path.h" #include "mir_test_framework/stub_platform_helpers.h" #ifdef MIR_BUILD_PLATFORM_ANDROID #include "mir/test/doubles/mock_android_hw.h" #endif #include "mir/client_platform_factory.h" #include "mir/shared_library.h" #include #include namespace mcl=mir::client; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { struct ClientPlatformTraits { ClientPlatformTraits(std::string const& library, std::function populator, MirPlatformType type) : platform_library_name{library}, populate_package_for{populator}, platform_type{type} { } std::string const platform_library_name; std::function const populate_package_for; MirPlatformType const platform_type; }; struct ClientPlatformTest : public ::testing::TestWithParam { ClientPlatformTest() : platform_library{mtf::client_platform(GetParam()->platform_library_name)}, create_client_platform{platform_library.load_function("create_client_platform")}, probe{platform_library.load_function("is_appropriate_module")} { using namespace testing; ON_CALL(context, populate_server_package(_)) .WillByDefault(Invoke(GetParam()->populate_package_for)); } mtd::MockClientContext context; #ifdef MIR_BUILD_PLATFORM_ANDROID testing::NiceMock hw_access_mock; #endif mir::SharedLibrary platform_library; mcl::CreateClientPlatform const create_client_platform; mcl::ClientPlatformProbe const probe; }; #ifdef MIR_BUILD_PLATFORM_ANDROID ClientPlatformTraits const android_platform{"android", [](MirPlatformPackage& pkg) { ::memset(&pkg, 0, sizeof(pkg)); }, mir_platform_type_android }; INSTANTIATE_TEST_CASE_P(Android, ClientPlatformTest, ::testing::Values(&android_platform)); #endif #if defined(MIR_BUILD_PLATFORM_MESA_KMS) || defined(MIR_BUILD_PLATFORM_MESA_X11) ClientPlatformTraits const mesa_platform{"mesa", [](MirPlatformPackage& pkg) { ::memset(&pkg, 0, sizeof(pkg)); pkg.fd_items = 1; }, mir_platform_type_gbm }; INSTANTIATE_TEST_CASE_P(Mesa, ClientPlatformTest, ::testing::Values(&mesa_platform)); #endif ClientPlatformTraits const dummy_platform{"dummy.so", [](MirPlatformPackage& pkg) { mtf::create_stub_platform_package(pkg); }, mir_platform_type_gbm }; INSTANTIATE_TEST_CASE_P(Dummy, ClientPlatformTest, ::testing::Values(&dummy_platform)); } TEST_P(ClientPlatformTest, platform_name) { auto platform = create_client_platform(&context); EXPECT_EQ(GetParam()->platform_type, platform->platform_type()); } TEST_P(ClientPlatformTest, platform_creates) { auto platform = create_client_platform(&context); auto buffer_factory = platform->create_buffer_factory(); EXPECT_NE(buffer_factory.get(), (mcl::ClientBufferFactory*) NULL); } TEST_P(ClientPlatformTest, platform_creates_native_window) { auto platform = create_client_platform(&context); auto mock_client_surface = std::make_shared(); auto native_window = platform->create_egl_native_window(mock_client_surface.get()); EXPECT_THAT(native_window.get(), testing::Ne(nullptr)); } TEST_P(ClientPlatformTest, platform_creates_egl_native_display) { auto platform = create_client_platform(&context); auto native_display = platform->create_egl_native_display(); EXPECT_NE(nullptr, native_display.get()); } TEST_P(ClientPlatformTest, platform_probe_returns_success_when_matching) { EXPECT_TRUE(probe(&context)); } TEST_P(ClientPlatformTest, platform_probe_returns_false_when_not_matching) { using namespace testing; ON_CALL(context, populate_server_package(_)) .WillByDefault(Invoke([](MirPlatformPackage& pkg) { //Mock up something that hopefully looks nothing like //what the platform is expecting... ::memset(&pkg, 0, sizeof(pkg)); pkg.data_items = mir_platform_package_max + 1; pkg.fd_items = -23; })); EXPECT_FALSE(probe(&context)); } ./tests/unit-tests/client/test_android_client_buffer_factory.cpp0000644000015600001650000000434212676616125025454 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_toolkit/mir_client_library.h" #include "src/client/android/android_client_buffer_factory.h" #include "src/client/android/android_client_buffer.h" #include "mir/test/doubles/mock_android_registrar.h" #include namespace geom = mir::geometry; namespace mcla = mir::client::android; namespace mt = mir::test; namespace mtd = mir::test::doubles; struct MirBufferFactoryTest : public testing::Test { void SetUp() { width = geom::Width(12); height =geom::Height(14); size = geom::Size{width, height}; pf = mir_pixel_format_abgr_8888; mock_registrar = std::make_shared(); package1 = std::make_shared(); package1->width = width.as_int(); package1->height = height.as_int(); } geom::Width width; geom::Height height; MirPixelFormat pf; geom::Size size; std::shared_ptr mock_registrar; std::shared_ptr package1; }; TEST_F(MirBufferFactoryTest, factory_sets_width_and_height) { using namespace testing; mcla::AndroidClientBufferFactory buffer_factory(mock_registrar); EXPECT_CALL(*mock_registrar, register_buffer(_)) .Times(1); EXPECT_CALL(*mock_registrar, unregister_buffer(_)) .Times(1); auto buffer = buffer_factory.create_buffer(package1, size, pf); EXPECT_EQ(package1->width, buffer->size().width.as_int()); EXPECT_EQ(package1->height, buffer->size().height.as_int()); EXPECT_EQ(buffer->pixel_format(), pf); } ./tests/unit-tests/client/test_aging_buffer.cpp0000644000015600001650000000520112676616157022034 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/aging_buffer.h" #include #include namespace mcl = mir::client; namespace geom = mir::geometry; namespace mir { namespace test { struct MyAgingBuffer : public mcl::AgingBuffer { std::shared_ptr secure_for_cpu_write() override { exit(1); } geom::Size size() const override { exit(1); } geom::Stride stride() const override { exit(1); } MirPixelFormat pixel_format() const override { exit(1); } std::shared_ptr native_buffer_handle() const override { exit(1); } void update_from(MirBufferPackage const&) override { } void fill_update_msg(MirBufferPackage&) override { } MirNativeBuffer* as_mir_native_buffer() const { exit(1); } void set_fence(MirNativeFence*, MirBufferAccess) { exit(1); } MirNativeFence* get_fence() const { exit(1); } bool wait_fence(MirBufferAccess, std::chrono::nanoseconds) { exit(1); } }; TEST(MirClientAgingBufferTest, buffer_age_starts_at_zero) { using namespace testing; MyAgingBuffer buffer; EXPECT_EQ(0u, buffer.age()); } TEST(MirClientAgingBufferTest, buffer_age_set_to_one_on_submit) { using namespace testing; MyAgingBuffer buffer; buffer.mark_as_submitted(); EXPECT_EQ(1u, buffer.age()); } TEST(MirClientAgingBufferTest, buffer_age_increases_on_increment) { using namespace testing; MyAgingBuffer buffer; buffer.mark_as_submitted(); for (uint32_t age = 2; age < 10; ++age) { buffer.increment_age(); EXPECT_EQ(age, buffer.age()); } } TEST(MirClientAgingBufferTest, incrementing_age_has_no_effect_for_unsubmitted_buffer) { using namespace testing; MyAgingBuffer buffer; buffer.increment_age(); EXPECT_EQ(0u, buffer.age()); } } } ./tests/unit-tests/client/mesa/0000755000015600001650000000000012676616160016574 5ustar jenkinsjenkins./tests/unit-tests/client/mesa/test_client_platform.cpp0000644000015600001650000001247512676616125023533 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/client_platform.h" #include "mir/shared_library.h" #include "mir/raii.h" #include "src/platforms/mesa/client/mesa_native_display_container.h" #include "mir_test_framework/client_platform_factory.h" #include "mir/test/doubles/mock_egl.h" #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/mesa/native_display.h" #include "mir_toolkit/mesa/platform_operation.h" #include #include #include namespace mcl = mir::client; namespace mclm = mir::client::mesa; namespace mtf = mir_test_framework; namespace { struct StubClientContext : mcl::ClientContext { void populate_server_package(MirPlatformPackage& platform_package) override { platform_package.data_items = 0; platform_package.fd_items = 1; } }; struct MesaClientPlatformTest : testing::Test { MirPlatformMessage* set_gbm_device(gbm_device* dev) { auto request_msg = mir::raii::deleter_for( mir_platform_message_create(MirMesaPlatformOperation::set_gbm_device), &mir_platform_message_release); MirMesaSetGBMDeviceRequest const request{dev}; mir_platform_message_set_data(request_msg.get(), &request, sizeof(request)); return platform->platform_operation(request_msg.get()); } StubClientContext client_context; std::shared_ptr platform = mtf::create_mesa_client_platform(&client_context); mir::test::doubles::MockEGL mock_egl; }; } TEST_F(MesaClientPlatformTest, egl_native_display_is_valid_until_released) { auto platform_lib = mtf::get_platform_library(); MirMesaEGLNativeDisplay* nd; { std::shared_ptr native_display = platform->create_egl_native_display(); nd = reinterpret_cast(*native_display); auto validate = platform_lib->load_function("mir_client_mesa_egl_native_display_is_valid"); EXPECT_EQ(MIR_MESA_TRUE, validate(nd)); } EXPECT_EQ(MIR_MESA_FALSE, mclm::mir_client_mesa_egl_native_display_is_valid(nd)); } TEST_F(MesaClientPlatformTest, handles_set_gbm_device_platform_operation) { using namespace testing; int const success{0}; auto const gbm_dev_dummy = reinterpret_cast(this); auto response_msg = mir::raii::deleter_for( set_gbm_device(gbm_dev_dummy), &mir_platform_message_release); ASSERT_THAT(response_msg, NotNull()); auto const response_data = mir_platform_message_get_data(response_msg.get()); ASSERT_THAT(response_data.size, Eq(sizeof(MirMesaSetGBMDeviceResponse))); MirMesaSetGBMDeviceResponse response{-1}; std::memcpy(&response, response_data.data, response_data.size); EXPECT_THAT(response.status, Eq(success)); } TEST_F(MesaClientPlatformTest, appends_gbm_device_to_platform_package) { using namespace testing; MirPlatformPackage pkg; platform->populate(pkg); int const previous_data_count{pkg.data_items}; auto const gbm_dev_dummy = reinterpret_cast(this); auto response_msg = mir::raii::deleter_for( set_gbm_device(gbm_dev_dummy), &mir_platform_message_release); platform->populate(pkg); EXPECT_THAT(pkg.data_items, Eq(previous_data_count + (sizeof(gbm_dev_dummy) / sizeof(int)))); gbm_device* device_in_package{nullptr}; std::memcpy(&device_in_package, &pkg.data[previous_data_count], sizeof(device_in_package)); EXPECT_THAT(device_in_package, Eq(gbm_dev_dummy)); } TEST_F(MesaClientPlatformTest, returns_gbm_compatible_pixel_formats_only) { using namespace testing; auto const d = reinterpret_cast(0x1234); auto const c = reinterpret_cast(0x5678); EXPECT_CALL(mock_egl, eglGetConfigAttrib(d, c, EGL_RED_SIZE, _)) .WillRepeatedly(DoAll(SetArgPointee<3>(8), Return(EGL_TRUE))); EXPECT_CALL(mock_egl, eglGetConfigAttrib(d, c, EGL_GREEN_SIZE, _)) .WillRepeatedly(DoAll(SetArgPointee<3>(8), Return(EGL_TRUE))); EXPECT_CALL(mock_egl, eglGetConfigAttrib(d, c, EGL_BLUE_SIZE, _)) .WillRepeatedly(DoAll(SetArgPointee<3>(8), Return(EGL_TRUE))); EXPECT_CALL(mock_egl, eglGetConfigAttrib(d, c, EGL_ALPHA_SIZE, _)) .WillOnce(DoAll(SetArgPointee<3>(8), Return(EGL_TRUE))) .WillOnce(DoAll(SetArgPointee<3>(0), Return(EGL_TRUE))) .WillOnce(DoAll(SetArgPointee<3>(666), Return(EGL_FALSE))); EXPECT_EQ(mir_pixel_format_argb_8888, platform->get_egl_pixel_format(d, c)); EXPECT_EQ(mir_pixel_format_xrgb_8888, platform->get_egl_pixel_format(d, c)); EXPECT_EQ(mir_pixel_format_invalid, platform->get_egl_pixel_format(d, c)); } ./tests/unit-tests/client/mesa/test_native_surface.cpp0000644000015600001650000001140612676616157023345 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/platforms/mesa/client/native_surface.h" #include "mir/client_buffer.h" #include "mir/test/doubles/mock_egl_native_surface.h" #include "mir/test/doubles/mock_client_buffer.h" #include #include namespace mtd=mir::test::doubles; namespace mcl=mir::client; namespace mcl=mir::client; namespace mclg=mir::client::mesa; namespace geom=mir::geometry; class MesaClientNativeSurfaceTest : public ::testing::Test { public: virtual void SetUp() { using namespace testing; surf_params.width = 530; surf_params.height = 715; surf_params.pixel_format = mir_pixel_format_abgr_8888; ON_CALL(mock_surface, get_parameters()) .WillByDefault(Return(surf_params)); ON_CALL(mock_surface, get_current_buffer()) .WillByDefault(Return( std::make_shared>())); } MirSurfaceParameters surf_params; testing::NiceMock mock_surface; mclg::NativeSurface native_surface{mock_surface}; }; TEST_F(MesaClientNativeSurfaceTest, basic_parameters) { MirSurfaceParameters params; native_surface.surface_get_parameters(&native_surface, ¶ms); EXPECT_EQ(surf_params.width, params.width); EXPECT_EQ(surf_params.height, params.height); EXPECT_EQ(surf_params.pixel_format, params.pixel_format); } TEST_F(MesaClientNativeSurfaceTest, first_advance_skips_request) { // Verify the workaround for LP: #1281938 is functioning, until it's // fixed in Mesa and we can remove it from Mir. using namespace testing; MirBufferPackage buffer_package; EXPECT_CALL(mock_surface, request_and_wait_for_next_buffer()) .Times(0); EXPECT_CALL(mock_surface, get_current_buffer()) .Times(1); native_surface.surface_advance_buffer(&native_surface, &buffer_package); } TEST_F(MesaClientNativeSurfaceTest, basic_advance) { using namespace testing; MirBufferPackage buffer_package; InSequence seq; EXPECT_CALL(mock_surface, get_current_buffer()) .Times(1); EXPECT_CALL(mock_surface, request_and_wait_for_next_buffer()) .Times(1); EXPECT_CALL(mock_surface, get_current_buffer()) .Times(1); native_surface.surface_advance_buffer(&native_surface, &buffer_package); native_surface.surface_advance_buffer(&native_surface, &buffer_package); } TEST_F(MesaClientNativeSurfaceTest, swapinterval_request) { using namespace testing; Sequence seq; EXPECT_CALL(mock_surface, request_and_wait_for_configure(mir_surface_attrib_swapinterval,0)) .InSequence(seq); EXPECT_CALL(mock_surface, request_and_wait_for_configure(mir_surface_attrib_swapinterval,1)) .InSequence(seq); native_surface.set_swapinterval(0); native_surface.set_swapinterval(1); } TEST_F(MesaClientNativeSurfaceTest, swapinterval_unsupported_request) { EXPECT_EQ(MIR_MESA_FALSE, native_surface.set_swapinterval(-1)); EXPECT_EQ(MIR_MESA_TRUE, native_surface.set_swapinterval(0)); EXPECT_EQ(MIR_MESA_TRUE, native_surface.set_swapinterval(1)); EXPECT_EQ(MIR_MESA_FALSE, native_surface.set_swapinterval(2)); } TEST_F(MesaClientNativeSurfaceTest, returns_error_on_advance_buffer_failure) { using namespace testing; EXPECT_CALL(mock_surface, get_current_buffer()) .WillOnce(Throw(std::runtime_error(""))); MirBufferPackage buffer_package; EXPECT_THAT(native_surface.advance_buffer(&buffer_package), Eq(MIR_MESA_FALSE)); } TEST_F(MesaClientNativeSurfaceTest, returns_error_on_get_parameters_failure) { using namespace testing; EXPECT_CALL(mock_surface, get_parameters()) .WillOnce(Throw(std::runtime_error(""))); MirSurfaceParameters surface_params; EXPECT_THAT(native_surface.get_parameters(&surface_params), Eq(MIR_MESA_FALSE)); } TEST_F(MesaClientNativeSurfaceTest, returns_error_on_set_swap_interval_failure) { using namespace testing; EXPECT_CALL(mock_surface, request_and_wait_for_configure(_,_)) .WillOnce(Throw(std::runtime_error(""))); EXPECT_THAT(native_surface.set_swapinterval(0), Eq(MIR_MESA_FALSE)); } ./tests/unit-tests/client/mesa/test_mesa_native_display_container.cpp0000644000015600001650000000360112676616125026422 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/platforms/mesa/client/mesa_native_display_container.h" #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/mesa/native_display.h" #include #include #include namespace mclg = mir::client::mesa; namespace { struct MesaNativeDisplayContainerSetup : public testing::Test { MesaNativeDisplayContainerSetup() : container(std::make_shared()), platform(nullptr) { } std::shared_ptr const container; mir::client::ClientPlatform* platform; }; } TEST_F(MesaNativeDisplayContainerSetup, valid_displays_come_from_factory) { using namespace ::testing; auto display = container->create(platform); EXPECT_TRUE(container->validate(display)); MirEGLNativeDisplayType invalid_native_display; EXPECT_FALSE(container->validate(&invalid_native_display)); } TEST_F(MesaNativeDisplayContainerSetup, releasing_displays_invalidates_address) { using namespace ::testing; auto display = container->create(platform); EXPECT_TRUE(container->validate(display)); container->release(display); EXPECT_FALSE(container->validate(display)); } ./tests/unit-tests/client/mesa/CMakeLists.txt0000644000015600001650000000245012676616125021336 0ustar jenkinsjenkins# Copyright © 2012 Canonical Ltd. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Authored by: Thomas Voss , # Alan Griffiths include_directories( ${PROJECT_SOURCE_DIR}/include/platforms/mesa ${PROJECT_SOURCE_DIR}/src/platforms/mesa/server/common ) add_library(unit_test_client_platform_mesa OBJECT ${CMAKE_CURRENT_SOURCE_DIR}/test_client_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_client_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mesa_native_display_container.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_native_surface.cpp ) list(APPEND UNIT_TEST_SOURCES $ $ ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/client/mesa/test_client_buffer.cpp0000644000015600001650000001376612676616125023164 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_toolkit/mir_client_library.h" #include "src/platforms/mesa/client/client_buffer.h" #include "src/platforms/mesa/client/client_buffer_factory.h" #include "src/platforms/mesa/client/buffer_file_ops.h" #include #include #include #include namespace geom=mir::geometry; namespace mclg=mir::client::mesa; namespace { class MockBufferFileOps : public mclg::BufferFileOps { public: MOCK_CONST_METHOD1(close, int(int fd)); MOCK_CONST_METHOD3(map, void*(int fd, off_t offset, size_t size)); MOCK_CONST_METHOD2(unmap, void(void* addr, size_t size)); }; struct MesaClientBufferTest : public testing::Test { void SetUp() { width = geom::Width(12); height =geom::Height(14); stride = geom::Stride(66); pf = mir_pixel_format_abgr_8888; size = geom::Size{width, height}; buffer_file_ops = std::make_shared>(); package = std::make_shared(); package->fd[0] = 167; package->fd_items = 1; package->stride = stride.as_uint32_t(); package->width = width.as_int(); package->height = height.as_int(); package_copy = std::make_shared(*package.get()); } geom::Width width; geom::Height height; geom::Stride stride; MirPixelFormat pf; geom::Size size; std::shared_ptr> buffer_file_ops; std::shared_ptr package; std::shared_ptr package_copy; }; } TEST_F(MesaClientBufferTest, width_and_height) { using namespace testing; mclg::ClientBuffer buffer(buffer_file_ops, package, size, pf); EXPECT_EQ(buffer.size().height, height); EXPECT_EQ(buffer.size().width, width); EXPECT_EQ(buffer.pixel_format(), pf); } TEST_F(MesaClientBufferTest, buffer_returns_correct_stride) { using namespace testing; mclg::ClientBuffer buffer(buffer_file_ops, package, size, pf); EXPECT_EQ(buffer.stride(), stride); } TEST_F(MesaClientBufferTest, buffer_returns_set_package) { using namespace testing; mclg::ClientBuffer buffer(buffer_file_ops, package, size, pf); auto package_return = buffer.native_buffer_handle(); EXPECT_EQ(package_return->data_items, package_copy->data_items); EXPECT_EQ(package_return->fd_items, package_copy->fd_items); EXPECT_EQ(package_return->stride, package_copy->stride); for (auto i=0; idata[i], package_copy->data[i]); for (auto i=0; ifd[i], package_copy->fd[i]); } TEST_F(MesaClientBufferTest, secure_for_cpu_write_maps_buffer_fd) { using namespace testing; void *map_addr{reinterpret_cast(0xabcdef)}; EXPECT_CALL(*buffer_file_ops, map(package->fd[0],_,_)) .WillOnce(Return(map_addr)); EXPECT_CALL(*buffer_file_ops, unmap(map_addr,_)) .Times(1); EXPECT_CALL(*buffer_file_ops, close(package->fd[0])) .Times(1); mclg::ClientBuffer buffer(buffer_file_ops, package, size, pf); auto mem_region = buffer.secure_for_cpu_write(); ASSERT_EQ(map_addr, mem_region->vaddr.get()); ASSERT_EQ(width, mem_region->width); ASSERT_EQ(height, mem_region->height); ASSERT_EQ(stride, mem_region->stride); ASSERT_EQ(pf, mem_region->format); } TEST_F(MesaClientBufferTest, secure_for_cpu_write_throws_on_map_failure) { using namespace testing; EXPECT_CALL(*buffer_file_ops, map(package->fd[0],_,_)) .WillOnce(Return(MAP_FAILED)); EXPECT_CALL(*buffer_file_ops, unmap(_,_)) .Times(0); EXPECT_CALL(*buffer_file_ops, close(package->fd[0])) .Times(1); mclg::ClientBuffer buffer(buffer_file_ops, package, size, pf); EXPECT_THROW({ auto mem_region = buffer.secure_for_cpu_write(); }, std::runtime_error); } TEST_F(MesaClientBufferTest, buffer_fd_closed_on_buffer_destruction) { using namespace testing; EXPECT_CALL(*buffer_file_ops, close(package->fd[0])) .Times(1); mclg::ClientBuffer buffer(buffer_file_ops, package, size, pf); } TEST_F(MesaClientBufferTest, factory_gets_size_from_package) { using namespace testing; mclg::ClientBufferFactory factory(buffer_file_ops); geom::Size unused_size{0, 0}; auto buffer = factory.create_buffer(package, unused_size, pf); auto const& buf_size = buffer->size(); EXPECT_EQ(package->width, buf_size.width.as_int()); EXPECT_EQ(package->height, buf_size.height.as_int()); EXPECT_NE(unused_size, buf_size); } TEST_F(MesaClientBufferTest, creation_with_invalid_buffer_package_throws) { using namespace testing; mclg::ClientBufferFactory factory(buffer_file_ops); auto const invalid_package = std::make_shared(); package->fd_items = 0; geom::Size const unused_size{0, 0}; EXPECT_THROW({ factory.create_buffer(invalid_package, unused_size, pf); }, std::runtime_error); } TEST_F(MesaClientBufferTest, packs_empty_update_msg) { using namespace testing; mclg::ClientBuffer buffer(buffer_file_ops, package, size, pf); MirBufferPackage msg; msg.data_items = 2; msg.fd_items = 2; buffer.fill_update_msg(msg); EXPECT_THAT(msg.data_items, Eq(0)); EXPECT_THAT(msg.fd_items, Eq(0)); } ./tests/unit-tests/client/input/0000755000015600001650000000000012676616126017010 5ustar jenkinsjenkins./tests/unit-tests/client/input/test_android_input_receiver.cpp0000644000015600001650000003700012676616125025275 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/client/input/android/android_input_receiver.h" #include "mir/input/null_input_receiver_report.h" #include "mir/input/xkb_mapper.h" #include "mir/events/event_private.h" #include "mir_toolkit/event.h" #include "mir/test/fd_utils.h" #include #include #include #include #include #include #include #include namespace mircv = mir::input::receiver; namespace mircva = mircv::android; namespace md = mir::dispatch; namespace mt = mir::test; namespace droidinput = android; namespace { class TestingInputProducer { public: TestingInputProducer(int fd) : input_publisher(std::make_shared(new droidinput::InputChannel("", fd))), incrementing_seq_id(1), // Sequence id must be > 0 or publisher will reject testing_key_event_scan_code(13) { } // The input publisher does not care about event semantics so we only highlight // a few fields for transport verification void produce_a_key_event() { input_publisher->publishKeyEvent(incrementing_seq_id, filler_device_id, 0 /* source */, 0 /* action */, 0 /* flags */, 0 /* key_code */, testing_key_event_scan_code, 0 /* meta_state */, 0 /* repeat_count */, {{}} /* mac */, std::chrono::nanoseconds(0) /* down_time */, std::chrono::nanoseconds(0) /* event_time */); } void produce_a_pointer_event(float x, float y, std::chrono::nanoseconds t) { droidinput::PointerProperties filler_pointer_properties; droidinput::PointerCoords filler_pointer_coordinates; memset(&filler_pointer_properties, 0, sizeof(droidinput::PointerProperties)); memset(&filler_pointer_coordinates, 0, sizeof(droidinput::PointerCoords)); filler_pointer_coordinates.setAxisValue(AMOTION_EVENT_AXIS_X, x); filler_pointer_coordinates.setAxisValue(AMOTION_EVENT_AXIS_Y, y); input_publisher->publishMotionEvent(incrementing_seq_id, filler_device_id, 0 /* source */, motion_event_action_flags, 0 /* flags */, 0 /* edge_flags */, 0 /* meta_state */, 0 /* button_state */, 0 /* x_offset */, 0 /* y_offset */, 0 /* x_precision */, 0 /* y_precision */, {{}} /* mac */, std::chrono::nanoseconds(0) /* down_time */, t, default_pointer_count, &filler_pointer_properties, &filler_pointer_coordinates); } bool must_receive_handled_signal() { uint32_t seq; bool handled; auto status = input_publisher->receiveFinishedSignal(&seq, &handled); return (status == droidinput::OK) && handled; } std::shared_ptr input_publisher; int incrementing_seq_id; int32_t testing_key_event_scan_code; // Some default values // device_id must be > 0 or input publisher will reject static const int32_t filler_device_id = 1; // event_action_move is necessary to engage batching behavior static const int32_t motion_event_action_flags = AMOTION_EVENT_ACTION_MOVE; // We have to have at least 1 pointer or the publisher will fail to marshal a motion event static const int32_t default_pointer_count = 1; }; class AndroidInputReceiverSetup : public testing::Test { public: AndroidInputReceiverSetup() : event_handler{[this](MirEvent* ev) { last_event = *ev; }} { auto status = droidinput::InputChannel::openInputFdPair(server_fd, client_fd); EXPECT_EQ(droidinput::OK, status); } ~AndroidInputReceiverSetup() { close(server_fd); close(client_fd); } void flush_channels() { fsync(server_fd); fsync(client_fd); } int server_fd, client_fd; static std::chrono::milliseconds const next_event_timeout; MirEvent last_event; std::function event_handler; }; std::chrono::milliseconds const AndroidInputReceiverSetup::next_event_timeout(1000); } TEST_F(AndroidInputReceiverSetup, receiver_receives_key_events) { mircva::InputReceiver receiver{client_fd, std::make_shared(), event_handler, std::make_shared()}; TestingInputProducer producer{server_fd}; producer.produce_a_key_event(); flush_channels(); EXPECT_TRUE(mt::fd_becomes_readable(receiver.watch_fd(), next_event_timeout)); receiver.dispatch(md::FdEvent::readable); EXPECT_EQ(mir_event_type_key, last_event.type); EXPECT_EQ(producer.testing_key_event_scan_code, last_event.key.scan_code); } TEST_F(AndroidInputReceiverSetup, receiver_handles_events) { mircva::InputReceiver receiver{client_fd, std::make_shared(), event_handler, std::make_shared()}; TestingInputProducer producer(server_fd); producer.produce_a_key_event(); flush_channels(); EXPECT_TRUE(mt::fd_becomes_readable(receiver.watch_fd(), next_event_timeout)); receiver.dispatch(md::FdEvent::readable); flush_channels(); EXPECT_TRUE(producer.must_receive_handled_signal()); } TEST_F(AndroidInputReceiverSetup, receiver_consumes_batched_motion_events) { mircva::InputReceiver receiver{client_fd, std::make_shared(), event_handler, std::make_shared()}; TestingInputProducer producer(server_fd); // Produce 3 motion events before client handles any. producer.produce_a_pointer_event(0, 0, std::chrono::nanoseconds(0)); producer.produce_a_pointer_event(0, 0, std::chrono::nanoseconds(0)); producer.produce_a_pointer_event(0, 0, std::chrono::nanoseconds(0)); flush_channels(); EXPECT_TRUE(mt::fd_becomes_readable(receiver.watch_fd(), next_event_timeout)); receiver.dispatch(md::FdEvent::readable); // Now there should be no events EXPECT_FALSE(mt::fd_is_readable(receiver.watch_fd())); } TEST_F(AndroidInputReceiverSetup, slow_raw_input_doesnt_cause_frameskipping) { // Regression test for LP: #1372300 using namespace testing; using namespace std::chrono; using namespace std::literals::chrono_literals; auto t = 0ns; MirEvent ev; bool handler_called{false}; mircva::InputReceiver receiver{client_fd, std::make_shared(), [&ev, &handler_called](MirEvent* event) { ev = *event; handler_called = true; }, std::make_shared(), [&t](int) { return t; }}; TestingInputProducer producer(server_fd); nanoseconds const one_frame = duration_cast(1s) / 60; producer.produce_a_pointer_event(123, 456, t); producer.produce_a_key_event(); flush_channels(); // Key events don't get resampled. Will be reported first. EXPECT_TRUE(mt::fd_becomes_readable(receiver.watch_fd(), next_event_timeout)); receiver.dispatch(md::FdEvent::readable); EXPECT_TRUE(handler_called); ASSERT_EQ(mir_event_type_key, ev.type); // The motion is still too new. Won't be reported yet, but is batched. auto start = high_resolution_clock::now(); EXPECT_TRUE(mt::fd_becomes_readable(receiver.watch_fd(), 1ms)); handler_called = false; receiver.dispatch(md::FdEvent::readable); // We've processed the data, but no new event has been generated. EXPECT_FALSE(handler_called); auto end = high_resolution_clock::now(); auto duration = end - start; // Verify we timed out in under a frame (LP: #1372300) // Sadly using real time as droidinput::Looper doesn't use a mocked clock. ASSERT_LT(duration, one_frame); // Verify we don't use all the CPU by not sleeping (LP: #1373809) EXPECT_GT(duration, 1ms); // But later in a frame or so, the motion will be reported: t += 2 * one_frame; // Account for the slower 59Hz event rate EXPECT_TRUE(mt::fd_becomes_readable(receiver.watch_fd(), next_event_timeout)); receiver.dispatch(md::FdEvent::readable); EXPECT_TRUE(handler_called); EXPECT_EQ(mir_event_type_motion, ev.type); } TEST_F(AndroidInputReceiverSetup, finish_signalled_after_handler) { bool handler_called = false; TestingInputProducer producer(server_fd); mircva::InputReceiver receiver{ client_fd, std::make_shared(), [&handler_called, &producer](MirEvent*) { EXPECT_FALSE(producer.must_receive_handled_signal()); handler_called = true; }, std::make_shared() }; producer.produce_a_key_event(); flush_channels(); receiver.dispatch(md::FdEvent::readable); EXPECT_TRUE(handler_called); EXPECT_TRUE(producer.must_receive_handled_signal()); } TEST_F(AndroidInputReceiverSetup, rendering_does_not_lag_behind_input) { using namespace testing; using namespace std::chrono; using namespace std::literals::chrono_literals; std::chrono::nanoseconds t; int frames_triggered = 0; mircva::InputReceiver receiver{client_fd, std::make_shared(), [&frames_triggered](MirEvent*) { ++frames_triggered; }, std::make_shared(), [&t](int) { return t; }}; TestingInputProducer producer(server_fd); std::chrono::nanoseconds const device_sample_interval = duration_cast(1s) / 250; std::chrono::nanoseconds const frame_interval = duration_cast(1s) / 60; std::chrono::nanoseconds const gesture_duration = 1s; std::chrono::nanoseconds last_produced = 0ns; for (t = 0ns; t < gesture_duration; t += 1ms) { if (!t.count() || t >= (last_produced + device_sample_interval)) { last_produced = t; float a = t.count() * M_PI / 1000000.0f; float x = 500.0f * sinf(a); float y = 1000.0f * cosf(a); producer.produce_a_pointer_event(x, y, t); flush_channels(); } if (mt::fd_is_readable(receiver.watch_fd())) { receiver.dispatch(md::FdEvent::readable); } } // If the rendering time resulting from the gesture is longer than the // gesture itself then that's laggy... std::chrono::nanoseconds render_duration = frame_interval * frames_triggered; EXPECT_THAT(render_duration, Le(gesture_duration)); int average_lag_milliseconds = (render_duration - gesture_duration) / (frames_triggered * 1ms); EXPECT_THAT(average_lag_milliseconds, Le(1)); } TEST_F(AndroidInputReceiverSetup, input_comes_in_phase_with_rendering) { using namespace testing; std::chrono::nanoseconds t; mircva::InputReceiver receiver{ client_fd, std::make_shared(), event_handler, std::make_shared(), }; TestingInputProducer producer(server_fd); std::chrono::nanoseconds const one_millisecond = std::chrono::nanoseconds(1000000ULL); std::chrono::nanoseconds const one_second = 1000 * one_millisecond; std::chrono::nanoseconds const device_sample_interval = one_second / 125; std::chrono::nanoseconds const frame_interval = one_second / 60; std::chrono::nanoseconds const gesture_duration = 10 * one_second; std::chrono::nanoseconds last_produced = std::chrono::nanoseconds(0); std::chrono::nanoseconds last_consumed = std::chrono::nanoseconds(0); std::chrono::nanoseconds last_rendered = std::chrono::nanoseconds(0); std::chrono::nanoseconds last_in_phase = std::chrono::nanoseconds(0); for (t = std::chrono::nanoseconds(0); t < gesture_duration; t += one_millisecond) { if (!t.count() || t >= (last_produced + device_sample_interval)) { last_produced = t; float a = t.count() * M_PI / 1000000.0f; float x = 500.0f * sinf(a); float y = 1000.0f * cosf(a); producer.produce_a_pointer_event(x, y, t); flush_channels(); } if (mt::fd_is_readable(receiver.watch_fd())) { receiver.dispatch(md::FdEvent::readable); last_consumed = t; } if (t >= (last_rendered + frame_interval)) { last_rendered = t; if (last_consumed.count()) { auto lag = last_rendered - last_consumed; // How often does vsync drift in phase (very close) with the // time that we emitted/consumed input events? if (lag < 4 * one_millisecond) last_in_phase = t; last_consumed = std::chrono::nanoseconds(0); } } // Verify input and vsync come into phase at least a few times every // second (if not always). This ensure visible lag is minimized. ASSERT_GE(250 * one_millisecond, (t - last_in_phase)); } } ./tests/unit-tests/client/input/test_xkb_mapper.cpp0000644000015600001650000000463612676616125022713 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/input/xkb_mapper.h" #include "mir/events/event_private.h" #include "mir/events/event_builders.h" #include #include #include #include namespace mircv = mir::input::receiver; namespace mev = mir::events; namespace { static int map_key(mircv::XKBMapper &mapper, MirKeyboardAction action, int scan_code) { auto ev = mev::make_event(MirInputDeviceId(0), std::chrono::nanoseconds(0), std::vector{}, action, 0, scan_code, mir_input_event_modifier_none); mapper.update_state_and_map_event(*ev); return ev->key.key_code; } } TEST(XKBMapper, maps_generic_us_english_keys) { mircv::XKBMapper mapper; EXPECT_EQ(XKB_KEY_4, map_key(mapper, mir_keyboard_action_down, KEY_4)); EXPECT_EQ(XKB_KEY_Shift_L, map_key(mapper, mir_keyboard_action_down, KEY_LEFTSHIFT)); EXPECT_EQ(XKB_KEY_dollar, map_key(mapper, mir_keyboard_action_down, KEY_4)); EXPECT_EQ(XKB_KEY_dollar, map_key(mapper, mir_keyboard_action_up, KEY_4)); EXPECT_EQ(XKB_KEY_Shift_L, map_key(mapper, mir_keyboard_action_up, KEY_LEFTSHIFT)); EXPECT_EQ(XKB_KEY_4, map_key(mapper, mir_keyboard_action_down, KEY_4)); } TEST(XKBMapper, key_repeats_do_not_recurse_modifier_state) { mircv::XKBMapper mapper; EXPECT_EQ(XKB_KEY_Shift_R, map_key(mapper, mir_keyboard_action_down, KEY_RIGHTSHIFT)); EXPECT_EQ(XKB_KEY_Shift_R, map_key(mapper, mir_keyboard_action_repeat, KEY_RIGHTSHIFT)); EXPECT_EQ(XKB_KEY_ampersand, map_key(mapper, mir_keyboard_action_down, KEY_7)); EXPECT_EQ(XKB_KEY_Shift_R, map_key(mapper, mir_keyboard_action_up, KEY_RIGHTSHIFT)); EXPECT_EQ(XKB_KEY_7, map_key(mapper, mir_keyboard_action_down, KEY_7)); } ./tests/unit-tests/client/input/CMakeLists.txt0000644000015600001650000000032112676616125021543 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_android_input_receiver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_xkb_mapper.cpp ) set( UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/client/android/0000755000015600001650000000000012676616160017267 5ustar jenkinsjenkins./tests/unit-tests/client/android/test_egl_native_surface_interpreter.cpp0000644000015600001650000002424012676616157027312 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_toolkit/mir_native_buffer.h" #include "native_buffer.h" #include "mir/egl_native_surface.h" #include "mir/client_buffer.h" #include "mir/frontend/client_constants.h" #include "src/platforms/android/client/egl_native_surface_interpreter.h" #include "mir/test/doubles/stub_android_native_buffer.h" #include "mir/test/doubles/mock_client_buffer.h" #include "mir/test/fake_shared.h" #include #include #include #include namespace mcl=mir::client; namespace mcla=mir::client::android; namespace mga=mir::graphics::android; namespace geom=mir::geometry; namespace mt=mir::test; namespace mtd=mir::test::doubles; struct MockMirSurface : public mcl::EGLNativeSurface { MockMirSurface(MirSurfaceParameters params) : params(params), client_buffer(std::make_shared()), buffer(std::make_shared()) { using namespace testing; ON_CALL(*this, get_parameters()) .WillByDefault(Return(params)); ON_CALL(*client_buffer, native_buffer_handle()) .WillByDefault(Return(buffer)); ON_CALL(*this, get_current_buffer()) .WillByDefault(Return( std::make_shared>())); } MOCK_CONST_METHOD0(get_parameters, MirSurfaceParameters()); MOCK_METHOD0(get_current_buffer, std::shared_ptr()); MOCK_METHOD0(request_and_wait_for_next_buffer, void()); MOCK_METHOD2(request_and_wait_for_configure, void(MirSurfaceAttrib, int)); MOCK_METHOD1(set_buffer_cache_size, void(unsigned int)); MirSurfaceParameters params; std::shared_ptr client_buffer; std::shared_ptr buffer; }; class AndroidInterpreter : public ::testing::Test { protected: virtual void SetUp() { using namespace testing; surf_params.width = 530; surf_params.height = 715; surf_params.pixel_format = mir_pixel_format_abgr_8888; mock_client_buffer = std::make_shared>(); ON_CALL(*mock_client_buffer, native_buffer_handle()) .WillByDefault(Return(std::make_shared())); } MirSurfaceParameters surf_params; std::shared_ptr mock_client_buffer; }; TEST_F(AndroidInterpreter, gets_buffer_via_the_surface_on_request) { using namespace testing; testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); EXPECT_CALL(mock_surface, get_current_buffer()) .Times(1) .WillOnce(Return(mock_client_buffer)); interpreter.driver_requests_buffer(); } TEST_F(AndroidInterpreter, gets_native_handle_from_returned_buffer) { using namespace testing; auto buffer = std::make_shared(); testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); EXPECT_CALL(*mock_client_buffer, native_buffer_handle()) .Times(1) .WillOnce(Return(buffer)); EXPECT_CALL(mock_surface, get_current_buffer()) .Times(1) .WillOnce(Return(mock_client_buffer)); auto returned_buffer = interpreter.driver_requests_buffer(); EXPECT_EQ(buffer.get(), returned_buffer); } TEST_F(AndroidInterpreter, advances_surface_on_buffer_return) { using namespace testing; ANativeWindowBuffer buffer; testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); EXPECT_CALL(mock_surface, request_and_wait_for_next_buffer()) .Times(1); interpreter.driver_returns_buffer(&buffer, -1); } /* format is an int that is set by the driver. these are not the HAL_PIXEL_FORMATS in android */ TEST_F(AndroidInterpreter, remembers_format) { int format = 945; testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); interpreter.dispatch_driver_request_format(format); auto tmp_format = interpreter.driver_requests_info(NATIVE_WINDOW_FORMAT); EXPECT_EQ(format, tmp_format); } TEST_F(AndroidInterpreter, returns_no_transform_for_transform_hint_query) { testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); /* transform hint is a bitmask of a few options for rotation/flipping buffer. a value of zero is no transform */ int transform_hint_zero = 0; auto transform = interpreter.driver_requests_info(NATIVE_WINDOW_TRANSFORM_HINT); EXPECT_EQ(transform_hint_zero, transform); } TEST_F(AndroidInterpreter, returns_width_as_default_width) { testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); auto default_width = interpreter.driver_requests_info(NATIVE_WINDOW_DEFAULT_WIDTH); EXPECT_EQ(surf_params.width, default_width); } TEST_F(AndroidInterpreter, returns_height_as_default_height) { testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); auto default_height = interpreter.driver_requests_info(NATIVE_WINDOW_DEFAULT_HEIGHT); EXPECT_EQ(surf_params.height, default_height); } TEST_F(AndroidInterpreter, returns_surface_as_concrete_type) { testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); auto concrete_type = interpreter.driver_requests_info(NATIVE_WINDOW_CONCRETE_TYPE); EXPECT_EQ(NATIVE_WINDOW_SURFACE, concrete_type); } TEST_F(AndroidInterpreter, returns_width) { testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); auto width = interpreter.driver_requests_info(NATIVE_WINDOW_WIDTH); EXPECT_EQ(surf_params.width, width); } TEST_F(AndroidInterpreter, returns_height) { testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); auto height = interpreter.driver_requests_info(NATIVE_WINDOW_HEIGHT); EXPECT_EQ(surf_params.height, height); } /* this is query key is a bit confusing from the system/window.h description. what it means is the minimum number of buffers that the server reserves for its own use in steady state. The drivers consider 'steady state' to begin after the first call to queueBuffer. So, for instance, if a driver requires 3 buffers to run at steady state, and the server needs to keep 2 buffers on hand at all time, the driver might dequeue 5 buffers, then cancel those 5 buffers. After the first call to queueBuffer however, the client may never own more than the number it has reserved (in this case, 3 buffers) */ TEST_F(AndroidInterpreter, returns_2_for_min_undequeued_query) { testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); auto num_buffers = interpreter.driver_requests_info(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS); EXPECT_EQ(2, num_buffers); } TEST_F(AndroidInterpreter, requests_swapinterval_change) { testing::NiceMock mock_surface{surf_params}; testing::InSequence seq; EXPECT_CALL(mock_surface, request_and_wait_for_configure(mir_surface_attrib_swapinterval, 1)); EXPECT_CALL(mock_surface, request_and_wait_for_configure(mir_surface_attrib_swapinterval, 0)); mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); interpreter.sync_to_display(true); interpreter.sync_to_display(false); } TEST_F(AndroidInterpreter, request_to_set_buffer_count_sets_cache_size) { int new_size = 5; testing::NiceMock mock_surface{surf_params}; EXPECT_CALL(mock_surface, set_buffer_cache_size(new_size)); mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); interpreter.dispatch_driver_request_buffer_count(new_size); } TEST_F(AndroidInterpreter, does_not_set_lower_than_mir_frontend_cache_size) { int new_size = mir::frontend::client_buffer_cache_size - 1; testing::NiceMock mock_surface{surf_params}; EXPECT_CALL(mock_surface, set_buffer_cache_size(new_size)) .Times(0); mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); interpreter.dispatch_driver_request_buffer_count(new_size); } TEST_F(AndroidInterpreter, returns_proper_usage_bits_based_on_surface) { using namespace testing; MirSurfaceParameters const software = { "", 1, 2, mir_pixel_format_abgr_8888, mir_buffer_usage_software, 0 }; MirSurfaceParameters const hardware = { "", 1, 2, mir_pixel_format_abgr_8888, mir_buffer_usage_hardware, 0 }; testing::NiceMock mock_surface{surf_params}; mcla::EGLNativeSurfaceInterpreter interpreter(mock_surface); EXPECT_CALL(mock_surface, get_parameters()) .Times(2) .WillOnce(Return(software)) .WillOnce(Return(hardware)); auto const hardware_bits = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER; auto const software_bits = GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE; EXPECT_THAT(interpreter.driver_requests_info(NATIVE_WINDOW_CONSUMER_USAGE_BITS), Eq(software_bits)); EXPECT_THAT(interpreter.driver_requests_info(NATIVE_WINDOW_CONSUMER_USAGE_BITS), Eq(hardware_bits)); } ./tests/unit-tests/client/android/test_android_client_platform.cpp0000644000015600001650000000574212676616125025725 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/client_platform.h" #include "mir/test/doubles/mock_client_context.h" #include "mir/test/doubles/mock_egl_native_surface.h" #include "mir/test/doubles/mock_egl.h" #include "mir_test_framework/client_platform_factory.h" #include #include #include #include using namespace testing; using namespace mir::client; using namespace mir::test; using namespace mir::test::doubles; using namespace mir_test_framework; struct AndroidClientPlatformTest : public Test { AndroidClientPlatformTest() : platform{create_android_client_platform()} { } std::shared_ptr platform; MockEGL mock_egl; }; TEST_F(AndroidClientPlatformTest, egl_native_display_is_egl_default_display) { MockEGLNativeSurface surface; auto mock_egl_native_surface = std::make_shared(); auto native_display = platform->create_egl_native_display(); EXPECT_EQ(EGL_DEFAULT_DISPLAY, *native_display); } TEST_F(AndroidClientPlatformTest, egl_native_window_is_set) { MockEGLNativeSurface surface; auto mock_egl_native_surface = std::make_shared(); auto egl_native_window = platform->create_egl_native_window(&surface); EXPECT_NE(nullptr, egl_native_window); } TEST_F(AndroidClientPlatformTest, egl_pixel_format_asks_the_driver) { auto const d = reinterpret_cast(0x1234); auto const c = reinterpret_cast(0x5678); EXPECT_CALL(mock_egl, eglGetConfigAttrib(d, c, EGL_NATIVE_VISUAL_ID, _)) .WillOnce(DoAll(SetArgPointee<3>(HAL_PIXEL_FORMAT_RGB_565), Return(EGL_TRUE))) .WillOnce(DoAll(SetArgPointee<3>(HAL_PIXEL_FORMAT_RGB_888), Return(EGL_TRUE))) .WillOnce(DoAll(SetArgPointee<3>(HAL_PIXEL_FORMAT_BGRA_8888), Return(EGL_TRUE))) .WillOnce(DoAll(SetArgPointee<3>(0), Return(EGL_FALSE))); EXPECT_EQ(mir_pixel_format_rgb_565, platform->get_egl_pixel_format(d, c)); EXPECT_EQ(mir_pixel_format_rgb_888, platform->get_egl_pixel_format(d, c)); EXPECT_EQ(mir_pixel_format_argb_8888, platform->get_egl_pixel_format(d, c)); EXPECT_EQ(mir_pixel_format_invalid, platform->get_egl_pixel_format(d, c)); } ./tests/unit-tests/client/android/test_buffer.cpp0000644000015600001650000001101412676616125022301 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/mock_buffer_registrar.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "src/platforms/android/client/buffer.h" #include "mir_toolkit/mir_client_library.h" #include #include #include #include #include namespace mtd = mir::test::doubles; namespace mcla = mir::client::android; namespace mg = mir::graphics; namespace mga = mir::graphics::android; namespace geom = mir::geometry; struct AndroidClientBuffer : public ::testing::Test { AndroidClientBuffer() : mock_registrar{std::make_shared>()}, native_handle{std::make_shared()}, mock_native_buffer{std::make_shared(size)} { ON_CALL(*mock_registrar, register_buffer(testing::_, testing::_)) .WillByDefault(testing::Return(mock_native_buffer)); package.height = height.as_int(); package.width = width.as_int(); package.stride = stride.as_int(); } geom::Height const height{124}; geom::Width const width{248}; geom::Size const size{width, height}; geom::Stride const stride{66}; MirPixelFormat const pf{mir_pixel_format_abgr_8888}; std::shared_ptr const mock_registrar; std::shared_ptr const native_handle; std::shared_ptr const mock_native_buffer; MirBufferPackage package; }; TEST_F(AndroidClientBuffer, registers_native_handle_with_correct_size) { EXPECT_CALL(*mock_registrar, register_buffer(testing::_, testing::_)) .WillOnce(testing::Return(mock_native_buffer)); mcla::Buffer buffer(mock_registrar, package, pf); EXPECT_EQ(size, buffer.size()); EXPECT_EQ(pf, buffer.pixel_format()); EXPECT_EQ(stride, buffer.stride()); } TEST_F(AndroidClientBuffer, packs_memory_region_correctly) { using namespace testing; geom::Rectangle rect{{0,0}, size}; std::shared_ptr empty_char = std::make_shared(); EXPECT_CALL(*mock_registrar, secure_for_cpu(Eq(mock_native_buffer),rect)) .Times(1) .WillOnce(Return(empty_char)); mcla::Buffer buffer(mock_registrar, package, pf); auto region = buffer.secure_for_cpu_write(); EXPECT_EQ(empty_char, region->vaddr); EXPECT_EQ(width, region->width); EXPECT_EQ(height, region->height); EXPECT_EQ(stride, region->stride); EXPECT_EQ(pf, region->format); } TEST_F(AndroidClientBuffer, update_from_package_merges_fence_when_present) { mga::NativeFence fake_fence{213}; EXPECT_CALL(*mock_native_buffer, update_usage(fake_fence, mga::BufferAccess::read)) .Times(1); mcla::Buffer buffer(mock_registrar, package, pf); package.data_items = 1; package.fd_items = 1; package.data[0] = static_cast(mga::BufferFlag::fenced); package.fd[0] = fake_fence; buffer.update_from(package); package.data[0] = static_cast(mga::BufferFlag::unfenced); buffer.update_from(package); } TEST_F(AndroidClientBuffer, fills_update_msg) { using namespace testing; using mir::graphics::android::BufferFlag; int stub_fence{44}; int invalid_fence{-1}; EXPECT_CALL(*mock_native_buffer, copy_fence()) .Times(2) .WillOnce(Return(stub_fence)) .WillOnce(Return(invalid_fence)); MirBufferPackage msg; mcla::Buffer buffer(mock_registrar, package, pf); buffer.fill_update_msg(msg); EXPECT_THAT(msg.data_items, Eq(1)); EXPECT_THAT(msg.data[0], Eq(static_cast(BufferFlag::fenced))); EXPECT_THAT(msg.fd_items, Eq(1)); EXPECT_THAT(msg.fd[0], Eq(stub_fence)); buffer.fill_update_msg(msg); EXPECT_THAT(msg.data_items, Eq(1)); EXPECT_THAT(msg.data[0], Eq(static_cast(BufferFlag::unfenced))); EXPECT_THAT(msg.fd_items, Eq(0)); } ./tests/unit-tests/client/android/test_android_native_window.cpp0000644000015600001650000002633612676616125025422 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_native_window.h" #include "android_driver_interpreter.h" #include "mir/egl_native_surface.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include "mir/test/doubles/stub_android_native_buffer.h" #include #include #include namespace mga=mir::graphics::android; namespace mtd=mir::test::doubles; namespace { class MockAndroidDriverInterpreter : public mga::AndroidDriverInterpreter { public: MockAndroidDriverInterpreter() : buffer(std::make_shared()) { using namespace testing; ON_CALL(*this, driver_requests_buffer()) .WillByDefault(Return(buffer.get())); } MOCK_METHOD0(driver_requests_buffer, mir::graphics::NativeBuffer*()); MOCK_METHOD2(driver_returns_buffer, void(ANativeWindowBuffer*, int)); MOCK_METHOD1(dispatch_driver_request_format, void(int)); MOCK_CONST_METHOD1(driver_requests_info, int(int)); MOCK_METHOD1(sync_to_display, void(bool)); MOCK_METHOD1(dispatch_driver_request_buffer_count, void(unsigned int)); std::shared_ptr buffer; }; class AndroidNativeWindowTest : public ::testing::Test { protected: std::shared_ptr const mock_driver_interpreter = std::make_shared>(); mga::MirNativeWindow mir_native_window{mock_driver_interpreter}; ANativeWindow& window = mir_native_window; int const failure_code{-1}; }; } TEST_F(AndroidNativeWindowTest, native_window_swapinterval) { using namespace testing; ASSERT_NE(nullptr, window.setSwapInterval); EXPECT_CALL(*mock_driver_interpreter, sync_to_display(true)) .Times(1); window.setSwapInterval(&window, 1); Mock::VerifyAndClearExpectations(mock_driver_interpreter.get()); EXPECT_CALL(*mock_driver_interpreter, sync_to_display(true)) .Times(1); window.setSwapInterval(&window, 2); Mock::VerifyAndClearExpectations(mock_driver_interpreter.get()); EXPECT_CALL(*mock_driver_interpreter, sync_to_display(false)) .Times(1); window.setSwapInterval(&window, 0); } /* Query hook tests */ TEST_F(AndroidNativeWindowTest, native_window_query_hook) { using namespace testing; int returned_width; int const width = 271828; ASSERT_NE(nullptr, window.query); EXPECT_CALL(*mock_driver_interpreter, driver_requests_info(NATIVE_WINDOW_WIDTH)) .Times(1) .WillOnce(Return(width)); window.query(&window, NATIVE_WINDOW_WIDTH ,&returned_width); EXPECT_EQ(width, returned_width); } /* perform hook tests */ TEST_F(AndroidNativeWindowTest, native_window_perform_hook_callable) { int const format = 4; EXPECT_CALL(*mock_driver_interpreter, dispatch_driver_request_format(format)) .Times(1); ASSERT_NE(nullptr, window.perform); window.perform(&window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format); } TEST_F(AndroidNativeWindowTest, native_window_perform_hook_calls_set_buffer) { int const size = 4; EXPECT_CALL(*mock_driver_interpreter, dispatch_driver_request_buffer_count(size)); ASSERT_NE(nullptr, window.perform); window.perform(&window, NATIVE_WINDOW_SET_BUFFER_COUNT, size); } /* setSwapInterval hook tests */ TEST_F(AndroidNativeWindowTest, native_window_setswapinterval_hook_callable) { int const swap = 2; ASSERT_NE(nullptr, window.setSwapInterval); window.setSwapInterval(&window, swap); } /* dequeue hook tests */ TEST_F(AndroidNativeWindowTest, native_window_dequeue_hook_callable) { ANativeWindowBuffer* returned_buffer; int fence_fd; ASSERT_NE(nullptr, window.dequeueBuffer); window.dequeueBuffer(&window, &returned_buffer, &fence_fd); } TEST_F(AndroidNativeWindowTest, native_window_dequeue_returns_right_buffer) { using namespace testing; int fake_fd = 4948; auto mock_buffer = std::make_shared>(); EXPECT_CALL(*mock_buffer, copy_fence()) .Times(1) .WillOnce(Return(fake_fd)); EXPECT_CALL(*mock_driver_interpreter, driver_requests_buffer()) .Times(1) .WillOnce(Return(mock_buffer.get())); int fence_fd; ANativeWindowBuffer* returned_buffer; window.dequeueBuffer(&window, &returned_buffer, &fence_fd); EXPECT_EQ(mock_buffer->anwb(), returned_buffer); EXPECT_EQ(fake_fd, fence_fd); } TEST_F(AndroidNativeWindowTest, native_window_dequeue_returns_previously_cancelled_buffer) { using namespace testing; ANativeWindowBuffer buffer; int fence_fd = 33; auto rc = window.cancelBuffer(&window, &buffer, fence_fd); EXPECT_EQ(0, rc); EXPECT_CALL(*mock_driver_interpreter, driver_requests_buffer()) .Times(0); ANativeWindowBuffer* dequeued_buffer; ANativeWindowBuffer* expected_buffer = &buffer; window.dequeueBuffer(&window, &dequeued_buffer, &fence_fd); EXPECT_EQ(dequeued_buffer, expected_buffer); } TEST_F(AndroidNativeWindowTest, native_window_dequeue_deprecated_hook_callable) { ANativeWindowBuffer* tmp; ASSERT_NE(nullptr, window.dequeueBuffer_DEPRECATED); window.dequeueBuffer_DEPRECATED(&window, &tmp); } TEST_F(AndroidNativeWindowTest, native_window_dequeue_deprecated_returns_right_buffer) { using namespace testing; ANativeWindowBuffer* returned_buffer; auto mock_buffer = std::make_shared>(); EXPECT_CALL(*mock_driver_interpreter, driver_requests_buffer()) .Times(1) .WillOnce(Return(mock_buffer.get())); EXPECT_CALL(*mock_buffer, ensure_available_for(mga::BufferAccess::write)) .Times(1); EXPECT_CALL(*mock_buffer, copy_fence()) .Times(0); window.dequeueBuffer_DEPRECATED(&window, &returned_buffer); EXPECT_EQ(mock_buffer->anwb(), returned_buffer); } TEST_F(AndroidNativeWindowTest, native_window_dequeue_deprecated_returns_previously_cancelled_buffer) { using namespace testing; ANativeWindowBuffer buffer; auto rc = window.cancelBuffer_DEPRECATED(&window, &buffer); EXPECT_EQ(0, rc); EXPECT_CALL(*mock_driver_interpreter, driver_requests_buffer()) .Times(0); ANativeWindowBuffer* dequeued_buffer; ANativeWindowBuffer* expected_buffer = &buffer; window.dequeueBuffer_DEPRECATED(&window, &dequeued_buffer); EXPECT_EQ(dequeued_buffer, expected_buffer); } /* queue hook tests */ TEST_F(AndroidNativeWindowTest, native_window_queue_hook_callable) { ANativeWindowBuffer* tmp = nullptr; ASSERT_NE(nullptr, window.queueBuffer); window.queueBuffer(&window, tmp, -1); } TEST_F(AndroidNativeWindowTest, native_window_queue_passes_buffer_back) { using namespace testing; ANativeWindowBuffer buffer; int fence_fd = 33; EXPECT_CALL(*mock_driver_interpreter, driver_returns_buffer(&buffer, fence_fd)) .Times(1); window.queueBuffer(&window, &buffer, fence_fd); } TEST_F(AndroidNativeWindowTest, native_window_queue_deprecated_hook_callable) { ANativeWindowBuffer* tmp = nullptr; ASSERT_NE(nullptr, window.queueBuffer_DEPRECATED); window.queueBuffer_DEPRECATED(&window, tmp); } TEST_F(AndroidNativeWindowTest, native_window_queue_deprecated_passes_buffer_back) { using namespace testing; ANativeWindowBuffer buffer; EXPECT_CALL(*mock_driver_interpreter, driver_returns_buffer(&buffer,_)) .Times(1); window.queueBuffer_DEPRECATED(&window, &buffer); } /* cancel hook tests */ TEST_F(AndroidNativeWindowTest, native_window_cancel_hooks_callable) { ANativeWindowBuffer* tmp = nullptr; ASSERT_NE(nullptr, window.cancelBuffer_DEPRECATED); ASSERT_NE(nullptr, window.cancelBuffer); window.cancelBuffer_DEPRECATED(&window, tmp); window.cancelBuffer(&window, tmp, -1); } /* lock hook tests */ TEST_F(AndroidNativeWindowTest, native_window_lock_hook_callable) { ANativeWindowBuffer* tmp = 0x0; ASSERT_NE(nullptr, window.lockBuffer_DEPRECATED); window.lockBuffer_DEPRECATED(&window, tmp); } TEST_F(AndroidNativeWindowTest, native_window_incref_hook_callable) { ASSERT_NE(nullptr, window.common.incRef); window.common.incRef(NULL); } TEST_F(AndroidNativeWindowTest, native_window_decref_hook_callable) { ASSERT_NE(nullptr, window.common.decRef); window.common.decRef(NULL); } TEST_F(AndroidNativeWindowTest, native_window_dequeue_deprecated_has_proper_rc) { ANativeWindowBuffer* tmp; auto ret = window.dequeueBuffer_DEPRECATED(&window, &tmp); EXPECT_EQ(0, ret); } TEST_F(AndroidNativeWindowTest, native_window_dequeue_has_proper_rc) { ANativeWindowBuffer* tmp; int fencefd; auto ret = window.dequeueBuffer(&window, &tmp, &fencefd); EXPECT_EQ(0, ret); } TEST_F(AndroidNativeWindowTest, native_window_cancel_hook_does_not_call_driver_interpreter) { using namespace testing; ANativeWindowBuffer buffer; int fence_fd = 33; EXPECT_CALL(*mock_driver_interpreter, driver_returns_buffer(&buffer, _)) .Times(0); auto rc = window.cancelBuffer(&window, &buffer, fence_fd); EXPECT_EQ(0, rc); } TEST_F(AndroidNativeWindowTest, returns_error_on_dequeue_buffer_failure) { using namespace testing; EXPECT_CALL(*mock_driver_interpreter, driver_requests_buffer()) .WillOnce(Throw(std::runtime_error(""))) .WillOnce(Throw(std::runtime_error(""))); EXPECT_THAT(window.dequeueBuffer(&window, nullptr, nullptr), Eq(failure_code)); EXPECT_THAT(window.dequeueBuffer_DEPRECATED(&window, nullptr), Eq(failure_code)); } TEST_F(AndroidNativeWindowTest, returns_error_on_queue_buffer_failure) { using namespace testing; EXPECT_CALL(*mock_driver_interpreter, driver_returns_buffer(_, _)) .WillOnce(Throw(std::runtime_error(""))) .WillOnce(Throw(std::runtime_error(""))); EXPECT_THAT(window.queueBuffer(&window, nullptr, 0), Eq(failure_code)); EXPECT_THAT(window.queueBuffer_DEPRECATED(&window, nullptr), Eq(failure_code)); } TEST_F(AndroidNativeWindowTest, returns_error_on_query_failure) { using namespace testing; EXPECT_CALL(*mock_driver_interpreter, driver_requests_info(_)) .WillOnce(Throw(std::runtime_error(""))); EXPECT_THAT(window.query(&window, 0, nullptr), Eq(failure_code)); } TEST_F(AndroidNativeWindowTest, returns_error_on_perform_failure) { using namespace testing; EXPECT_CALL(*mock_driver_interpreter, dispatch_driver_request_format(_)) .WillOnce(Throw(std::runtime_error(""))); auto perform = [this] (int key, ...) { va_list args; va_start(args, key); EXPECT_THAT(window.perform(&window, key, args), Eq(failure_code)); va_end(args); }; perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT, 0); } ./tests/unit-tests/client/android/CMakeLists.txt0000644000015600001650000000220512676616125022027 0ustar jenkinsjenkins# Copyright © 2012 Canonical Ltd. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Authored by: Thomas Voss , # Alan Griffiths list(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_gralloc_registrar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_android_native_window.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_android_client_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_egl_native_surface_interpreter.cpp $ ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/client/android/test_gralloc_registrar.cpp0000644000015600001650000002337312676616125024550 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "native_buffer.h" #include "src/platforms/android/client/gralloc_registrar.h" #include "mir/test/doubles/mock_android_native_buffer.h" #include #include #include #include namespace mcla = mir::client::android; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; class MockRegistrarDevice : public gralloc_module_t { public: MockRegistrarDevice() { registerBuffer = hook_registerBuffer; unregisterBuffer = hook_unregisterBuffer; lock = hook_lock; unlock = hook_unlock; } MOCK_CONST_METHOD2(registerBuffer_interface, int(struct gralloc_module_t const*, buffer_handle_t)); MOCK_CONST_METHOD2(unregisterBuffer_interface, int(struct gralloc_module_t const*, buffer_handle_t)); MOCK_CONST_METHOD8(lock_interface, int(struct gralloc_module_t const*, buffer_handle_t, int, int, int, int, int, void**)); MOCK_CONST_METHOD2(unlock_interface, int(struct gralloc_module_t const*, buffer_handle_t)); static int hook_registerBuffer( struct gralloc_module_t const* module, buffer_handle_t handle) { auto registrar = static_cast(module); return registrar->registerBuffer_interface(module, handle); } static int hook_unregisterBuffer( struct gralloc_module_t const* module, buffer_handle_t handle) { auto registrar = static_cast(module); return registrar->unregisterBuffer_interface(module, handle); } static int hook_lock( struct gralloc_module_t const* module, buffer_handle_t handle, int usage, int l, int t, int w, int h, void** vaddr) { auto registrar = static_cast(module); return registrar->lock_interface( module, handle, usage, l, t, w, h, vaddr); } static int hook_unlock( struct gralloc_module_t const* module, buffer_handle_t handle) { auto registrar = static_cast(module); return registrar->unlock_interface(module, handle); } }; struct GrallocRegistrar : public ::testing::Test { GrallocRegistrar() : mock_module(std::make_shared>()), mock_native_buffer(std::make_shared>()) { stub_package.width = width; stub_package.height = height; stub_package.stride = stride; stub_package.fd_items = 4; stub_package.data_items = 21; for (auto i = 0; i < stub_package.fd_items; i++) stub_package.fd[i] = (i*4); stub_package.data[0] = static_cast(mir::graphics::android::BufferFlag::unfenced); for (auto i = 1; i < stub_package.data_items; i++) stub_package.data[i] = (i*3); } uint32_t const width{41}; uint32_t const height{43}; uint32_t const top{0}; uint32_t const left{1}; uint32_t const stride{11235}; MirPixelFormat const pf{mir_pixel_format_abgr_8888}; geom::Rectangle const rect{geom::Point{top, left}, geom::Size{width, height}}; std::shared_ptr const mock_module; MirBufferPackage stub_package; std::shared_ptr const mock_native_buffer; }; TEST_F(GrallocRegistrar, client_buffer_converts_stub_package) { int const flag_offset{1}; mcla::GrallocRegistrar registrar(mock_module); auto buffer = registrar.register_buffer(stub_package, pf); auto handle = buffer->handle(); ASSERT_NE(nullptr, handle); ASSERT_EQ(stub_package.fd_items, handle->numFds); ASSERT_EQ(stub_package.data_items - 1, handle->numInts); for (auto i = 0; i < stub_package.fd_items; i++) EXPECT_EQ(stub_package.fd[i], handle->data[i]); for (auto i = 0; i < stub_package.data_items - 1; i++) EXPECT_EQ(stub_package.data[i + flag_offset], handle->data[i + stub_package.fd_items]); } TEST_F(GrallocRegistrar, client_sets_correct_version) { mcla::GrallocRegistrar registrar(mock_module); auto buffer = registrar.register_buffer(stub_package, pf); EXPECT_EQ(buffer->handle()->version, static_cast(sizeof(native_handle_t))); } TEST_F(GrallocRegistrar, registrar_registers_using_module) { using namespace testing; native_handle_t const* handle1 = nullptr; native_handle_t const* handle2 = nullptr; EXPECT_CALL(*mock_module, registerBuffer_interface(mock_module.get(),_)) .Times(1) .WillOnce(DoAll(SaveArg<1>(&handle1), Return(0))); EXPECT_CALL(*mock_module, unregisterBuffer_interface(mock_module.get(),_)) .Times(1) .WillOnce(DoAll(SaveArg<1>(&handle2), Return(0))); mcla::GrallocRegistrar registrar(mock_module); { auto buffer = registrar.register_buffer(stub_package, pf); EXPECT_EQ(handle1, buffer->handle()); } EXPECT_EQ(handle1, handle2); } TEST_F(GrallocRegistrar, registrar_frees_fds) { using namespace testing; MirBufferPackage stub_package; memset(&stub_package, 0, sizeof(MirBufferPackage)); stub_package.data_items = 1; stub_package.data[0] = static_cast(mir::graphics::android::BufferFlag::unfenced); stub_package.fd_items = 2; EXPECT_EQ(0, pipe(static_cast(stub_package.fd))); { mcla::GrallocRegistrar registrar(mock_module); auto buffer = registrar.register_buffer(stub_package, pf); } EXPECT_EQ(-1, fcntl(stub_package.fd[0], F_GETFD)); EXPECT_EQ(-1, fcntl(stub_package.fd[1], F_GETFD)); } TEST_F(GrallocRegistrar, register_failure) { using namespace testing; EXPECT_CALL(*mock_module, registerBuffer_interface(_, _)) .Times(1) .WillOnce(Return(-1)); EXPECT_CALL(*mock_module, unregisterBuffer_interface(_,_)) .Times(0); mcla::GrallocRegistrar registrar(mock_module); EXPECT_THROW({ registrar.register_buffer(stub_package, pf); }, std::runtime_error); } TEST_F(GrallocRegistrar, region_is_cleaned_up_correctly) { using namespace testing; mcla::GrallocRegistrar registrar(mock_module); gralloc_module_t const* gralloc_dev_alloc{nullptr}; gralloc_module_t const* gralloc_dev_freed{nullptr}; native_handle_t const* handle_alloc{nullptr}; native_handle_t const* handle_freed{nullptr}; Sequence seq; EXPECT_CALL(*mock_module, lock_interface(_,_,_,_,_,_,_,_)) .InSequence(seq) .WillOnce(DoAll( SaveArg<0>(&gralloc_dev_alloc), SaveArg<1>(&handle_alloc), Return(0))); EXPECT_CALL(*mock_module, unlock_interface(_,_)) .InSequence(seq) .WillOnce(DoAll( SaveArg<0>(&gralloc_dev_freed), SaveArg<1>(&handle_freed), Return(0))); registrar.secure_for_cpu(mock_native_buffer, rect); EXPECT_EQ(gralloc_dev_freed, gralloc_dev_alloc); EXPECT_EQ(handle_alloc, handle_freed); } TEST_F(GrallocRegistrar, maps_buffer_for_cpu_correctly) { EXPECT_CALL(*mock_module, lock_interface( mock_module.get(), mock_native_buffer->handle(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, top, left, width, height, testing::_)) .Times(1); mcla::GrallocRegistrar registrar(mock_module); registrar.secure_for_cpu(mock_native_buffer, rect); } TEST_F(GrallocRegistrar, lock_failure_throws) { using namespace testing; EXPECT_CALL(*mock_module, lock_interface(_,_,_,_,_,_,_,_)) .Times(1) .WillOnce(Return(-1)); mcla::GrallocRegistrar registrar(mock_module); EXPECT_THROW({ registrar.secure_for_cpu(mock_native_buffer, rect); }, std::runtime_error); } /* unlock is called in destructor. should not throw */ TEST_F(GrallocRegistrar, unlock_failure_doesnt_throw) { using namespace testing; EXPECT_CALL(*mock_module, unlock_interface(_,_)) .Times(1) .WillOnce(Return(-1)); mcla::GrallocRegistrar registrar(mock_module); auto region = registrar.secure_for_cpu(mock_native_buffer, rect); EXPECT_NO_THROW({ region.reset(); }); } TEST_F(GrallocRegistrar, produces_valid_anwb) { using namespace testing; mcla::GrallocRegistrar registrar(mock_module); int correct_usage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER; int32_t const expected_stride_in_pixels = static_cast(stride / MIR_BYTES_PER_PIXEL(pf)); auto native_handle = registrar.register_buffer(stub_package, pf); ASSERT_THAT(native_handle, Ne(nullptr)); auto anwb = native_handle->anwb(); ASSERT_THAT(anwb, Ne(nullptr)); EXPECT_THAT(native_handle->handle(), Ne(nullptr)); EXPECT_THAT(native_handle->handle(), Eq(native_handle->anwb()->handle)); EXPECT_EQ(width, static_cast(anwb->width)); EXPECT_EQ(height, static_cast(anwb->height)); EXPECT_EQ(correct_usage, anwb->usage); EXPECT_EQ(expected_stride_in_pixels, anwb->stride); ASSERT_THAT(anwb->common.incRef, Ne(nullptr)); ASSERT_THAT(anwb->common.decRef, Ne(nullptr)); anwb->common.incRef(&anwb->common); anwb->common.decRef(&anwb->common); } ./tests/unit-tests/client/test_connection_resource_map.cpp0000644000015600001650000000720312676616157024325 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/client/connection_surface_map.h" #include "src/client/mir_surface.h" #include "src/client/presentation_chain.h" #include "mir/test/doubles/mock_client_buffer_stream.h" #include "mir/test/doubles/mock_protobuf_server.h" #include namespace mf = mir::frontend; namespace mcl = mir::client; namespace mtd = mir::test::doubles; namespace { void buffer_cb(MirPresentationChain*, MirBuffer*, void*) { } } struct ConnectionResourceMap : testing::Test { std::shared_ptr wh { std::make_shared() }; std::shared_ptr surface{std::make_shared("a string", nullptr, mf::SurfaceId{2}, wh)}; std::shared_ptr stream{ std::make_shared() }; std::shared_ptr buffer { std::make_shared(buffer_cb, nullptr, 0, nullptr, nullptr, mir_buffer_usage_software) }; mtd::MockProtobufServer mock_server; std::shared_ptr chain{ std::make_shared( nullptr, 0, mock_server, nullptr, nullptr) }; mf::SurfaceId const surface_id{43}; mf::BufferStreamId const stream_id{43}; int const buffer_id{43}; }; TEST_F(ConnectionResourceMap, maps_surface_when_surface_inserted) { using namespace testing; auto surface_called = false; mcl::ConnectionSurfaceMap map; map.insert(surface_id, surface); map.with_surface_do(surface_id, [&](MirSurface* surf) { EXPECT_THAT(surf, Eq(surface.get())); surface_called = true; }); EXPECT_TRUE(surface_called); } TEST_F(ConnectionResourceMap, removes_surface_when_surface_removed) { using namespace testing; mcl::ConnectionSurfaceMap map; map.insert(surface_id, surface); map.erase(surface_id); EXPECT_THROW({ map.with_stream_do(stream_id, [](mcl::BufferReceiver*){}); }, std::runtime_error); } TEST_F(ConnectionResourceMap, maps_streams) { using namespace testing; auto stream_called = false; mcl::ConnectionSurfaceMap map; map.insert(stream_id, stream); map.with_stream_do(stream_id, [&](mcl::BufferReceiver* str) { EXPECT_THAT(str, Eq(stream.get())); stream_called = true; }); EXPECT_TRUE(stream_called); map.erase(stream_id); } TEST_F(ConnectionResourceMap, maps_chains) { using namespace testing; auto chain_called = false; mcl::ConnectionSurfaceMap map; map.insert(stream_id, chain); map.with_stream_do(stream_id, [&](mcl::BufferReceiver* str) { EXPECT_THAT(str, Eq(chain.get())); chain_called = true; }); EXPECT_TRUE(chain_called); map.erase(stream_id); } TEST_F(ConnectionResourceMap, maps_buffers) { using namespace testing; mcl::ConnectionSurfaceMap map; EXPECT_THAT(map.buffer(buffer_id), Eq(nullptr)); map.insert(buffer_id, buffer); EXPECT_THAT(map.buffer(buffer_id), Eq(buffer)); map.erase(buffer_id); EXPECT_THAT(map.buffer(buffer_id), Eq(nullptr)); } ./tests/unit-tests/client/test_wait_handle.cpp0000644000015600001650000000750012676616125021674 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include #include #include "src/client/mir_wait_handle.h" namespace { int const arbitrary_number_of_events = 100; } TEST(WaitHandle, symmetric_synchronous) { MirWaitHandle w; for (int i = 0; i != arbitrary_number_of_events; ++i) w.expect_result(); for (int i = 0; i < arbitrary_number_of_events; i++) { w.result_received(); } w.wait_for_all(); EXPECT_TRUE(true); // Failure would be hanging in the above loop } TEST(WaitHandle, asymmetric_synchronous) { MirWaitHandle w; for (int i = 1; i <= arbitrary_number_of_events; i++) { for (int j = 0; j != i; ++j) w.expect_result(); for (int j = 1; j <= i; j++) w.result_received(); w.wait_for_all(); } EXPECT_TRUE(true); // Failure would be hanging in the above loop } namespace { int symmetric_result = 0; void symmetric_thread(MirWaitHandle *in, MirWaitHandle *out, int max) { for (int i = 1; i <= max; i++) { in->expect_result(); in->wait_for_all(); symmetric_result = i; out->result_received(); } } } TEST(WaitHandle, symmetric_asynchronous) { const int max = 100; MirWaitHandle in, out; std::thread t(symmetric_thread, &in, &out, max); for (int i = 1; i <= max; i++) { out.expect_result(); in.result_received(); out.wait_for_all(); ASSERT_EQ(symmetric_result, i); } t.join(); } namespace { int asymmetric_result = 0; void asymmetric_thread(MirWaitHandle *in, MirWaitHandle *out, int max) { for (int i = 1; i <= max; i++) { in->expect_result(); in->wait_for_all(); asymmetric_result = i; for (int j = 1; j <= i; j++) out->result_received(); } } } TEST(WaitHandle, asymmetric_asynchronous) { const int max = 100; MirWaitHandle in, out; std::thread t(asymmetric_thread, &in, &out, max); for (int i = 1; i <= max; i++) { in.result_received(); for (int j = 1; j <= i; j++) out.expect_result(); out.wait_for_all(); ASSERT_EQ(asymmetric_result, i); } t.join(); } namespace { void crowded_thread(MirWaitHandle *w, int max) { for (int i = 0; i < max; i++) { /* * This doesn't work with wait_for_all, because waiters will * race and the winner would gobble up all the results, leaving * the other waiters hanging forever. So we have wait_for_one() * to allow many threads to safely wait on the same handle and * guarantee that they will all get the number of results they * expect. */ w->wait_for_one(); } } } TEST(WaitHandle, many_waiters) { const int max = 100; MirWaitHandle w; std::thread a(crowded_thread, &w, max); std::thread b(crowded_thread, &w, max); std::thread c(crowded_thread, &w, max); for (int n = 0; n < max * 3; n++) w.result_received(); a.join(); b.join(); c.join(); } ./tests/unit-tests/client/test_probing_client_platform_factory.cpp0000644000015600001650000001755012676616125026054 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/client_platform.h" #include "src/client/probing_client_platform_factory.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/mock_client_context.h" #include "mir_test_framework/executable_path.h" #include "mir_test_framework/stub_platform_helpers.h" #include #include #include namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace { std::vector all_available_modules() { std::vector modules; #if defined(MIR_BUILD_PLATFORM_MESA_KMS) || defined(MIR_BUILD_PLATFORM_MESA_X11) modules.push_back(mtf::client_platform("mesa")); #endif #ifdef MIR_BUILD_PLATFORM_ANDROID modules.push_back(mtf::client_platform("android")); #endif return modules; } bool loaded(std::string const& path) { void* x = dlopen(path.c_str(), RTLD_LAZY | RTLD_NOLOAD); if (x) dlclose(x); return !!x; } void populate_valid(MirPlatformPackage& pkg) { memset(&pkg, 0, sizeof(MirPlatformPackage)); pkg.fd_items = 1; pkg.fd[0] = 23; } } TEST(ProbingClientPlatformFactory, ThrowsErrorWhenConstructedWithNoPlatforms) { std::vector> empty_modules; EXPECT_THROW(mir::client::ProbingClientPlatformFactory( mir::report::null_shared_library_prober_report(), {}, {}), std::runtime_error); } TEST(ProbingClientPlatformFactory, ThrowsErrorWhenNoPlatformPluginProbesSuccessfully) { using namespace testing; mir::client::ProbingClientPlatformFactory factory( mir::report::null_shared_library_prober_report(), all_available_modules(), {}); mtd::MockClientContext context; ON_CALL(context, populate_server_package(_)) .WillByDefault(Invoke([](MirPlatformPackage& pkg) { ::memset(&pkg, 0, sizeof(MirPlatformPackage)); // Mock up a platform package that looks nothing like // either an Android or Mesa package pkg.fd_items = 0xdeadbeef; pkg.data_items = -23; })); EXPECT_THROW(factory.create_client_platform(&context), std::runtime_error); } TEST(ProbingClientPlatformFactory, DoesNotLeakTheUsedDriverModuleOnShutdown) { // Regression test for LP: #1527449 using namespace testing; auto const modules = all_available_modules(); ASSERT_FALSE(modules.empty()); std::string const preferred_module = modules.front(); mir::client::ProbingClientPlatformFactory factory( mir::report::null_shared_library_prober_report(), {preferred_module}, {}); std::shared_ptr platform; mtd::MockClientContext context; ON_CALL(context, populate_server_package(_)) .WillByDefault(Invoke(populate_valid)); ASSERT_FALSE(loaded(preferred_module)); platform = factory.create_client_platform(&context); ASSERT_TRUE(loaded(preferred_module)); platform.reset(); EXPECT_FALSE(loaded(preferred_module)); } TEST(ProbingClientPlatformFactory, DoesNotLeakUnusedDriverModulesOnStartup) { // Regression test for LP: #1527449 and LP: #1526658 using namespace testing; auto const modules = all_available_modules(); ASSERT_FALSE(modules.empty()); // Note: This test is only really effective with nmodules>1, which many of // our builds will have. But nmodules==1 is harmless. mir::client::ProbingClientPlatformFactory factory( mir::report::null_shared_library_prober_report(), modules, {}); std::shared_ptr platform; mtd::MockClientContext context; ON_CALL(context, populate_server_package(_)) .WillByDefault(Invoke(populate_valid)); int nloaded = 0; for (auto const& m : modules) if (loaded(m)) ++nloaded; ASSERT_EQ(0, nloaded); platform = factory.create_client_platform(&context); nloaded = 0; for (auto const& m : modules) if (loaded(m)) ++nloaded; EXPECT_EQ(1, nloaded); platform.reset(); nloaded = 0; for (auto const& m : modules) if (loaded(m)) ++nloaded; ASSERT_EQ(0, nloaded); } #if defined(MIR_BUILD_PLATFORM_MESA_KMS) || defined(MIR_BUILD_PLATFORM_MESA_X11) TEST(ProbingClientPlatformFactory, CreatesMesaPlatformWhenAppropriate) #else TEST(ProbingClientPlatformFactory, DISABLED_CreatesMesaPlatformWhenAppropriate) #endif { using namespace testing; mir::client::ProbingClientPlatformFactory factory( mir::report::null_shared_library_prober_report(), all_available_modules(), {}); mtd::MockClientContext context; ON_CALL(context, populate_server_package(_)) .WillByDefault(Invoke([](MirPlatformPackage& pkg) { ::memset(&pkg, 0, sizeof(MirPlatformPackage)); // Mock up something that looks like a GBM platform package, // until we send the actual platform type over the wire! pkg.fd_items = 1; pkg.fd[0] = 23; })); auto platform = factory.create_client_platform(&context); EXPECT_EQ(mir_platform_type_gbm, platform->platform_type()); } #ifdef MIR_BUILD_PLATFORM_ANDROID TEST(ProbingClientPlatformFactory, CreatesAndroidPlatformWhenAppropriate) #else TEST(ProbingClientPlatformFactory, DISABLED_CreatesAndroidPlatformWhenAppropriate) #endif { using namespace testing; mir::client::ProbingClientPlatformFactory factory( mir::report::null_shared_library_prober_report(), all_available_modules(), {}); mtd::MockClientContext context; ON_CALL(context, populate_server_package(_)) .WillByDefault(Invoke([](MirPlatformPackage& pkg) { // Mock up something that looks like a Android platform package, // until we send the actual platform type over the wire! ::memset(&pkg, 0, sizeof(MirPlatformPackage)); })); auto platform = factory.create_client_platform(&context); EXPECT_EQ(mir_platform_type_android, platform->platform_type()); } TEST(ProbingClientPlatformFactory, IgnoresNonClientPlatformModules) { using namespace testing; auto modules = all_available_modules(); // NOTE: For minimum fuss, load something that has minimal side-effects... modules.push_back("libc.so.6"); modules.push_back(mtf::client_platform("dummy.so")); mir::client::ProbingClientPlatformFactory factory( mir::report::null_shared_library_prober_report(), modules, {}); mtd::MockClientContext context; ON_CALL(context, populate_server_package(_)) .WillByDefault(Invoke([](MirPlatformPackage& pkg) { mtf::create_stub_platform_package(pkg); })); auto platform = factory.create_client_platform(&context); } ./tests/unit-tests/client/test_stream_transport.cpp0000644000015600001650000012741712676616157023043 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "src/client/rpc/stream_transport.h" #include "src/client/rpc/stream_socket_transport.h" #include "mir/fd.h" #include "mir/test/auto_unblock_thread.h" #include "mir/test/signal.h" #include "mir/test/fd_utils.h" #include "mir/raii.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mclr = mir::client::rpc; namespace mt = mir::test; namespace md = mir::dispatch; namespace { class MockObserver : public mclr::StreamTransport::Observer { public: MOCK_METHOD0(on_data_available, void()); MOCK_METHOD0(on_disconnected, void()); }; } template class StreamTransportTest : public ::testing::Test { public: StreamTransportTest() { int socket_fds[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds) < 0) { throw std::system_error(errno, std::system_category()); } test_fd = mir::Fd{socket_fds[0]}; transport_fd = mir::Fd{socket_fds[1]}; transport = std::make_shared(transport_fd); } mir::Fd transport_fd; mir::Fd test_fd; std::shared_ptr transport; }; typedef ::testing::Types Transports; TYPED_TEST_CASE(StreamTransportTest, Transports); TYPED_TEST(StreamTransportTest, ReturnsValidWatchFd) { // A valid fd is >= 0, and we know that stdin, stdout, and stderr aren't correct. EXPECT_GE(this->transport->watch_fd(), 3); } TYPED_TEST(StreamTransportTest, watch_fd_is_pollable) { pollfd socket_readable; socket_readable.events = POLLIN; socket_readable.fd = this->transport->watch_fd(); ASSERT_TRUE(mt::std_call_succeeded(poll(&socket_readable, 1, 0))); EXPECT_FALSE(socket_readable.revents & POLLERR); EXPECT_FALSE(socket_readable.revents & POLLNVAL); } TYPED_TEST(StreamTransportTest, watch_fd_notifies_readable_when_data_pending) { uint64_t dummy{0xdeadbeef}; EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); } TYPED_TEST(StreamTransportTest, watch_fd_remains_unreadable_until_event_pending) { EXPECT_FALSE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); uint64_t dummy{0xdeadbeef}; EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); } TYPED_TEST(StreamTransportTest, watch_fd_is_no_longer_readable_after_event_processing) { using namespace testing; uint64_t dummy{0xdeadbeef}; auto observer = std::make_shared>(); ON_CALL(*observer, on_data_available()) .WillByDefault(Invoke([dummy, this]() { decltype(dummy) buffer; this->transport->receive_data(&buffer, sizeof(dummy)); })); this->transport->register_observer(observer); EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); ASSERT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); this->transport->dispatch(md::FdEvent::readable); EXPECT_FALSE(mt::fd_is_readable(this->test_fd)); } TYPED_TEST(StreamTransportTest, no_events_dispatched_until_dispatch_called) { using namespace testing; auto observer = std::make_shared>(); bool data_available{false}; bool disconnected{false}; uint64_t dummy{0xdeadbeef}; EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); ::close(this->test_fd); ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([this, dummy, &data_available]() { decltype(dummy) buffer; this->transport->receive_data(&buffer, sizeof(buffer)); data_available = true; })); ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]() { disconnected = true; })); this->transport->register_observer(observer); std::this_thread::sleep_for(std::chrono::seconds{1}); EXPECT_FALSE(data_available); EXPECT_FALSE(disconnected); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); while (mt::fd_is_readable(this->transport->watch_fd()) && this->transport->dispatch(md::FdEvent::readable | md::FdEvent::remote_closed)) ; EXPECT_TRUE(data_available); EXPECT_TRUE(disconnected); } TYPED_TEST(StreamTransportTest, dispatches_single_event_at_a_time) { using namespace testing; auto observer = std::make_shared>(); bool data_available{false}; bool disconnected{false}; uint64_t dummy{0xdeadbeef}; EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); ::close(this->test_fd); ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([this, dummy, &data_available]() { decltype(dummy) buffer; this->transport->receive_data(&buffer, sizeof(buffer)); data_available = true; })); ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]() { disconnected = true; })); this->transport->register_observer(observer); EXPECT_FALSE(data_available); EXPECT_FALSE(disconnected); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); this->transport->dispatch(md::FdEvent::readable | md::FdEvent::remote_closed); EXPECT_TRUE(data_available xor disconnected); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); this->transport->dispatch(md::FdEvent::readable | md::FdEvent::remote_closed); EXPECT_TRUE(data_available); EXPECT_TRUE(disconnected); } TYPED_TEST(StreamTransportTest, notices_remote_disconnect) { using namespace testing; auto observer = std::make_shared>(); bool disconnected{false}; ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]() { disconnected = true; })); this->transport->register_observer(observer); close(this->test_fd); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); while (mt::fd_is_readable(this->transport->watch_fd()) && this->transport->dispatch(md::FdEvent::remote_closed)) ; EXPECT_TRUE(disconnected); } TYPED_TEST(StreamTransportTest, notices_remote_disconnect_while_reading) { using namespace testing; auto observer = std::make_shared>(); bool disconnected{false}; bool receive_error_detected{false}; ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]() { disconnected = true; })); this->transport->register_observer(observer); mir::test::AutoJoinThread closer{[this]() { std::this_thread::sleep_for(std::chrono::seconds{1}); ::close(this->test_fd); }}; try { char buffer[8]; this->transport->receive_data(buffer, sizeof(buffer)); } catch (std::runtime_error) { receive_error_detected = true; } // There should now be a disconnect event pending... EXPECT_TRUE(mt::fd_is_readable(this->transport->watch_fd())); this->transport->dispatch(md::FdEvent::remote_closed); EXPECT_TRUE(disconnected); EXPECT_TRUE(receive_error_detected); } TYPED_TEST(StreamTransportTest, notifies_on_data_available) { using namespace testing; auto observer = std::make_shared>(); bool notified_data_available{false}; ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([¬ified_data_available]() { notified_data_available = true; })); this->transport->register_observer(observer); uint64_t dummy{0xdeadbeef}; EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); this->transport->dispatch(md::FdEvent::readable); EXPECT_TRUE(notified_data_available); } TYPED_TEST(StreamTransportTest, keeps_notifying_of_available_data_until_all_data_is_read) { using namespace testing; auto observer = std::make_shared>(); std::array data; data.fill(0); size_t bytes_left{data.size()}; ON_CALL(*observer, on_data_available()) .WillByDefault(Invoke([&bytes_left, this]() { int dummy; this->transport->receive_data(&dummy, sizeof(dummy)); bytes_left -= sizeof(dummy); })); this->transport->register_observer(observer); EXPECT_EQ(data.size(), write(this->test_fd, data.data(), data.size())); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); while (mt::fd_is_readable(this->transport->watch_fd()) && this->transport->dispatch(md::FdEvent::readable)) ; EXPECT_EQ(0, bytes_left); } TYPED_TEST(StreamTransportTest, stops_notifying_once_all_data_is_read) { using namespace testing; auto observer = std::make_shared>(); std::array data; data.fill(0); size_t bytes_left{data.size()}; ON_CALL(*observer, on_data_available()) .WillByDefault(Invoke([&bytes_left, this]() { int dummy; this->transport->receive_data(&dummy, sizeof(dummy)); bytes_left -= sizeof(dummy); })); this->transport->register_observer(observer); EXPECT_EQ(data.size(), write(this->test_fd, data.data(), data.size())); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); while (bytes_left > 0) { this->transport->dispatch(md::FdEvent::readable); } EXPECT_FALSE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); } TYPED_TEST(StreamTransportTest, doesnt_send_data_available_notification_on_disconnect) { using namespace testing; auto observer = std::make_shared>(); int notify_count{0}; bool disconnected{false}; uint64_t dummy{0xdeedfaac}; EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]() { disconnected = true; })); ON_CALL(*observer, on_data_available()) .WillByDefault(Invoke([dummy, ¬ify_count, this]() { notify_count++; decltype(dummy) buffer; this->transport->receive_data(&buffer, sizeof(buffer)); })); this->transport->register_observer(observer); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); while (mt::fd_is_readable(this->transport->watch_fd())) { this->transport->dispatch(md::FdEvent::readable); } ::close(this->test_fd); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); while (mt::fd_is_readable(this->transport->watch_fd()) && this->transport->dispatch(md::FdEvent::remote_closed | md::FdEvent::readable)) ; EXPECT_EQ(1, notify_count); EXPECT_TRUE(disconnected); } TYPED_TEST(StreamTransportTest, notifies_all_observers) { using namespace testing; constexpr int const observer_count{10}; std::array read_notified; std::array disconnect_notified; read_notified.fill(false); disconnect_notified.fill(false); for (int i = 0; i < observer_count; ++i) { auto observer = std::make_shared>(); ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected = disconnect_notified[i]]() mutable { disconnected = true; })); ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([&read = read_notified[i]]() mutable { read = true; })); this->transport->register_observer(observer); } this->transport->dispatch(md::FdEvent::readable); this->transport->dispatch(md::FdEvent::remote_closed); for (auto const& read_flag : read_notified) { EXPECT_TRUE(read_flag); } for (auto const& disconnected_flag : disconnect_notified) { EXPECT_TRUE(disconnected_flag); } } /* * TODO: ThreadSafeList doesn't report any error when trying to remove a non-existent element. * * It probably should, but let's not do that now. */ TYPED_TEST(StreamTransportTest, DISABLED_unregistering_a_not_registered_observer_is_an_error) { using namespace testing; auto observer = std::make_shared>(); EXPECT_THROW(this->transport->unregister_observer(observer), std::logic_error); } TYPED_TEST(StreamTransportTest, unregistering_an_observer_prevents_further_observation) { using namespace testing; auto observer = std::make_shared>(); int notification_count{0}; ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([¬ification_count]() { ++notification_count; })); ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([¬ification_count]() { ++notification_count; })); this->transport->register_observer(observer); this->transport->dispatch(md::FdEvent::readable); EXPECT_THAT(notification_count, Eq(1)); this->transport->unregister_observer(observer); this->transport->dispatch(md::FdEvent::readable); // No new notification of readability... EXPECT_THAT(notification_count, Eq(1)); // Likewise, no notification of disconnection. this->transport->dispatch(md::FdEvent::remote_closed); EXPECT_THAT(notification_count, Eq(1)); } TYPED_TEST(StreamTransportTest, unregistering_in_a_callback_succeeds) { using namespace testing; auto observer = std::make_shared>(); int notification_count{0}; // g++ has trouble lambda-capturing observer (whose type involves anonymous namespace) std::shared_ptr frig{observer}; // Pass the observer shared_ptr by reference here to avoid a trivial reference cycle. ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([¬ification_count, this, &frig]() { ++notification_count; this->transport->unregister_observer(frig); })); ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([¬ification_count]() { ++notification_count; })); this->transport->register_observer(observer); this->transport->dispatch(md::FdEvent::readable); EXPECT_THAT(notification_count, Eq(1)); this->transport->dispatch(md::FdEvent::readable); // No new notification of readability... EXPECT_THAT(notification_count, Eq(1)); // Likewise, no notification of disconnection. this->transport->dispatch(md::FdEvent::remote_closed); EXPECT_THAT(notification_count, Eq(1)); } TYPED_TEST(StreamTransportTest, reads_correct_data) { using namespace testing; auto observer = std::make_shared>(); std::string expected{"I am the very model of a modern major general"}; std::vector received(expected.size()); ON_CALL(*observer, on_data_available()) .WillByDefault(Invoke([&received, this]() { this->transport->receive_data(received.data(), received.size()); })); this->transport->register_observer(observer); EXPECT_EQ(expected.size(), write(this->test_fd, expected.data(), expected.size())); EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); while (mt::fd_is_readable(this->transport->watch_fd()) && this->transport->dispatch(md::FdEvent::readable)) ; EXPECT_EQ(0, memcmp(expected.data(), received.data(), expected.size())); } TYPED_TEST(StreamTransportTest, writes_correct_data) { using namespace testing; auto observer = std::make_shared>(); auto done = std::make_shared(); std::string expected{"I am the very model of a modern major general"}; std::vector send_buffer{expected.begin(), expected.end()}; std::vector received(expected.size()); std::vector fds; this->transport->send_message(send_buffer, fds); pollfd read_listener; read_listener.fd = this->test_fd; read_listener.events = POLLIN; ASSERT_EQ(1, poll(&read_listener, 1, 1000)) << "Failed to poll(): " << strerror(errno); EXPECT_EQ(expected.size(), read(this->test_fd, received.data(), received.size())); EXPECT_EQ(0, memcmp(expected.data(), received.data(), expected.size())); } namespace { bool alarm_raised; void set_alarm_raised(int /*signo*/) { alarm_raised = true; } class SocketBlockThreshold { public: SocketBlockThreshold(int send_fd, int recv_fd) : send_fd{send_fd}, recv_fd{recv_fd} { int val{256}; socklen_t val_size{sizeof(val)}; getsockopt(recv_fd, SOL_SOCKET, SO_RCVBUF, &old_recvbuf, &val_size); getsockopt(send_fd, SOL_SOCKET, SO_SNDBUF, &old_sndbuf, &val_size); setsockopt(recv_fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)); setsockopt(send_fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)); } ~SocketBlockThreshold() { setsockopt(recv_fd, SOL_SOCKET, SO_SNDBUF, &old_sndbuf, sizeof(old_sndbuf)); setsockopt(send_fd, SOL_SOCKET, SO_RCVBUF, &old_recvbuf, sizeof(old_recvbuf)); } size_t data_size_which_should_block() { int sendbuf, recvbuf; socklen_t buf_size{sizeof(sendbuf)}; getsockopt(recv_fd, SOL_SOCKET, SO_RCVBUF, &recvbuf, &buf_size); getsockopt(send_fd, SOL_SOCKET, SO_SNDBUF, &sendbuf, &buf_size); return sendbuf + recvbuf; } private: int send_fd, recv_fd; int old_sndbuf, old_recvbuf; }; class TemporarySignalHandler { public: TemporarySignalHandler(int signo, void (*handler)(int)) : signo{signo} { struct sigaction alarm_handler; sigset_t blocked_signals; sigemptyset(&blocked_signals); alarm_handler.sa_handler = handler; alarm_handler.sa_flags = 0; alarm_handler.sa_mask = blocked_signals; if (sigaction(signo, &alarm_handler, &old_handler) < 0) { throw std::system_error{errno, std::system_category(), "Failed to set signal handler"}; } } ~TemporarySignalHandler() noexcept(false) { if (sigaction(signo, &old_handler, nullptr) < 0) { throw std::system_error{errno, std::system_category(), "Failed to restore SIGALRM handler"}; } } private: int const signo; struct sigaction old_handler; }; } TYPED_TEST(StreamTransportTest, reads_full_data_from_multiple_chunks) { size_t const chunk_size{8}; std::vector expected(chunk_size * 4); uint8_t counter{0}; for (auto& byte : expected) { byte = counter++; } std::vector received(expected.size()); mir::test::AutoJoinThread reader([&]() { this->transport->receive_data(received.data(), received.size()); }); size_t bytes_written{0}; while (bytes_written < expected.size()) { auto result = send(this->test_fd, expected.data() + bytes_written, std::min(chunk_size, expected.size() - bytes_written), MSG_DONTWAIT); ASSERT_NE(-1, result) << "Failed to send(): " << strerror(errno); bytes_written += result; } reader.stop(); EXPECT_EQ(expected, received); } TYPED_TEST(StreamTransportTest, reads_full_data_when_interrupted_with_signals) { SocketBlockThreshold sockopt{this->test_fd, this->transport_fd}; size_t const chunk_size{sockopt.data_size_which_should_block()}; std::vector expected(chunk_size * 4); uint8_t counter{0}; for (auto& byte : expected) { byte = counter++; } std::vector received(expected.size()); TemporarySignalHandler sig_alarm_handler{SIGALRM, &set_alarm_raised}; auto read_now_waiting = std::make_shared(); mir::test::AutoJoinThread reader([&]() { alarm_raised = false; read_now_waiting->raise(); this->transport->receive_data(received.data(), received.size()); EXPECT_TRUE(alarm_raised); }); EXPECT_TRUE(read_now_waiting->wait_for(std::chrono::seconds{1})); size_t bytes_written{0}; while (bytes_written < expected.size()) { pollfd socket_writable; socket_writable.events = POLLOUT; socket_writable.fd = this->test_fd; ASSERT_GE(poll(&socket_writable, 1, 10000), 1); ASSERT_EQ(0, socket_writable.revents & (POLLERR | POLLHUP)); auto result = send(this->test_fd, expected.data() + bytes_written, std::min(chunk_size, expected.size() - bytes_written), MSG_DONTWAIT); ASSERT_NE(-1, result) << "Failed to send(): " << strerror(errno); bytes_written += result; pthread_kill(reader.native_handle(), SIGALRM); } reader.stop(); EXPECT_EQ(expected, received); } namespace { template ssize_t send_with_fds(int socket, std::array fds, void* msg_data, size_t msg_size, int flags) { struct iovec iov; iov.iov_base = msg_data; iov.iov_len = msg_size; // Allocate space for control message static auto const fds_bytes = fds.size() * sizeof(int); std::array control; memset(control.data(), 0, control.size()); // Message to send struct msghdr header; header.msg_name = NULL; header.msg_namelen = 0; header.msg_iov = &iov; header.msg_iovlen = 1; header.msg_controllen = control.size(); header.msg_control = control.data(); header.msg_flags = 0; // Control message contains file descriptors struct cmsghdr* message = CMSG_FIRSTHDR(&header); message->cmsg_len = CMSG_LEN(fds_bytes); message->cmsg_level = SOL_SOCKET; message->cmsg_type = SCM_RIGHTS; int* const data = reinterpret_cast(CMSG_DATA(message)); memcpy(data, fds.data(), fds.size() * sizeof(int)); auto const sent = sendmsg(socket, &header, flags); if (sent < 0) throw std::system_error(errno, std::system_category(), "Failed to send data + fds"); return sent; } ::testing::AssertionResult fds_are_equivalent(char const* fd_one_expr, char const* fd_two_expr, int fd_one, int fd_two) { std::string first_proc_path{"/proc/self/fd/" + std::to_string(fd_one)}; std::string second_proc_path{"/proc/self/fd/" + std::to_string(fd_two)}; struct stat sb; if (lstat(first_proc_path.c_str(), &sb) < 0) { throw std::system_error(errno, std::system_category(), "Failed to stat fd" + std::to_string(fd_one)); } std::vector first_path(sb.st_size + 1, '\0'); auto result = readlink(first_proc_path.c_str(), first_path.data(), sb.st_size); if (result < 0) { throw std::system_error(errno, std::system_category(), "Failed to find fd's path"); } if (lstat(second_proc_path.c_str(), &sb) < 0) { throw std::system_error(errno, std::system_category(), "Failed to stat fd" + std::to_string(fd_two)); } std::vector second_path(sb.st_size + 1, '\0'); result = readlink(second_proc_path.c_str(), second_path.data(), sb.st_size); if (result < 0) { throw std::system_error(errno, std::system_category(), "Failed to find fd's path"); } // For more reliability we could compare stat results, check inode numbers, etc // This should do, though. if (first_path == second_path) return testing::AssertionSuccess() << fd_one_expr << " and " << fd_two_expr << " both point to " << first_path.data(); return testing::AssertionFailure() << fd_one_expr << " and " << fd_two_expr << " are not equivalent: " << fd_one_expr << " corresponds to " << first_path.data() << std::endl << fd_two_expr << " corresponds to " << second_path.data(); } class TestFd { public: TestFd() : filename(strlen(file_template) + 1, '\0') { strncpy(filename.data(), file_template, filename.size()); fd = mkstemp(filename.data()); if (fd < 0) throw std::system_error(errno, std::system_category(), "Failed to create test fd"); } ~TestFd() { unlink(filename.data()); close(fd); } int fd; static constexpr char const* file_template = "/tmp/mir-test-file-XXXXXX"; std::vector filename; }; } TYPED_TEST(StreamTransportTest, reads_data_with_fds) { size_t const chunk_size{8}; int const num_chunks{4}; int const num_fds{6}; std::vector expected(chunk_size * num_chunks); std::array test_files; std::array test_fds; for (unsigned int i = 0; i < test_fds.size(); ++i) { test_fds[i] = test_files[i].fd; } uint8_t counter{0}; for (auto& byte : expected) { byte = counter++; } std::vector received(expected.size()); std::vector received_fds(num_fds); auto receive_done = std::make_shared(); mir::test::AutoUnblockThread receive_thread{[this]() { ::close(this->test_fd); }, [&]() { try { this->transport->receive_data(received.data(), received.size(), received_fds); } catch (std::exception& e) { FAIL() << "Exception caught while reading data: " << e.what(); } receive_done->raise(); }}; EXPECT_EQ(expected.size(), send_with_fds(this->test_fd, test_fds, expected.data(), expected.size(), MSG_DONTWAIT)); EXPECT_TRUE(receive_done->wait_for(std::chrono::seconds{1})); EXPECT_EQ(expected, received); // Man, I'd really love std::zip() here... for (unsigned int i = 0; i < test_files.size(); ++i) { EXPECT_PRED_FORMAT2(fds_are_equivalent, test_files[i].fd, received_fds[i]); ::close(received_fds[i]); } } TYPED_TEST(StreamTransportTest, reads_fds_from_multiple_chunks) { size_t const chunk_size{8}; int const num_chunks{4}; int const num_fds{6}; std::vector expected(chunk_size * num_chunks); std::array first_test_files; std::array second_test_files; std::array first_test_fds; std::array second_test_fds; for (unsigned int i = 0; i < num_fds; ++i) { first_test_fds[i] = first_test_files[i].fd; second_test_fds[i] = second_test_files[i].fd; } uint8_t counter{0}; for (auto& byte : expected) { byte = counter++; } std::vector received(expected.size() * 2); std::vector received_fds(num_fds * 2); auto receive_done = std::make_shared(); mir::test::AutoUnblockThread receive_thread{[this]() { ::close(this->test_fd); }, [&]() { try { this->transport->receive_data(received.data(), received.size(), received_fds); } catch (std::exception& e) { ADD_FAILURE() << "Exception caught while reading data: " << e.what(); } receive_done->raise(); }}; EXPECT_EQ(expected.size(), send_with_fds(this->test_fd, first_test_fds, expected.data(), expected.size(), MSG_DONTWAIT)); EXPECT_EQ(expected.size(), send_with_fds(this->test_fd, second_test_fds, expected.data(), expected.size(), MSG_DONTWAIT)); EXPECT_TRUE(receive_done->wait_for(std::chrono::seconds{1})); // Man, I'd really love std::zip() here... for (unsigned int i = 0; i < num_fds; ++i) { EXPECT_PRED_FORMAT2(fds_are_equivalent, first_test_files[i].fd, received_fds[i]); EXPECT_PRED_FORMAT2(fds_are_equivalent, second_test_files[i].fd, received_fds[i + num_fds]); ::close(received_fds[i]); ::close(received_fds[i + num_fds]); } } TYPED_TEST(StreamTransportTest, reads_full_data_and_fds_when_interrupted_with_signals) { SocketBlockThreshold sockopt{this->test_fd, this->transport_fd}; size_t const chunk_size{sockopt.data_size_which_should_block()}; int const num_chunks{4}; int const num_fds{5}; std::vector expected(chunk_size * num_chunks); uint8_t counter{0}; for (auto& byte : expected) { byte = counter++; } std::array test_files; std::array test_fds; for (unsigned int i = 0; i < test_fds.size(); ++i) { test_fds[i] = test_files[i].fd; } std::vector received(expected.size()); std::vector received_fds(num_fds); TemporarySignalHandler sig_alarm_handler{SIGALRM, &set_alarm_raised}; auto receive_done = std::make_shared(); mir::test::AutoUnblockThread reader{[this]() { ::close(this->test_fd); }, [&]() { try { alarm_raised = false; this->transport->receive_data(received.data(), received.size(), received_fds); EXPECT_TRUE(alarm_raised); } catch (std::exception& e) { ADD_FAILURE() << "Exception caught while reading data: " << e.what(); } receive_done->raise(); }}; size_t bytes_written{0}; while (bytes_written < expected.size()) { pollfd socket_writable; socket_writable.events = POLLOUT; socket_writable.fd = this->test_fd; ASSERT_GE(poll(&socket_writable, 1, 10000), 1); ASSERT_EQ(0, socket_writable.revents & (POLLERR | POLLHUP)); ssize_t result; if (bytes_written + chunk_size < expected.size()) { result = send(this->test_fd, expected.data() + bytes_written, std::min(chunk_size, expected.size() - bytes_written), MSG_DONTWAIT); ASSERT_NE(-1, result) << "Failed to send(): " << strerror(errno); } else { result = send_with_fds(this->test_fd, test_fds, expected.data() + bytes_written, std::min(chunk_size, expected.size() - bytes_written), MSG_DONTWAIT); } bytes_written += result; pthread_kill(reader.native_handle(), SIGALRM); std::this_thread::yield(); pthread_kill(reader.native_handle(), SIGALRM); } EXPECT_TRUE(receive_done->wait_for(std::chrono::seconds{1})); EXPECT_EQ(expected, received); // Man, I'd really love std::zip() here... for (unsigned int i = 0; i < test_files.size(); ++i) { EXPECT_PRED_FORMAT2(fds_are_equivalent, test_files[i].fd, received_fds[i]); ::close(received_fds[i]); } } namespace { /* * Find the first integer n ≥ starting_fd_count where the CMSG_SPACE for sending * n fds is different to the CMSG_SPACE for sending n+1 fds. * * Note: because there are alignment constraints, this is not necessarily * just starting_fd_count. */ constexpr int cmsg_space_boundary(int starting_fd_count) { return CMSG_SPACE(starting_fd_count * sizeof(int)) != CMSG_SPACE((starting_fd_count + 1) * sizeof(int)) ? starting_fd_count : cmsg_space_boundary(starting_fd_count + 1); } /* * Find the first integer n ≥ starting_fd_count where the CMSG_SPACE for sending * n fds is the same as the CMSG_SPACE for sending n+1 fds. * * Note: because there are alignment constraints, this can exist * * Returns max_count if no such n has been found such that n < max_count; * this occurs on some architectures. */ constexpr int cmsg_space_alias(int starting_fd_count, int max_count) { return CMSG_SPACE(starting_fd_count * sizeof(int)) == CMSG_SPACE((starting_fd_count + 1) * sizeof(int)) ? starting_fd_count : starting_fd_count >= max_count ? max_count : cmsg_space_alias(starting_fd_count + 1, max_count); } } TYPED_TEST(StreamTransportTest, receiving_more_fds_than_expected_on_cmsg_boundary_is_an_error) { constexpr int num_fds{cmsg_space_boundary(1)}; std::array test_files; std::array test_fds; for (unsigned int i = 0; i < test_fds.size(); ++i) { test_fds[i] = test_files[i].fd; } std::vector received_fds(num_fds); auto receive_done = std::make_shared(); mir::test::AutoUnblockThread reader{[this]() { ::close(this->test_fd); }, [&]() { uint32_t dummy; EXPECT_THROW(this->transport->receive_data(&dummy, sizeof(dummy), received_fds), std::runtime_error); receive_done->raise(); }}; int32_t dummy{0}; EXPECT_EQ(sizeof(dummy), send_with_fds(this->test_fd, test_fds, &dummy, sizeof(dummy), MSG_DONTWAIT)); EXPECT_TRUE(receive_done->wait_for(std::chrono::seconds{1})); } TYPED_TEST(StreamTransportTest, receiving_more_fds_than_requested_with_same_cmsg_space_is_an_error) { constexpr int num_fds{cmsg_space_alias(1, 20)}; std::array test_files; std::array test_fds; for (unsigned int i = 0; i < test_fds.size(); ++i) { test_fds[i] = test_files[i].fd; } std::vector received_fds(num_fds); auto receive_done = std::make_shared(); mir::test::AutoUnblockThread reader{[this]() { ::close(this->test_fd); }, [&]() { uint32_t dummy; EXPECT_THROW(this->transport->receive_data(&dummy, sizeof(dummy), received_fds), std::runtime_error); receive_done->raise(); }}; int32_t dummy{0}; EXPECT_EQ(sizeof(dummy), send_with_fds(this->test_fd, test_fds, &dummy, sizeof(dummy), MSG_DONTWAIT)); EXPECT_TRUE(receive_done->wait_for(std::chrono::seconds{1})); } /* Amusingly, if recvmsg gets interrupted while reading a control message * it turns out that we get back the fds, and then the next read will * *also* get back (duplicates of) the fds. * * This would seem to be maybe a kernel bug, but this makes it impossible to * distinguish between “we were expecting some fds, but the server sent us * twice as many in two batches” and “recvmsg was interrupted and we got the same * fds twice” * * Rad. */ TYPED_TEST(StreamTransportTest, DISABLED_receiving_more_fds_than_expected_in_multiple_chunks_raises_exception) { size_t const chunk_size{8}; int const num_chunks{4}; int const num_fds{6}; std::vector expected(chunk_size * num_chunks); std::array first_test_files; std::array second_test_files; std::array first_test_fds; std::array second_test_fds; for (unsigned int i = 0; i < num_fds; ++i) { first_test_fds[i] = first_test_files[i].fd; second_test_fds[i] = second_test_files[i].fd; } uint8_t counter{0}; for (auto& byte : expected) { byte = counter++; } std::vector received(expected.size() * 2); std::vector received_fds(num_fds); auto receive_done = std::make_shared(); mir::test::AutoUnblockThread receive_thread{[this]() { ::close(this->test_fd); }, [&]() { EXPECT_THROW(this->transport->receive_data(received.data(), received.size(), received_fds), std::runtime_error); receive_done->raise(); }}; EXPECT_EQ(expected.size(), send_with_fds(this->test_fd, first_test_fds, expected.data(), expected.size(), MSG_DONTWAIT)); EXPECT_EQ(expected.size(), send_with_fds(this->test_fd, second_test_fds, expected.data(), expected.size(), MSG_DONTWAIT)); EXPECT_TRUE(receive_done->wait_for(std::chrono::seconds{1})); // Man, I'd really love std::zip() here... for (unsigned int i = 0; i < num_fds; ++i) { EXPECT_PRED_FORMAT2(fds_are_equivalent, first_test_files[i].fd, received_fds[i]); ::close(received_fds[i]); } } TYPED_TEST(StreamTransportTest, mismatched_fd_expectations_have_appropriate_error_messages) { constexpr int num_fds{5}; std::array test_files; std::array test_fds; for (unsigned int i = 0; i < test_fds.size(); ++i) { test_fds[i] = test_files[i].fd; } int32_t dummy{0}; EXPECT_EQ(sizeof(dummy), send_with_fds(this->test_fd, test_fds, &dummy, sizeof(dummy), MSG_DONTWAIT)); EXPECT_EQ(sizeof(dummy), send_with_fds(this->test_fd, test_fds, &dummy, sizeof(dummy), MSG_DONTWAIT)); try { std::vector dummy_fds(num_fds + 1); this->transport->receive_data(&dummy, sizeof(dummy), dummy_fds); FAIL() << "Receiving fewer fds than sent unexpectedly succeeded"; } catch (std::runtime_error const& err) { EXPECT_THAT(err.what(), testing::HasSubstr("fewer fds than expected")); } try { std::vector dummy_fds(num_fds - 1); this->transport->receive_data(&dummy, sizeof(dummy), dummy_fds); FAIL() << "Receiving more fds than sent unexpectedly succeeded"; } catch (std::runtime_error const& err) { EXPECT_THAT(err.what(), testing::HasSubstr("more fds than expected")); } } TYPED_TEST(StreamTransportTest, sends_full_messages_when_interrupted) { SocketBlockThreshold socketopt(this->transport_fd, this->test_fd); size_t const chunk_size{socketopt.data_size_which_should_block()}; std::vector expected(chunk_size * 4); uint8_t counter{0}; for (auto& byte : expected) { byte = counter++; } std::vector received(expected.size()); std::vector fds; TemporarySignalHandler sig_alarm_handler{SIGALRM, &set_alarm_raised}; auto write_now_waiting = std::make_shared(); mir::test::AutoJoinThread writer([&]() { alarm_raised = false; write_now_waiting->raise(); this->transport->send_message(expected, fds); EXPECT_TRUE(alarm_raised); }); size_t bytes_read{0}; while (bytes_read < received.size()) { pollfd socket_readable; socket_readable.events = POLLIN; socket_readable.fd = this->test_fd; ASSERT_GE(poll(&socket_readable, 1, 10000), 1); ASSERT_EQ(0, socket_readable.revents & (POLLERR | POLLHUP)); auto result = read(this->test_fd, received.data() + bytes_read, std::min(received.size() - bytes_read, chunk_size)); ASSERT_GE(result, 0) << "Failed to read(): " << strerror(errno); bytes_read += result; pthread_kill(writer.native_handle(), SIGALRM); std::this_thread::yield(); pthread_kill(writer.native_handle(), SIGALRM); } writer.stop(); EXPECT_EQ(expected, received); } TYPED_TEST(StreamTransportTest, reading_zero_bytes_is_an_error) { EXPECT_THROW(this->transport->receive_data(nullptr, 0), std::logic_error); std::vector dummy; EXPECT_THROW(this->transport->receive_data(nullptr, 0, dummy), std::logic_error); } TYPED_TEST(StreamTransportTest, receiving_data_without_asking_for_fds_is_an_error_when_there_are_fds) { constexpr int num_fds{1}; std::array test_files; std::array test_fds; for (unsigned int i = 0; i < test_fds.size(); ++i) { test_fds[i] = test_files[i].fd; } auto receive_done = std::make_shared(); mir::test::AutoUnblockThread reader{[this]() { ::close(this->test_fd); }, [&]() { uint32_t dummy; EXPECT_THROW(this->transport->receive_data(&dummy, sizeof(dummy)), std::runtime_error); receive_done->raise(); }}; int32_t dummy{0}; EXPECT_EQ(sizeof(dummy), send_with_fds(this->test_fd, test_fds, &dummy, sizeof(dummy), MSG_DONTWAIT)); EXPECT_TRUE(receive_done->wait_for(std::chrono::seconds{1})); } ./tests/unit-tests/client/test_client_buffer_stream.cpp0000644000015600001650000005760112676616157023613 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "src/client/buffer_stream.h" #include "src/client/perf_report.h" #include "src/client/rpc/mir_display_server.h" #include "mir/client_platform.h" #include "mir/test/doubles/null_client_buffer.h" #include "mir/test/doubles/mock_client_buffer_factory.h" #include "mir/test/doubles/stub_client_buffer_factory.h" #include "mir/test/doubles/null_logger.h" #include "mir/test/doubles/mock_protobuf_server.h" #include "mir/test/doubles/mock_client_buffer.h" #include "mir/test/fake_shared.h" #include "mir_toolkit/mir_client_library.h" #include #include namespace mp = mir::protobuf; namespace ml = mir::logging; namespace mg = mir::graphics; namespace mcl = mir::client; namespace mclr = mir::client::rpc; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mt::doubles; using namespace testing; namespace { ACTION_P(SetResponseError, message) { arg1->set_error(message); } struct StubClientPlatform : public mcl::ClientPlatform { StubClientPlatform( std::shared_ptr const& bf) : buffer_factory(bf) { } MirPlatformType platform_type() const override { return MirPlatformType(); } void populate(MirPlatformPackage& /* package */) const override { } std::shared_ptr create_egl_native_window(mcl::EGLNativeSurface * /* surface */) override { return mt::fake_shared(egl_native_window); } std::shared_ptr create_egl_native_display() override { return nullptr; } MirNativeBuffer* convert_native_buffer(mg::NativeBuffer*) const override { return nullptr; } std::shared_ptr create_buffer_factory() override { return buffer_factory; } MirPlatformMessage* platform_operation(MirPlatformMessage const* /* request */) { return nullptr; } MirPixelFormat get_egl_pixel_format(EGLDisplay, EGLConfig) const override { return mir_pixel_format_invalid; } static EGLNativeWindowType egl_native_window; std::shared_ptr const buffer_factory; }; struct MockPerfReport : public mcl::PerfReport { MOCK_METHOD1(name_surface, void(char const*)); MOCK_METHOD1(begin_frame, void(int)); MOCK_METHOD1(end_frame, void(int)); }; EGLNativeWindowType StubClientPlatform::egl_native_window{ reinterpret_cast(&StubClientPlatform::egl_native_window)}; MirBufferPackage a_buffer_package() { MirBufferPackage bp; bp.fd_items = 1; bp.fd[0] = 16; bp.data_items = 2; bp.data[0] = 100; bp.data[1] = 234; bp.stride = 768; bp.width = 90; bp.height = 30; return bp; } // Just ensure we have a unique ID in order to not confuse the buffer depository caching logic... std::atomic unique_buffer_id{1}; void fill_protobuf_buffer_from_package(mp::Buffer* mb, MirBufferPackage const& buffer_package) { mb->set_buffer_id(unique_buffer_id++); /* assemble buffers */ mb->set_fds_on_side_channel(buffer_package.fd_items); for (int i=0; iadd_data(buffer_package.data[i]); } for (int i=0; iadd_fd(buffer_package.fd[i]); } mb->set_stride(buffer_package.stride); mb->set_width(buffer_package.width); mb->set_height(buffer_package.height); } struct ClientBufferStream : TestWithParam { ClientBufferStream() { ON_CALL(mock_factory, create_buffer(_,_,_)) .WillByDefault(Return(std::make_shared())); } mp::BufferStream a_protobuf_buffer_stream(MirPixelFormat format, MirBufferUsage usage, MirBufferPackage const& package) { mp::BufferStream protobuf_bs; mp::BufferStreamId bs_id; bs_id.set_value(1); *protobuf_bs.mutable_id() = bs_id; protobuf_bs.set_pixel_format(format); protobuf_bs.set_buffer_usage(usage); if (legacy_exchange_buffer) fill_protobuf_buffer_from_package(protobuf_bs.mutable_buffer(), package); return protobuf_bs; } bool const legacy_exchange_buffer = GetParam(); void service_requests_for(mcl::ClientBufferStream& bs, unsigned int count) { for(auto i = 0u; i < count; i++) { mp::Buffer buffer; fill_protobuf_buffer_from_package(&buffer, a_buffer_package()); buffer.set_width(size.width.as_int()); buffer.set_height(size.height.as_int()); bs.buffer_available(buffer); } } std::shared_ptr wait_handle; testing::NiceMock mock_factory; mtd::StubClientBufferFactory stub_factory; testing::NiceMock mock_protobuf_server; MirPixelFormat const default_pixel_format = mir_pixel_format_argb_8888; MirBufferUsage const default_buffer_usage = mir_buffer_usage_hardware; std::shared_ptr const perf_report = std::make_shared(); MirBufferPackage buffer_package = a_buffer_package(); geom::Size size{buffer_package.width, buffer_package.height}; mp::BufferStream response = a_protobuf_buffer_stream( default_pixel_format, default_buffer_usage, buffer_package); size_t nbuffers{3}; }; MATCHER_P(BufferPackageMatches, package, "") { if (package.data_items != arg->data_items) return false; if (package.fd_items != arg->fd_items) return false; if (memcmp(package.data, arg->data, sizeof(package.data[0]) * package.data_items)) return false; if (package.stride != arg->stride) return false; if (package.width != arg->width) return false; if (package.height != arg->height) return false; return true; } ACTION_P(SetBufferInfoFromPackage, buffer_package) { arg2->set_buffer_id(unique_buffer_id++); arg2->set_width(buffer_package.width); arg2->set_height(buffer_package.height); } ACTION_P(SetPartialBufferInfoFromPackage, buffer_package) { arg2->set_buffer_id(unique_buffer_id++); } } TEST_P(ClientBufferStream, protobuf_requirements) { auto valid_bs = a_protobuf_buffer_stream(default_pixel_format, default_buffer_usage, a_buffer_package()); EXPECT_NO_THROW({ mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), valid_bs, perf_report, "", size, nbuffers); }); valid_bs.clear_buffer(); EXPECT_NO_THROW({ mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), valid_bs, perf_report, "", size, nbuffers); }); auto error_bs = valid_bs; error_bs.set_error("An error"); EXPECT_THROW({ mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), error_bs, perf_report, "", size, nbuffers); }, std::runtime_error); auto no_id_bs = valid_bs; no_id_bs.clear_id(); EXPECT_THROW({ mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), no_id_bs, perf_report, "", size, nbuffers); }, std::runtime_error); } TEST_P(ClientBufferStream, uses_buffer_message_from_server) { EXPECT_CALL(mock_factory, create_buffer(BufferPackageMatches(buffer_package),_,_)) .WillOnce(Return(std::make_shared())); mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(mock_factory)), response, perf_report, "", size, nbuffers); service_requests_for(bs, 1); } TEST_P(ClientBufferStream, producer_streams_call_submit_buffer_on_next_buffer) { EXPECT_CALL(mock_protobuf_server, submit_buffer(_,_,_)) .WillOnce(mtd::RunProtobufClosure()); mcl::BufferStream bs{ nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, perf_report, "", size, nbuffers}; service_requests_for(bs, mock_protobuf_server.alloc_count); bs.next_buffer([]{}); } TEST_P(ClientBufferStream, invokes_callback_on_next_buffer) { mp::Buffer buffer; mcl::BufferStream bs{ nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, perf_report, "", size, nbuffers}; service_requests_for(bs, mock_protobuf_server.alloc_count); ON_CALL(mock_protobuf_server, submit_buffer(_,_,_)) .WillByDefault(DoAll( mtd::RunProtobufClosure(), InvokeWithoutArgs([&bs, &buffer]{ bs.buffer_available(buffer);}))); bool callback_invoked = false; bs.next_buffer([&callback_invoked](){ callback_invoked = true; })->wait_for_all(); EXPECT_EQ(callback_invoked, true); } TEST_P(ClientBufferStream, returns_correct_surface_parameters) { int const width = 73; int const height = 32; response.mutable_buffer()->set_width(width); response.mutable_buffer()->set_height(height); mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, perf_report, "", size, nbuffers); auto params = bs.get_parameters(); EXPECT_STREQ("", params.name); EXPECT_EQ(width, params.width); EXPECT_EQ(height, params.height); EXPECT_EQ(default_pixel_format, params.pixel_format); EXPECT_EQ(default_buffer_usage, params.buffer_usage); } TEST_P(ClientBufferStream, returns_current_client_buffer) { auto const client_buffer_1 = std::make_shared(size); auto const client_buffer_2 = std::make_shared(size); auto buffer_package_1 = a_buffer_package(); auto buffer_package_2 = a_buffer_package(); mp::Buffer protobuf_buffer_1; mp::Buffer protobuf_buffer_2; fill_protobuf_buffer_from_package(&protobuf_buffer_1, buffer_package_1); fill_protobuf_buffer_from_package(&protobuf_buffer_2, buffer_package_2); auto protobuf_bs = a_protobuf_buffer_stream(default_pixel_format, default_buffer_usage, buffer_package_1); Sequence seq; EXPECT_CALL(mock_factory, create_buffer(BufferPackageMatches(buffer_package_1),_,_)) .InSequence(seq) .WillOnce(Return(client_buffer_1)); EXPECT_CALL(mock_factory, create_buffer(BufferPackageMatches(buffer_package_2),_,_)) .InSequence(seq) .WillOnce(Return(client_buffer_2)); mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(mock_factory)), protobuf_bs, perf_report, "", size, nbuffers); service_requests_for(bs, 1); EXPECT_EQ(client_buffer_1, bs.get_current_buffer()); bs.buffer_available(protobuf_buffer_2); bs.next_buffer([]{}); EXPECT_EQ(client_buffer_2, bs.get_current_buffer()); } TEST_P(ClientBufferStream, caches_width_and_height_in_case_of_partial_updates) { auto const client_buffer_1 = std::make_shared(size); auto const client_buffer_2 = std::make_shared(size); auto buffer_package_1 = a_buffer_package(); auto buffer_package_2 = a_buffer_package(); auto protobuf_bs = a_protobuf_buffer_stream(default_pixel_format, default_buffer_usage, buffer_package_1); mp::Buffer protobuf_buffer_2; fill_protobuf_buffer_from_package(&protobuf_buffer_2, buffer_package_2); auto expected_size = geom::Size{buffer_package_1.width, buffer_package_1.height}; Sequence seq; EXPECT_CALL(mock_factory, create_buffer(BufferPackageMatches(buffer_package_1),expected_size,_)) .InSequence(seq) .WillOnce(Return(client_buffer_1)); EXPECT_CALL(mock_factory, create_buffer(BufferPackageMatches(buffer_package_2),expected_size,_)) .InSequence(seq) .WillOnce(Return(client_buffer_2)); mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(mock_factory)), protobuf_bs, perf_report, "", size, nbuffers); service_requests_for(bs, 1); EXPECT_EQ(client_buffer_1, bs.get_current_buffer()); bs.buffer_available(protobuf_buffer_2); bs.next_buffer([]{}); EXPECT_EQ(client_buffer_2, bs.get_current_buffer()); } TEST_P(ClientBufferStream, gets_egl_native_window) { mcl::BufferStream bs{ nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, perf_report, "", size, nbuffers}; EXPECT_EQ(StubClientPlatform::egl_native_window, bs.egl_native_window()); } TEST_P(ClientBufferStream, map_graphics_region) { mtd::MockClientBuffer mock_client_buffer; ON_CALL(mock_client_buffer, size()) .WillByDefault(Return(size)); EXPECT_CALL(mock_factory, create_buffer(BufferPackageMatches(buffer_package),_,_)) .WillOnce(Return(mt::fake_shared(mock_client_buffer))); mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(mock_factory)), response, perf_report, "", size, nbuffers); service_requests_for(bs, 1); mcl::MemoryRegion expected_memory_region; EXPECT_CALL(mock_client_buffer, secure_for_cpu_write()) .WillOnce(Return(mt::fake_shared(expected_memory_region))); EXPECT_EQ(&expected_memory_region, bs.secure_for_cpu_write().get()); } //lp: #1463873 TEST_P(ClientBufferStream, maps_graphics_region_only_once_per_swapbuffers) { mtd::MockClientBuffer mock_client_buffer; ON_CALL(mock_client_buffer, size()) .WillByDefault(Return(size)); ON_CALL(mock_factory, create_buffer(BufferPackageMatches(buffer_package),_,_)) .WillByDefault(Return(mt::fake_shared(mock_client_buffer))); mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(mock_factory)), response, perf_report, "", size, nbuffers); service_requests_for(bs, 2); mcl::MemoryRegion first_expected_memory_region; mcl::MemoryRegion second_expected_memory_region; EXPECT_CALL(mock_client_buffer, secure_for_cpu_write()) .Times(2) .WillOnce(Return(mt::fake_shared(first_expected_memory_region))) .WillOnce(Return(mt::fake_shared(second_expected_memory_region))); EXPECT_EQ(&first_expected_memory_region, bs.secure_for_cpu_write().get()); bs.secure_for_cpu_write(); bs.secure_for_cpu_write(); bs.request_and_wait_for_next_buffer(); EXPECT_EQ(&second_expected_memory_region, bs.secure_for_cpu_write().get()); bs.secure_for_cpu_write(); bs.secure_for_cpu_write(); } TEST_P(ClientBufferStream, passes_name_to_perf_report) { NiceMock mock_perf_report; std::string const name = "a_unique_surface_name"; EXPECT_CALL(mock_perf_report, name_surface(StrEq(name))); mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, mt::fake_shared(mock_perf_report), name, size, nbuffers); } TEST_P(ClientBufferStream, receives_unsolicited_buffer) { int id = 88; mtd::MockClientBuffer mock_client_buffer; ON_CALL(mock_client_buffer, size()) .WillByDefault(Return(size)); mtd::MockClientBuffer second_mock_client_buffer; ON_CALL(second_mock_client_buffer, size()) .WillByDefault(Return(size)); EXPECT_CALL(mock_factory, create_buffer(BufferPackageMatches(buffer_package),_,_)) .WillOnce(Return(mt::fake_shared(mock_client_buffer))); mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(mock_factory)), response, perf_report, "", size, nbuffers); service_requests_for(bs, 1); mir::protobuf::Buffer another_buffer_package; another_buffer_package.set_buffer_id(id); another_buffer_package.set_width(size.width.as_int()); another_buffer_package.set_height(size.height.as_int()); EXPECT_CALL(mock_factory, create_buffer(_,_,_)) .WillOnce(Return(mt::fake_shared(second_mock_client_buffer))); EXPECT_CALL(mock_protobuf_server, submit_buffer(_,_,_)) .WillOnce(mtd::RunProtobufClosure()); bs.buffer_available(another_buffer_package); bs.next_buffer([]{}); EXPECT_THAT(bs.get_current_buffer().get(), Eq(&second_mock_client_buffer)); EXPECT_THAT(bs.get_current_buffer_id(), Eq(id)); } TEST_P(ClientBufferStream, waiting_client_can_unblock_on_shutdown) { using namespace std::literals::chrono_literals; mtd::MockClientBuffer mock_client_buffer; ON_CALL(mock_factory, create_buffer(BufferPackageMatches(buffer_package),_,_)) .WillByDefault(Return(mt::fake_shared(mock_client_buffer))); ON_CALL(mock_protobuf_server, submit_buffer(_,_,_)) .WillByDefault(mtd::RunProtobufClosure()); std::mutex mutex; std::condition_variable cv; bool started{false}; mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(mock_factory)), response, perf_report, "", size, nbuffers); service_requests_for(bs, mock_protobuf_server.alloc_count); auto never_serviced_request = std::async(std::launch::async,[&] { { std::unique_lock lk(mutex); started = true; cv.notify_all(); } bs.request_and_wait_for_next_buffer(); }); std::unique_lock lk(mutex); EXPECT_TRUE(cv.wait_for(lk, 4s, [&]{ return started; })); bs.buffer_unavailable(); EXPECT_THAT(never_serviced_request.wait_for(4s), Ne(std::future_status::timeout)); EXPECT_THROW({ bs.request_and_wait_for_next_buffer(); }, std::exception); } TEST_P(ClientBufferStream, invokes_callback_on_buffer_available_before_wait_handle_has_result) { MirWaitHandle* wh{nullptr}; bool wait_handle_has_result_in_callback = false; mcl::BufferStream bs{ nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, perf_report, "", size, nbuffers}; service_requests_for(bs, mock_protobuf_server.alloc_count); wh = bs.next_buffer( [&] { if (wh) wait_handle_has_result_in_callback = wh->has_result(); }); EXPECT_FALSE(wait_handle_has_result_in_callback); } TEST_P(ClientBufferStream, invokes_callback_on_buffer_unavailable_before_wait_handle_has_result) { MirWaitHandle* wh{nullptr}; bool wait_handle_has_result_in_callback = false; mcl::BufferStream bs{ nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, perf_report, "", size, nbuffers}; service_requests_for(bs, mock_protobuf_server.alloc_count); wh = bs.next_buffer( [&] { if (wh) wait_handle_has_result_in_callback = wh->has_result(); }); bs.buffer_unavailable(); EXPECT_FALSE(wait_handle_has_result_in_callback); } TEST_P(ClientBufferStream, configures_swap_interval) { mcl::BufferStream bs{ nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, perf_report, "", size, nbuffers}; service_requests_for(bs, mock_protobuf_server.alloc_count); EXPECT_CALL(mock_protobuf_server, configure_buffer_stream(_,_,_)); bs.set_swap_interval(0); } TEST_P(ClientBufferStream, sets_swap_interval_requested) { mcl::BufferStream bs{ nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, perf_report, "", size, nbuffers}; service_requests_for(bs, mock_protobuf_server.alloc_count); bs.set_swap_interval(1); EXPECT_EQ(1, bs.swap_interval()); bs.set_swap_interval(0); EXPECT_EQ(0, bs.swap_interval()); } TEST_P(ClientBufferStream, environment_overrides_requested_swap_interval) { setenv("MIR_CLIENT_FORCE_SWAP_INTERVAL", "0", 1); mcl::BufferStream bs{ nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, perf_report, "", size, nbuffers}; service_requests_for(bs, mock_protobuf_server.alloc_count); bs.set_swap_interval(1); EXPECT_EQ(0, bs.swap_interval()); unsetenv("MIR_CLIENT_FORCE_SWAP_INTERVAL"); } MATCHER_P(StreamConfigScaleIs, val, "") { if (!arg->has_scale() || !val.has_scale()) return false; auto const id_matches = arg->id().value() == val.id().value(); auto const tolerance = 0.01f; auto const scale_matches = ((arg->scale() + tolerance >= val.scale()) && (arg->scale() - tolerance <= val.scale())); return id_matches && scale_matches; } TEST_P(ClientBufferStream, configures_scale) { if (!legacy_exchange_buffer) return; mcl::BufferStream bs{ nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(stub_factory)), response, perf_report, "", size, nbuffers}; service_requests_for(bs, mock_protobuf_server.alloc_count); float scale = 2.1; mp::StreamConfiguration expected_config; expected_config.set_scale(scale); expected_config.mutable_id()->set_value(1); EXPECT_CALL(mock_protobuf_server, configure_buffer_stream(StreamConfigScaleIs(expected_config),_,_)); bs.set_scale(scale); } TEST_P(ClientBufferStream, returns_correct_surface_parameters_with_nondefault_format) { auto format = mir_pixel_format_bgr_888; response.set_pixel_format(format); EXPECT_CALL(mock_factory, create_buffer(_,_,format)) .WillRepeatedly(Return(std::make_shared())); mcl::BufferStream bs( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(mock_factory)), response, perf_report, "", size, nbuffers); service_requests_for(bs, 1); auto params = bs.get_parameters(); EXPECT_THAT(params.pixel_format, Eq(format)); } TEST_P(ClientBufferStream, keeps_accurate_buffer_id) { ON_CALL(mock_factory, create_buffer(_,_,_)) .WillByDefault(Return(std::make_shared(size))); mcl::BufferStream stream( nullptr, wait_handle, mock_protobuf_server, std::make_shared(mt::fake_shared(mock_factory)), response, perf_report, "", size, nbuffers); for(auto i = 0u; i < 2; i++) { mp::Buffer buffer; fill_protobuf_buffer_from_package(&buffer, a_buffer_package()); buffer.set_buffer_id(i+10); buffer.set_width(size.width.as_int()); buffer.set_height(size.height.as_int()); stream.buffer_available(buffer); } EXPECT_THAT(stream.get_current_buffer_id(), Eq(10)); } INSTANTIATE_TEST_CASE_P(BufferSemanticsMode, ClientBufferStream, Bool()); ./tests/unit-tests/client/test_protobuf_rpc_channel.cpp0000644000015600001650000005440512676616157023624 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "src/client/rpc/mir_protobuf_rpc_channel.h" #include "src/client/rpc/stream_transport.h" #include "src/client/rpc/mir_display_server.h" #include "src/client/surface_map.h" #include "src/client/display_configuration.h" #include "src/client/rpc/null_rpc_report.h" #include "src/client/lifecycle_control.h" #include "src/client/ping_handler.h" #include "src/client/buffer_factory.h" #include "mir_protobuf.pb.h" #include "mir_protobuf_wire.pb.h" #include "mir/input/input_devices.h" #include "mir/test/doubles/null_client_event_sink.h" #include "mir/test/doubles/mock_client_buffer_stream.h" #include "mir/test/fd_utils.h" #include #include #include #include #include #include #include namespace mcl = mir::client; namespace mclr = mir::client::rpc; namespace mtd = mir::test::doubles; namespace md = mir::dispatch; namespace mt = mir::test; namespace { struct MockSurfaceMap : mcl::SurfaceMap { MOCK_CONST_METHOD2(with_surface_do, void(mir::frontend::SurfaceId, std::function const&)); MOCK_CONST_METHOD2(with_stream_do, void(mir::frontend::BufferStreamId, std::function const&)); MOCK_CONST_METHOD1(with_all_streams_do, void(std::function const&)); MOCK_CONST_METHOD1(buffer, std::shared_ptr(int)); MOCK_METHOD2(insert, void(int, std::shared_ptr const&)); MOCK_METHOD1(erase, void(int)); }; class StubSurfaceMap : public mcl::SurfaceMap { public: void with_surface_do( mir::frontend::SurfaceId, std::function const&) const override { } void with_stream_do( mir::frontend::BufferStreamId, std::function const&) const override { } void with_all_streams_do(std::function const&) const override { } std::shared_ptr buffer(int) const { return nullptr; } void insert(int, std::shared_ptr const&) { } void erase(int) { } }; class MockStreamTransport : public mclr::StreamTransport { public: MockStreamTransport() : event_fd{eventfd(0, EFD_CLOEXEC)} { if (event_fd == mir::Fd::invalid) { BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to create event fd"})); } using namespace testing; ON_CALL(*this, register_observer(_)) .WillByDefault(Invoke(std::bind(&MockStreamTransport::register_observer_default, this, std::placeholders::_1))); ON_CALL(*this, receive_data(_,_)) .WillByDefault(Invoke([this](void* buffer, size_t message_size) { receive_data_default(buffer, message_size); })); ON_CALL(*this, receive_data(_,_,_)) .WillByDefault(Invoke([this](void* buffer, size_t message_size, std::vector& fds) { receive_data_default(buffer, message_size, fds); })); ON_CALL(*this, send_message(_,_)) .WillByDefault(Invoke(std::bind(&MockStreamTransport::send_message_default, this, std::placeholders::_1))); } void add_server_message(std::vector const& message) { eventfd_t data_added{message.size()}; eventfd_write(event_fd, data_added); received_data.insert(received_data.end(), message.begin(), message.end()); } void add_server_message(std::vector const& message, std::initializer_list fds) { add_server_message(message); received_fds.insert(received_fds.end(), fds); } bool all_data_consumed() const { return received_data.empty() && received_fds.empty(); } MOCK_METHOD1(register_observer, void(std::shared_ptr const&)); MOCK_METHOD1(unregister_observer, void(std::shared_ptr const&)); MOCK_METHOD2(receive_data, void(void*, size_t)); MOCK_METHOD3(receive_data, void(void*, size_t, std::vector&)); MOCK_METHOD2(send_message, void(std::vector const&, std::vector const&)); mir::Fd watch_fd() const override { return event_fd; } bool dispatch(md::FdEvents /*events*/) override { for (auto& observer : observers) { observer->on_data_available(); } return true; } md::FdEvents relevant_events() const override { return md::FdEvent::readable; } // Transport interface void register_observer_default(std::shared_ptr const& observer) { observers.push_back(observer); } void receive_data_default(void* buffer, size_t read_bytes) { static std::vector dummy; receive_data_default(buffer, read_bytes, dummy); } void receive_data_default(void* buffer, size_t read_bytes, std::vector& fds) { if (read_bytes == 0) return; auto num_fds = fds.size(); if (read_bytes > received_data.size()) { throw std::runtime_error("Attempt to read more data than is available"); } if (num_fds > received_fds.size()) { throw std::runtime_error("Attempt to receive more fds than are available"); } memcpy(buffer, received_data.data(), read_bytes); fds.assign(received_fds.begin(), received_fds.begin() + num_fds); received_data.erase(received_data.begin(), received_data.begin() + read_bytes); received_fds.erase(received_fds.begin(), received_fds.begin() + num_fds); eventfd_t remaining_bytes; eventfd_read(event_fd, &remaining_bytes); remaining_bytes -= read_bytes; eventfd_write(event_fd, remaining_bytes); } void send_message_default(std::vector const& buffer) { sent_messages.push_back(buffer); } std::list> observers; size_t read_offset{0}; std::vector received_data; std::vector received_fds; std::list> sent_messages; private: mir::Fd event_fd; }; class MirProtobufRpcChannelTest : public testing::Test { public: MirProtobufRpcChannelTest() : transport{new testing::NiceMock}, lifecycle{std::make_shared()}, channel{new mclr::MirProtobufRpcChannel{ std::unique_ptr{transport}, std::make_shared(), std::make_shared(), std::make_shared(), std::make_shared(), std::make_shared(), lifecycle, std::make_shared(), std::make_shared()}} { } MockStreamTransport* transport; std::shared_ptr lifecycle; std::shared_ptr channel; mtd::MockClientBufferStream stream; }; } TEST_F(MirProtobufRpcChannelTest, reads_full_messages) { std::vector empty_message(sizeof(uint16_t)); std::vector small_message(sizeof(uint16_t) + 8); std::vector large_message(sizeof(uint16_t) + 4096); *reinterpret_cast(empty_message.data()) = htobe16(0); *reinterpret_cast(small_message.data()) = htobe16(8); *reinterpret_cast(large_message.data()) = htobe16(4096); transport->add_server_message(empty_message); transport->dispatch(md::FdEvent::readable); EXPECT_TRUE(transport->all_data_consumed()); transport->add_server_message(small_message); transport->dispatch(md::FdEvent::readable); EXPECT_TRUE(transport->all_data_consumed()); transport->add_server_message(large_message); transport->dispatch(md::FdEvent::readable); EXPECT_TRUE(transport->all_data_consumed()); } TEST_F(MirProtobufRpcChannelTest, reads_all_queued_messages) { std::vector empty_message(sizeof(uint16_t)); std::vector small_message(sizeof(uint16_t) + 8); std::vector large_message(sizeof(uint16_t) + 4096); *reinterpret_cast(empty_message.data()) = htobe16(0); *reinterpret_cast(small_message.data()) = htobe16(8); *reinterpret_cast(large_message.data()) = htobe16(4096); transport->add_server_message(empty_message); transport->add_server_message(small_message); transport->add_server_message(large_message); while(mt::fd_is_readable(channel->watch_fd())) { channel->dispatch(md::FdEvent::readable); } EXPECT_TRUE(transport->all_data_consumed()); } TEST_F(MirProtobufRpcChannelTest, sends_messages_atomically) { mclr::DisplayServer channel_user{channel}; mir::protobuf::ConnectParameters message; message.set_application_name("I'm a little teapot!"); channel_user.connect(&message, nullptr, nullptr); EXPECT_EQ(transport->sent_messages.size(), 1); } TEST_F(MirProtobufRpcChannelTest, sets_correct_size_when_sending_message) { mclr::DisplayServer channel_user{channel}; mir::protobuf::ConnectParameters message; message.set_application_name("I'm a little teapot!"); channel_user.connect(&message, nullptr, nullptr); uint16_t message_header = *reinterpret_cast(transport->sent_messages.front().data()); message_header = be16toh(message_header); EXPECT_EQ(transport->sent_messages.front().size() - sizeof(uint16_t), message_header); } TEST_F(MirProtobufRpcChannelTest, reads_fds) { mclr::DisplayServer channel_user{channel}; mir::protobuf::Buffer reply; mir::protobuf::BufferRequest request; channel_user.exchange_buffer(&request, &reply, google::protobuf::NewCallback([](){})); std::initializer_list fds = {mir::Fd{open("/dev/null", O_RDONLY)}, mir::Fd{open("/dev/null", O_RDONLY)}, mir::Fd{open("/dev/null", O_RDONLY)}}; ASSERT_EQ(transport->sent_messages.size(), 1); { mir::protobuf::Buffer reply_message; for (auto fd : fds) reply_message.add_fd(fd); reply_message.set_fds_on_side_channel(fds.size()); mir::protobuf::wire::Invocation request; mir::protobuf::wire::Result reply; request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t), transport->sent_messages.front().size() - sizeof(uint16_t)); reply.set_id(request.id()); reply.set_response(reply_message.SerializeAsString()); ASSERT_TRUE(reply.has_id()); ASSERT_TRUE(reply.has_response()); std::vector buffer(reply.ByteSize() + sizeof(uint16_t)); *reinterpret_cast(buffer.data()) = htobe16(reply.ByteSize()); ASSERT_TRUE(reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t))); transport->add_server_message(buffer); // Because our protocol is a bit silly... std::vector dummy = {1}; transport->add_server_message(dummy, fds); while(mt::fd_is_readable(channel->watch_fd())) { channel->dispatch(md::FdEvent::readable); } } ASSERT_EQ(reply.fd_size(), fds.size()); int i = 0; for (auto fd : fds) { EXPECT_EQ(reply.fd(i), fd); ++i; } } TEST_F(MirProtobufRpcChannelTest, notifies_streams_of_disconnect) { using namespace testing; auto stream_map = std::make_shared(); EXPECT_CALL(stream, buffer_unavailable()).Times(AtLeast(1)); EXPECT_CALL(*stream_map, with_all_streams_do(_)).Times(AtLeast(1)) .WillRepeatedly(InvokeArgument<0>(&stream)); mclr::MirProtobufRpcChannel channel{ std::make_unique>(), stream_map, std::make_shared(), std::make_shared(), std::make_shared(), std::make_shared(), lifecycle, std::make_shared(), std::make_shared()}; channel.on_disconnected(); } TEST_F(MirProtobufRpcChannelTest, notifies_of_disconnect_on_write_error) { using namespace ::testing; bool disconnected{false}; lifecycle->set_callback([&disconnected](MirLifecycleState state) { if (state == mir_lifecycle_connection_lost) { disconnected = true; } }); EXPECT_CALL(*transport, send_message(_,_)) .WillOnce(Throw(std::runtime_error("Eaten by giant space goat"))); mclr::DisplayServer channel_user{channel}; mir::protobuf::Buffer reply; mir::protobuf::BufferRequest request; EXPECT_THROW( channel_user.exchange_buffer(&request, &reply, google::protobuf::NewCallback([](){})), std::runtime_error); EXPECT_TRUE(disconnected); } TEST_F(MirProtobufRpcChannelTest, forwards_disconnect_notification) { using namespace ::testing; bool disconnected{false}; lifecycle->set_callback([&disconnected](MirLifecycleState state) { if (state == mir_lifecycle_connection_lost) { disconnected = true; } }); for(auto& observer : transport->observers) { observer->on_disconnected(); } EXPECT_TRUE(disconnected); } TEST_F(MirProtobufRpcChannelTest, notifies_of_disconnect_only_once) { using namespace ::testing; bool disconnected{false}; lifecycle->set_callback([&disconnected](MirLifecycleState state) { if (state == mir_lifecycle_connection_lost) { if (disconnected) { FAIL()<<"Received disconnected message twice"; } disconnected = true; } }); EXPECT_CALL(*transport, send_message(_,_)) .WillOnce(DoAll(Throw(std::runtime_error("Eaten by giant space goat")), InvokeWithoutArgs([this]() { for(auto& observer : transport->observers) { observer->on_disconnected(); } }))); mclr::DisplayServer channel_user{channel}; mir::protobuf::Buffer reply; mir::protobuf::BufferRequest request; EXPECT_THROW( channel_user.exchange_buffer(&request, &reply, google::protobuf::NewCallback([](){})), std::runtime_error); EXPECT_TRUE(disconnected); } namespace { void set_flag(bool* flag) { *flag = true; } } TEST_F(MirProtobufRpcChannelTest, delays_messages_not_requested) { using namespace ::testing; auto typed_channel = std::dynamic_pointer_cast(channel); mclr::DisplayServer channel_user{channel}; mir::protobuf::PingEvent request; mir::protobuf::Void reply; bool first_response_called{false}; bool second_response_called{false}; channel_user.pong(&request, &reply, google::protobuf::NewCallback(&set_flag, &first_response_called)); typed_channel->process_next_request_first(); channel_user.pong(&request, &reply, google::protobuf::NewCallback(&set_flag, &second_response_called)); mir::protobuf::wire::Invocation wire_request; mir::protobuf::wire::Result wire_reply; wire_request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t), transport->sent_messages.front().size() - sizeof(uint16_t)); transport->sent_messages.pop_front(); wire_reply.set_id(wire_request.id()); wire_reply.set_response(reply.SerializeAsString()); std::vector buffer(wire_reply.ByteSize() + sizeof(uint16_t)); *reinterpret_cast(buffer.data()) = htobe16(wire_reply.ByteSize()); ASSERT_TRUE(wire_reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t))); transport->add_server_message(buffer); wire_request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t), transport->sent_messages.front().size() - sizeof(uint16_t)); transport->sent_messages.pop_front(); wire_reply.set_id(wire_request.id()); wire_reply.set_response(reply.SerializeAsString()); buffer.resize(wire_reply.ByteSize() + sizeof(uint16_t)); *reinterpret_cast(buffer.data()) = htobe16(wire_reply.ByteSize()); ASSERT_TRUE(wire_reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t))); transport->add_server_message(buffer); // Read the first message; this should be queued for later processing... EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd())); typed_channel->dispatch(md::FdEvent::readable); EXPECT_FALSE(first_response_called); EXPECT_FALSE(second_response_called); // Read the second message; this should be processed immediately... EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd())); typed_channel->dispatch(md::FdEvent::readable); EXPECT_FALSE(first_response_called); EXPECT_TRUE(second_response_called); // Now, the first message should be ready to be processed... EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd())); typed_channel->dispatch(md::FdEvent::readable); EXPECT_TRUE(first_response_called); EXPECT_TRUE(second_response_called); } TEST_F(MirProtobufRpcChannelTest, delays_messages_with_fds_not_requested) { using namespace ::testing; auto typed_channel = std::dynamic_pointer_cast(channel); mclr::DisplayServer channel_user{channel}; mir::protobuf::PingEvent pong_request; mir::protobuf::Void pong_reply; mir::protobuf::Buffer buffer_reply; mir::protobuf::BufferRequest buffer_request; bool first_response_called{false}; bool second_response_called{false}; channel_user.exchange_buffer(&buffer_request, &buffer_reply, google::protobuf::NewCallback(&set_flag, &first_response_called)); typed_channel->process_next_request_first(); channel_user.pong(&pong_request, &pong_reply, google::protobuf::NewCallback(&set_flag, &second_response_called)); std::initializer_list fds = {mir::Fd{open("/dev/null", O_RDONLY)}, mir::Fd{open("/dev/null", O_RDONLY)}, mir::Fd{open("/dev/null", O_RDONLY)}}; { mir::protobuf::Buffer reply_message; for (auto fd : fds) reply_message.add_fd(fd); reply_message.set_fds_on_side_channel(fds.size()); mir::protobuf::wire::Invocation request; mir::protobuf::wire::Result reply; request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t), transport->sent_messages.front().size() - sizeof(uint16_t)); transport->sent_messages.pop_front(); reply.set_id(request.id()); reply.set_response(reply_message.SerializeAsString()); ASSERT_TRUE(reply.has_id()); ASSERT_TRUE(reply.has_response()); std::vector buffer(reply.ByteSize() + sizeof(uint16_t)); *reinterpret_cast(buffer.data()) = htobe16(reply.ByteSize()); ASSERT_TRUE(reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t))); transport->add_server_message(buffer); // Because our protocol is a bit silly... std::vector dummy = {1}; transport->add_server_message(dummy, fds); } { mir::protobuf::Void reply; mir::protobuf::wire::Invocation wire_request; mir::protobuf::wire::Result wire_reply; wire_request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t), transport->sent_messages.front().size() - sizeof(uint16_t)); transport->sent_messages.pop_front(); wire_reply.set_id(wire_request.id()); wire_reply.set_response(reply.SerializeAsString()); std::vector buffer(wire_reply.ByteSize() + sizeof(uint16_t)); *reinterpret_cast(buffer.data()) = htobe16(wire_reply.ByteSize()); ASSERT_TRUE(wire_reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t))); transport->add_server_message(buffer); } // Read the first message; this should be queued for later processing... EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd())); typed_channel->dispatch(md::FdEvent::readable); EXPECT_FALSE(first_response_called); EXPECT_FALSE(second_response_called); // Read the second message; this should be processed immediately... EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd())); typed_channel->dispatch(md::FdEvent::readable); EXPECT_FALSE(first_response_called); EXPECT_TRUE(second_response_called); // Now, the first message should be ready to be processed... EXPECT_TRUE(mt::fd_is_readable(typed_channel->watch_fd())); typed_channel->dispatch(md::FdEvent::readable); EXPECT_TRUE(first_response_called); EXPECT_TRUE(second_response_called); } ./tests/unit-tests/client/test_periodic_perf_report.cpp0000644000015600001650000000774012676616125023630 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "src/client/periodic_perf_report.h" #include #include #include #include "mir/test/doubles/advanceable_clock.h" using namespace mir; namespace mtd = mir::test::doubles; namespace { class MockPeriodicPerfReport : public client::PeriodicPerfReport { public: MockPeriodicPerfReport(mir::time::Duration period, std::shared_ptr const& clock) : client::PeriodicPerfReport(period, clock) { } MOCK_CONST_METHOD5(display, void(const char*,long,long,long,int)); }; struct PeriodicPerfReport : ::testing::Test { std::chrono::seconds const period{1}; std::shared_ptr const clock = std::make_shared(); MockPeriodicPerfReport report{period, clock}; }; } // namespace TEST_F(PeriodicPerfReport, reports_the_right_numbers_at_full_speed) { int const fps = 50; int const nbuffers = 3; std::chrono::microseconds const render_time = std::chrono::milliseconds(3); auto const frame_time = std::chrono::microseconds(1000000 / fps); const char* const name = "Foo"; report.name_surface(name); int const nframes = 1000; long const expected_render_time = render_time.count(); long const expected_lag = nbuffers * frame_time.count() - expected_render_time; using namespace testing; int const nreports = nframes / (period.count() * fps); EXPECT_CALL(report, display(StrEq(name), fps*100, expected_render_time, Le(expected_lag), // first report is less nbuffers)) .Times(1); EXPECT_CALL(report, display(StrEq(name), fps*100, expected_render_time, expected_lag, // exact, after first report nbuffers)) .Times(nreports - 1); for (int f = 0; f < nframes; ++f) { int const buffer_id = f % nbuffers; clock->advance_by(frame_time - render_time); report.begin_frame(buffer_id); clock->advance_by(render_time); report.end_frame(buffer_id); } } TEST_F(PeriodicPerfReport, reports_the_right_numbers_at_low_speed) { int const nbuffers = 3; std::chrono::microseconds const render_time = std::chrono::milliseconds(3); auto const frame_time = std::chrono::seconds(4); const char* const name = "Foo"; report.name_surface(name); using namespace testing; int const nframes = 7; EXPECT_CALL(report, display(StrEq(name), 100/frame_time.count(), render_time.count(), _, _)) .Times(nframes); for (int f = 0; f < nframes; ++f) { int const buffer_id = f % nbuffers; clock->advance_by(frame_time - render_time); report.begin_frame(buffer_id); clock->advance_by(render_time); report.end_frame(buffer_id); } } TEST_F(PeriodicPerfReport, reports_nothing_on_idle) { using namespace testing; EXPECT_CALL(report, display(_,_,_,_,_)).Times(0); clock->advance_by(std::chrono::seconds(10)); } ./tests/unit-tests/client/test_event_distributor.cpp0000644000015600001650000001110712676616125023166 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Nick Dedekind */ #include "src/client/mir_event_distributor.h" #include "mir/events/event_private.h" #include #include #include #include namespace mcl = mir::client; namespace mt = mir::test; namespace { class EventDistributorTest : public testing::Test { public: MOCK_METHOD1(event_handled1, void(MirEvent const&)); MOCK_METHOD1(event_handled2, void(MirEvent const&)); MirEventDistributor event_distributor; }; MATCHER_P(MirEventTypeIs, type, "") { return (arg.type == type); } } TEST_F(EventDistributorTest, calls_back_when_registered) { using namespace testing; event_distributor.register_event_handler([this](MirEvent const& event) { event_handled1(event); }); event_distributor.register_event_handler([this](MirEvent const& event) { event_handled2(event); }); MirEvent e; e.type = mir_event_type_prompt_session_state_change; EXPECT_CALL(*this, event_handled1(MirEventTypeIs(e.type))).Times(1); EXPECT_CALL(*this, event_handled2(MirEventTypeIs(e.type))).Times(1); event_distributor.handle_event(e); } TEST_F(EventDistributorTest, no_calls_back_after_unregistered) { using namespace testing; event_distributor.register_event_handler([this](MirEvent const& event) { event_handled1(event); }); int reg_id2 = event_distributor.register_event_handler([this](MirEvent const& event) { event_handled2(event); }); event_distributor.unregister_event_handler(reg_id2); MirEvent e; e.type = mir_event_type_prompt_session_state_change; EXPECT_CALL(*this, event_handled1(MirEventTypeIs(e.type))).Times(1); EXPECT_CALL(*this, event_handled2(MirEventTypeIs(e.type))).Times(0); event_distributor.handle_event(e); } TEST_F(EventDistributorTest, no_callback_on_callback_deregistration) { using namespace testing; int reg_id2; event_distributor.register_event_handler( [this, ®_id2](MirEvent const& event) { event_handled1(event); event_distributor.unregister_event_handler(reg_id2); }); reg_id2 = event_distributor.register_event_handler([this](MirEvent const& event) { event_handled2(event); }); MirEvent e; e.type = mir_event_type_prompt_session_state_change; EXPECT_CALL(*this, event_handled1(MirEventTypeIs(e.type))).Times(1); EXPECT_CALL(*this, event_handled2(MirEventTypeIs(e.type))).Times(0); event_distributor.handle_event(e); } TEST_F(EventDistributorTest, succeeds_with_thread_delete_unregister) { using namespace testing; struct EventCatcher { EventCatcher(mcl::EventDistributor* event_distributor) : event_distributor(event_distributor) { locked = false; reg = event_distributor->register_event_handler( [this](MirEvent const&) { if (locked) { locked = false; mutex.unlock(); } }); mutex.lock(); locked = true; } ~EventCatcher() { std::unique_lock lk(mutex); event_distributor->unregister_event_handler(reg); } mcl::EventDistributor* event_distributor; int reg; bool locked; std::mutex mutex; }; MirEvent e; e.type = mir_event_type_prompt_session_state_change; std::vector catchers; for (int p = 0; p < 10; p++) { catchers.push_back(new EventCatcher(&event_distributor)); } mt::WaitCondition thread_done; auto thread = std::thread{ [&] { for (auto catcher : catchers) delete catcher; thread_done.wake_up_everyone(); }}; while(!thread_done.woken()) { event_distributor.handle_event(e); std::this_thread::yield(); } thread.join(); } ./tests/unit-tests/client/CMakeLists.txt0000644000015600001650000000314512676616157020420 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_aging_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_stream_transport.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_client.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_client_buffer_depository.cpp #to deprecate soon ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_vault.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_client_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_client_mir_surface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mir_connection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_wait_handle.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_client_display_conf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mir_screencast.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_protobuf_rpc_channel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_probing_client_platform_factory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mir_prompt_session.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_event_distributor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_probing_client_platform_factory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_periodic_perf_report.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_client_buffer_stream.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_screencast_stream.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_connection_resource_map.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_presentation_chain.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mir_buffer.cpp ) if(MIR_TEST_PLATFORM STREQUAL "android") add_subdirectory("android") endif() if(MIR_TEST_PLATFORM STREQUAL "mesa-kms" OR MIR_TEST_PLATFORM STREQUAL "mesa-x11" ) add_subdirectory("mesa") endif() if (NOT MIR_DISABLE_INPUT) add_subdirectory("input") endif() set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/client/test_presentation_chain.cpp0000644000015600001650000003013312676616157023275 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/mock_protobuf_server.h" #include "mir/test/doubles/stub_client_buffer_factory.h" #include "mir/test/doubles/mock_client_buffer.h" #include "mir/test/fake_shared.h" #include "src/client/presentation_chain.h" #include "src/client/buffer_factory.h" #include "mir/client_buffer_factory.h" #include #include #include using namespace testing; namespace mtd = mir::test::doubles; namespace geom = mir::geometry; namespace mcl = mir::client; namespace mp = mir::protobuf; namespace gp = google::protobuf; namespace { struct MockClientBufferFactory : public mcl::ClientBufferFactory { MockClientBufferFactory() { ON_CALL(*this, create_buffer(_,_,_)) .WillByDefault(Return(std::make_shared>())); } MOCK_METHOD3(create_buffer, std::shared_ptr( std::shared_ptr const&, geom::Size, MirPixelFormat)); }; struct PresentationChain : Test { PresentationChain() { ipc_buf.set_width(size.width.as_int()); ipc_buf.set_height(size.height.as_int()); ipc_buf.set_buffer_id(buffer_id); } int rpc_id { 33 }; MirConnection* connection {reinterpret_cast(this)}; geom::Size size {100, 200}; MirPixelFormat format = mir_pixel_format_abgr_8888; MirBufferUsage usage = mir_buffer_usage_software; mtd::MockProtobufServer mock_server; std::shared_ptr const factory { std::make_shared>() }; int buffer_id {4312}; mp::Buffer ipc_buf; }; struct BufferCallbackContext { void set_buffer(MirBuffer* b) { std::unique_lock lk(mut); buffer = b; cv.notify_all(); } bool buffer_is_set() { std::unique_lock lk(mut); return buffer; } MirBuffer* wait_for_buffer() { std::unique_lock lk(mut); if (!cv.wait_for(lk, std::chrono::seconds(5), [this] { return buffer; })) throw std::runtime_error("timeout waiting for buffer"); return buffer; } private: std::mutex mut; std::condition_variable cv; MirBuffer* buffer = nullptr; }; struct BufferCount { std::mutex mut; std::condition_variable cv; MirBuffer* buffer = nullptr; unsigned int count = 0; }; MATCHER_P(BufferRequestMatches, val, "") { return ((arg->id().value() == val.id().value()) && arg->has_buffer() && val.has_buffer() && arg->buffer().buffer_id() == val.buffer().buffer_id()); } MATCHER_P(BufferAllocationMatches, val, "") { return ((arg->id().value() == val.id().value()) && (arg->buffer_requests_size() == 1) && (val.buffer_requests_size() == 1) && (arg->buffer_requests(0).width() == val.buffer_requests(0).width()) && (arg->buffer_requests(0).height() == val.buffer_requests(0).height()) && (arg->buffer_requests(0).pixel_format() == val.buffer_requests(0).pixel_format()) && (arg->buffer_requests(0).buffer_usage() == val.buffer_requests(0).buffer_usage())); } MATCHER_P(BufferReleaseMatches, val, "") { return ((arg->id().value() == val.id().value()) && (arg->buffers_size() == 1) && (val.buffers_size() == 1) && (arg->buffers(0).buffer_id() == val.buffers(0).buffer_id())); } } static void buffer_callback(MirPresentationChain*, MirBuffer* buffer, void* context) { static_cast(context)->set_buffer(buffer); } static void counting_buffer_callback(MirPresentationChain*, MirBuffer* buffer, void* context) { BufferCount* c = static_cast(context); c->buffer = buffer; c->count = c->count + 1; } TEST_F(PresentationChain, returns_associated_connection) { mcl::PresentationChain chain( connection, rpc_id, mock_server, std::make_shared(), std::make_shared()); EXPECT_THAT(chain.connection(), Eq(connection)); } TEST_F(PresentationChain, returns_associated_rpc_id) { mcl::PresentationChain chain( connection, rpc_id, mock_server, std::make_shared(), std::make_shared()); EXPECT_THAT(chain.rpc_id(), Eq(rpc_id)); } TEST_F(PresentationChain, creates_buffer_when_asked) { BufferCallbackContext buffer; mp::BufferAllocation mp_alloc; auto params = mp_alloc.add_buffer_requests(); params->set_width(size.width.as_int()); params->set_height(size.height.as_int()); params->set_buffer_usage(usage); params->set_pixel_format(format); mp_alloc.mutable_id()->set_value(rpc_id); EXPECT_CALL(mock_server, allocate_buffers(BufferAllocationMatches(mp_alloc),_,_)) .WillOnce(mtd::RunProtobufClosure()); EXPECT_CALL(*factory, create_buffer(_, size, format)); mcl::PresentationChain chain( connection, rpc_id, mock_server, factory, std::make_shared()); chain.allocate_buffer(size, format, usage, buffer_callback, &buffer); EXPECT_FALSE(buffer.buffer_is_set()); mp::Buffer ipc_buf; ipc_buf.set_width(size.width.as_int()); ipc_buf.set_height(size.height.as_int()); chain.buffer_available(ipc_buf); EXPECT_TRUE(buffer.wait_for_buffer()); } TEST_F(PresentationChain, creates_correct_buffer_when_buffers_arrive) { size_t const num_buffers = 3u; std::array sizes { { geom::Size{2,2}, geom::Size{2,1}, geom::Size{1,2} } }; std::array ipc_buf; std::array buffer; std::array mp_alloc; Sequence seq; for (auto i = 0u; i < num_buffers; i++) { mp_alloc[i].mutable_id()->set_value(rpc_id); auto params = mp_alloc[i].add_buffer_requests(); params->set_width(sizes[i].width.as_int()); params->set_height(sizes[i].height.as_int()); params->set_buffer_usage(usage); params->set_pixel_format(format); EXPECT_CALL(mock_server, allocate_buffers(BufferAllocationMatches(mp_alloc[i]),_,_)) .InSequence(seq) .WillOnce(mtd::RunProtobufClosure()); ipc_buf[i].set_buffer_id(i); ipc_buf[i].set_width(sizes[i].width.as_int()); ipc_buf[i].set_height(sizes[i].height.as_int()); } mcl::PresentationChain chain( connection, rpc_id, mock_server, std::make_shared(), std::make_shared()); for (auto i = 0u; i < num_buffers; i++) chain.allocate_buffer(sizes[i], format, usage, buffer_callback, &buffer[i]); chain.buffer_available(ipc_buf[1]); EXPECT_FALSE(buffer[0].buffer_is_set()); EXPECT_TRUE(buffer[1].wait_for_buffer()); EXPECT_FALSE(buffer[2].buffer_is_set()); chain.buffer_available(ipc_buf[2]); EXPECT_FALSE(buffer[0].buffer_is_set()); EXPECT_TRUE(buffer[2].wait_for_buffer()); chain.buffer_available(ipc_buf[0]); EXPECT_TRUE(buffer[0].wait_for_buffer()); } TEST_F(PresentationChain, frees_buffer_when_asked) { BufferCallbackContext buffer; mp::BufferRelease release_msg; release_msg.mutable_id()->set_value(rpc_id); auto released_buffer = release_msg.add_buffers(); released_buffer->set_buffer_id(buffer_id); EXPECT_CALL(mock_server, allocate_buffers(_,_,_)) .WillOnce(mtd::RunProtobufClosure()); EXPECT_CALL(mock_server, release_buffers(BufferReleaseMatches(release_msg),_,_)) .WillOnce(mtd::RunProtobufClosure()); mcl::PresentationChain chain( connection, rpc_id, mock_server, std::make_shared(), std::make_shared()); chain.allocate_buffer(size, format, usage, buffer_callback, &buffer); chain.buffer_available(ipc_buf); auto b = buffer.wait_for_buffer(); ASSERT_THAT(b, Ne(nullptr)); chain.release_buffer(b); } TEST_F(PresentationChain, submits_buffer_when_asked) { BufferCallbackContext buffer; mp::BufferRequest request; request.mutable_id()->set_value(rpc_id); request.mutable_buffer()->set_buffer_id(buffer_id); EXPECT_CALL(mock_server, allocate_buffers(_,_,_)) .WillOnce(mtd::RunProtobufClosure()); EXPECT_CALL(mock_server, submit_buffer(BufferRequestMatches(request),_,_)) .WillOnce(mtd::RunProtobufClosure()); mcl::PresentationChain chain( connection, rpc_id, mock_server, std::make_shared(), std::make_shared()); chain.allocate_buffer(size, format, usage, buffer_callback, &buffer); chain.buffer_available(ipc_buf); auto b = buffer.wait_for_buffer(); ASSERT_THAT(b, Ne(nullptr)); chain.submit_buffer(b); } TEST_F(PresentationChain, updates_buffer) { mtd::MockClientBuffer mock_buffer; BufferCallbackContext buffer; mp::BufferRequest request; request.mutable_id()->set_value(rpc_id); request.mutable_buffer()->set_buffer_id(buffer_id); EXPECT_CALL(mock_server, allocate_buffers(_,_,_)) .WillOnce(mtd::RunProtobufClosure()); EXPECT_CALL(mock_server, submit_buffer(BufferRequestMatches(request),_,_)) .WillOnce(mtd::RunProtobufClosure()); EXPECT_CALL(mock_buffer, update_from(_)); EXPECT_CALL(*factory, create_buffer(_,_,_)) .WillOnce(Return(mir::test::fake_shared(mock_buffer))); mcl::PresentationChain chain( connection, rpc_id, mock_server, factory, std::make_shared()); chain.allocate_buffer(size, format, usage, buffer_callback, &buffer); chain.buffer_available(ipc_buf); auto b = buffer.wait_for_buffer(); ASSERT_THAT(b, Ne(nullptr)); chain.submit_buffer(b); chain.buffer_available(ipc_buf); } TEST_F(PresentationChain, double_submission_throws) { BufferCallbackContext buffer; EXPECT_CALL(mock_server, allocate_buffers(_,_,_)) .WillOnce(mtd::RunProtobufClosure()); EXPECT_CALL(mock_server, submit_buffer(_,_,_)) .WillOnce(mtd::RunProtobufClosure()); mcl::PresentationChain chain( connection, rpc_id, mock_server, std::make_shared(), std::make_shared()); chain.allocate_buffer(size, format, usage, buffer_callback, &buffer); chain.buffer_available(ipc_buf); auto b = buffer.wait_for_buffer(); ASSERT_THAT(b, Ne(nullptr)); chain.submit_buffer(b); EXPECT_THROW({ chain.submit_buffer(b); }, std::logic_error); } TEST_F(PresentationChain, callback_invoked_when_buffer_returned_from_allocation_and_submission) { BufferCount counter; EXPECT_CALL(mock_server, allocate_buffers(_,_,_)) .WillOnce(mtd::RunProtobufClosure()); EXPECT_CALL(mock_server, submit_buffer(_,_,_)) .WillOnce(mtd::RunProtobufClosure()); mcl::PresentationChain chain( connection, rpc_id, mock_server, std::make_shared(), std::make_shared()); chain.allocate_buffer(size, format, usage, counting_buffer_callback, &counter); chain.buffer_available(ipc_buf); std::unique_lock lk(counter.mut); EXPECT_TRUE(counter.cv.wait_for(lk, std::chrono::seconds(5), [&] { return counter.buffer; })); lk.unlock(); chain.submit_buffer(counter.buffer); chain.buffer_available(ipc_buf); lk.lock(); EXPECT_THAT(counter.count, Eq(2)); } ./tests/unit-tests/client/test_mir_prompt_session.cpp0000644000015600001650000001325112676616125023350 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Nick Dedekind */ #include "src/client/mir_prompt_session.h" #include "src/client/mir_event_distributor.h" #include "src/client/rpc/mir_display_server.h" #include "mir/events/event_builders.h" #include "mir/test/fake_shared.h" #include #include #include namespace mcl = mir::client; namespace mclr = mir::client::rpc; namespace mev = mir::events; namespace mt = mir::test; namespace google { namespace protobuf { class RpcController; } } namespace { struct MockProtobufServer : mclr::DisplayServer { MockProtobufServer() : mclr::DisplayServer(nullptr) {} MOCK_METHOD3(start_prompt_session, void( mir::protobuf::PromptSessionParameters const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/)); MOCK_METHOD3(stop_prompt_session, void( mir::protobuf::Void const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/)); }; class StubProtobufServer : public mclr::DisplayServer { public: StubProtobufServer() : mclr::DisplayServer(nullptr) {} void start_prompt_session( mir::protobuf::PromptSessionParameters const* /*request*/, mir::protobuf::Void* response, google::protobuf::Closure* done) override { if (server_thread.joinable()) server_thread.join(); server_thread = std::thread{ [response, done, this] { response->clear_error(); done->Run(); }}; } void stop_prompt_session( mir::protobuf::Void const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* done) override { if (server_thread.joinable()) server_thread.join(); server_thread = std::thread{[done, this] { done->Run(); }}; } ~StubProtobufServer() { if (server_thread.joinable()) server_thread.join(); } private: std::thread server_thread; }; class MirPromptSessionTest : public testing::Test { public: MirPromptSessionTest() { } static void prompt_session_state_change(MirPromptSession*, MirPromptSessionState new_state, void* context) { MirPromptSessionTest* test = static_cast(context); test->state_updated(new_state); } MOCK_METHOD1(state_updated, void(MirPromptSessionState)); testing::NiceMock mock_server; StubProtobufServer stub_server; MirEventDistributor event_distributor; }; struct MockCallback { MOCK_METHOD2(call, void(void*, void*)); }; void mock_callback_func(MirPromptSession* prompt_session, void* context) { auto mock_cb = static_cast(context); mock_cb->call(prompt_session, context); } void null_callback_func(MirPromptSession*, void*) { } ACTION(RunClosure) { arg2->Run(); } } TEST_F(MirPromptSessionTest, start_prompt_session) { using namespace testing; EXPECT_CALL(mock_server, start_prompt_session(_,_,_)) .WillOnce(RunClosure()); MirPromptSession prompt_session{ mock_server, mt::fake_shared(event_distributor)}; prompt_session.start(__LINE__, null_callback_func, nullptr); } TEST_F(MirPromptSessionTest, stop_prompt_session) { using namespace testing; EXPECT_CALL(mock_server, stop_prompt_session(_,_,_)) .WillOnce(RunClosure()); MirPromptSession prompt_session{ mock_server, mt::fake_shared(event_distributor)}; prompt_session.stop(null_callback_func, nullptr); } TEST_F(MirPromptSessionTest, executes_callback_on_start) { using namespace testing; MockCallback mock_cb; EXPECT_CALL(mock_cb, call(_, &mock_cb)); MirPromptSession prompt_session{ stub_server, mt::fake_shared(event_distributor)}; prompt_session.start(__LINE__, mock_callback_func, &mock_cb)->wait_for_all(); } TEST_F(MirPromptSessionTest, executes_callback_on_stop) { using namespace testing; MockCallback mock_cb; EXPECT_CALL(mock_cb, call(_, &mock_cb)); MirPromptSession prompt_session{ stub_server, mt::fake_shared(event_distributor)}; prompt_session.stop(mock_callback_func, &mock_cb)->wait_for_all(); } TEST_F(MirPromptSessionTest, notifies_event_callback) { using namespace testing; MirPromptSession prompt_session{ mock_server, mt::fake_shared(event_distributor)}; prompt_session.register_prompt_session_state_change_callback(&MirPromptSessionTest::prompt_session_state_change, this); InSequence seq; EXPECT_CALL(*this, state_updated(mir_prompt_session_state_started)); EXPECT_CALL(*this, state_updated(mir_prompt_session_state_stopped)); event_distributor.handle_event(*mev::make_event(mir_prompt_session_state_started)); event_distributor.handle_event(*mev::make_event(mir_prompt_session_state_stopped)); } ./tests/unit-tests/test_raii.cpp0000644000015600001650000001243712676616125017070 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/raii.h" #include #include namespace { struct RaiiTest : public ::testing::Test { RaiiTest(); ~RaiiTest(); MOCK_METHOD0(create_void, void()); MOCK_METHOD0(destroy_void, void()); MOCK_METHOD0(create_ptr, RaiiTest*()); MOCK_METHOD1(destroy_ptr, void (RaiiTest*)); MOCK_METHOD0(test_call, void()); }; RaiiTest* self = nullptr; RaiiTest::RaiiTest() { self = this; } RaiiTest::~RaiiTest() { self = nullptr; } void create_void() { return self->create_void(); } void destroy_void() { self->destroy_void(); } RaiiTest* create_ptr() { return self->create_ptr(); } void destroy_ptr(RaiiTest* p) { self->destroy_ptr(p); } } using namespace testing; TEST_F(RaiiTest, free_create_free_destroy_ptr) { InSequence seq; EXPECT_CALL(*this, create_ptr()).Times(1).WillRepeatedly(Return(this)); EXPECT_CALL(*this, test_call()).Times(1); EXPECT_CALL(*this, destroy_ptr(this)).Times(1); auto const raii = mir::raii::paired_calls( ::create_ptr, ::destroy_ptr); raii->test_call(); EXPECT_EQ(this, raii.get()); } TEST_F(RaiiTest, lambda_create_free_destroy_ptr) { InSequence seq; EXPECT_CALL(*this, create_ptr()).Times(1).WillRepeatedly(Return(this)); EXPECT_CALL(*this, test_call()).Times(1); EXPECT_CALL(*this, destroy_ptr(this)).Times(1); auto const raii = mir::raii::paired_calls( [this] { return create_ptr(); }, ::destroy_ptr); raii->test_call(); EXPECT_EQ(this, raii.get()); } TEST_F(RaiiTest, free_create_lambda_destroy_ptr) { InSequence seq; EXPECT_CALL(*this, create_ptr()).Times(1).WillRepeatedly(Return(this)); EXPECT_CALL(*this, test_call()).Times(1); EXPECT_CALL(*this, destroy_ptr(this)).Times(1); auto const raii = mir::raii::paired_calls( ::create_ptr, [this] (RaiiTest*p){ ::destroy_ptr(p); }); raii->test_call(); EXPECT_EQ(this, raii.get()); } TEST_F(RaiiTest, lambda_create_lambda_destroy_ptr) { InSequence seq; EXPECT_CALL(*this, create_ptr()).Times(1).WillRepeatedly(Return(this)); EXPECT_CALL(*this, test_call()).Times(1); EXPECT_CALL(*this, destroy_ptr(this)).Times(1); auto const raii = mir::raii::paired_calls( [this] { return create_ptr(); }, [this] (RaiiTest*p){ destroy_ptr(p); }); raii->test_call(); EXPECT_EQ(this, raii.get()); } TEST_F(RaiiTest, free_create_free_destroy_void) { EXPECT_CALL(*this, create_void()).Times(1); auto const raii = mir::raii::paired_calls( ::create_void, ::destroy_void); Mock::VerifyAndClearExpectations(this); EXPECT_CALL(*this, destroy_void()).Times(1); } TEST_F(RaiiTest, lambda_create_free_destroy_void) { InSequence seq; EXPECT_CALL(*this, create_void()).Times(1); EXPECT_CALL(*this, destroy_void()).Times(1); auto const raii = mir::raii::paired_calls( [this] { return create_void(); }, ::destroy_void); } TEST_F(RaiiTest, free_create_lambda_destroy_void) { InSequence seq; EXPECT_CALL(*this, create_void()).Times(1); EXPECT_CALL(*this, destroy_void()).Times(1); auto const raii = mir::raii::paired_calls( ::create_void, [this] (){ destroy_void(); }); } TEST_F(RaiiTest, lambda_create_lambda_destroy_void) { InSequence seq; EXPECT_CALL(*this, create_void()).Times(1); EXPECT_CALL(*this, destroy_void()).Times(1); auto const raii = mir::raii::paired_calls( [this] { return create_void(); }, [this] (){ destroy_void(); }); } TEST_F(RaiiTest, deleter_for_free_destroy_ptr) { EXPECT_CALL(*this, destroy_ptr(this)).Times(1); auto const raii = mir::raii::deleter_for( static_cast(this), ::destroy_ptr); EXPECT_EQ(this, raii.get()); } TEST_F(RaiiTest, deleter_for_lambda_destroy_ptr) { EXPECT_CALL(*this, destroy_ptr(this)).Times(1); auto const raii = mir::raii::deleter_for( static_cast(this), [this] (RaiiTest*p){ destroy_ptr(p); }); EXPECT_EQ(this, raii.get()); } TEST_F(RaiiTest, paired_call_takes_std_function_refs) { int creator_call_count = 0; int deleter_call_count = 0; int const expected_calls = 2; std::function creator = [&creator_call_count] { creator_call_count++; }; std::function deleter = [&deleter_call_count] { deleter_call_count++; }; for (int i = 0; i < expected_calls; i++) { auto const raii = mir::raii::paired_calls( std::ref(creator), std::ref(deleter)); } EXPECT_THAT(creator_call_count, Eq(expected_calls)); EXPECT_THAT(deleter_call_count, Eq(expected_calls)); } ./tests/unit-tests/geometry/0000755000015600001650000000000012676616126016226 5ustar jenkinsjenkins./tests/unit-tests/geometry/test-point.cpp0000644000015600001650000000261212676616125021040 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/geometry/point.h" #include #include namespace geom = mir::geometry; TEST(geometry, point) { using namespace geom; Point const pointx2y4{X(2), Y(4)}; EXPECT_EQ(X(2), pointx2y4.x); EXPECT_EQ(Y(4), pointx2y4.y); Point const copy = pointx2y4; EXPECT_EQ(X(2), copy.x); EXPECT_EQ(Y(4), copy.y); EXPECT_EQ(pointx2y4, copy); Point defaultValue; EXPECT_EQ(X(0), defaultValue.x); EXPECT_EQ(Y(0), defaultValue.y); EXPECT_NE(pointx2y4, defaultValue); } TEST(geometry, point_is_usable) { using namespace geom; int x = 0; int y = 1; auto p = Point{x, y}; EXPECT_EQ(X(x), p.x); EXPECT_EQ(Y(y), p.y); } ./tests/unit-tests/geometry/test-rectangles.cpp0000644000015600001650000002161112676616125022036 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/geometry/rectangles.h" #include #include #include #include using namespace mir::geometry; using namespace testing; namespace { struct TestRectangles : Test { Rectangles rectangles; auto contents_of(Rectangles const& rects) -> std::vector { return {std::begin(rects), std::end(rects)}; } }; } TEST_F(TestRectangles, rectangles_empty) { EXPECT_EQ(0, std::distance(rectangles.begin(), rectangles.end())); EXPECT_EQ(0u, rectangles.size()); } TEST_F(TestRectangles, rectangles_not_empty) { std::vector const rectangles_vec = { {Point{1, 2}, Size{Width{10}, Height{11}}}, {Point{3, 4}, Size{Width{12}, Height{13}}}, {Point{5, 6}, Size{Width{14}, Height{15}}} }; for (auto const& rect : rectangles_vec) rectangles.add(rect); EXPECT_EQ(static_cast(rectangles_vec.size()), std::distance(rectangles.begin(), rectangles.end())); EXPECT_EQ(rectangles_vec.size(), rectangles.size()); std::vector rect_found(rectangles_vec.size(), false); for (auto const& rect : rectangles) { auto iter = std::find(rectangles.begin(), rectangles.end(), rect); ASSERT_NE(rectangles.end(), iter); auto index = std::distance(rectangles.begin(), iter); EXPECT_FALSE(rect_found[index]); rect_found[index] = true; } EXPECT_TRUE(std::all_of(rect_found.begin(), rect_found.end(), [](bool b){return b;})); } TEST_F(TestRectangles, rectangles_clear) { std::vector const rectangles_vec = { {Point{1, 2}, Size{Width{10}, Height{11}}}, {Point{3, 4}, Size{Width{12}, Height{13}}}, {Point{5, 6}, Size{Width{14}, Height{15}}} }; Rectangles const rectangles_empty; for (auto const& rect : rectangles_vec) rectangles.add(rect); EXPECT_NE(rectangles_empty, rectangles); rectangles.clear(); EXPECT_EQ(rectangles_empty, rectangles); } TEST_F(TestRectangles, rectangles_bounding_rectangle) { std::vector const rectangles_vec = { {Point{0, 0}, Size{Width{10}, Height{5}}}, {Point{2, 2}, Size{Width{4}, Height{1}}}, {Point{1, 1}, Size{Width{10}, Height{5}}}, {Point{-1, -2}, Size{Width{3}, Height{3}}}, {Point{-2, -3}, Size{Width{14}, Height{10}}}, {Point{-2, -3}, Size{Width{14}, Height{10}}} }; std::vector const expected_bounding_rectangles = { {Point{0, 0}, Size{Width{10}, Height{5}}}, {Point{0, 0}, Size{Width{10}, Height{5}}}, {Point{0, 0}, Size{Width{11}, Height{6}}}, {Point{-1, -2}, Size{Width{12}, Height{8}}}, {Point{-2, -3}, Size{Width{14}, Height{10}}}, {Point{-2, -3}, Size{Width{14}, Height{10}}} }; EXPECT_EQ(Rectangle(), rectangles.bounding_rectangle()); for (size_t i = 0; i < rectangles_vec.size(); i++) { rectangles.add(rectangles_vec[i]); EXPECT_EQ(expected_bounding_rectangles[i], rectangles.bounding_rectangle()) << "i=" << i; } } TEST_F(TestRectangles, rectangles_equality) { std::vector const rectangles_vec1 = { {Point{0, 0}, Size{Width{10}, Height{5}}}, {Point{2, 2}, Size{Width{4}, Height{1}}}, {Point{1, 1}, Size{Width{10}, Height{5}}}, {Point{-1, -2}, Size{Width{3}, Height{3}}}, {Point{-2, -3}, Size{Width{14}, Height{10}}}, {Point{-2, -3}, Size{Width{14}, Height{10}}} }; std::vector const rectangles_vec2 = { {Point{0, 0}, Size{Width{10}, Height{5}}}, {Point{1, 1}, Size{Width{10}, Height{5}}}, {Point{-1, -2}, Size{Width{3}, Height{3}}}, {Point{-2, -3}, Size{Width{14}, Height{10}}} }; std::vector reverse_rectangles_vec1{rectangles_vec1}; std::reverse(reverse_rectangles_vec1.begin(), reverse_rectangles_vec1.end()); Rectangles rectangles1; Rectangles rectangles2; Rectangles rectangles3; Rectangles rectangles_empty; for (auto const& rect : rectangles_vec1) rectangles1.add(rect); for (auto const& rect : rectangles_vec2) rectangles2.add(rect); for (auto const& rect : reverse_rectangles_vec1) rectangles3.add(rect); EXPECT_EQ(rectangles1, rectangles3); EXPECT_NE(rectangles1, rectangles2); EXPECT_NE(rectangles2, rectangles3); EXPECT_NE(rectangles1, rectangles_empty); } TEST_F(TestRectangles, rectangles_copy_assign) { std::vector const rectangles_vec = { {Point{0, 0}, Size{Width{10}, Height{5}}}, {Point{2, 2}, Size{Width{4}, Height{1}}}, {Point{0, 1}, Size{Width{10}, Height{5}}}, {Point{-1, -2}, Size{Width{3}, Height{3}}}, {Point{-5, -3}, Size{Width{14}, Height{10}}} }; Rectangles rectangles1; Rectangles rectangles2; for (auto const& rect : rectangles_vec) rectangles1.add(rect); rectangles2 = rectangles1; Rectangles const rectangles3{rectangles2}; EXPECT_EQ(rectangles1, rectangles2); EXPECT_EQ(rectangles1, rectangles3); EXPECT_EQ(rectangles2, rectangles3); EXPECT_EQ(rectangles1.bounding_rectangle(), rectangles2.bounding_rectangle()); EXPECT_EQ(rectangles1.bounding_rectangle(), rectangles3.bounding_rectangle()); EXPECT_EQ(rectangles2.bounding_rectangle(), rectangles3.bounding_rectangle()); } TEST_F(TestRectangles, rectangles_confine) { std::vector const rectangles_vec = { {Point{0,0}, Size{800,600}}, {Point{0,600}, Size{100,100}}, {Point{800,0}, Size{100,100}} }; std::vector> const point_tuples{ std::make_tuple(Point{0,0}, Point{0,0}), std::make_tuple(Point{900,50}, Point{899,50}), std::make_tuple(Point{850,100}, Point{850,99}), std::make_tuple(Point{801,100}, Point{801,99}), std::make_tuple(Point{800,101}, Point{799,101}), std::make_tuple(Point{800,600}, Point{799,599}), std::make_tuple(Point{-1,700}, Point{0,699}), std::make_tuple(Point{-1,-1}, Point{0,0}), std::make_tuple(Point{-1,50}, Point{0,50}), std::make_tuple(Point{799,-1}, Point{799,0}), std::make_tuple(Point{800,-1}, Point{800,0}) }; for (auto const& rect : rectangles_vec) rectangles.add(rect); for (auto const& t : point_tuples) { Point confined_point{std::get<0>(t)}; Point const expected_point{std::get<1>(t)}; rectangles.confine(confined_point); EXPECT_EQ(expected_point, confined_point); } } TEST_F(TestRectangles, tracks_add_and_remove) { Rectangle const rect[]{ {{ 0, 0}, {800, 600}}, {{ 0, 600}, {100, 100}}, {{800, 0}, {100, 100}}}; rectangles = Rectangles{rect[0], rect[1], rect[2]}; EXPECT_THAT(contents_of(rectangles), UnorderedElementsAre(rect[0], rect[1], rect[2])); EXPECT_THAT(rectangles.bounding_rectangle(), Eq(Rectangle{{0,0}, {900,700}})); rectangles.remove(rect[1]); EXPECT_THAT(contents_of(rectangles), UnorderedElementsAre(rect[0], rect[2])); EXPECT_THAT(rectangles.bounding_rectangle(), Eq(Rectangle{{0,0}, {900,600}})); rectangles.add(rect[2]); EXPECT_THAT(contents_of(rectangles), UnorderedElementsAre(rect[0], rect[2], rect[2])); EXPECT_THAT(rectangles.bounding_rectangle(), Eq(Rectangle{{0,0}, {900,600}})); rectangles.add(rect[1]); rectangles.remove(rect[2]); EXPECT_THAT(contents_of(rectangles), UnorderedElementsAre(rect[0], rect[1], rect[2])); EXPECT_THAT(rectangles.bounding_rectangle(), Eq(Rectangle{{0,0}, {900,700}})); } TEST_F(TestRectangles, can_add_same_rectangle_many_times) { int const many_times = 10; Rectangle const rectangle{{ 0, 0}, {800, 600}}; for (auto i = 0; i != many_times; ++i) rectangles.add(rectangle); EXPECT_THAT(rectangles.size(), Eq(many_times)); } TEST_F(TestRectangles, remove_only_removes_one_instance) { int const many_times = 10; Rectangle const rectangle{{ 0, 0}, {800, 600}}; for (auto i = 0; i != many_times; ++i) rectangles.add(rectangle); for (auto i = many_times; i-- != 0;) { rectangles.remove(rectangle); EXPECT_THAT(rectangles.size(), Eq(i)); } } ./tests/unit-tests/geometry/test-displacement.cpp0000644000015600001650000000507612676616125022366 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/geometry/displacement.h" #include #include namespace geom = mir::geometry; TEST(geometry, displacement) { using namespace geom; Displacement disp; Displacement const dx2dy4{DeltaX{2}, DeltaY{4}}; EXPECT_EQ(DeltaX{0}, disp.dx); EXPECT_EQ(DeltaY{0}, disp.dy); EXPECT_EQ(DeltaX{2}, dx2dy4.dx); EXPECT_EQ(DeltaY{4}, dx2dy4.dy); EXPECT_EQ(disp, disp); EXPECT_NE(disp, dx2dy4); EXPECT_EQ(dx2dy4, dx2dy4); EXPECT_NE(dx2dy4, disp); } TEST(geometry, displacement_arithmetic) { using namespace geom; Displacement const dx2dy4{DeltaX{2}, DeltaY{4}}; Displacement const dx3dy9{DeltaX{3}, DeltaY{9}}; Displacement const expected_add{DeltaX{5}, DeltaY{13}}; Displacement const expected_sub{DeltaX{1}, DeltaY{5}}; Displacement const add = dx3dy9 + dx2dy4; Displacement const sub = dx3dy9 - dx2dy4; EXPECT_EQ(expected_add, add); EXPECT_EQ(expected_sub, sub); } TEST(geometry, displacement_point_arithmetic) { using namespace geom; Point const x2y4{X{2}, Y{4}}; Point const x3y9{X{3}, Y{9}}; Displacement const dx2dy4{DeltaX{2}, DeltaY{4}}; Displacement const dx7dy11{DeltaX{7}, DeltaY{11}}; Point const expected_pd_add{X{5}, Y{13}}; Point const expected_pd_sub{X{1}, Y{5}}; Point const expected_pd_sub2{X{-4}, Y{-2}}; Displacement const expected_pp_sub{DeltaX{1}, DeltaY{5}}; Displacement const expected_pp_sub2{DeltaX{-1}, DeltaY{-5}}; Point const pd_add = x3y9 + dx2dy4; Point const pd_sub = x3y9 - dx2dy4; Point const pd_sub2 = x3y9 - dx7dy11; Displacement const pp_sub = x3y9 - x2y4; Displacement const pp_sub2 = x2y4 - x3y9; EXPECT_EQ(expected_pd_add, pd_add); EXPECT_EQ(expected_pd_sub, pd_sub); EXPECT_EQ(expected_pd_sub2, pd_sub2); EXPECT_EQ(expected_pp_sub, pp_sub); EXPECT_EQ(expected_pp_sub2, pp_sub2); } ./tests/unit-tests/geometry/test-rectangle.cpp0000644000015600001650000001560512676616125021661 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/geometry/rectangle.h" #include #include #include namespace geom = mir::geometry; TEST(geometry, rectangle) { using namespace geom; Point const x3y9{X(3), Y(9)}; Size const w2h4{2, 4}; Rectangle const rect{x3y9, w2h4}; EXPECT_EQ(x3y9, rect.top_left); EXPECT_EQ(w2h4, rect.size); Rectangle const copy = rect; EXPECT_EQ(x3y9, copy.top_left); EXPECT_EQ(w2h4, copy.size); EXPECT_EQ(rect, copy); Rectangle default_rect; EXPECT_EQ(Point(), default_rect.top_left); EXPECT_EQ(Size(), default_rect.size); EXPECT_NE(rect, default_rect); } TEST(geometry, rectangle_bottom_right) { using namespace testing; using namespace geom; Rectangle const rect{{0,0}, {1,1}}; Rectangle const rect_empty{{2,2}, {0,0}}; EXPECT_EQ(Point(1,1), rect.bottom_right()); EXPECT_EQ(Point(2,2), rect_empty.bottom_right()); } TEST(geometry, rectangle_contains_rectangle) { using namespace testing; using namespace geom; const int left = 3; const int top = 3; const int width = 3; const int height = 3; const Rectangle r{{left,top}, {width,height}}; const int right = left + width; const int bottom = top + height; // Test for _every_ rectangle contained in r for (int y = top; y <= bottom; y++) { for (int x = left; x <= right; x++) { const int max_height = bottom - y; const int max_width = right - x; for (int h = 0; h <= max_height; h++) { for (int w = 0; w <= max_width; w++) { EXPECT_TRUE(r.contains(Rectangle{{x,y}, {w,h}})); } } EXPECT_FALSE(r.contains(Rectangle{{x,y}, {max_width+1,max_height}})); EXPECT_FALSE(r.contains(Rectangle{{x,y}, {max_width,max_height+1}})); } } EXPECT_FALSE(r.contains(Rectangle{{2,2}, {5,5}})); EXPECT_FALSE(r.contains(Rectangle{{2,2}, {1,1}})); EXPECT_FALSE(r.contains(Rectangle{{-4,-3}, {10,10}})); EXPECT_FALSE(r.contains(Rectangle{{1,3}, {3,3}})); EXPECT_FALSE(r.contains(Rectangle{{3,1}, {3,3}})); EXPECT_FALSE(r.contains(Rectangle{{5,3}, {3,3}})); EXPECT_FALSE(r.contains(Rectangle{{3,5}, {3,3}})); EXPECT_FALSE(r.contains(Rectangle{{9,9}, {1,1}})); EXPECT_FALSE(r.contains(Rectangle{{-6,-7}, {3,4}})); } TEST(geometry, empty_rectangle_contains_point_only) { using namespace testing; using namespace geom; const int left = 3; const int top = 3; const Rectangle r{{left,top}, {0,0}}; EXPECT_TRUE(r.contains(Rectangle{{left,top}, {0,0}})); EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,0}})); EXPECT_FALSE(r.contains(Rectangle{{left,top}, {0,1}})); EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,1}})); EXPECT_FALSE(r.contains(Rectangle{{left-1,top}, {3,0}})); EXPECT_FALSE(r.contains(Rectangle{{left-1,top}, {0,0}})); EXPECT_FALSE(r.contains(Rectangle{{left,top+1}, {0,0}})); } TEST(geometry, elongated_empty_rectangle_contains_points_only) { using namespace testing; using namespace geom; const int left = 3; const int top = 3; const Rectangle r{{left,top}, {0,3}}; EXPECT_TRUE(r.contains(Rectangle{{left,top}, {0,0}})); EXPECT_TRUE(r.contains(Rectangle{{left,top+1}, {0,0}})); EXPECT_TRUE(r.contains(Rectangle{{left,top+2}, {0,0}})); EXPECT_TRUE(r.contains(Rectangle{{left,top+3}, {0,0}})); EXPECT_TRUE(r.contains(Rectangle{{left,top}, {0,1}})); EXPECT_TRUE(r.contains(Rectangle{{left,top+1}, {0,2}})); EXPECT_FALSE(r.contains(Rectangle{{left,top+4}, {0,0}})); EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,0}})); EXPECT_FALSE(r.contains(Rectangle{{left,top}, {1,1}})); EXPECT_FALSE(r.contains(Rectangle{{left-1,top}, {3,0}})); EXPECT_FALSE(r.contains(Rectangle{{left-1,top}, {0,0}})); } TEST(geometry, rectangle_contains_point) { using namespace testing; using namespace geom; Rectangle const rect{{0,0}, {1,1}}; Rectangle const rect_empty{{2,2}, {0,0}}; EXPECT_TRUE(rect.contains({0,0})); EXPECT_FALSE(rect.contains({0,1})); EXPECT_FALSE(rect.contains({1,0})); EXPECT_FALSE(rect.contains({1,1})); EXPECT_FALSE(rect_empty.contains({2,2})); } TEST(geometry, rectangle_overlaps) { using namespace testing; using namespace geom; Rectangle const rect1{{0,0}, {1,1}}; Rectangle const rect2{{1,1}, {1,1}}; Rectangle const rect3{{0,0}, {2,2}}; Rectangle const rect4{{-1,-1}, {2,2}}; Rectangle const rect_empty{{0,0}, {0,0}}; EXPECT_FALSE(rect_empty.overlaps(rect_empty)); EXPECT_FALSE(rect_empty.overlaps(rect1)); EXPECT_FALSE(rect_empty.overlaps(rect4)); EXPECT_FALSE(rect1.overlaps(rect2)); EXPECT_FALSE(rect2.overlaps(rect1)); EXPECT_FALSE(rect4.overlaps(rect2)); EXPECT_FALSE(rect2.overlaps(rect4)); EXPECT_TRUE(rect1.overlaps(rect1)); EXPECT_TRUE(rect4.overlaps(rect4)); EXPECT_TRUE(rect3.overlaps(rect1)); EXPECT_TRUE(rect1.overlaps(rect3)); EXPECT_TRUE(rect3.overlaps(rect2)); EXPECT_TRUE(rect2.overlaps(rect3)); EXPECT_TRUE(rect4.overlaps(rect1)); EXPECT_TRUE(rect1.overlaps(rect4)); EXPECT_TRUE(rect4.overlaps(rect3)); EXPECT_TRUE(rect3.overlaps(rect3)); } TEST(geometry, rectangle_intersection) { using namespace testing; using namespace geom; Rectangle const rect_base{{5,5}, {5,5}}; struct TestData { Rectangle const rect; Rectangle const intersection; }; std::vector const test_data { { rect_base, rect_base }, { Rectangle(), Rectangle() }, { {{4,4}, {2,2}}, {{5,5}, {1,1}} }, { {{5,4}, {5,2}}, {{5,5}, {5,1}} }, { {{6,6}, {3,3}}, {{6,6}, {3,3}} }, { {{9,9}, {1,1}}, {{9,9}, {1,1}} }, { {{4,6}, {8,2}}, {{5,6}, {5,2}} } }; for (auto const& test_case : test_data) { EXPECT_THAT(rect_base.intersection_with(test_case.rect), Eq(test_case.intersection)) << "test_case.rect = " << test_case.rect; EXPECT_THAT(test_case.rect.intersection_with(rect_base), Eq(test_case.intersection)) << "test_case.rect = " << test_case.rect; } } ./tests/unit-tests/geometry/test-dimensions.cpp0000644000015600001650000000573712676616125022072 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/geometry/dimensions.h" #include #include namespace geom = mir::geometry; TEST(geometry, width) { geom::Width width0 {0}; geom::Width width42 {42}; EXPECT_EQ(uint32_t{0}, width0.as_uint32_t()); EXPECT_EQ(uint32_t{42}, width42.as_uint32_t()); EXPECT_EQ(width0, width0); EXPECT_NE(width0, width42); EXPECT_EQ(width42, width42); EXPECT_NE(width42, width0); } TEST(geometry, height) { geom::Height height0 {0}; geom::Height height42 {42}; EXPECT_EQ(uint32_t{0}, height0.as_uint32_t()); EXPECT_EQ(uint32_t{42}, height42.as_uint32_t()); EXPECT_EQ(height0, height0); EXPECT_NE(height0, height42); EXPECT_EQ(height42, height42); EXPECT_NE(height42, height0); } TEST(geometry, delta_arithmetic) { using namespace geom; DeltaX dx1{1}; DeltaY dy1{1}; DeltaX x2 = DeltaX(1) + dx1; EXPECT_EQ(DeltaX(2), x2); EXPECT_EQ(DeltaX(1), x2-dx1); } TEST(geometry, coordinates) { using namespace geom; X x1{1}; X x2{2}; DeltaX dx1{1}; EXPECT_EQ(X(2), x1 + dx1); EXPECT_EQ(X(1), x2 - dx1); Y y24{24}; Y y42{42}; DeltaY dx18{18}; EXPECT_EQ(dx18, y42 - y24); } TEST(geometry, conversions) { using namespace geom; Width w1{1}; DeltaX dx1{1}; EXPECT_EQ(w1, dim_cast(dx1)); EXPECT_EQ(dx1, dim_cast(w1)); EXPECT_NE(dx1, dim_cast(X())); } TEST(geometry, signed_dimensions) { using namespace geom; X const x0{0}; X const x2{2}; X const xn5{-5}; Y const y0{0}; Y const y3{3}; Y const yn6{-6}; Y const yn7{-7}; // Compare against zero to catch regressions of signed->unsigned that // wouldn't be caught using as_*int()... EXPECT_GT(x0, xn5); EXPECT_GT(y0, yn7); EXPECT_LT(xn5, x0); EXPECT_LT(xn5, x2); EXPECT_LT(yn7, yn6); EXPECT_LT(yn7, y0); EXPECT_LT(yn7, y3); EXPECT_LE(xn5, x0); EXPECT_LE(xn5, x2); EXPECT_LE(yn7, yn6); EXPECT_LE(yn7, y0); EXPECT_LE(yn7, y3); EXPECT_LE(yn7, yn7); EXPECT_GT(x0, xn5); EXPECT_GT(x2, xn5); EXPECT_GT(yn6, yn7); EXPECT_GT(y0, yn7); EXPECT_GT(y3, yn7); EXPECT_GE(x0, xn5); EXPECT_GE(x2, xn5); EXPECT_GE(yn6, yn7); EXPECT_GE(y0, yn7); EXPECT_GE(y3, yn7); EXPECT_GE(yn7, yn7); } ./tests/unit-tests/geometry/test-length.cpp0000644000015600001650000000630412676616125021172 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "mir/geometry/length.h" #include using namespace mir::geometry; TEST(Length, zero) { Length zero; EXPECT_EQ(0, zero.as(Length::micrometres)); EXPECT_EQ(0, zero.as(Length::millimetres)); EXPECT_EQ(0, zero.as(Length::centimetres)); EXPECT_EQ(0, zero.as(Length::inches)); EXPECT_EQ(zero, 0_mm); EXPECT_EQ(zero, 0.0_mm); EXPECT_EQ(zero, 0_cm); EXPECT_EQ(zero, 0.0_cm); EXPECT_EQ(zero, 0_in); EXPECT_EQ(zero, 0.0_in); } TEST(Length, millimetres) { Length one_mm(1, Length::millimetres); EXPECT_EQ(Length(1000, Length::micrometres), one_mm); EXPECT_EQ(1.0f, one_mm.as(Length::millimetres)); EXPECT_FLOAT_EQ(0.1f, one_mm.as(Length::centimetres)); EXPECT_FLOAT_EQ(0.039370079f, one_mm.as(Length::inches)); for (int n = 0; n < 10; ++n) { EXPECT_EQ(Length(1000*n, Length::micrometres), Length(n, Length::millimetres)); } } TEST(Length, centimetres) { Length one_cm(1, Length::centimetres); EXPECT_EQ(Length(10000, Length::micrometres), one_cm); EXPECT_EQ(Length(10, Length::millimetres), one_cm); EXPECT_EQ(1.0f, one_cm.as(Length::centimetres)); EXPECT_EQ(10.0f, one_cm.as(Length::millimetres)); EXPECT_FLOAT_EQ(0.39370079f, one_cm.as(Length::inches)); EXPECT_EQ(one_cm, 1_cm); EXPECT_EQ(one_cm, 1.0_cm); EXPECT_EQ(one_cm, 10_mm); EXPECT_EQ(one_cm, 10.0_mm); for (int n = 0; n < 10; ++n) { EXPECT_EQ(Length(10000*n, Length::micrometres), Length(n, Length::centimetres)); } } TEST(Length, inches) { Length one_in(1, Length::inches); EXPECT_EQ(Length(25400, Length::micrometres), one_in); EXPECT_FLOAT_EQ(2.54f, one_in.as(Length::centimetres)); EXPECT_EQ(one_in, 1.0_in); EXPECT_EQ(one_in, 2.54_cm); EXPECT_EQ(one_in, 25.4_mm); for (int n = 0; n < 10; ++n) { EXPECT_EQ(Length(25400*n, Length::micrometres), Length(n, Length::inches)); } } TEST(Length, DPI) { EXPECT_EQ(24, (0.25_in).as_pixels(96.0f)); EXPECT_FLOAT_EQ(3543.3070866f, (30_cm).as_pixels(300.0f)); EXPECT_EQ(123456, Length(123456, Length::inches).as_pixels(1.0f)); } TEST(Length, assign) { auto const a = 123.45_in; Length b; EXPECT_NE(a, b); EXPECT_EQ(0, b.as(Length::inches)); b = a; EXPECT_EQ(a, b); EXPECT_EQ(3135630, b.as(Length::micrometres)); } TEST(Length, copy) { auto const a = 123.45_in; Length b(a); EXPECT_EQ(a, b); EXPECT_EQ(3135630, b.as(Length::micrometres)); } ./tests/unit-tests/geometry/CMakeLists.txt0000644000015600001650000000065212676616125020770 0ustar jenkinsjenkinslist(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test-dimensions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test-size.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test-point.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test-displacement.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test-rectangle.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test-rectangles.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test-length.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) ./tests/unit-tests/geometry/test-size.cpp0000644000015600001650000000235612676616125020666 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/geometry/size.h" #include #include namespace geom = mir::geometry; TEST(geometry, size) { using namespace geom; Size const size2x4{2, 4}; EXPECT_EQ(Width(2), size2x4.width); EXPECT_EQ(Height(4), size2x4.height); Size const copy = size2x4; EXPECT_EQ(Width(2), copy.width); EXPECT_EQ(Height(4), copy.height); EXPECT_EQ(size2x4, copy); Size const defaultValue; EXPECT_EQ(Width(0), defaultValue.width); EXPECT_EQ(Height(0), defaultValue.height); EXPECT_NE(size2x4, defaultValue); } ./tests/unit-tests/test_shared_library_prober.cpp0000644000015600001650000001713012676616157022507 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/shared_library_prober.h" #include "mir_test_framework/executable_path.h" #include #include #include #include #include #include #include #include namespace mtf = mir_test_framework; namespace { class MockSharedLibraryProberReport : public mir::SharedLibraryProberReport { public: MOCK_METHOD1(probing_path, void(boost::filesystem::path const&)); MOCK_METHOD2(probing_failed, void(boost::filesystem::path const&, std::exception const&)); MOCK_METHOD1(loading_library, void(boost::filesystem::path const&)); MOCK_METHOD2(loading_failed, void(boost::filesystem::path const&, std::exception const&)); }; class SharedLibraryProber : public testing::Test { public: SharedLibraryProber() : library_path{mtf::test_data_path() + "/shared-libraries"} { // Can't use std::string, as mkdtemp mutates its argument. auto tmp_name = std::unique_ptr>{strdup("/tmp/mir_empty_directory_XXXXXX"), [](char* data) {free(data);}}; if (mkdtemp(tmp_name.get()) == NULL) { throw std::system_error{errno, std::system_category(), "Failed to create temporary directory"}; } temporary_directory = std::string{tmp_name.get()}; } ~SharedLibraryProber() { // Can't do anything useful in case of failure... rmdir(temporary_directory.c_str()); } std::string const library_path; std::string temporary_directory; testing::NiceMock null_report; }; } TEST_F(SharedLibraryProber, returns_non_empty_list_for_path_containing_libraries) { auto libraries = mir::libraries_for_path(library_path, null_report); EXPECT_GE(libraries.size(), 1); } TEST_F(SharedLibraryProber, raises_exception_for_nonexistent_path) { EXPECT_THROW(mir::libraries_for_path("/a/path/that/certainly/doesnt/exist", null_report), std::system_error); } TEST_F(SharedLibraryProber, exception_mentions_missing_path_name) { bool thrown = false; try { mir::libraries_for_path("/panacea", null_report); } catch (std::system_error& err) { thrown = true; EXPECT_THAT(err.what(), testing::HasSubstr("/panacea")); } EXPECT_TRUE(thrown); } TEST_F(SharedLibraryProber, non_existent_path_raises_ENOENT_error) { try { mir::libraries_for_path("/a/path/that/certainly/doesnt/exist", null_report); } catch (std::system_error &err) { EXPECT_EQ(err.code(), std::error_code(ENOENT, std::system_category())); } } TEST_F(SharedLibraryProber, path_with_no_shared_libraries_returns_empty_list) { auto libraries = mir::libraries_for_path(temporary_directory.c_str(), null_report); EXPECT_EQ(0, libraries.size()); } TEST_F(SharedLibraryProber, logs_start_of_probe) { using namespace testing; testing::NiceMock report; EXPECT_CALL(report, probing_path(testing::Eq(library_path))); mir::libraries_for_path(library_path, report); } TEST_F(SharedLibraryProber, logs_for_nonexistent_path) { using namespace testing; NiceMock report; EXPECT_CALL(report, probing_path(testing::Eq("/yo/dawg/I/heard/you/liked/slashes"))); EXPECT_THROW(mir::libraries_for_path("/yo/dawg/I/heard/you/liked/slashes", report), std::runtime_error); } TEST_F(SharedLibraryProber, logs_failure_for_nonexistent_path) { using namespace testing; NiceMock report; EXPECT_CALL(report, probing_failed(Eq("/yo/dawg/I/heard/you/liked/slashes"), _)); EXPECT_THROW(mir::libraries_for_path("/yo/dawg/I/heard/you/liked/slashes", report), std::runtime_error); } TEST_F(SharedLibraryProber, logs_no_libraries_for_path_without_libraries) { using namespace testing; NiceMock report; EXPECT_CALL(report, loading_library(_)).Times(0); mir::libraries_for_path(temporary_directory.c_str(), report); } namespace { MATCHER_P(FilenameMatches, matcher, "") { using namespace testing; *result_listener << "where the path is " << arg; return Matches(matcher)(arg.filename().native()); } } TEST_F(SharedLibraryProber, logs_each_library_probed) { using namespace testing; NiceMock report; auto const dso_filename_regex = ".*\\.so(\\..*)?"; // We have at least 8 DSOs to probe: // i386, amd64, armhf, arm64, powerpc, ppc64el, this-arch, libinvalid.so.3 EXPECT_CALL(report, loading_library(FilenameMatches(MatchesRegex(dso_filename_regex)))).Times(AtLeast(8)); // We shouldn't probe anything that doesn't look like a DSO. EXPECT_CALL(report, loading_library(FilenameMatches(Not(MatchesRegex(dso_filename_regex))))).Times(0); mir::libraries_for_path(library_path, report); } TEST_F(SharedLibraryProber, logs_failure_for_load_failure) { using namespace testing; NiceMock report; std::unordered_map probing_map; ON_CALL(report, loading_library(_)) .WillByDefault(Invoke([&probing_map](auto const& filename) { probing_map[filename.filename().native()] = true; })); ON_CALL(report, loading_failed(_,_)) .WillByDefault(Invoke([&probing_map](auto const& filename, auto const&) { probing_map[filename.filename().native()] = false; })); mir::libraries_for_path(library_path, report); EXPECT_FALSE(probing_map.at("libinvalid.so.3")); // As we add extra test DSOs you can add them to this list, but it's not mandatory. // At least one of these should fail on any conceivable architecture. EXPECT_THAT(probing_map, Contains(AnyOf( std::make_pair("lib386.so", false), std::make_pair("libamd64.so", false), std::make_pair("libarmhf.so", false), std::make_pair("libarm64.so", false), std::make_pair("libpowerpc.so", false), std::make_pair("libppc64el.so", false)))); } TEST_F(SharedLibraryProber, does_not_log_failure_on_success) { using namespace testing; NiceMock report; std::unordered_map probing_map; ON_CALL(report, loading_library(_)) .WillByDefault(Invoke([&probing_map](auto const& filename) { probing_map[filename.filename().native()] = true; })); ON_CALL(report, loading_failed(_,_)) .WillByDefault(Invoke([&probing_map](auto const& filename, auto const&) { probing_map[filename.filename().native()] = false; })); mir::libraries_for_path(library_path, report); // libthis-arch should always be loadable... EXPECT_TRUE(probing_map.at("libthis-arch.so")); } ./tests/unit-tests/test_thread_safe_list.cpp0000644000015600001650000000701012676616125021433 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/thread_safe_list.h" #include "mir/test/wait_condition.h" #include #include namespace { struct Dummy {}; using Element = std::shared_ptr; using SharedPtrList = mir::ThreadSafeList; struct ThreadSafeListTest : testing::Test { SharedPtrList list; Element const element1 = std::make_shared(); Element const element2 = std::make_shared(); }; } TEST_F(ThreadSafeListTest, can_remove_element_while_iterating_same_element) { using namespace testing; list.add(element1); list.for_each( [&] (Element const& element) { list.remove(element); }); int elements_seen = 0; list.for_each( [&] (Element const&) { ++elements_seen; }); EXPECT_THAT(elements_seen, Eq(0)); } TEST_F(ThreadSafeListTest, can_remove_unused_element_while_iterating_different_element) { using namespace testing; list.add(element1); list.add(element2); int elements_seen = 0; list.for_each( [&] (Element const&) { list.remove(element2); ++elements_seen; }); EXPECT_THAT(elements_seen, Eq(1)); } TEST_F(ThreadSafeListTest, can_remove_unused_element_while_different_element_is_used_in_different_thread) { using namespace testing; list.add(element1); list.add(element2); mir::test::WaitCondition first_element_in_use; mir::test::WaitCondition second_element_removed; int elements_seen = 0; std::thread t{ [&] { list.for_each( [&] (Element const&) { first_element_in_use.wake_up_everyone(); second_element_removed.wait_for_at_most_seconds(3); EXPECT_TRUE(second_element_removed.woken()); ++elements_seen; }); }}; first_element_in_use.wait_for_at_most_seconds(3); list.remove(element2); second_element_removed.wake_up_everyone(); t.join(); EXPECT_THAT(elements_seen, Eq(1)); } TEST_F(ThreadSafeListTest, removes_all_matching_elements) { using namespace testing; std::vector elements_seen; list.add(element1); list.add(element2); list.add(element1); list.remove_all(element1); list.for_each( [&] (Element const& element) { elements_seen.push_back(element); }); EXPECT_THAT(elements_seen, ElementsAre(element2)); } TEST_F(ThreadSafeListTest, clears_all_elements) { using namespace testing; int elements_seen = 0; list.add(element1); list.add(element2); list.add(element1); list.clear(); list.for_each( [&] (Element const&) { ++elements_seen; }); EXPECT_THAT(elements_seen, Eq(0)); } ./tests/privileged-tests/0000755000015600001650000000000012676616126015546 5ustar jenkinsjenkins./tests/privileged-tests/README0000644000015600001650000000017012676616125016423 0ustar jenkinsjenkinsmir_privileged_tests are tests that, depending on system configuration, may require special privileges to run properly. ./tests/privileged-tests/ui_get_sysname_ioctl_override.cpp0000644000015600001650000000746512676616125024371 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ // This file provides a custom implementation for the UI_GET_SYSNAME ioctl // request. Although libevent-uinput provides a fallback for platforms missing // this ioctl, the fallback doesn't work well with some of our ARM devices. In // particular, the fallback chooses the virtual device by checking for // devices that were created while we were creating the device, like: // // a = now // create device // b = now // ... // if (a <= device_creation_time <= b) choose device // // This is reasonable, but on some devices, for unknown reasons, the creation // time of the virtual device occasionally falls a bit before this interval. // // The custom implementation provided in this file uses similar logic but it's // less strict in its timing requirements. It chooses the first device it finds // that was created at most 2 seconds ago. // // Note that both approaches are racy in real conditions (e.g. other devices // could have been created while we are creating our device, hence the need for // the UI_GET_SYSNAME ioctl), but they are work reasonably well in a controlled // testing environment. #include #include #include #include #include #include #include #include namespace { time_t entry_creation_age(std::string const& parent_path, struct dirent* entry) { auto const full = parent_path + "/" + entry->d_name; struct stat sb; stat(full.c_str(), &sb); return time(nullptr) - sb.st_ctime; } std::string find_new_virtual_device() { std::string const path{"/sys/devices/virtual/input"}; time_t const max_creation_age = 2; auto dp = opendir(path.c_str()); if (dp == nullptr) return ""; struct dirent* entry; while ((entry = readdir(dp))) { if (entry_creation_age(path, entry) <= max_creation_age) break; } closedir(dp); if (entry) return entry->d_name; else return ""; } bool request_is_ui_get_sysname(unsigned long int request) { return static_cast(request & ~IOCSIZE_MASK) == static_cast(UI_GET_SYSNAME(0)); } } extern "C" int ioctl(int fd, unsigned long int request, ...) __THROW { va_list vargs; va_start(vargs, request); using ioctl_func = int(*)(int, unsigned long int, void*); static ioctl_func const real_ioctl = reinterpret_cast(dlsym(RTLD_NEXT, "ioctl")); void* arg = va_arg(vargs, void*); int result = real_ioctl(fd, request, arg); // If system doesn't support the UI_GET_SYSNAME ioctl, // try to service the request with our custom implementation if (result < 0 && request_is_ui_get_sysname(request)) { std::cout << "Note: Using custom implementation for the UI_GET_SYSNAME ioctl, since system doesn't support it." << std::endl; auto const carg = static_cast(arg); int const size = _IOC_SIZE(request); auto const vdev = find_new_virtual_device(); auto ncopied = vdev.copy(carg, size - 1); carg[ncopied] = '\0'; result = vdev.empty() ? -1 : 0; } va_end(vargs); return result; } ./tests/privileged-tests/CMakeLists.txt0000644000015600001650000000070612676616125020310 0ustar jenkinsjenkinsinclude_directories (${LIBEVDEV_INCLUDE_DIRS}) set( PRIVILEGED_TESTS_SOURCES test_input_events.cpp ui_get_sysname_ioctl_override.cpp ) mir_add_wrapped_executable( mir_privileged_tests ${PRIVILEGED_TESTS_SOURCES} ) add_dependencies(mir_privileged_tests GMock) target_link_libraries( mir_privileged_tests ${GTEST_BOTH_LIBRARIES} ${LIBEVDEV_LDFLAGS} ${LIBEVDEV_LIBRARIES} mir-test-static mir-test-framework-static mirclient ) ./tests/privileged-tests/test_input_events.cpp0000644000015600001650000002167212676616125022043 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/mir_client_library.h" #include "mir/test/wait_condition.h" #include "mir/test/spin_wait.h" #include "mir/test/event_matchers.h" #include "mir_test_framework/process.h" #include "mir_test_framework/executable_path.h" #include #include #include #include #include #include #include #include #include #include namespace mt = mir::test; namespace mtf = mir_test_framework; using namespace std::literals::chrono_literals; namespace { template using UPtrWithDeleter = std::unique_ptr; using MirConnectionUPtr = UPtrWithDeleter; using MirSurfaceUPtr = UPtrWithDeleter; using EvDevUPtr = UPtrWithDeleter; using EvDevUInputUPtr = UPtrWithDeleter; struct FakeInputDevice { FakeInputDevice() { auto const dev_raw = libevdev_new(); if (!dev_raw) throw std::runtime_error("Failed to create libevdev object"); dev.reset(dev_raw); libevdev_set_name(dev.get(), "test device"); libevdev_enable_event_type(dev.get(), EV_KEY); libevdev_enable_event_code(dev.get(), EV_KEY, KEY_A, nullptr); libevdev_uinput* uidev_raw{nullptr}; auto err = libevdev_uinput_create_from_device( dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev_raw); if (err != 0) throw std::runtime_error("Failed to create libevdev_uinput object"); uidev.reset(uidev_raw); } void emit_event(int type, int code, int value) { auto const err = libevdev_uinput_write_event(uidev.get(), type, code, value); if (err != 0) throw std::runtime_error("Failed to write event to uidev"); } void syn() { auto const err = libevdev_uinput_write_event(uidev.get(), EV_SYN, SYN_REPORT, 0); if (err != 0) throw std::runtime_error("Failed to write sync event to uidev"); } EvDevUPtr dev{nullptr, libevdev_free}; EvDevUInputUPtr uidev{nullptr, libevdev_uinput_destroy}; }; struct MockInputHandler { MOCK_METHOD1(handle_input, void(MirEvent const*)); }; // Template string: // X is replaced with random alphabetic character // # is replaced with pid std::string available_filename(std::string const& tmpl) { std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution random_char{'a','z'}; std::string s; struct stat sb; do { s.clear(); for (auto c : tmpl) { if (c == 'X') s += random_char(gen); else if (c == '#') s += std::to_string(getpid()); else s += c; } } while (stat(s.c_str(), &sb) != -1); return s; } struct InputEvents : testing::Test { std::string const host_socket{available_filename("/tmp/host_mir_socket_#_XXXXXX")}; std::string const nested_socket{available_filename("/tmp/nested_mir_socket_#_XXXXXX")}; std::string const server_path{mtf::executable_path() + "/mir_demo_server_minimal"}; InputEvents() { host_server = mtf::fork_and_run_in_a_different_process( [this] { execlp( server_path.c_str(), server_path.c_str(), "-f", host_socket.c_str(), nullptr); }, [] { return 0; }); wait_for_socket(host_socket); nested_server = mtf::fork_and_run_in_a_different_process( [this] { execlp( server_path.c_str(), server_path.c_str(), "-f", nested_socket.c_str(), "--host-socket", host_socket.c_str(), nullptr); }, [] { return 0; }); wait_for_socket(nested_socket); } static void handle_input(MirSurface*, MirEvent const* ev, void* context) { auto const handler = static_cast(context); auto const type = mir_event_get_type(ev); if (type == mir_event_type_input) handler->handle_input(ev); } void wait_for_socket(std::string const& path) { bool const success = mt::spin_wait_for_condition_or_timeout( [&] { struct stat sb; if (stat(path.c_str(), &sb) == -1) return false; return S_ISSOCK(sb.st_mode); }, std::chrono::seconds{5}); if (!success) throw std::runtime_error("Timeout waiting for socket to appear"); } void wait_for_surface_to_become_focused_and_exposed(MirSurface* surface) { bool const success = mt::spin_wait_for_condition_or_timeout( [&] { return mir_surface_get_visibility(surface) == mir_surface_visibility_exposed && mir_surface_get_focus(surface) == mir_surface_focused; }, std::chrono::seconds{5}); if (!success) throw std::runtime_error("Timeout waiting for surface to become focused and exposed"); } MirSurfaceUPtr create_surface_with_input_handler( MirConnection* connection, MockInputHandler* handler) { MirPixelFormat pixel_format; unsigned int valid_formats; mir_connection_get_available_surface_formats(connection, &pixel_format, 1, &valid_formats); auto spec = mir_connection_create_spec_for_normal_surface(connection, 640, 480, pixel_format); auto const surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); if (!mir_surface_is_valid(surface)) throw std::runtime_error("Failed to create MirSurface"); mir_surface_set_event_handler(surface, handle_input, handler); mir_buffer_stream_swap_buffers_sync( mir_surface_get_buffer_stream(surface)); wait_for_surface_to_become_focused_and_exposed(surface); return MirSurfaceUPtr(surface, mir_surface_release_sync); } static int const key_down = 1; static int const key_up = 0; std::shared_ptr host_server; std::shared_ptr nested_server; FakeInputDevice fake_keyboard; }; } TEST_F(InputEvents, reach_host_client) { using namespace testing; MockInputHandler handler; MirConnectionUPtr host_connection{ mir_connect_sync(host_socket.c_str(), "test"), mir_connection_release}; auto surface = create_surface_with_input_handler(host_connection.get(), &handler); mt::WaitCondition all_events_received; InSequence seq; EXPECT_CALL(handler, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_a)))); EXPECT_CALL(handler, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_a)))) .WillOnce(mt::WakeUp(&all_events_received)); fake_keyboard.emit_event(EV_KEY, KEY_A, key_down); fake_keyboard.emit_event(EV_KEY, KEY_A, key_up); fake_keyboard.syn(); all_events_received.wait_for_at_most_seconds(5); } TEST_F(InputEvents, reach_nested_client) { using namespace testing; MockInputHandler handler; MirConnectionUPtr nested_connection{ mir_connect_sync(nested_socket.c_str(), "test"), mir_connection_release}; auto surface = create_surface_with_input_handler(nested_connection.get(), &handler); // Give some time to the nested server to swap its framebuffer, so the nested // server window becomes visible and focused and can accept input events. // TODO: Find a more reliable way to wait for the nested server to gain focus std::this_thread::sleep_for(100ms); mt::WaitCondition all_events_received; InSequence seq; EXPECT_CALL(handler, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_a)))); EXPECT_CALL(handler, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_a)))) .WillOnce(mt::WakeUp(&all_events_received)); fake_keyboard.emit_event(EV_KEY, KEY_A, key_down); fake_keyboard.emit_event(EV_KEY, KEY_A, key_up); fake_keyboard.syn(); all_events_received.wait_for_at_most_seconds(5); } ./tests/acceptance-tests/0000755000015600001650000000000012676616160015500 5ustar jenkinsjenkins./tests/acceptance-tests/test_server_startup.cpp0000644000015600001650000000562012676616157022344 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths * Thomas Voss */ #include "mir_test_framework/interprocess_client_server_test.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/detect_server.h" #include #include #include #include namespace mf = mir::frontend; namespace mc = mir::compositor; namespace mtf = mir_test_framework; using ServerStartup = mtf::InterprocessClientServerTest; TEST_F(ServerStartup, creates_endpoint_on_filesystem) { ASSERT_FALSE(mtf::detect_server(mtf::test_socket_file(), std::chrono::milliseconds(0))); run_in_server([]{}); run_in_client([] { EXPECT_TRUE(mtf::detect_server(mtf::test_socket_file(), std::chrono::milliseconds(100))); }); } TEST_F(ServerStartup, after_server_sigkilled_can_start_new_instance) { ASSERT_FALSE(mtf::detect_server(mtf::test_socket_file(), std::chrono::milliseconds(0))); run_in_server([]{}); if (is_test_process()) { /* Under valgrind, raise(SIGKILL) results in a memcheck orphan process * we kill the server process from the test process instead */ EXPECT_TRUE(sigkill_server_process()); run_in_server([]{}); } run_in_client([] { EXPECT_TRUE(mtf::detect_server(mtf::test_socket_file(), std::chrono::milliseconds(100))); }); } TEST(ServerStartupReliability, DISABLED_can_start_with_low_entropy) { // Regression test for LP: #1536662 and LP: #1541188 using namespace ::testing; // Flush the entropy pool int fd = open("/dev/random", O_RDONLY | O_NONBLOCK); ASSERT_THAT(fd, Ge(0)); char buf[256]; while (read(fd, buf, sizeof buf) > 0) {} close(fd); auto start = std::chrono::high_resolution_clock::now(); struct Server : mtf::HeadlessInProcessServer { void TestBody() override {} } server; EXPECT_NO_THROW(server.SetUp();); auto duration = std::chrono::high_resolution_clock::now() - start; int seconds = std::chrono::duration_cast(duration).count(); EXPECT_THAT(seconds, Lt(15)); server.TearDown(); } ./tests/acceptance-tests/test_prompt_session_client_api.cpp0000644000015600001650000005521712676616157024536 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Nick Dedekind */ #include "mir_toolkit/mir_prompt_session.h" #include "mir/scene/prompt_session_listener.h" #include "mir/scene/prompt_session.h" #include "mir/scene/prompt_session_manager.h" #include "mir/scene/session.h" #include "mir/shell/shell_wrapper.h" #include "mir/frontend/session_credentials.h" #include "mir/cached_ptr.h" #include "mir/fd.h" #include "mir/test/doubles/stub_session_authorizer.h" #include "mir/test/doubles/mock_prompt_session_listener.h" #include "mir_test_framework/executable_path.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir/test/popen.h" #include #include #include #include #include namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace ms = mir::scene; namespace msh = mir::shell; namespace mf = mir::frontend; using namespace testing; namespace { struct MockSessionAuthorizer : public mtd::StubSessionAuthorizer { MockSessionAuthorizer() { ON_CALL(*this, connection_is_allowed(_)).WillByDefault(Return(true)); ON_CALL(*this, prompt_session_is_allowed(_)).WillByDefault(Return(true)); } MOCK_METHOD1(connection_is_allowed, bool(mf::SessionCredentials const&)); MOCK_METHOD1(prompt_session_is_allowed, bool(mf::SessionCredentials const&)); }; // We need to fake any client_pids used to identify sessions class PidFakingShell : public msh::ShellWrapper { public: PidFakingShell( std::shared_ptr const& wrapped, std::vector const& pids) : msh::ShellWrapper(wrapped), pids(pids) { } auto open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) -> std::shared_ptr override { auto const override_pid = (next != pids.end()) ? *next++ : client_pid; return wrapped->open_session(override_pid, name, sink); } private: std::vector const pids; std::vector::const_iterator next{pids.begin()}; }; struct PromptSessionClientAPI : mtf::HeadlessInProcessServer { MirConnection* connection = nullptr; static constexpr pid_t application_session_pid = __LINE__; std::shared_ptr application_session; MirConnection* application_connection{nullptr}; std::shared_ptr server_prompt_session; mtf::UsingStubClientPlatform using_stub_client_platform; mir::CachedPtr mock_prompt_session_listener; mir::CachedPtr mock_prompt_session_authorizer; std::atomic prompt_session_state_change_callback_called; std::shared_ptr the_mock_session_authorizer() { return mock_prompt_session_authorizer([this] { return std::make_shared>(); }); } std::shared_ptr the_mock_prompt_session_listener() { return mock_prompt_session_listener([] { return std::make_shared>(); }); } auto new_prompt_connection() -> std::string { auto const prompt_fd = server.open_prompt_socket(); return HeadlessInProcessServer::connection(prompt_fd); } void start_application_session() { std::mutex application_session_mutex; std::condition_variable application_session_cv; auto connect_handler = [&](std::shared_ptr const& session) { std::lock_guard lock(application_session_mutex); application_session = session; application_session_cv.notify_one(); }; auto const fd = server.open_client_socket(connect_handler); application_connection = mir_connect_sync(HeadlessInProcessServer::connection(fd).c_str(), __PRETTY_FUNCTION__); std::unique_lock lock(application_session_mutex); application_session_cv.wait(lock, [&] { return !!application_session; }); } void SetUp() override { auto shell_wrapper = [&](std::shared_ptr const& wrapped) -> std::shared_ptr { std::vector fake_pids; fake_pids.push_back(application_session_pid); return std::make_shared(wrapped, fake_pids); }; server.override_the_session_authorizer([this]() ->std::shared_ptr { return the_mock_session_authorizer(); }); server.override_the_prompt_session_listener([this] { return the_mock_prompt_session_listener(); }); server.wrap_shell(shell_wrapper); mtf::HeadlessInProcessServer::SetUp(); start_application_session(); } void capture_server_prompt_session() { EXPECT_CALL(*the_mock_prompt_session_listener(), starting(_)). WillOnce(SaveArg<0>(&server_prompt_session)); } void wait_for_state_change_callback() { for (int i = 0; !prompt_session_state_change_callback_called.load() && i < 5000; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::yield(); } prompt_session_state_change_callback_called.store(false); } void TearDown() override { application_session.reset(); if (application_connection) mir_connection_release(application_connection); if (connection) mir_connection_release(connection); mtf::HeadlessInProcessServer::TearDown(); } std::shared_ptr the_prompt_session_manager() { return server.the_prompt_session_manager(); } MOCK_METHOD2(prompt_session_state_change, void(MirPromptSession* prompt_provider, MirPromptSessionState state)); std::mutex mutex; std::vector actual_fds; std::condition_variable cv; bool called_back = false; bool wait_for_callback(std::chrono::milliseconds timeout) { std::unique_lock lock(mutex); return cv.wait_for(lock, timeout, [this]{ return called_back; }); } char const* fd_connect_string(int fd) { static char client_connect_string[32] = {0}; sprintf(client_connect_string, "fd://%d", fd); return client_connect_string; } MOCK_METHOD1(process_line, void(std::string const&)); std::vector> list_providers_for( std::shared_ptr const& prompt_session) { std::vector> results; auto providers_fn = [&results](std::weak_ptr const& session) { results.push_back(session.lock()); }; the_prompt_session_manager()->for_each_provider_in(prompt_session, providers_fn); return results; } static int const no_of_prompt_providers{2}; static constexpr char const* const provider_name[no_of_prompt_providers] { "child_provider0", "child_provider1" }; }; constexpr pid_t PromptSessionClientAPI::application_session_pid; mir_prompt_session_state_change_callback const null_state_change_callback{nullptr}; constexpr char const* const PromptSessionClientAPI::provider_name[]; extern "C" void prompt_session_state_change_callback( MirPromptSession* prompt_provider, MirPromptSessionState state, void* context) { PromptSessionClientAPI* self = static_cast(context); self->prompt_session_state_change(prompt_provider, state); self->prompt_session_state_change_callback_called.store(true); } void client_fd_callback(MirPromptSession*, size_t count, int const* fds, void* context) { auto const self = static_cast(context); std::unique_lockmutex)> lock(self->mutex); self->actual_fds.clear(); for (size_t i = 0; i != count; ++i) self->actual_fds.push_back(mir::Fd{fds[i]}); self->called_back = true; self->cv.notify_one(); } struct DummyPromptProvider { DummyPromptProvider(char const* connect_string, char const* app_name) : connection{mir_connect_sync(connect_string, app_name)} { EXPECT_THAT(connection, NotNull()); } ~DummyPromptProvider() noexcept { mir_connection_release(connection); } MirConnection* const connection; }; MATCHER_P(IsSessionWithPid, pid, "") { return arg->process_id() == pid; } MATCHER_P(SessionWithName, name, "") { return arg->name() == name; } } TEST_F(PromptSessionClientAPI, can_start_and_stop_a_prompt_session) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); { InSequence server_seq; EXPECT_CALL(*the_mock_prompt_session_listener(), starting(_)); EXPECT_CALL(*the_mock_prompt_session_listener(), stopping(_)); } MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); ASSERT_THAT(prompt_session, Ne(nullptr)); EXPECT_THAT(mir_prompt_session_is_valid(prompt_session), Eq(true)); EXPECT_THAT(mir_prompt_session_error_message(prompt_session), StrEq("")); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, notifies_start_and_stop) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); InSequence seq; EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_started)); EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_stopped)); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, prompt_session_state_change_callback, this); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, can_get_fds_for_prompt_providers) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); static std::size_t const arbritary_fd_request_count = 3; mir_prompt_session_new_fds_for_prompt_providers( prompt_session, arbritary_fd_request_count, &client_fd_callback, this); EXPECT_TRUE(wait_for_callback(std::chrono::milliseconds(500))); EXPECT_THAT(actual_fds.size(), Eq(arbritary_fd_request_count)); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, when_prompt_provider_connects_over_fd_prompt_provider_added_with_right_pid) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); mir_prompt_session_new_fds_for_prompt_providers( prompt_session, 1, &client_fd_callback, this); ASSERT_TRUE(wait_for_callback(std::chrono::milliseconds(500))); auto const expected_pid = getpid(); EXPECT_CALL(*the_mock_prompt_session_listener(), prompt_provider_added(_, IsSessionWithPid(expected_pid))); DummyPromptProvider{fd_connect_string(actual_fds[0]), __PRETTY_FUNCTION__}; mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, DISABLED_client_pid_is_associated_with_session) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto const server_pid = getpid(); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); mir_prompt_session_new_fds_for_prompt_providers(prompt_session, 1, &client_fd_callback, this); wait_for_callback(std::chrono::milliseconds(500)); EXPECT_CALL(*the_mock_prompt_session_listener(), prompt_provider_added(_, Not(IsSessionWithPid(server_pid)))); InSequence seq; EXPECT_CALL(*this, process_line(StrEq("Starting"))); EXPECT_CALL(*this, process_line(StrEq("Connected"))); EXPECT_CALL(*this, process_line(StrEq("Surface created"))); EXPECT_CALL(*this, process_line(StrEq("Surface released"))); EXPECT_CALL(*this, process_line(StrEq("Connection released"))); auto const command = mtf::executable_path() + "/mir_demo_client_basic -m" + fd_connect_string(actual_fds[0]); mir::test::Popen output(command); std::string line; while (output.get_line(line)) process_line(line); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, notifies_when_server_closes_prompt_session) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); { InSequence seq; EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_started)); EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_stopped)); } capture_server_prompt_session(); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, prompt_session_state_change_callback, this); wait_for_state_change_callback(); the_prompt_session_manager()->stop_prompt_session(server_prompt_session); wait_for_state_change_callback(); // Verify we have got the "stopped" notification before we go on and release the session Mock::VerifyAndClearExpectations(this); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, notifies_when_server_suspends_prompt_session) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); { InSequence seq; EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_started)); EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_suspended)); } capture_server_prompt_session(); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, prompt_session_state_change_callback, this); wait_for_state_change_callback(); the_prompt_session_manager()->suspend_prompt_session(server_prompt_session); wait_for_state_change_callback(); // Verify we have got the "suspend" notification before we go on and release the session Mock::VerifyAndClearExpectations(this); EXPECT_CALL(*this, prompt_session_state_change(_, mir_prompt_session_state_stopped)); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, after_server_closes_prompt_session_api_isnt_broken) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); capture_server_prompt_session(); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); the_prompt_session_manager()->stop_prompt_session(server_prompt_session); mir_wait_for(mir_prompt_session_new_fds_for_prompt_providers( prompt_session, no_of_prompt_providers, &client_fd_callback, this)); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, server_retrieves_application_session) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); capture_server_prompt_session(); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); EXPECT_THAT(the_prompt_session_manager()->application_for(server_prompt_session), Eq(application_session)); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, server_retrieves_helper_session) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); capture_server_prompt_session(); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); // can get the helper session. but it will be the current pid. EXPECT_THAT( the_prompt_session_manager()->helper_for(server_prompt_session), IsSessionWithPid(getpid())); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, server_retrieves_child_provider_sessions) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); capture_server_prompt_session(); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); mir_wait_for(mir_prompt_session_new_fds_for_prompt_providers( prompt_session, no_of_prompt_providers, &client_fd_callback, this)); DummyPromptProvider provider1{fd_connect_string(actual_fds[0]), provider_name[0]}; DummyPromptProvider provider2{fd_connect_string(actual_fds[1]), provider_name[1]}; EXPECT_THAT(list_providers_for(server_prompt_session), ElementsAre( SessionWithName(provider_name[0]), SessionWithName(provider_name[1]))); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, cannot_start_a_prompt_session_without_authorization) { EXPECT_CALL(*the_mock_session_authorizer(), prompt_session_is_allowed(_)) .WillOnce(Return(false)); connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_CALL(*the_mock_prompt_session_listener(), starting(_)).Times(0); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); EXPECT_THAT(mir_prompt_session_is_valid(prompt_session), Eq(false)); EXPECT_THAT(mir_prompt_session_error_message(prompt_session), HasSubstr("Prompt sessions disabled")); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, can_start_a_prompt_session_without_authorization_on_prompt_connection) { ON_CALL(*the_mock_session_authorizer(), prompt_session_is_allowed(_)) .WillByDefault(Return(false)); EXPECT_CALL(*the_mock_session_authorizer(), prompt_session_is_allowed(_)).Times(0); connection = mir_connect_sync(new_prompt_connection().c_str(), __PRETTY_FUNCTION__); { InSequence server_seq; EXPECT_CALL(*the_mock_prompt_session_listener(), starting(_)); EXPECT_CALL(*the_mock_prompt_session_listener(), stopping(_)); } MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); EXPECT_THAT(mir_prompt_session_is_valid(prompt_session), Eq(true)); EXPECT_THAT(mir_prompt_session_error_message(prompt_session), StrEq("")); mir_prompt_session_release_sync(prompt_session); } TEST_F(PromptSessionClientAPI, prompt_providers_started_via_trusted_socket_are_not_authorized_by_shell) { connection = mir_connect_sync(new_prompt_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_CALL(*the_mock_session_authorizer(), connection_is_allowed(_)).Times(0); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, application_session_pid, null_state_change_callback, this); mir_wait_for(mir_prompt_session_new_fds_for_prompt_providers( prompt_session, no_of_prompt_providers, &client_fd_callback, this)); DummyPromptProvider provider1{fd_connect_string(actual_fds[0]), provider_name[0]}; DummyPromptProvider provider2{fd_connect_string(actual_fds[1]), provider_name[1]}; mir_prompt_session_release_sync(prompt_session); } // lp:1377968 TEST_F(PromptSessionClientAPI, when_application_pid_is_invalid_starting_a_prompt_session_fails) { connection = mir_connect_sync(new_prompt_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_CALL(*the_mock_prompt_session_listener(), starting(_)).Times(0); MirPromptSession* prompt_session = mir_connection_create_prompt_session_sync( connection, ~application_session_pid, null_state_change_callback, this); EXPECT_THAT(mir_prompt_session_is_valid(prompt_session), Eq(false)); EXPECT_THAT(mir_prompt_session_error_message(prompt_session), HasSubstr("Could not identify application")); mir_prompt_session_release_sync(prompt_session); } // Test canary for kernel regression (also compiles as a standalone C test) #include #include #include #include #include #include #include #ifndef TEST int main() #else TEST(LP, DISABLED_1540731) #endif { enum { server, client, size }; int socket_fd[size]; int const opt = 1; assert(socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fd) == 0); char const msg[] = "A random message"; send(socket_fd[client], msg, sizeof msg, MSG_DONTWAIT | MSG_NOSIGNAL); assert(setsockopt(socket_fd[server], SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)) != -1); union { struct cmsghdr cmh; char control[CMSG_SPACE(sizeof(ucred))]; } control_un; control_un.cmh.cmsg_len = CMSG_LEN(sizeof(ucred)); control_un.cmh.cmsg_level = SOL_SOCKET; control_un.cmh.cmsg_type = SCM_CREDENTIALS; msghdr msgh; msgh.msg_name = NULL; msgh.msg_namelen = 0; msgh.msg_iov = NULL; msgh.msg_iovlen = 0; msgh.msg_control = control_un.control; msgh.msg_controllen = sizeof(control_un.control); errno = 0; #ifndef EXPECT_THAT if (recvmsg(socket_fd[server], &msgh, MSG_PEEK) == -1) { printf("Error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } else { printf("Success!\n"); exit(EXIT_SUCCESS); } #else EXPECT_THAT(recvmsg(socket_fd[server], &msgh, MSG_PEEK), Ne(-1)) << strerror(errno); close(socket_fd[server]); close(socket_fd[client]); #endif } ./tests/acceptance-tests/server_configuration_wrapping.cpp0000644000015600001650000001011412676616125024346 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/input/cursor_listener.h" #include "mir/shell/shell_wrapper.h" #include "mir/shell/surface_stack_wrapper.h" #include "mir/geometry/point.h" #include "mir_test_framework/headless_test.h" #include "mir_test_framework/executable_path.h" #include #include namespace mi = mir::input; namespace ms = mir::scene; namespace msh = mir::shell; namespace mtf = mir_test_framework; using namespace testing; namespace { struct MyShell : msh::ShellWrapper { using msh::ShellWrapper::ShellWrapper; MOCK_METHOD0(focus_next_session, void()); }; struct MyCursorListener : mi::CursorListener { MyCursorListener(std::shared_ptr const& wrapped) : wrapped{wrapped} {} MOCK_METHOD2(cursor_moved_to, void(float abs_x, float abs_y)); std::shared_ptr const wrapped; }; struct MySurfaceStack : msh::SurfaceStackWrapper { using msh::SurfaceStackWrapper::SurfaceStackWrapper; }; struct ServerConfigurationWrapping : mir_test_framework::HeadlessTest { void SetUp() override { server.wrap_shell([] (std::shared_ptr const& wrapped) { return std::make_shared(wrapped); }); server.wrap_cursor_listener([] (std::shared_ptr const& wrapped) { return std::make_shared(wrapped); }); server.wrap_surface_stack([] (std::shared_ptr const& wrapped) { return std::make_shared(wrapped); }); server.apply_settings(); shell = server.the_shell(); cursor_listener = server.the_cursor_listener(); surface_stack = server.the_surface_stack(); } std::shared_ptr shell; std::shared_ptr cursor_listener; std::shared_ptr surface_stack; }; } TEST_F(ServerConfigurationWrapping, shell_is_of_wrapper_type) { auto const my_shell = std::dynamic_pointer_cast(shell); EXPECT_THAT(my_shell, Ne(nullptr)); } TEST_F(ServerConfigurationWrapping, can_override_shell_methods) { auto const my_shell = std::dynamic_pointer_cast(shell); EXPECT_CALL(*my_shell, focus_next_session()).Times(1); shell->focus_next_session(); } TEST_F(ServerConfigurationWrapping, returns_same_shell_from_cache) { ASSERT_THAT(server.the_shell(), Eq(shell)); } TEST_F(ServerConfigurationWrapping, cursor_listener_is_of_wrapper_type) { auto const my_cursor_listener = std::dynamic_pointer_cast(cursor_listener); EXPECT_THAT(my_cursor_listener, Ne(nullptr)); } TEST_F(ServerConfigurationWrapping, can_override_cursor_listener_methods) { float const abs_x{1}; float const abs_y(2); auto const my_cursor_listener = std::dynamic_pointer_cast(cursor_listener); EXPECT_CALL(*my_cursor_listener, cursor_moved_to(abs_x, abs_y)).Times(1); cursor_listener->cursor_moved_to(abs_x, abs_y); } TEST_F(ServerConfigurationWrapping, returns_same_cursor_listener_from_cache) { ASSERT_THAT(server.the_cursor_listener(), Eq(cursor_listener)); } TEST_F(ServerConfigurationWrapping, surface_stack_is_of_wrapper_type) { auto const my_surface_stack = std::dynamic_pointer_cast(surface_stack); EXPECT_THAT(my_surface_stack, Ne(nullptr)); } ./tests/acceptance-tests/test_client_surface_events.cpp0000644000015600001650000003460012676616125023621 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Nick Dedekind */ #include "mir_toolkit/mir_client_library.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/graphics/display.h" #include "mir/shell/display_configuration_controller.h" #include "mir/test/event_matchers.h" #include "mir/test/doubles/wrap_shell_to_track_latest_surface.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir_test_framework/any_surface.h" #include "mir/test/signal.h" #include #include #include #include #include namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace mg = mir::graphics; using namespace testing; namespace { struct ClientSurfaceEvents : mtf::ConnectedClientWithASurface { MirSurface* other_surface; std::mutex last_event_mutex; MirEventType event_filter{mir_event_type_surface}; std::condition_variable last_event_cv; MirEvent const* last_event = nullptr; MirSurface* last_event_surface = nullptr; std::shared_ptr scene_surface; ~ClientSurfaceEvents() { if (last_event) mir_event_unref(last_event); } static void event_callback(MirSurface* surface, MirEvent const* event, void* ctx) { ClientSurfaceEvents* self = static_cast(ctx); std::lock_guardlast_event_mutex)> last_event_lock{self->last_event_mutex}; // Don't overwrite an interesting event with an uninteresting one! if (mir_event_get_type(event) != self->event_filter) return; if (self->last_event) mir_event_unref(self->last_event); self->last_event = mir_event_ref(event); self->last_event_surface = surface; self->last_event_cv.notify_one(); } bool wait_for_event(std::chrono::milliseconds delay) { std::unique_lock last_event_lock{last_event_mutex}; return last_event_cv.wait_for(last_event_lock, delay, [&] { return last_event_surface == surface && mir_event_get_type(last_event) == event_filter; }); } void set_event_filter(MirEventType type) { std::lock_guard last_event_lock{last_event_mutex}; event_filter = type; } void reset_last_event() { std::lock_guard last_event_lock{last_event_mutex}; if (last_event != nullptr) mir_event_unref(last_event); last_event = nullptr; last_event_surface = nullptr; } std::shared_ptr the_mock_shell() const { return mock_shell.lock(); } std::shared_ptr the_latest_surface() const { return the_mock_shell()->latest_surface.lock(); } void SetUp() override { server.wrap_shell([&](std::shared_ptr const& wrapped) -> std::shared_ptr { auto const msc = std::make_shared(wrapped); mock_shell = msc; return msc; }); mtf::ConnectedClientWithASurface::SetUp(); mir_surface_set_event_handler(surface, &event_callback, this); scene_surface = the_latest_surface(); other_surface = mtf::make_any_surface(connection); mir_surface_set_event_handler(other_surface, nullptr, nullptr); reset_last_event(); } void TearDown() override { mir_surface_release_sync(other_surface); scene_surface.reset(); mtf::ConnectedClientWithASurface::TearDown(); } std::weak_ptr mock_shell; }; } TEST_F(ClientSurfaceEvents, surface_receives_state_events) { { mir_wait_for(mir_surface_set_state(surface, mir_surface_state_fullscreen)); mir_wait_for(mir_surface_set_state(other_surface, mir_surface_state_vertmaximized)); std::lock_guard last_event_lock{last_event_mutex}; EXPECT_THAT(last_event, mt::SurfaceEvent(mir_surface_attrib_state, mir_surface_state_fullscreen)); } { mir_wait_for(mir_surface_set_state(surface, static_cast(999))); std::lock_guard last_event_lock{last_event_mutex}; EXPECT_THAT(last_event, mt::SurfaceEvent(mir_surface_attrib_state, mir_surface_state_fullscreen)); } reset_last_event(); { mir_wait_for(mir_surface_set_state(surface, mir_surface_state_vertmaximized)); std::lock_guard last_event_lock{last_event_mutex}; EXPECT_THAT(last_event, mt::SurfaceEvent(mir_surface_attrib_state, mir_surface_state_vertmaximized)); } reset_last_event(); { mir_wait_for(mir_surface_set_state(surface, static_cast(777))); mir_wait_for(mir_surface_set_state(other_surface, mir_surface_state_maximized)); std::lock_guard last_event_lock{last_event_mutex}; EXPECT_EQ(nullptr, last_event); } } struct OrientationEvents : ClientSurfaceEvents, ::testing::WithParamInterface {}; TEST_P(OrientationEvents, surface_receives_orientation_events) { set_event_filter(mir_event_type_orientation); auto const direction = GetParam(); scene_surface->set_orientation(direction); EXPECT_TRUE(wait_for_event(std::chrono::seconds(1))); std::lock_guard last_event_lock{last_event_mutex}; EXPECT_THAT(last_event, mt::OrientationEvent(direction)); } INSTANTIATE_TEST_CASE_P(ClientSurfaceEvents, OrientationEvents, Values(mir_orientation_normal, mir_orientation_left, mir_orientation_inverted, mir_orientation_right)); TEST_F(ClientSurfaceEvents, client_can_query_current_orientation) { set_event_filter(mir_event_type_orientation); for (auto const direction: {mir_orientation_normal, mir_orientation_left, mir_orientation_inverted, mir_orientation_right, mir_orientation_normal, mir_orientation_inverted, mir_orientation_left, mir_orientation_inverted, mir_orientation_right}) { reset_last_event(); scene_surface->set_orientation(direction); EXPECT_TRUE(wait_for_event(std::chrono::seconds(1))); EXPECT_THAT(mir_surface_get_orientation(surface), Eq(direction)); } } TEST_F(ClientSurfaceEvents, surface_receives_close_event) { set_event_filter(mir_event_type_close_surface); scene_surface->request_client_surface_close(); EXPECT_TRUE(wait_for_event(std::chrono::seconds(1))); std::lock_guard last_event_lock{last_event_mutex}; EXPECT_THAT(last_event_surface, Eq(surface)); EXPECT_THAT(mir_event_get_type(last_event), Eq(mir_event_type_close_surface)); } TEST_F(ClientSurfaceEvents, client_can_query_preferred_orientation) { for (auto const mode: {mir_orientation_mode_portrait, mir_orientation_mode_portrait_inverted, mir_orientation_mode_landscape, mir_orientation_mode_landscape_inverted, mir_orientation_mode_portrait_any, mir_orientation_mode_landscape_any, mir_orientation_mode_any}) { reset_last_event(); mir_wait_for(mir_surface_set_preferred_orientation(surface, mode)); EXPECT_THAT(mir_surface_get_preferred_orientation(surface), Eq(mode)); } } TEST_F(ClientSurfaceEvents, surface_receives_output_event_when_configuration_changes) { using namespace std::literals::chrono_literals; auto constexpr form_factor = mir_form_factor_tablet; float constexpr scale = 2.15f; auto display_configuration = server.the_display()->configuration(); display_configuration->for_each_output( [](mg::UserDisplayConfigurationOutput& output_config) { output_config.scale = scale; output_config.form_factor = form_factor; }); set_event_filter(mir_event_type_surface_output); reset_last_event(); auto display_controller = server.the_display_configuration_controller(); display_controller->set_base_configuration(std::move(display_configuration)); ASSERT_TRUE(wait_for_event(1min)); std::lock_guard last_event_lock{last_event_mutex}; EXPECT_THAT(mir_event_get_type(last_event), Eq(mir_event_type_surface_output)); auto output_event = mir_event_get_surface_output_event(last_event); EXPECT_THAT(mir_surface_output_event_get_form_factor(output_event), Eq(form_factor)); EXPECT_THAT(mir_surface_output_event_get_scale(output_event), FloatEq(scale)); } namespace { class WrapShellGeneratingCloseEvent : public mir::shell::ShellWrapper { using mir::shell::ShellWrapper::ShellWrapper; mir::frontend::SurfaceId create_surface( std::shared_ptr const& session, mir::scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override { auto const surface = mir::shell::ShellWrapper::create_surface(session, params, sink); session->surface(surface)->request_client_surface_close(); return surface; } }; class ClientSurfaceStartupEvents : public mtf::ConnectedClientHeadlessServer { void SetUp() override { server.wrap_shell([&](std::shared_ptr const& wrapped) -> std::shared_ptr { return std::make_shared(wrapped); }); mtf::ConnectedClientHeadlessServer::SetUp(); } }; void raise_signal_on_close_event(MirSurface*, MirEvent const* ev, void* ctx) { if (mir_event_get_type(ev) == mir_event_type_close_surface) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" mir_event_get_close_surface_event(ev); #pragma GCC diagnostic pop auto signal = reinterpret_cast(ctx); signal->raise(); } } } TEST_F(ClientSurfaceStartupEvents, receives_event_sent_during_surface_construction) { mt::Signal done; auto spec = mir_connection_create_spec_for_normal_surface(connection, 100, 100, mir_pixel_format_abgr_8888); mir_surface_spec_set_event_handler(spec, &raise_signal_on_close_event, &done); auto surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); /* This expectation will fail if the event generated during surface creation is * sent before the create_surface reply. * * In that case, libmirclient first receives a close_surface event for a surface * it doesn't know about, throws it away, and then receives the SurfaceID of the * surface it just created. */ EXPECT_TRUE(done.wait_for(std::chrono::seconds{10})); mir_surface_release_sync(surface); } struct EventContext { EventContext() : event{nullptr} { } ~EventContext() { if (event != nullptr) mir_event_unref(event); } mt::Signal captured; MirEvent const* event; }; void surface_output_capturing_callback(MirSurface*, MirEvent const* ev, void* ctx) { if (mir_event_get_type(ev) == mir_event_type_surface_output) { auto out_event = reinterpret_cast(ctx); out_event->event = mir_event_ref(ev); out_event->captured.raise(); } } TEST_F(ClientSurfaceEvents, surface_receives_output_event_on_creation) { using namespace std::literals::chrono_literals; auto constexpr form_factor = mir_form_factor_tablet; float constexpr scale = 2.15f; std::vector display_ids; { mt::Signal display_config_changed; auto const callback = [](MirConnection*, void* context) { static_cast(context)->raise(); }; mir_connection_set_display_config_change_callback(connection, callback, &display_config_changed); std::shared_ptr const display_configuration{server.the_display()->configuration()}; display_configuration->for_each_output( [&display_ids](mg::UserDisplayConfigurationOutput& output_config) { output_config.scale = scale; output_config.form_factor = form_factor; display_ids.push_back(static_cast(output_config.id.as_value())); }); set_event_filter(mir_event_type_surface_output); reset_last_event(); auto const display_controller = server.the_display_configuration_controller(); display_controller->set_base_configuration(display_configuration); ASSERT_TRUE(display_config_changed.wait_for(1s)); //Wait until the existing surface has received the surface output event //to avoid racing against this source output event notification and the //one given during surface creation. ASSERT_TRUE(wait_for_event(1s)); } EventContext context; auto spec = mir_connection_create_spec_for_normal_surface(connection, 640, 480, mir_pixel_format_abgr_8888); mir_surface_spec_set_event_handler(spec, &surface_output_capturing_callback, &context); auto surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); ASSERT_TRUE(context.captured.wait_for(10s)); ASSERT_THAT(mir_event_get_type(context.event), Eq(mir_event_type_surface_output)); auto surface_event = mir_event_get_surface_output_event(context.event); EXPECT_THAT(mir_surface_output_event_get_form_factor(surface_event), Eq(form_factor)); EXPECT_THAT(mir_surface_output_event_get_scale(surface_event), Eq(scale)); EXPECT_THAT(display_ids, Contains(Eq(mir_surface_output_event_get_output_id(surface_event)))); mir_surface_release_sync(surface); } ./tests/acceptance-tests/test_client_surfaces.cpp0000644000015600001650000003066312676616125022425 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/debug/surface.h" #include "mir/test/doubles/mock_window_manager.h" #include "mir/scene/session.h" #include "mir/geometry/rectangle.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir_test_framework/any_surface.h" #include "mir/test/validity_matchers.h" #include "mir/test/fake_shared.h" #include #include #include #include namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace msh = mir::shell; namespace ms = mir::scene; namespace mt = mir::test; namespace mg = mir::geometry; namespace { struct SurfaceSync { void surface_created(MirSurface * new_surface) { std::unique_lock lock(guard); surface = new_surface; wait_condition.notify_all(); } void surface_released(MirSurface * /*released_surface*/) { std::unique_lock lock(guard); surface = nullptr; wait_condition.notify_all(); } void wait_for_surface_create() { std::unique_lock lock(guard); wait_condition.wait(lock, [&]{ return !!surface; }); } void wait_for_surface_release() { std::unique_lock lock(guard); wait_condition.wait(lock, [&]{ return !surface; }); } std::mutex guard; std::condition_variable wait_condition; MirSurface * surface{nullptr}; }; struct ClientSurfaces : mtf::ConnectedClientHeadlessServer { static const int max_surface_count = 5; SurfaceSync ssync[max_surface_count]; void SetUp() override { server.override_the_window_manager_builder([this](msh::FocusController*) { return mt::fake_shared(window_manager); }); ConnectedClientHeadlessServer::SetUp(); } testing::NiceMock window_manager; }; extern "C" void create_surface_callback(MirSurface* surface, void * context) { SurfaceSync* config = reinterpret_cast(context); config->surface_created(surface); } extern "C" void release_surface_callback(MirSurface* surface, void * context) { SurfaceSync* config = reinterpret_cast(context); config->surface_released(surface); } void wait_for_surface_create(SurfaceSync* context) { context->wait_for_surface_create(); } void wait_for_surface_release(SurfaceSync* context) { context->wait_for_surface_release(); } } TEST_F(ClientSurfaces, are_created_with_correct_size) { int width_1 = 640, height_1 = 480, width_2 = 1600, height_2 = 1200; auto spec = mir_connection_create_spec_for_normal_surface(connection, width_1, height_1, mir_pixel_format_abgr_8888); mir_surface_create(spec, create_surface_callback, ssync); wait_for_surface_create(ssync); mir_surface_spec_set_width(spec, width_2); mir_surface_spec_set_height(spec, height_2); mir_surface_create(spec, create_surface_callback, ssync+1); wait_for_surface_create(ssync+1); mir_surface_spec_release(spec); MirSurfaceParameters response_params; mir_surface_get_parameters(ssync->surface, &response_params); EXPECT_EQ(640, response_params.width); EXPECT_EQ(480, response_params.height); EXPECT_EQ(mir_pixel_format_abgr_8888, response_params.pixel_format); EXPECT_EQ(mir_buffer_usage_hardware, response_params.buffer_usage); mir_surface_get_parameters(ssync[1].surface, &response_params); EXPECT_EQ(1600, response_params.width); EXPECT_EQ(1200, response_params.height); EXPECT_EQ(mir_pixel_format_abgr_8888, response_params.pixel_format); EXPECT_EQ(mir_buffer_usage_hardware, response_params.buffer_usage); mir_surface_release(ssync[1].surface, release_surface_callback, ssync+1); wait_for_surface_release(ssync+1); mir_surface_release(ssync->surface, release_surface_callback, ssync); wait_for_surface_release(ssync); } TEST_F(ClientSurfaces, have_distinct_ids) { auto surface_1 = mtf::make_any_surface(connection); auto surface_2 = mtf::make_any_surface(connection); EXPECT_NE(mir_debug_surface_id(surface_1), mir_debug_surface_id(surface_2)); mir_surface_release_sync(surface_1); mir_surface_release_sync(surface_2); } TEST_F(ClientSurfaces, creates_need_not_be_serialized) { for (int i = 0; i != max_surface_count; ++i) { auto spec = mir_connection_create_spec_for_normal_surface(connection, 1, 1, mir_pixel_format_abgr_8888); mir_surface_create(spec, create_surface_callback, ssync+i); mir_surface_spec_release(spec); } for (int i = 0; i != max_surface_count; ++i) wait_for_surface_create(ssync+i); for (int i = 0; i != max_surface_count; ++i) { for (int j = 0; j != max_surface_count; ++j) { if (i == j) EXPECT_EQ( mir_debug_surface_id(ssync[i].surface), mir_debug_surface_id(ssync[j].surface)); else EXPECT_NE( mir_debug_surface_id(ssync[i].surface), mir_debug_surface_id(ssync[j].surface)); } } for (int i = 0; i != max_surface_count; ++i) mir_surface_release(ssync[i].surface, release_surface_callback, ssync+i); for (int i = 0; i != max_surface_count; ++i) wait_for_surface_release(ssync+i); } struct WithOrientation : ClientSurfaces, ::testing::WithParamInterface {}; TEST_P(WithOrientation, have_requested_preferred_orientation) { auto spec = mir_connection_create_spec_for_normal_surface(connection, 1, 1, mir_pixel_format_abgr_8888); ASSERT_THAT(spec, NotNull()); MirOrientationMode mode{GetParam()}; mir_surface_spec_set_preferred_orientation(spec, mode); auto surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); ASSERT_THAT(surface, IsValid()); EXPECT_EQ(mir_surface_get_preferred_orientation(surface), mode); mir_surface_release_sync(surface); } INSTANTIATE_TEST_CASE_P(ClientSurfaces, WithOrientation, ::testing::Values( mir_orientation_mode_portrait, mir_orientation_mode_landscape, mir_orientation_mode_portrait_inverted, mir_orientation_mode_landscape_inverted, mir_orientation_mode_portrait_any, mir_orientation_mode_landscape_any, mir_orientation_mode_any)); TEST_F(ClientSurfaces, can_be_menus) { auto parent = mtf::make_any_surface(connection); MirRectangle attachment_rect{100, 200, 100, 100}; auto spec = mir_connection_create_spec_for_menu(connection, 640, 480, mir_pixel_format_abgr_8888, parent, &attachment_rect, mir_edge_attachment_vertical); ASSERT_THAT(spec, NotNull()); auto menu = mir_surface_create_sync(spec); mir_surface_spec_release(spec); ASSERT_THAT(menu, IsValid()); EXPECT_EQ(mir_surface_get_type(menu), mir_surface_type_menu); mir_surface_release_sync(parent); mir_surface_release_sync(menu); } TEST_F(ClientSurfaces, can_be_tooltips) { auto parent = mtf::make_any_surface(connection); MirRectangle zone_rect{100, 200, 100, 100}; auto spec = mir_connection_create_spec_for_tooltip(connection, 640, 480, mir_pixel_format_abgr_8888, parent, &zone_rect); ASSERT_THAT(spec, NotNull()); auto tooltip = mir_surface_create_sync(spec); mir_surface_spec_release(spec); ASSERT_THAT(tooltip, IsValid()); EXPECT_EQ(mir_surface_get_type(tooltip), mir_surface_type_tip); mir_surface_release_sync(parent); mir_surface_release_sync(tooltip); } TEST_F(ClientSurfaces, can_be_dialogs) { auto spec = mir_connection_create_spec_for_dialog(connection, 640, 480, mir_pixel_format_abgr_8888); ASSERT_THAT(spec, NotNull()); auto dialog = mir_surface_create_sync(spec); mir_surface_spec_release(spec); ASSERT_THAT(dialog, IsValid()); EXPECT_EQ(mir_surface_get_type(dialog), mir_surface_type_dialog); mir_surface_release_sync(dialog); } TEST_F(ClientSurfaces, can_be_modal_dialogs) { auto parent = mtf::make_any_surface(connection); auto spec = mir_connection_create_spec_for_modal_dialog(connection, 640, 480, mir_pixel_format_abgr_8888, parent); ASSERT_THAT(spec, NotNull()); auto dialog = mir_surface_create_sync(spec); mir_surface_spec_release(spec); ASSERT_THAT(dialog, IsValid()); EXPECT_EQ(mir_surface_get_type(dialog), mir_surface_type_dialog); mir_surface_release_sync(parent); mir_surface_release_sync(dialog); } TEST_F(ClientSurfaces, can_be_input_methods) { auto spec = mir_connection_create_spec_for_input_method(connection, 640, 480, mir_pixel_format_abgr_8888); ASSERT_THAT(spec, NotNull()); auto im = mir_surface_create_sync(spec); mir_surface_spec_release(spec); EXPECT_EQ(mir_surface_get_type(im), mir_surface_type_inputmethod); mir_surface_release_sync(im); } TEST_F(ClientSurfaces, can_be_renamed) { auto spec = mir_connection_create_spec_for_normal_surface( connection, 123, 456, mir_pixel_format_abgr_8888); ASSERT_THAT(spec, NotNull()); auto surf = mir_surface_create_sync(spec); mir_surface_spec_release(spec); /* * Generally no windowing system ever censors window names. They are * freeform strings set by the app. * * We do lack a getter to be able to read the name back and verify * these, but really don't care -- such a function is not required * right now. Although might be in future to support some toolkits. * * At least verify the rename completes without blocking... */ spec = mir_connection_create_spec_for_changes(connection); ASSERT_THAT(spec, NotNull()); mir_surface_spec_set_name(spec, "New Name"); mir_surface_apply_spec(surf, spec); mir_surface_spec_set_name(spec, ""); mir_surface_apply_spec(surf, spec); mir_surface_spec_set_name(spec, "Alice"); mir_surface_apply_spec(surf, spec); mir_surface_spec_release(spec); mir_surface_release_sync(surf); } TEST_F(ClientSurfaces, input_methods_get_corret_parent_coordinates) { using namespace testing; mg::Rectangle const server_rect{mg::Point{100, 100}, mg::Size{10, 10}}; MirRectangle client_rect{ 100, 100, 10, 10 }; auto const edge_attachment = mir_edge_attachment_vertical; std::shared_ptr parent_surface; InSequence seq; EXPECT_CALL(window_manager, add_surface(_,_,_)).WillOnce(Invoke([&parent_surface](auto session, auto params, auto builder) { auto id = builder(session, params); parent_surface = session->surface(id); return id; })); EXPECT_CALL(window_manager, add_surface(_,_,_)).WillOnce(Invoke([&parent_surface, server_rect, edge_attachment](auto session, auto params, auto builder) { EXPECT_THAT(params.parent.lock(), Eq(parent_surface)); EXPECT_TRUE(params.aux_rect.is_set()); EXPECT_THAT(params.aux_rect.value(), Eq(server_rect)); EXPECT_TRUE(params.edge_attachment.is_set()); EXPECT_THAT(params.edge_attachment.value(), Eq(edge_attachment)); return builder(session, params); })); auto surface = mtf::make_any_surface(connection); auto parent_id = mir_surface_request_persistent_id_sync(surface); auto im_connection = mir_connect_sync(new_connection().c_str(), "Mock IM connection"); ASSERT_THAT(im_connection, IsValid()); auto spec = mir_connection_create_spec_for_input_method(im_connection, 100, 20, mir_pixel_format_abgr_8888); ASSERT_THAT(spec, NotNull()); mir_surface_spec_attach_to_foreign_parent(spec, parent_id, &client_rect, edge_attachment); mir_persistent_id_release(parent_id); auto im = mir_surface_create_sync(spec); mir_surface_spec_release(spec); mir_surface_release_sync(im); mir_surface_release_sync(surface); mir_connection_release(im_connection); } ./tests/acceptance-tests/test_server_without_active_outputs.cpp0000644000015600001650000000304212676616125025472 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir_test_framework/any_surface.h" #include "mir/geometry/rectangle.h" #include #include namespace mtf = mir_test_framework; namespace geom = mir::geometry; namespace { struct ServerWithoutActiveOutputs : mtf::ConnectedClientHeadlessServer { void SetUp() override { initial_display_layout(std::vector{}); mtf::ConnectedClientHeadlessServer::SetUp(); } }; } TEST_F(ServerWithoutActiveOutputs, creates_valid_client_surface) { using namespace testing; auto const surface = mtf::make_any_surface(connection); EXPECT_THAT(mir_surface_is_valid(surface), Eq(true)) << mir_surface_get_error_message(surface); mir_surface_release_sync(surface); } ./tests/acceptance-tests/test_client_library_callbacks.cpp0000644000015600001650000001247312676616125024254 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/any_surface.h" #include "mir/test/spin_wait.h" #include #include #include #include #include namespace mt = mir::test; namespace mtf = mir_test_framework; using namespace std::chrono_literals; namespace { struct ClientLibraryCallbacks : mtf::HeadlessInProcessServer { std::atomic connection{nullptr}; std::atomic surface{nullptr}; std::atomic buffers{0}; void TearDown() override { if (surface) mir_surface_release_sync(surface); if (connection) mir_connection_release(connection); mtf::HeadlessInProcessServer::TearDown(); } static void connection_callback(MirConnection* connection, void* context) { auto const config = reinterpret_cast(context); config->connected(connection); } static void create_surface_callback(MirSurface* surface, void* context) { auto const config = reinterpret_cast(context); config->surface_created(surface); } static void next_buffer_callback(MirBufferStream* bs, void* context) { auto const config = reinterpret_cast(context); config->next_buffer(bs); } static void release_surface_callback(MirSurface* surface, void* context) { auto const config = reinterpret_cast(context); config->surface_released(surface); } virtual void connected(MirConnection* conn) { std::this_thread::sleep_for(10ms); connection = conn; } virtual void surface_created(MirSurface* new_surface) { std::this_thread::sleep_for(10ms); surface = new_surface; } virtual void next_buffer(MirBufferStream*) { std::this_thread::sleep_for(10ms); ++buffers; } void surface_released(MirSurface*) { std::this_thread::sleep_for(10ms); surface = nullptr; } mtf::UsingStubClientPlatform using_stub_client_platform; }; } using namespace testing; TEST_F(ClientLibraryCallbacks, connect_callback_is_called_before_wait_handler_has_result) { auto const wh = mir_connect( new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, this); mir_wait_for(wh); EXPECT_THAT(connection.load(), NotNull()); // Even if the test fails, wait for object to become ready so we can // tear down properly mt::spin_wait_for_condition_or_timeout( [this] { return connection != nullptr; }, 3s); } TEST_F(ClientLibraryCallbacks, create_surface_callback_is_called_before_wait_handler_has_result) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto const spec = mir_connection_create_spec_for_normal_surface( connection, 100, 100, mir_pixel_format_argb_8888); auto const wh = mir_surface_create(spec, create_surface_callback, this); mir_surface_spec_release(spec); mir_wait_for(wh); EXPECT_THAT(surface.load(), NotNull()); // Even if the test fails, wait for object to become ready so we can // tear down properly mt::spin_wait_for_condition_or_timeout( [this] { return surface != nullptr; }, 3s); } TEST_F(ClientLibraryCallbacks, swap_buffers_callback_is_called_before_wait_handler_has_result) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); surface = mtf::make_any_surface(connection); auto const wh = mir_buffer_stream_swap_buffers( mir_surface_get_buffer_stream(surface), next_buffer_callback, this); mir_wait_for(wh); EXPECT_THAT(buffers, Eq(1)); // Even if the test fails, wait for object to become ready so we can // tear down properly mt::spin_wait_for_condition_or_timeout( [this] { return buffers == 1; }, 3s); } TEST_F(ClientLibraryCallbacks, release_surface_callback_is_called_before_wait_handler_has_result) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); surface = mtf::make_any_surface(connection); auto const wh = mir_surface_release(surface, release_surface_callback, this); mir_wait_for(wh); EXPECT_THAT(surface.load(), IsNull()); // Even if the test fails, wait for object to become ready so we can // tear down properly mt::spin_wait_for_condition_or_timeout( [this] { return surface == nullptr; }, 3s); } ./tests/acceptance-tests/test_client_header_version.cpp0000644000015600001650000000552512676616125023606 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include #include TEST(ClientHeaderVersion, mir_client_version_ge_is_sane) { // We have to be greater than 0.0.0 EXPECT_TRUE(MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(0, 0, 0)); // Our client API has been stable for a while; we're past 1.0.0 EXPECT_TRUE(MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(1, 0, 0)); // We should be greater than or equal to our current version EXPECT_TRUE(MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION, MIR_CLIENT_MINOR_VERSION, MIR_CLIENT_MICRO_VERSION)); EXPECT_FALSE(MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION + 1, 0, 0)); EXPECT_FALSE(MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION, MIR_CLIENT_MINOR_VERSION + 1, 0)); EXPECT_FALSE(MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION, MIR_CLIENT_MINOR_VERSION, MIR_CLIENT_MICRO_VERSION + 1)); } TEST(ClientHeaderVersion, mir_client_version_ge_is_usable_in_preprocessor) { #if MIR_CLIENT_VERSION < MIR_VERSION_NUMBER(0, 0, 0) FAIL() << "MIR_CLIENT_VERSION < MIR_VERSION_NUMBER(0, 0, 0)"; #endif #if MIR_CLIENT_VERSION < MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION, MIR_CLIENT_MINOR_VERSION, MIR_CLIENT_MICRO_VERSION) FAIL() << "MIR_CLIENT_VERSION < MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION, MIR_CLIENT_MINOR_VERSION, MIR_CLIENT_MICRO_VERSION)"; #endif #if MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION + 1, 0, 0) FAIL() << "MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION + 1, 0, 0)"; #endif #if MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION, MIR_CLIENT_MINOR_VERSION + 1, 0) FAIL() << "MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION, MIR_CLIENT_MINOR_VERSION + 1, 0)"; #endif #if MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION, MIR_CLIENT_MINOR_VERSION, MIR_CLIENT_MICRO_VERSION + 1) FAIL() << "MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION, MIR_CLIENT_MINOR_VERSION, MIR_CLIENT_MICRO_VERSION + 1)"; #endif } ./tests/acceptance-tests/test_surface_raise.cpp0000644000015600001650000002020512676616125022056 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Brandon Schaefer */ #include "mir/input/input_device_info.h" #include "mir_test_framework/fake_input_device.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir_test_framework/any_surface.h" #include "mir/test/wait_condition.h" #include "mir/test/spin_wait.h" #include "mir/cookie/authority.h" #include "mir_toolkit/mir_client_library.h" #include #include #include #include namespace mtf = mir_test_framework; namespace mt = mir::test; namespace mi = mir::input; namespace mis = mir::input::synthesis; namespace { std::chrono::seconds const max_wait{4}; void cookie_capturing_callback(MirSurface* surface, MirEvent const* ev, void* ctx); } struct RaiseSurfaces : mtf::ConnectedClientHeadlessServer { void SetUp() override { ConnectedClientHeadlessServer::SetUp(); surface1 = mtf::make_any_surface(connection); mir_surface_set_event_handler(surface1, &cookie_capturing_callback, this); mir_buffer_stream_swap_buffers_sync( mir_surface_get_buffer_stream(surface1)); surface2 = mtf::make_any_surface(connection); mir_surface_set_event_handler(surface2, &cookie_capturing_callback, this); mir_buffer_stream_swap_buffers_sync( mir_surface_get_buffer_stream(surface2)); // Need fullscreen for the cursor events auto const spec = mir_connection_create_spec_for_changes(connection); mir_surface_spec_set_fullscreen_on_output(spec, 1); mir_surface_apply_spec(surface1, spec); mir_surface_apply_spec(surface2, spec); mir_surface_spec_release(spec); bool surface_fullscreen = mt::spin_wait_for_condition_or_timeout( [this] { return mir_surface_get_state(surface1) == mir_surface_state_fullscreen && mir_surface_get_state(surface2) == mir_surface_state_fullscreen; }, std::chrono::seconds{max_wait}); ready_to_accept_events.wait_for_at_most_seconds(max_wait); if (!ready_to_accept_events.woken()) BOOST_THROW_EXCEPTION(std::runtime_error("Timeout waiting for surface to become focused and exposed")); EXPECT_TRUE(surface_fullscreen); } MirSurface* surface1; MirSurface* surface2; mir::test::WaitCondition ready_to_accept_events; std::vector> out_cookies; size_t event_count{0}; mutable std::mutex mutex; bool exposed{false}; bool focused{false}; std::unique_ptr fake_keyboard{ mtf::add_fake_input_device(mi::InputDeviceInfo{"keyboard", "keyboard-uid", mi::DeviceCapability::keyboard}) }; std::unique_ptr fake_pointer{ mtf::add_fake_input_device(mi::InputDeviceInfo{"mouse", "mouse-uid", mi::DeviceCapability::pointer}) }; }; namespace { void cookie_capturing_callback(MirSurface* /*surface*/, MirEvent const* ev, void* ctx) { auto const event_type = mir_event_get_type(ev); auto raise_surfaces = static_cast(ctx); if (event_type == mir_event_type_surface) { auto event = mir_event_get_surface_event(ev); auto const attrib = mir_surface_event_get_attribute(event); auto const value = mir_surface_event_get_attribute_value(event); std::lock_guard lk(raise_surfaces->mutex); if (attrib == mir_surface_attrib_visibility && value == mir_surface_visibility_exposed) { raise_surfaces->exposed = true; } if (attrib == mir_surface_attrib_focus && value == mir_surface_focused) { raise_surfaces->focused = true; } if (raise_surfaces->exposed && raise_surfaces->focused) raise_surfaces->ready_to_accept_events.wake_up_everyone(); } else if (event_type == mir_event_type_input) { auto const* iev = mir_event_get_input_event(ev); std::lock_guard lk(raise_surfaces->mutex); if (mir_input_event_has_cookie(iev)) { auto cookie = mir_input_event_get_cookie(iev); size_t size = mir_cookie_buffer_size(cookie); std::vector cookie_bytes(size); mir_cookie_to_buffer(cookie, cookie_bytes.data(), size); mir_cookie_release(cookie); raise_surfaces->out_cookies.push_back(cookie_bytes); } raise_surfaces->event_count++; } } bool wait_for_n_events(size_t n, RaiseSurfaces const* raise_surfaces) { bool all_events = mt::spin_wait_for_condition_or_timeout( [&n, &raise_surfaces] { std::lock_guard lk(raise_surfaces->mutex); return raise_surfaces->event_count >= n; }, std::chrono::seconds{max_wait}); EXPECT_TRUE(all_events); return all_events; } bool attempt_focus(MirSurface* surface, MirCookie const* cookie) { mir_surface_raise(surface, cookie); bool surface_becomes_focused = mt::spin_wait_for_condition_or_timeout( [&surface] { return mir_surface_get_focus(surface) == mir_surface_focused; }, std::chrono::seconds{max_wait}); return surface_becomes_focused; } } TEST_F(RaiseSurfaces, key_event_with_cookie) { fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_M)); int events = 1; if (wait_for_n_events(events, this)) { std::lock_guard lk(mutex); ASSERT_FALSE(out_cookies.empty()); EXPECT_EQ(mir_surface_get_focus(surface2), mir_surface_focused); MirCookie const* cookie = mir_cookie_from_buffer(out_cookies.back().data(), out_cookies.back().size()); attempt_focus(surface2, cookie); mir_cookie_release(cookie); } } TEST_F(RaiseSurfaces, older_timestamp_does_not_focus) { fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_M)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_M)); int events = 2; if (wait_for_n_events(events, this)) { std::lock_guard lk(mutex); ASSERT_FALSE(out_cookies.empty()); EXPECT_EQ(mir_surface_get_focus(surface2), mir_surface_focused); MirCookie const* cookie = mir_cookie_from_buffer(out_cookies.front().data(), out_cookies.front().size()); mir_surface_raise(surface1, cookie); mir_cookie_release(cookie); // Need to wait for this call to actually go through std::this_thread::sleep_for(std::chrono::milliseconds{1000}); EXPECT_EQ(mir_surface_get_focus(surface2), mir_surface_focused); } } TEST_F(RaiseSurfaces, motion_events_dont_prevent_raise) { fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_M)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_M)); int events = 2; if (wait_for_n_events(events, this)) { fake_pointer->emit_event(mis::a_pointer_event().with_movement(1, 1)); EXPECT_EQ(out_cookies.size(), events); if (wait_for_n_events(events + 1, this)) { std::lock_guard lk(mutex); ASSERT_FALSE(out_cookies.empty()); EXPECT_EQ(mir_surface_get_focus(surface2), mir_surface_focused); // We still have 2 cookie, but have gotten more then 2 events EXPECT_EQ(out_cookies.size(), events); EXPECT_GE(event_count, events + 1); } } } ./tests/acceptance-tests/test_command_line_handling.cpp0000644000015600001650000000544412676616125023544 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir_test_framework/headless_test.h" #include #include using namespace testing; struct CommandLineHandling : mir_test_framework::HeadlessTest { MOCK_METHOD1(callback, void(std::vector const&)); std::function callback_functor = [this](int argc, char const* const* argv) { // Copy to a vector as ElementsAre() is convenient for checking std::vector const copy{argv, argv+argc}; callback(copy); }; }; TEST_F(CommandLineHandling, valid_options_are_accepted_by_default_callback) { char const* argv[] = { "dummy-exe-name", "--file", "test", "--enable-input", "off"}; int const argc = std::distance(std::begin(argv), std::end(argv)); server.set_command_line(argc, argv); server.apply_settings(); } TEST_F(CommandLineHandling, unrecognised_tokens_cause_default_callback_to_throw) { char const* argv[] = { "dummy-exe-name", "--file", "test", "--hello", "world", "--enable-input", "off"}; int const argc = std::distance(std::begin(argv), std::end(argv)); server.set_command_line(argc, argv); EXPECT_THROW(server.apply_settings(), std::runtime_error); } TEST_F(CommandLineHandling, valid_options_are_not_passed_to_callback) { char const* argv[] = { "dummy-exe-name", "--file", "test", "--enable-input", "off"}; int const argc = std::distance(std::begin(argv), std::end(argv)); server.set_command_line_handler(callback_functor); server.set_command_line(argc, argv); EXPECT_CALL(*this, callback(_)).Times(Exactly(0)); server.apply_settings(); } TEST_F(CommandLineHandling, unrecognised_tokens_are_passed_to_callback) { char const* argv[] = { "dummy-exe-name", "--file", "test", "--hello", "world", "--enable-input", "off"}; int const argc = std::distance(std::begin(argv), std::end(argv)); server.set_command_line_handler(callback_functor); server.set_command_line(argc, argv); EXPECT_CALL(*this, callback(ElementsAre(StrEq("--hello"), StrEq("world")))); server.apply_settings(); } ./tests/acceptance-tests/test_client_surface_visibility.cpp0000644000015600001650000001557412676616125024515 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/mir_client_library.h" #include "mir/scene/session.h" #include "mir/scene/surface.h" #include "mir/shell/shell_wrapper.h" #include "mir/shell/surface_stack.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/test/wait_condition.h" #include #include #include #include namespace mf = mir::frontend; namespace mtf = mir_test_framework; namespace mt = mir::test; namespace ms = mir::scene; namespace mc = mir::compositor; namespace msh = mir::shell; namespace geom = mir::geometry; namespace { class StoringShell : public msh::ShellWrapper { public: StoringShell( std::shared_ptr const& wrapped, std::shared_ptr const surface_stack) : msh::ShellWrapper{wrapped}, surface_stack{surface_stack} {} mf::SurfaceId create_surface( std::shared_ptr const& session, ms::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override { auto const result = msh::ShellWrapper::create_surface(session, params, sink); auto const surface = session->surface(result); surface->move_to({0, 0}); surfaces.push_back(surface); return result; } std::shared_ptr surface(int index) { return surfaces[index].lock(); } void raise(int index) { surface_stack->raise(surface(index)); } using msh::ShellWrapper::raise; private: std::shared_ptr const surface_stack; std::vector> surfaces; }; struct MockVisibilityCallback { MOCK_METHOD2(handle, void(MirSurface*,MirSurfaceVisibility)); }; void event_callback(MirSurface* surface, MirEvent const* event, void* ctx) { if (mir_event_get_type(event) != mir_event_type_surface) return; auto sev = mir_event_get_surface_event(event); if (mir_surface_event_get_attribute(sev) != mir_surface_attrib_visibility) return; auto const mock_visibility_callback = reinterpret_cast*>(ctx); mock_visibility_callback->handle( surface, static_cast(mir_surface_event_get_attribute_value(sev))); } struct MirSurfaceVisibilityEvent : mtf::ConnectedClientWithASurface { void SetUp() override { server.wrap_shell([&](std::shared_ptr const& wrapped) { auto const result = std::make_shared(wrapped, server.the_surface_stack()); shell = result; return result; }); mtf::ConnectedClientWithASurface::SetUp(); mir_surface_set_event_handler(surface, &event_callback, &mock_visibility_callback); // Swap enough buffers to ensure compositor threads are into run loop for (auto i = 0; i != 11; ++i) mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); } void TearDown() override { // Don't call ConnectedClientWithASurface::TearDown() - the sequence matters mir_surface_release_sync(surface); if (second_surface) mir_surface_release_sync(second_surface); mtf::ConnectedClientHeadlessServer::TearDown(); } void create_larger_surface_on_top() { auto spec = mir_connection_create_spec_for_normal_surface(connection, 800, 600, mir_pixel_format_bgr_888); second_surface = mir_surface_create_sync(spec); ASSERT_TRUE(mir_surface_is_valid(second_surface)); mir_surface_spec_release(spec); shell.lock()->raise(1); mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(second_surface)); } std::shared_ptr server_surface(size_t index) { return shell.lock()->surface(index); } void move_surface_off_screen() { server_surface(0)->move_to(geom::Point{10000, 10000}); } void move_surface_into_screen() { server_surface(0)->move_to(geom::Point{0, 0}); } void raise_surface_on_top() { shell.lock()->raise(0); } void expect_surface_visibility_event_after( MirSurfaceVisibility visibility, std::function const& action) { using namespace testing; mt::WaitCondition event_received; Mock::VerifyAndClearExpectations(&mock_visibility_callback); EXPECT_CALL(mock_visibility_callback, handle(surface, visibility)) .WillOnce(DoAll(Invoke([&visibility](MirSurface *s, MirSurfaceVisibility) { EXPECT_EQ(visibility, mir_surface_get_visibility(s)); }), mt::WakeUp(&event_received))); action(); event_received.wait_for_at_most_seconds(2); Mock::VerifyAndClearExpectations(&mock_visibility_callback); } MirSurface* second_surface = nullptr; testing::NiceMock mock_visibility_callback; std::weak_ptr shell; }; } TEST_F(MirSurfaceVisibilityEvent, occluded_received_when_surface_goes_off_screen) { expect_surface_visibility_event_after( mir_surface_visibility_occluded, [this] { move_surface_off_screen(); }); } TEST_F(MirSurfaceVisibilityEvent, exposed_received_when_surface_reenters_screen) { expect_surface_visibility_event_after( mir_surface_visibility_occluded, [this] { move_surface_off_screen(); }); expect_surface_visibility_event_after( mir_surface_visibility_exposed, [this] { move_surface_into_screen(); }); } TEST_F(MirSurfaceVisibilityEvent, occluded_received_when_surface_occluded_by_other_surface) { expect_surface_visibility_event_after( mir_surface_visibility_occluded, [this] { create_larger_surface_on_top(); }); } TEST_F(MirSurfaceVisibilityEvent, exposed_received_when_surface_raised_over_occluding_surface) { expect_surface_visibility_event_after( mir_surface_visibility_occluded, [this] { create_larger_surface_on_top(); }); expect_surface_visibility_event_after( mir_surface_visibility_exposed, [this] { raise_surface_on_top(); }); } ./tests/acceptance-tests/test_client_with_custom_display_config_deadlock.cpp0000644000015600001650000000374212676616125030055 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir_test_framework/any_surface.h" #include namespace mtf = mir_test_framework; using ClientWithCustomDisplayConfiguration = mtf::ConnectedClientHeadlessServer; // Regression test for LP:#1340669 // Test is not deterministic since we are testing a race, but failure can be // reproduced easily with repeated runs. TEST_F(ClientWithCustomDisplayConfiguration, does_not_deadlock_server_with_existing_client_when_disconnecting) { auto second_connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto second_surface = mtf::make_any_surface(connection); ASSERT_TRUE(mir_surface_is_valid(second_surface)); auto configuration = mir_connection_create_display_config(connection); mir_wait_for(mir_connection_apply_display_config(connection, configuration)); EXPECT_STREQ("", mir_connection_get_error_message(connection)); mir_display_config_destroy(configuration); mir_connection_release(second_connection); // Server (and therefore the test) will deadlock and won't be able to // shut down without the fix. It's not ideal to deadlock on test failure, // but it's the best check we have at the moment. } ./tests/acceptance-tests/test_application_not_responding_detection.cpp0000644000015600001650000001566212676616125026727 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir_test_framework/connected_client_headless_server.h" #include "mir/scene/application_not_responding_detector.h" #include "mir/scene/session.h" #include "mir/test/signal.h" #include "mir/test/validity_matchers.h" #include #include #include #include namespace mtf = mir_test_framework; namespace mt = mir::test; namespace ms = mir::scene; namespace { class ApplicationNotRespondingDetection : public mtf::ConnectedClientHeadlessServer { public: void SetUp() override { // Deliberately fail to start up the server so we can override server config // in each test. } void complete_setup() { mtf::ConnectedClientHeadlessServer::SetUp(); } }; class DelegateObserver : public ms::ApplicationNotRespondingDetector::Observer { public: DelegateObserver( std::function const& unresponsive_delegate, std::function const& now_responsive_delegate) : unresponsive_delegate{unresponsive_delegate}, now_responsive_delegate{now_responsive_delegate} { } void session_unresponsive(mir::scene::Session const* session) override { unresponsive_delegate(session); } void session_now_responsive(mir::scene::Session const* session) override { now_responsive_delegate(session); } private: std::function unresponsive_delegate; std::function now_responsive_delegate; }; class MockANRDetector : public ms::ApplicationNotRespondingDetector { public: MOCK_METHOD2(register_session, void(mir::frontend::Session const*, std::function const&)); MOCK_METHOD1(unregister_session, void(mir::frontend::Session const*)); MOCK_METHOD1(pong_received, void(mir::frontend::Session const*)); MOCK_METHOD1(register_observer, void(std::shared_ptr const&)); MOCK_METHOD1(unregister_observer, void(std::shared_ptr const&)); }; } namespace { void black_hole_pong(MirConnection*, int32_t, void*) { } } TEST_F(ApplicationNotRespondingDetection, failure_to_pong_is_noticed) { using namespace std::literals::chrono_literals; complete_setup(); mt::Signal marked_as_unresponsive; auto anr_observer = std::make_shared( [&marked_as_unresponsive](auto) { marked_as_unresponsive.raise(); }, [](auto){} ); server.the_application_not_responding_detector()->register_observer(anr_observer); mir_connection_set_ping_event_callback(connection, &black_hole_pong, nullptr); EXPECT_TRUE(marked_as_unresponsive.wait_for(10s)); } TEST_F(ApplicationNotRespondingDetection, can_override_anr_detector) { using namespace std::literals::chrono_literals; using namespace testing; auto anr_detector = std::make_shared>(); server.override_the_application_not_responding_detector([anr_detector]() { return anr_detector; }); EXPECT_CALL(*anr_detector, register_session(_,_)).Times(AtLeast(1)); complete_setup(); } TEST_F(ApplicationNotRespondingDetection, each_new_client_is_registered) { using namespace std::literals; using namespace testing; constexpr int const client_start_count = 10; constexpr int const expected_count = client_start_count + 1; // Test fixture also connects. auto anr_detector = std::make_shared>(); server.override_the_application_not_responding_detector([anr_detector]() { return anr_detector; }); EXPECT_CALL(*anr_detector, register_session(_,_)).Times(expected_count); // ConnectiedClientHeadlessServer::TearDown guarantees that the initial client is // disconnected before the server is torn down, so it should also be unregistered. EXPECT_CALL(*anr_detector, unregister_session(_)).Times(expected_count); complete_setup(); for (int i = 0; i < client_start_count; ++i) { mir_connection_release(mir_connect_sync(new_connection().c_str(), ("Test client "s + std::to_string(i)).c_str())); } } namespace { struct PingContext { int ping_count; mt::Signal pung_thrice; }; void respond_to_ping(MirConnection* connection, int32_t, void* context) { auto ping_context = reinterpret_cast(context); ping_context->ping_count++; mir_connection_pong(connection, 0); if (ping_context->ping_count > 2) { // If a client does not respond to a ping for a whole ping cycle it is marked // as unresponsive. Wait for 2 ping cycles to ensure that the test has // had time to process the pongs. ping_context->pung_thrice.raise(); } } } TEST_F(ApplicationNotRespondingDetection, responding_client_is_not_marked_as_unresponsive) { using namespace std::literals::chrono_literals; complete_setup(); bool unresponsive_called{false}; auto anr_observer = std::make_shared( [&unresponsive_called](auto) { unresponsive_called = true; }, [](auto){} ); server.the_application_not_responding_detector()->register_observer(anr_observer); auto ping_context = std::make_unique(); mir_connection_set_ping_event_callback(connection, &respond_to_ping, ping_context.get()); EXPECT_TRUE(ping_context->pung_thrice.wait_for(10s)); EXPECT_FALSE(unresponsive_called); } namespace { void ping_counting_blackhole_pong(MirConnection*, int32_t, void* ctx) { auto count = reinterpret_cast*>(ctx); (*count)++; } } TEST_F(ApplicationNotRespondingDetection, unresponsive_client_stops_receiving_pings) { using namespace std::literals::chrono_literals; using namespace testing; complete_setup(); std::atomic count{0}; mir_connection_set_ping_event_callback(connection, &ping_counting_blackhole_pong, &count); auto responsive_connection = mir_connect_sync(new_connection().c_str(), "Aardvarks"); ASSERT_THAT(responsive_connection, IsValid()); auto ping_context = std::make_unique(); mir_connection_set_ping_event_callback(responsive_connection, &respond_to_ping, ping_context.get()); EXPECT_TRUE(ping_context->pung_thrice.wait_for(10s)); EXPECT_THAT(count, Eq(1)); mir_connection_release(responsive_connection); } ./tests/acceptance-tests/test_display_configuration.cpp0000644000015600001650000004407612676616157023660 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis * Kevin DuBois */ #include "mir/main_loop.h" #include "mir/frontend/session_authorizer.h" #include "mir/graphics/event_handler_register.h" #include "mir/shell/display_configuration_controller.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/test/doubles/null_platform.h" #include "mir/test/doubles/fake_display.h" #include "mir/test/doubles/null_display_sync_group.h" #include "mir/test/doubles/null_platform.h" #include "mir/test/display_config_matchers.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/stub_session_authorizer.h" #include "mir/test/fake_shared.h" #include "mir/test/pipe.h" #include "mir/test/wait_condition.h" #include "mir/test/signal.h" #include "mir_toolkit/mir_client_library.h" #include #include #include #include namespace mg = mir::graphics; namespace geom = mir::geometry; namespace mf = mir::frontend; namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mt = mir::test; using namespace testing; using namespace std::literals::chrono_literals; namespace { mtd::StubDisplayConfig stub_display_config; mtd::StubDisplayConfig changed_stub_display_config{1}; class MockDisplay : public mtd::FakeDisplay { public: MockDisplay(): mtd::FakeDisplay() { using namespace testing; ON_CALL(*this, configure(_)) .WillByDefault(Invoke( [this](mg::DisplayConfiguration const& new_config) { mtd::FakeDisplay::configure(new_config); })); } MOCK_METHOD1(configure, void(mg::DisplayConfiguration const&)); }; struct StubAuthorizer : mtd::StubSessionAuthorizer { bool configure_display_is_allowed(mf::SessionCredentials const&) override { return allow_configure_display; } bool set_base_display_configuration_is_allowed(mf::SessionCredentials const&) override { return allow_set_base_display_configuration; } std::atomic allow_configure_display{true}; std::atomic allow_set_base_display_configuration{true}; }; void wait_for_server_actions_to_finish(mir::ServerActionQueue& server_action_queue) { mt::WaitCondition last_action_done; server_action_queue.enqueue( &last_action_done, [&] { last_action_done.wake_up_everyone(); }); last_action_done.wait_for_at_most_seconds(5); } } struct LegacyDisplayConfigurationTest : mtf::ConnectedClientWithASurface { void SetUp() override { server.override_the_session_authorizer([this] { return mt::fake_shared(stub_authorizer); }); preset_display(mt::fake_shared(mock_display)); mtf::ConnectedClientWithASurface::SetUp(); } testing::NiceMock mock_display; StubAuthorizer stub_authorizer; }; TEST_F(LegacyDisplayConfigurationTest, display_configuration_reaches_client) { auto configuration = mir_connection_create_display_config(connection); EXPECT_THAT(*configuration, mt::DisplayConfigMatches(std::cref(stub_display_config))); mir_display_config_destroy(configuration); } namespace { void display_change_handler(MirConnection* connection, void* context) { auto configuration = mir_connection_create_display_config(connection); EXPECT_THAT(*configuration, mt::DisplayConfigMatches(std::cref(changed_stub_display_config))); mir_display_config_destroy(configuration); auto callback_called = static_cast*>(context); *callback_called = true; } } TEST_F(LegacyDisplayConfigurationTest, hw_display_change_notification_reaches_all_clients) { std::atomic callback_called{false}; mir_connection_set_display_config_change_callback(connection, &display_change_handler, &callback_called); MirConnection* unsubscribed_connection = mir_connect_sync(new_connection().c_str(), "notifier"); mock_display.emit_configuration_change_event( mt::fake_shared(changed_stub_display_config)); while (!callback_called) std::this_thread::sleep_for(std::chrono::microseconds{500}); // At this point, the message has gone out on the wire. since with unsubscribed_connection // we're emulating a client that is passively subscribed, we will just wait for the display // configuration to change and then will check the new config. auto configuration = mir_connection_create_display_config(unsubscribed_connection); while(configuration->num_outputs != changed_stub_display_config.outputs.size()) { mir_display_config_destroy(configuration); std::this_thread::sleep_for(std::chrono::microseconds(500)); configuration = mir_connection_create_display_config(unsubscribed_connection); } EXPECT_THAT(*configuration, mt::DisplayConfigMatches(std::cref(changed_stub_display_config))); mir_display_config_destroy(configuration); mir_connection_release(unsubscribed_connection); } TEST_F(LegacyDisplayConfigurationTest, display_change_request_for_unauthorized_client_fails) { stub_authorizer.allow_configure_display = false; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto configuration = mir_connection_create_display_config(connection); mir_wait_for(mir_connection_apply_display_config(connection, configuration)); EXPECT_THAT(mir_connection_get_error_message(connection), testing::HasSubstr("not authorized to apply display configurations")); mir_display_config_destroy(configuration); mir_connection_release(connection); } namespace { struct SimpleClient { SimpleClient(std::string const& mir_test_socket) : mir_test_socket{mir_test_socket} {} void connect() { connection = mir_connect_sync(mir_test_socket.c_str(), __PRETTY_FUNCTION__); auto const spec = mir_connection_create_spec_for_normal_surface(connection, 100, 100, mir_pixel_format_abgr_8888); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); } void disconnect() { mir_surface_release_sync(surface); mir_connection_release(connection); } void disconnect_without_releasing_surface() { mir_connection_release(connection); } std::string mir_test_socket; MirConnection* connection{nullptr}; MirSurface* surface{nullptr}; }; struct DisplayClient : SimpleClient { using SimpleClient::SimpleClient; void apply_config() { auto configuration = mir_connection_create_display_config(connection); mir_wait_for(mir_connection_apply_display_config(connection, configuration)); EXPECT_STREQ("", mir_connection_get_error_message(connection)); mir_display_config_destroy(configuration); } std::unique_ptr get_base_config() { return {mir_connection_create_display_config(connection), mir_display_config_destroy}; } void set_base_config(MirDisplayConfiguration* configuration) { mir_wait_for(mir_connection_set_base_display_config(connection, configuration)); EXPECT_STREQ("", mir_connection_get_error_message(connection)); } }; } TEST_F(LegacyDisplayConfigurationTest, changing_config_for_focused_client_configures_display) { EXPECT_CALL(mock_display, configure(_)).Times(0); DisplayClient display_client{new_connection()}; display_client.connect(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(testing::_)).Times(1); display_client.apply_config(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); display_client.disconnect(); } TEST_F(LegacyDisplayConfigurationTest, focusing_client_with_display_config_configures_display) { EXPECT_CALL(mock_display, configure(_)).Times(0); SimpleClient simple_client{new_connection()}; /* Connect the simple client. After this the simple client should have the focus. */ simple_client.connect(); /* Apply the display config while not focused */ auto const configuration = mir_connection_create_display_config(connection); mir_wait_for(mir_connection_apply_display_config(connection, configuration)); mir_display_config_destroy(configuration); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(testing::_)).Times(1); /* * Shut down the simple client. After this the focus should have changed to the * display client and its configuration should have been applied. */ simple_client.disconnect(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); } TEST_F(LegacyDisplayConfigurationTest, changing_focus_from_client_with_config_to_client_without_config_configures_display) { DisplayClient display_client{new_connection()}; SimpleClient simple_client{new_connection()}; /* Connect the simple client. */ simple_client.connect(); /* Connect the display config client and apply a display config. */ display_client.connect(); wait_for_server_actions_to_finish(*server.the_main_loop()); EXPECT_CALL(mock_display, configure(_)).Times(1); display_client.apply_config(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(testing::_)).Times(1); /* * Shut down the display client. After this the focus should have changed to the * simple client and the base configuration should have been applied. */ display_client.disconnect(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); simple_client.disconnect(); } TEST_F(LegacyDisplayConfigurationTest, hw_display_change_doesnt_apply_base_config_if_per_session_config_is_active) { DisplayClient display_client{new_connection()}; display_client.connect(); display_client.apply_config(); wait_for_server_actions_to_finish(*server.the_main_loop()); Mock::VerifyAndClearExpectations(&mock_display); /* * A client with a per-session config is active, the base configuration * shouldn't be applied. */ EXPECT_CALL(mock_display, configure(_)).Times(0); mock_display.emit_configuration_change_event( mt::fake_shared(changed_stub_display_config)); mock_display.wait_for_configuration_change_handler(); wait_for_server_actions_to_finish(*server.the_main_loop()); Mock::VerifyAndClearExpectations(&mock_display); display_client.disconnect(); } namespace { struct DisplayConfigMatchingContext { std::function matcher; mt::Signal done; }; void new_display_config_matches(MirConnection* connection, void* ctx) { auto context = reinterpret_cast(ctx); auto config = mir_connection_create_display_config(connection); context->matcher(config); mir_display_config_destroy(config); context->done.raise(); } } TEST_F(LegacyDisplayConfigurationTest, shell_initiated_display_configuration_notifies_clients) { using namespace testing; // Create a new client for explicit lifetime handling. SimpleClient client{new_connection()}; client.connect(); std::shared_ptr new_conf; new_conf = server.the_display()->configuration(); new_conf->for_each_output([](mg::UserDisplayConfigurationOutput& output) { if (output.connected) { output.used = !output.used; } }); DisplayConfigMatchingContext context; context.matcher = [new_conf](MirDisplayConfiguration* conf) { EXPECT_THAT(conf, mt::DisplayConfigMatches(std::cref(*new_conf))); }; mir_connection_set_display_config_change_callback( client.connection, &new_display_config_matches, &context); server.the_display_configuration_controller()->set_base_configuration(new_conf); EXPECT_TRUE(context.done.wait_for(std::chrono::seconds{10})); EXPECT_THAT( *server.the_display()->configuration(), mt::DisplayConfigMatches(std::cref(*new_conf))); client.disconnect(); } TEST_F(LegacyDisplayConfigurationTest, client_setting_base_config_configures_display_if_a_session_config_is_not_applied) { DisplayClient display_client{new_connection()}; display_client.connect(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(testing::_)).Times(1); display_client.set_base_config(display_client.get_base_config().get()); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); display_client.disconnect(); } TEST_F(LegacyDisplayConfigurationTest, client_setting_base_config_does_not_configure_display_if_a_session_config_is_applied) { DisplayClient display_client{new_connection()}; DisplayClient display_client_with_session_config{new_connection()}; // Client with session config should have focus after this display_client.connect(); display_client_with_session_config.connect(); display_client_with_session_config.apply_config(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(testing::_)).Times(0); display_client.set_base_config(display_client.get_base_config().get()); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); display_client_with_session_config.disconnect(); display_client.disconnect(); } namespace { void display_config_change_handler(MirConnection*, void* context) { auto callback_called = static_cast(context); callback_called->raise(); } } TEST_F(LegacyDisplayConfigurationTest, client_is_notified_of_new_base_config_eventually_after_set_base_configuration) { DisplayClient display_client{new_connection()}; display_client.connect(); mt::Signal callback_called; mir_connection_set_display_config_change_callback( display_client.connection, &display_config_change_handler, &callback_called); auto requested_config = display_client.get_base_config(); EXPECT_THAT(requested_config->outputs[0].used, Eq(1)); requested_config->outputs[0].used = 0; display_client.set_base_config(requested_config.get()); EXPECT_THAT(callback_called.wait_for(5s), Eq(true)); auto const new_config = display_client.get_base_config(); EXPECT_THAT(new_config.get(), mt::DisplayConfigMatches(requested_config.get())); display_client.disconnect(); } TEST_F(LegacyDisplayConfigurationTest, set_base_configuration_for_unauthorized_client_fails) { stub_authorizer.allow_set_base_display_configuration = false; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto configuration = mir_connection_create_display_config(connection); mir_wait_for(mir_connection_set_base_display_config(connection, configuration)); EXPECT_THAT(mir_connection_get_error_message(connection), testing::HasSubstr("not authorized to set base display configuration")); mir_display_config_destroy(configuration); mir_connection_release(connection); } TEST_F(LegacyDisplayConfigurationTest, disconnection_of_client_with_display_config_reconfigures_display) { EXPECT_CALL(mock_display, configure(_)).Times(0); DisplayClient display_client{new_connection()}; display_client.connect(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(testing::_)).Times(1); display_client.apply_config(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(_)).Times(1); display_client.disconnect(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); } TEST_F(LegacyDisplayConfigurationTest, disconnection_without_releasing_surfaces_of_client_with_display_config_reconfigures_display) { EXPECT_CALL(mock_display, configure(_)).Times(0); DisplayClient display_client{new_connection()}; display_client.connect(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(testing::_)).Times(1); display_client.apply_config(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); EXPECT_CALL(mock_display, configure(_)).Times(1); display_client.disconnect_without_releasing_surface(); wait_for_server_actions_to_finish(*server.the_main_loop()); testing::Mock::VerifyAndClearExpectations(&mock_display); } ./tests/acceptance-tests/test_unresponsive_client.cpp0000644000015600001650000001117312676616125023345 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/scene/null_session_listener.h" #include "mir/scene/session.h" #include "mir/scene/surface.h" #include "mir_test_framework/interprocess_client_server_test.h" #include "mir_test_framework/any_surface.h" #include "mir_test_framework/process.h" #include "mir/test/cross_process_action.h" #include "mir/test/fake_shared.h" #include "mir_toolkit/mir_client_library.h" #include #include #include #include #include namespace ms = mir::scene; namespace mt = mir::test; namespace mtf = mir_test_framework; using UnresponsiveClient = mtf::InterprocessClientServerTest; namespace { struct SessionListener : ms::NullSessionListener { ~SessionListener() { std::lock_guard lk{guard}; sessions.clear(); } void starting(std::shared_ptr const& session) override { std::lock_guard lk{guard}; sessions.insert(session); } void stopping(std::shared_ptr const& session) override { std::lock_guard lk{guard}; sessions.erase(session); } void for_each(std::function const&)> f) const { std::lock_guard lk{guard}; for (auto& session : sessions) f(session); } private: std::mutex mutable guard; std::set> sessions; }; } TEST_F(UnresponsiveClient, does_not_hang_server) { mt::CrossProcessAction server_send_events; mt::CrossProcessAction client_connect; mt::CrossProcessAction client_release; mt::CrossProcessAction server_finish; SessionListener sessions; init_server([&] { server.override_the_session_listener([&] { return mt::fake_shared(sessions); }); }); run_in_server([&] { mt::AutoJoinThread t{ [&] { server_send_events.exec([&] { for (int i = 0; i < 1000; ++i) { sessions.for_each( [i] (std::shared_ptr const& session) { session->default_surface()->resize({i + 1, i + 1}); }); } }); }}; server_finish.exec([]{}); }); auto const client_code = [&] { MirConnection* connection = nullptr; MirSurface* surface = nullptr; client_connect.exec([&] { connection = mir_connect_sync(mir_test_socket, __PRETTY_FUNCTION__); surface = mtf::make_any_surface(connection); }); client_release.exec([&] { // We would normally explicitly release the surface at this // point. However, because we have been filling the server // send socket buffer, releasing the surface may cause the // server to try to write to a full socket buffer when // responding, leading the server to believe that the client // is blocked, and causing a premature client disconnection. mir_connection_release(connection); }); }; auto client_process = new_client_process(client_code); if (is_test_process()) { client_connect(); kill(client_pid(), SIGSTOP); try { server_send_events(std::chrono::seconds{10}); } catch(...) { ADD_FAILURE() << "Server blocked while sending events"; } kill(client_pid(), SIGCONT); client_release(); server_finish(); auto const result = client_process->wait_for_termination(); EXPECT_THAT(result.exit_code, testing::Eq(EXIT_SUCCESS)); } } ./tests/acceptance-tests/test_system_compositor_window_manager.cpp0000644000015600001650000001342512676616125026134 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/shell/system_compositor_window_manager.h" #include "mir_toolkit/mir_client_library.h" #include "mir/geometry/rectangle.h" #include "mir_test_framework/headless_test.h" #include "mir/test/signal.h" #include "gmock/gmock.h" namespace msh = mir::shell; namespace mt = mir::test; namespace mtf = mir_test_framework; using namespace testing; using namespace std::chrono_literals; namespace { class SurfaceHandle { public: explicit SurfaceHandle(MirSurface* surface) : surface{surface} {} ~SurfaceHandle() { if (surface) mir_surface_release_sync(surface); } operator MirSurface*() const { return surface; } void post_buffer() { mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); } SurfaceHandle(SurfaceHandle const&& that) : surface{that.surface} { surface = nullptr; } private: SurfaceHandle(SurfaceHandle const&) = delete; MirSurface* surface; }; struct MockClient { explicit MockClient(char const* connect_string) : connection{mir_connect_sync(connect_string, __PRETTY_FUNCTION__)} { } MockClient(MockClient&& source) : connection{nullptr} { std::swap(connection, source.connection); } auto create_surface(int output_id) -> SurfaceHandle { auto const spec = mir_connection_create_spec_for_normal_surface( connection, 800, 600, mir_pixel_format_bgr_888); mir_surface_spec_set_fullscreen_on_output(spec, output_id); auto const surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); mir_surface_set_event_handler(surface, on_surface_event, this); return SurfaceHandle{surface}; }; void disconnect() { if (connection) mir_connection_release(connection); connection = nullptr; } ~MockClient() noexcept { disconnect(); } MOCK_METHOD2(surface_event, void(MirSurface* surface, const MirEvent* event)); private: MirConnection* connection{nullptr}; static void on_surface_event(MirSurface* surface, const MirEvent* event, void* client_ptr) { static_cast(client_ptr)->surface_event(surface, event); } }; struct SystemCompositorWindowManager : mtf::HeadlessTest { void SetUp() override { add_to_environment("MIR_SERVER_NO_FILE", ""); initial_display_layout({{{0, 0}, { 640, 480}}, {{480, 0}, {1920, 1080}}}); server.override_the_window_manager_builder( [this](msh::FocusController* focus_controller) { return std::make_shared( focus_controller, server.the_shell_display_layout(), server.the_session_coordinator()); }); start_server(); } void TearDown() override { stop_server(); } MockClient connect_client() { return MockClient(new_connection().c_str()); } }; MATCHER_P(MirFocusEvent, expected, "") { if (mir_event_get_type(arg) != mir_event_type_surface) return false; auto surface_event = mir_event_get_surface_event(arg); auto attrib = mir_surface_event_get_attribute(surface_event); auto value = mir_surface_event_get_attribute_value(surface_event); return (attrib == mir_surface_attrib_focus) && (value == expected); } } TEST_F(SystemCompositorWindowManager, when_output_is_valid_surfaces_creation_succeeds) { auto client = connect_client(); auto surface1 = client.create_surface(1); auto surface2 = client.create_surface(2); EXPECT_TRUE(mir_surface_is_valid(surface1)); EXPECT_TRUE(mir_surface_is_valid(surface2)); } TEST_F(SystemCompositorWindowManager, when_output_ID_not_specified_surfaces_creation_fails) { auto client = connect_client(); auto surface = client.create_surface(0); EXPECT_FALSE(mir_surface_is_valid(surface)); EXPECT_THAT(mir_surface_get_error_message(surface), HasSubstr("An output ID must be specified")); } TEST_F(SystemCompositorWindowManager, if_a_surface_posts_client_gets_focus) { auto client = connect_client(); // Throw away all uninteresting surface events EXPECT_CALL(client, surface_event(_, Not(MirFocusEvent(mir_surface_focused)))).Times(AnyNumber()); mt::Signal signal; EXPECT_CALL(client, surface_event(_, MirFocusEvent(mir_surface_focused))).Times(1) .WillOnce(InvokeWithoutArgs([&] { signal.raise(); })); auto surface = client.create_surface(1); surface.post_buffer(); signal.wait_for(1s); } TEST_F(SystemCompositorWindowManager, if_no_surface_posts_client_never_gets_focus) { auto client = connect_client(); // Throw away all uninteresting surface events EXPECT_CALL(client, surface_event(_, Not(MirFocusEvent(mir_surface_focused)))).Times(AnyNumber()); mt::Signal signal; ON_CALL(client, surface_event(_, MirFocusEvent(mir_surface_focused))) .WillByDefault(InvokeWithoutArgs([&] { signal.raise(); })); auto surface = client.create_surface(1); EXPECT_FALSE(signal.wait_for(100ms)) << "Unexpected surface_focused event received"; } ./tests/acceptance-tests/test_client_library_old.cpp0000644000015600001650000000604012676616125023104 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Guest */ #include "mir_test_framework/interprocess_client_server_test.h" #include "mir_test_framework/any_surface.h" #include "mir_toolkit/mir_client_library.h" #include #include namespace mtf = mir_test_framework; namespace { bool signalled; } static void SIGIO_handler(int /*signo*/) { signalled = true; } struct ClientLibraryThread : mtf::InterprocessClientServerTest { void SetUp() override { mtf::InterprocessClientServerTest::SetUp(); run_in_server([]{}); } }; TEST_F(ClientLibraryThread, handles_no_signals) { run_in_client([&] { signalled = false; sigset_t sigset; sigemptyset(&sigset); struct sigaction act; act.sa_handler = &SIGIO_handler; act.sa_mask = sigset; act.sa_flags = 0; act.sa_restorer = nullptr; if (sigaction(SIGIO, &act, NULL)) FAIL() << "Failed to set SIGIO action"; MirConnection* conn = mir_connect_sync(mir_test_socket, __PRETTY_FUNCTION__); sigaddset(&sigset, SIGIO); pthread_sigmask(SIG_BLOCK, &sigset, NULL); // SIGIO should be blocked if (kill(getpid(), SIGIO)) FAIL() << "Failed to send SIGIO signal"; // Make a roundtrip to the server to ensure the SIGIO has time to be handled auto surface = mtf::make_any_surface(conn); mir_surface_release_sync(surface); mir_connection_release(conn); EXPECT_FALSE(signalled); }); } TEST_F(ClientLibraryThread, does_not_interfere_with_client_signal_handling) { run_in_client([&] { signalled = false; sigset_t sigset; sigemptyset(&sigset); struct sigaction act; act.sa_handler = &SIGIO_handler; act.sa_mask = sigset; act.sa_flags = 0; act.sa_restorer = nullptr; if (sigaction(SIGIO, &act, NULL)) FAIL() << "Failed to set SIGIO action"; MirConnection* conn = mir_connect_sync(mir_test_socket, __PRETTY_FUNCTION__); // We should receieve SIGIO if (kill(getpid(), SIGIO)) FAIL() << "Failed to send SIGIO signal"; mir_connection_release(conn); EXPECT_TRUE(signalled); }); } ./tests/acceptance-tests/test_nested_input.cpp0000644000015600001650000001717512676616157021765 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/input/input_device_info.h" #include "mir/input/input_device_hub.h" #include "mir/input/device.h" #include "mir/input/event_filter.h" #include "mir/input/composite_event_filter.h" #include "mir_test_framework/fake_input_device.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir_test_framework/headless_nested_server_runner.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/any_surface.h" #include "mir/test/doubles/nested_mock_egl.h" #include "mir/test/doubles/mock_input_device_observer.h" #include "mir/test/event_factory.h" #include "mir/test/event_matchers.h" #include "mir/test/wait_condition.h" #include "mir/test/wait_condition.h" #include "mir/test/spin_wait.h" #include "mir_toolkit/mir_client_library.h" #include #include #include #include namespace mi = mir::input; namespace mis = mi::synthesis; namespace mt = mir::test; namespace mtd = mt::doubles; namespace mtf = mir_test_framework; using namespace ::testing; namespace { struct MockEventFilter : public mi::EventFilter { // Work around GMock wanting to know how to construct MirEvent MOCK_METHOD1(handle, bool(MirEvent const*)); bool handle(MirEvent const& ev) { handle(&ev); return true; } }; std::vector const display_geometry { {{ 0, 0}, { 640, 480}}, {{480, 0}, {1920, 1080}} }; struct NestedServerWithMockEventFilter : mtf::HeadlessNestedServerRunner { NestedServerWithMockEventFilter(std:: string const& connection_string) : mtf::HeadlessNestedServerRunner(connection_string) { start_server(); server.the_composite_event_filter()->append(mock_event_filter); } ~NestedServerWithMockEventFilter() { stop_server(); } std::shared_ptr const mock_event_filter = std::make_shared(); }; struct NestedInput : public mtf::HeadlessInProcessServer { void SetUp() { initial_display_layout(display_geometry); mtf::HeadlessInProcessServer::SetUp(); } mtd::NestedMockEGL mock_egl; mtf::UsingStubClientPlatform using_stub_client_platform; std::unique_ptr fake_keyboard{ mtf::add_fake_input_device(mi::InputDeviceInfo{"keyboard", "keyboard-uid" , mi::DeviceCapability::keyboard}) }; mir::test::WaitCondition all_events_received; mir::test::WaitCondition input_device_changes_complete; std::shared_ptr> mock_observer{ std::make_shared>()}; }; struct ExposedSurface { public: ExposedSurface(std::string const& connect_string) { // Ensure the nested server posts a frame connection = mir_connect_sync(connect_string.c_str(), __PRETTY_FUNCTION__); surface = mtf::make_any_surface(connection); mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); } ~ExposedSurface() { mir_surface_release_sync(surface); mir_connection_release(connection); } protected: ExposedSurface(ExposedSurface const&) = delete; ExposedSurface& operator=(ExposedSurface const&) = delete; private: MirConnection *connection; MirSurface *surface; }; } TEST_F(NestedInput, nested_event_filter_receives_keyboard_from_host) { NestedServerWithMockEventFilter nested_mir{new_connection()}; ExposedSurface client(nested_mir.new_connection()); std::atomic num_key_a_events{0}; auto const increase_key_a_events = [&num_key_a_events] { ++num_key_a_events; }; InSequence seq; EXPECT_CALL(*nested_mir.mock_event_filter, handle(mt::KeyOfScanCode(KEY_A))). Times(AtLeast(1)). WillRepeatedly(DoAll(InvokeWithoutArgs(increase_key_a_events), Return(true))); EXPECT_CALL(*nested_mir.mock_event_filter, handle(mt::KeyOfScanCode(KEY_RIGHTSHIFT))). Times(2). WillOnce(Return(true)). WillOnce(DoAll(mt::WakeUp(&all_events_received), Return(true))); // Because we are testing a nested setup, it's difficult to guarantee // that the nested framebuffer surface (and consenquently the client surface // contained in it) is going to be ready (i.e., exposed and focused) to receive // events when we send them. We work around this issue by first sending some // dummy events and waiting until we receive one of them. auto const dummy_events_received = mt::spin_wait_for_condition_or_timeout( [&] { if (num_key_a_events > 0) return true; fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_A)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_A)); return false; }, std::chrono::seconds{5}); EXPECT_TRUE(dummy_events_received); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_RIGHTSHIFT)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_RIGHTSHIFT)); all_events_received.wait_for_at_most_seconds(10); } TEST_F(NestedInput, nested_input_device_hub_lists_keyboard) { NestedServerWithMockEventFilter nested_mir{new_connection()}; auto nested_hub = nested_mir.server.the_input_device_hub(); nested_hub->for_each_input_device( [](auto const& dev) { EXPECT_THAT(dev.name(), StrEq("keyboard")); EXPECT_THAT(dev.unique_id(), StrEq("keyboard-uid")); EXPECT_THAT(dev.capabilities(), Eq(mi::DeviceCapability::keyboard)); }); } TEST_F(NestedInput, on_add_device_observer_gets_device_added_calls_on_existing_devices) { NestedServerWithMockEventFilter nested_mir{new_connection()}; auto nested_hub = nested_mir.server.the_input_device_hub(); EXPECT_CALL(*mock_observer, device_added(_)).Times(1); EXPECT_CALL(*mock_observer, changes_complete()) .Times(1) .WillOnce(mt::WakeUp(&input_device_changes_complete)); nested_hub->add_observer(mock_observer); input_device_changes_complete.wait_for_at_most_seconds(10); } TEST_F(NestedInput, device_added_on_host_triggeres_nested_device_observer) { NestedServerWithMockEventFilter nested_mir{new_connection()}; auto nested_hub = nested_mir.server.the_input_device_hub(); EXPECT_CALL(*mock_observer, changes_complete()).Times(1) .WillOnce(mt::WakeUp(&input_device_changes_complete)); nested_hub->add_observer(mock_observer); input_device_changes_complete.wait_for_at_most_seconds(10); EXPECT_THAT(input_device_changes_complete.woken(), Eq(true)); input_device_changes_complete.reset(); EXPECT_CALL(*mock_observer, changes_complete()) .WillOnce(mt::WakeUp(&input_device_changes_complete)); auto mouse = mtf::add_fake_input_device(mi::InputDeviceInfo{"mouse", "mouse-uid" , mi::DeviceCapability::pointer}); input_device_changes_complete.wait_for_at_most_seconds(10); } ./tests/acceptance-tests/test_surface_specification.cpp0000644000015600001650000006143312676616125023603 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/events/event_builders.h" #include "mir/scene/null_surface_observer.h" #include "mir/scene/surface.h" #include "mir/test/doubles/wrap_shell_to_track_latest_surface.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir_test_framework/visible_surface.h" #include "mir/test/fake_shared.h" #include "mir/test/signal.h" #include "mir_toolkit/common.h" #include "mir_toolkit/client_types.h" #include namespace mev = mir::events; namespace mf = mir::frontend; namespace mtf = mir_test_framework; namespace ms = mir::scene; namespace msh = mir::shell; namespace mt = mir::test; namespace mtd = mir::test::doubles; using namespace mir::geometry; using namespace testing; using namespace std::chrono_literals; namespace { class MockSurfaceObserver : public ms::NullSurfaceObserver { public: MOCK_METHOD1(renamed, void(char const*)); MOCK_METHOD2(attrib_changed, void(MirSurfaceAttrib attrib, int value)); MOCK_METHOD1(resized_to, void(Size const& size)); }; struct SurfaceSpecification : mtf::ConnectedClientHeadlessServer { SurfaceSpecification() { add_to_environment("MIR_SERVER_ENABLE_INPUT", "OFF"); } Rectangle const first_display {{ 0, 0}, {640, 480}}; Rectangle const second_display{{640, 0}, {640, 480}}; void SetUp() override { initial_display_layout({first_display, second_display}); server.wrap_shell([this](std::shared_ptr const& wrapped) { auto const msc = std::make_shared(wrapped); shell = msc; return msc; }); mtf::ConnectedClientHeadlessServer::SetUp(); init_pixel_format(); } void TearDown() override { shell.reset(); mtf::ConnectedClientHeadlessServer::TearDown(); } std::shared_ptr latest_shell_surface() const { auto const result = shell->latest_surface.lock(); // ASSERT_THAT(result, NotNull()); //<= doesn't compile!? EXPECT_THAT(result, NotNull()); return result; } template mtf::VisibleSurface create_surface(Specifier const& specifier) { auto del = [] (MirSurfaceSpec* spec) { mir_surface_spec_release(spec); }; std::unique_ptr spec(mir_create_surface_spec(connection), del); specifier(spec.get()); return mtf::VisibleSurface{spec.get()}; } NiceMock surface_observer; void change_observed() { signal_change.raise(); } MirPixelFormat pixel_format{mir_pixel_format_invalid}; static auto const width = 97; static auto const height= 101; MirInputDeviceId const device_id = MirInputDeviceId(7); std::chrono::nanoseconds const timestamp = std::chrono::nanoseconds(39); void generate_alt_click_at(Point const& click_position) { auto const modifiers = mir_input_event_modifier_alt; auto const x_axis_value = click_position.x.as_float(); auto const y_axis_value = click_position.y.as_float(); auto const hscroll_value = 0.0; auto const vscroll_value = 0.0; auto const action = mir_pointer_action_button_down; auto const relative_x_value = 0.0; auto const relative_y_value = 0.0; auto const click_event = mev::make_event(device_id, timestamp, cookie, modifiers, action, mir_pointer_button_tertiary, x_axis_value, y_axis_value, hscroll_value, vscroll_value, relative_x_value, relative_y_value); server.the_shell()->handle(*click_event); } void generate_alt_move_to(Point const& drag_position) { auto const modifiers = mir_input_event_modifier_alt; auto const x_axis_value = drag_position.x.as_float(); auto const y_axis_value = drag_position.y.as_float(); auto const hscroll_value = 0.0; auto const vscroll_value = 0.0; auto const action = mir_pointer_action_motion; auto const relative_x_value = 0.0; auto const relative_y_value = 0.0; auto const drag_event = mev::make_event(device_id, timestamp, cookie, modifiers, action, mir_pointer_button_tertiary, x_axis_value, y_axis_value, hscroll_value, vscroll_value, relative_x_value, relative_y_value); server.the_shell()->handle(*drag_event); } void wait_for_arbitrary_change(MirSurface* surface) { auto const new_title = __PRETTY_FUNCTION__; EXPECT_CALL(surface_observer, renamed(StrEq(new_title))). WillOnce(InvokeWithoutArgs([&]{ change_observed(); })); signal_change.reset(); auto const spec = mir_create_surface_spec(connection); mir_surface_spec_set_name(spec, new_title); mir_surface_apply_spec(surface, spec); mir_surface_spec_release(spec); signal_change.wait_for(1s); } private: std::shared_ptr shell; mt::Signal signal_change; std::vector cookie; void init_pixel_format() { unsigned int valid_formats{0}; mir_connection_get_available_surface_formats(connection, &pixel_format, 1, &valid_formats); } }; struct SurfaceSpecificationCase : SurfaceSpecification, ::testing::WithParamInterface {}; using SurfaceWithoutParent = SurfaceSpecificationCase; using SurfaceNeedingParent = SurfaceSpecificationCase; using SurfaceMayHaveParent = SurfaceSpecificationCase; MATCHER(IsValidSurface, "") { if (arg == nullptr) return false; if (!mir_surface_is_valid(arg)) return false; return true; } MATCHER_P(WidthEq, value, "") { return Width(value) == arg.width; } MATCHER_P(HeightEq, value, "") { return Height(value) == arg.height; } } TEST_F(SurfaceSpecification, surface_spec_min_width_is_respected) { auto const min_width = 17; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_min_width(spec, min_width); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; EXPECT_CALL(surface_observer, resized_to(WidthEq(min_width))); generate_alt_click_at(bottom_right); generate_alt_move_to(shell_surface->top_left()); } TEST_F(SurfaceSpecification, surface_spec_min_height_is_respected) { auto const min_height = 19; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_min_height(spec, min_height); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; EXPECT_CALL(surface_observer, resized_to(HeightEq(min_height))); generate_alt_click_at(bottom_right); generate_alt_move_to(shell_surface->top_left()); } TEST_F(SurfaceSpecification, surface_spec_max_width_is_respected) { auto const max_width = 23; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_max_width(spec, max_width); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; EXPECT_CALL(surface_observer, resized_to(WidthEq(max_width))); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaX(max_width)); } TEST_F(SurfaceSpecification, surface_spec_max_height_is_respected) { auto const max_height = 29; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_max_height(spec, max_height); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; EXPECT_CALL(surface_observer, resized_to(HeightEq(max_height))); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaY(max_height)); } TEST_F(SurfaceSpecification, surface_spec_width_inc_is_respected) { auto const width_inc = 13; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_width_increment(spec, width_inc); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaX(16)); EXPECT_TRUE(actual.width.as_int() % width_inc == 0); } TEST_F(SurfaceSpecification, surface_spec_with_min_width_and_width_inc_is_respected) { auto const width_inc = 13; auto const min_width = 7; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_width_increment(spec, width_inc); mir_surface_spec_set_min_width(spec, min_width); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaX(16)); EXPECT_TRUE((actual.width.as_int() - min_width) % width_inc == 0); } TEST_F(SurfaceSpecification, surface_spec_height_inc_is_respected) { auto const height_inc = 13; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_height_increment(spec, height_inc); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaY(16)); EXPECT_TRUE(actual.height.as_int() % height_inc == 0); } TEST_F(SurfaceSpecification, surface_spec_with_min_height_and_height_inc_is_respected) { auto const height_inc = 13; auto const min_height = 7; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_height_increment(spec, height_inc); mir_surface_spec_set_min_height(spec, min_height); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaY(16)); EXPECT_TRUE((actual.height.as_int() - min_height) % height_inc == 0); } TEST_F(SurfaceSpecification, surface_spec_with_min_aspect_ratio_is_respected) { auto const aspect_width = 11; auto const aspect_height = 7; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_min_aspect_ratio(spec, aspect_width, aspect_height); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; auto const bottom_left = shell_surface->input_bounds().bottom_left() + Displacement{1,-1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_left); EXPECT_THAT(actual.width.as_float()/actual.height.as_float(), Ge(float(aspect_width)/aspect_height)); } TEST_F(SurfaceSpecification, surface_spec_with_max_aspect_ratio_is_respected) { auto const aspect_width = 7; auto const aspect_height = 11; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_max_aspect_ratio(spec, aspect_width, aspect_height); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; auto const top_right = shell_surface->input_bounds().top_right() - Displacement{1,-1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(top_right); EXPECT_THAT(actual.width.as_float()/actual.height.as_float(), Le(float(aspect_width)/aspect_height)); } TEST_F(SurfaceSpecification, surface_spec_with_fixed_aspect_ratio_and_size_range_is_respected) { auto const aspect_width = 11; auto const aspect_height = 7; auto const min_width = 10*aspect_width; auto const min_height = 10*aspect_height; auto const max_width = 20*aspect_width; auto const max_height = 20*aspect_height; auto const width_inc = 11; auto const height_inc = 7; auto const expected_aspect_ratio = FloatEq(float(aspect_width)/aspect_height); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_min_aspect_ratio(spec, aspect_width, aspect_height); mir_surface_spec_set_max_aspect_ratio(spec, aspect_width, aspect_height); mir_surface_spec_set_min_height(spec, min_height); mir_surface_spec_set_min_width(spec, min_width); mir_surface_spec_set_max_height(spec, max_height); mir_surface_spec_set_max_width(spec, max_width); mir_surface_spec_set_width_increment(spec, width_inc); mir_surface_spec_set_height_increment(spec, height_inc); mir_surface_spec_set_height(spec, min_height); mir_surface_spec_set_width(spec, min_width); }); auto const shell_surface = latest_shell_surface(); shell_surface->add_observer(mt::fake_shared(surface_observer)); Size actual; EXPECT_CALL(surface_observer, resized_to(_)).Times(AnyNumber()).WillRepeatedly(SaveArg<0>(&actual)); for (int delta = 1; delta != 20; ++delta) { auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; // Introduce small variation around "accurate" resize motion auto const jitter = Displacement{delta%2 ? +2 : -2, (delta/2)%2 ? +2 : -2}; auto const motion = Displacement{width_inc, height_inc} + jitter; generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + motion); Size const expected_size{ std::min(max_width, min_width + delta*width_inc), std::min(max_height, min_height + delta*height_inc)}; EXPECT_THAT(actual.width.as_float()/actual.height.as_float(), expected_aspect_ratio); EXPECT_THAT(actual, Eq(expected_size)); }; } TEST_P(SurfaceWithoutParent, not_setting_parent_succeeds) { auto const type = GetParam(); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); EXPECT_THAT(surface, IsValidSurface()); } TEST_P(SurfaceWithoutParent, setting_parent_fails) { auto const type = GetParam(); auto const parent = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_parent(spec, parent); }); EXPECT_THAT(surface, Not(IsValidSurface())); } TEST_P(SurfaceNeedingParent, setting_parent_succeeds) { auto const type = GetParam(); auto const parent = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_parent(spec, parent); }); EXPECT_THAT(surface, IsValidSurface()); } TEST_P(SurfaceNeedingParent, not_setting_parent_fails) { auto const type = GetParam(); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); EXPECT_THAT(surface, Not(IsValidSurface())); } TEST_P(SurfaceMayHaveParent, setting_parent_succeeds) { auto const type = GetParam(); auto const parent = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_parent(spec, parent); }); EXPECT_THAT(surface, IsValidSurface()); } TEST_P(SurfaceMayHaveParent, not_setting_parent_succeeds) { auto const type = GetParam(); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); EXPECT_THAT(surface, IsValidSurface()); } INSTANTIATE_TEST_CASE_P(SurfaceSpecification, SurfaceWithoutParent, Values(mir_surface_type_utility, mir_surface_type_normal)); INSTANTIATE_TEST_CASE_P(SurfaceSpecification, SurfaceNeedingParent, Values(mir_surface_type_satellite, mir_surface_type_gloss, mir_surface_type_tip)); INSTANTIATE_TEST_CASE_P(SurfaceSpecification, SurfaceMayHaveParent, Values(mir_surface_type_dialog, mir_surface_type_freestyle)); ./tests/acceptance-tests/test_debug_api.cpp0000644000015600001650000001364612676616125021175 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/scene/session.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/scene/coordinate_translator.h" #include "mir/shell/shell_wrapper.h" #include "mir_test_framework/headless_test.h" #include "mir_test_framework/any_surface.h" #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/debug/surface.h" #include #include namespace ms = mir::scene; namespace mf = mir::frontend; namespace msh = mir::shell; namespace mtf = mir_test_framework; namespace { class SimpleConfigurablePlacementShell : public msh::ShellWrapper { public: using msh::ShellWrapper::ShellWrapper; mir::frontend::SurfaceId create_surface( std::shared_ptr const& session, ms::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override { auto const result = msh::ShellWrapper::create_surface(session, params, sink); auto const surface = session->surface(result); surface->move_to(placement.top_left); surface->resize(placement.size); return result; } mir::geometry::Rectangle placement{{0, 0}, {100, 100}}; }; char const* const debugenv = "MIR_SERVER_DEBUG"; mir::geometry::Point const testpoint{13, 7}; void dont_kill_me_bro(MirConnection* /*unused*/, MirLifecycleState /*unused*/, void* /*unused*/) { } class SimpleCoordinateTranslator : public mir::scene::CoordinateTranslator { public: mir::geometry::Point surface_to_screen( std::shared_ptr /*surface*/, int32_t /*x*/, int32_t /*y*/) override { return testpoint; } }; class DebugAPI : public mtf::HeadlessTest { public: void SetUp() override { add_to_environment("MIR_SERVER_NO_FILE", ""); server.wrap_shell([&](std::shared_ptr const& wrapped) { return placement_strategy = std::make_shared(wrapped); }); mtf::HeadlessTest::SetUp(); } void set_surface_placement(mir::geometry::Rectangle const& where) { placement_strategy->placement = where; } void start_server_with_debug(bool debug) { if (debug) { add_to_environment(debugenv, ""); } else { add_to_environment(debugenv, nullptr); } start_server(); connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); if (!mir_connection_is_valid(connection)) { throw std::runtime_error{std::string{"Failed to connect to test server:"} + mir_connection_get_error_message(connection)}; } mir_connection_set_lifecycle_event_callback(connection, dont_kill_me_bro, nullptr); } void TearDown() override { placement_strategy.reset(); if (connection) { mir_connection_release(connection); } stop_server(); mtf::HeadlessTest::TearDown(); } MirConnection* connection{nullptr}; private: std::shared_ptr placement_strategy; }; } TEST_F(DebugAPI, translates_surface_coordinates_to_screen_coordinates) { start_server_with_debug(true); mir::geometry::Rectangle surface_location{{200, 100}, {800, 600}}; set_surface_placement(surface_location); auto surf = mtf::make_any_surface(connection); ASSERT_TRUE(mir_surface_is_valid(surf)); int screen_x, screen_y, x, y; x = 35, y = 21; ASSERT_TRUE(mir_debug_surface_coords_to_screen(surf, x, y, &screen_x, &screen_y)); EXPECT_EQ(x + surface_location.top_left.x.as_int(), screen_x); EXPECT_EQ(y + surface_location.top_left.y.as_int(), screen_y); mir_surface_release_sync(surf); surface_location.top_left = {100, 250}; set_surface_placement(surface_location); surf = mtf::make_any_surface(connection); ASSERT_TRUE(mir_surface_is_valid(surf)); ASSERT_TRUE(mir_debug_surface_coords_to_screen(surf, x, y, &screen_x, &screen_y)); EXPECT_EQ(x + surface_location.top_left.x.as_int(), screen_x); EXPECT_EQ(y + surface_location.top_left.y.as_int(), screen_y); mir_surface_release_sync(surf); } TEST_F(DebugAPI, is_unavailable_when_server_not_started_with_debug) { start_server_with_debug(false); auto surf = mtf::make_any_surface(connection); ASSERT_TRUE(mir_surface_is_valid(surf)); int screen_x, screen_y; EXPECT_FALSE(mir_debug_surface_coords_to_screen(surf, 0, 0, &screen_x, &screen_y)); mir_surface_release_sync(surf); } TEST_F(DebugAPI, is_overrideable) { server.override_the_coordinate_translator([&]() ->std::shared_ptr { return std::make_shared(); }); start_server_with_debug(false); auto surf = mtf::make_any_surface(connection); ASSERT_TRUE(mir_surface_is_valid(surf)); int screen_x, screen_y; EXPECT_TRUE(mir_debug_surface_coords_to_screen(surf, 0, 0, &screen_x, &screen_y)); EXPECT_EQ(testpoint.x.as_int(), screen_x); EXPECT_EQ(testpoint.y.as_int(), screen_y); mir_surface_release_sync(surf); } ./tests/acceptance-tests/precompiled.hpp0000644000015600001650000000163612676616125020523 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_ACCEPTANCE_TESTS_PRECOMPILED_H_ #define MIR_ACCEPTANCE_TESTS_PRECOMPILED_H_ #include #include #include #include #include #include #endif ./tests/acceptance-tests/test_latency.cpp0000644000015600001650000002540112676616157020712 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/graphics/buffer.h" #include "mir/optional_value.h" #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/debug/surface.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/test/doubles/null_display.h" #include "mir/test/doubles/null_display_buffer.h" #include "mir/test/doubles/null_display_sync_group.h" #include "mir/test/fake_shared.h" #include "mir_test_framework/visible_surface.h" #include "mir/options/option.h" #include "mir/test/doubles/null_logger.h" // for mtd::logging_opt #include #include #include #include #include namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mt = mir::test; namespace mg = mir::graphics; namespace { unsigned int const expected_client_buffers = 3; int const refresh_rate = 60; std::chrono::microseconds const vblank_interval(1000000/refresh_rate); class Stats { public: void post() { std::lock_guard lock{mutex}; post_count++; posted.notify_one(); } void record_submission(uint32_t submission_id) { std::lock_guard lock{mutex}; submissions.push_back({submission_id, post_count}); } auto latency_for(uint32_t submission_id) { std::lock_guard lock{mutex}; mir::optional_value latency; for (auto i = submissions.begin(); i != submissions.end(); i++) { if (i->buffer_id == submission_id) { // The server is skipping a frame we gave it, which may or // may not be a bug. TODO: investigate. // EXPECT_TRUE(i == submissions.begin()); // Fix it up so our stats aren't skewed by the miss: if (i != submissions.begin()) i = submissions.erase(submissions.begin(), i); latency = post_count - i->time; submissions.erase(i); break; } } return latency; } bool wait_for_posts(unsigned int count, std::chrono::milliseconds timeout) { std::unique_lock lock(mutex); auto const deadline = std::chrono::system_clock::now() + timeout; while (post_count < count) { if (posted.wait_until(lock, deadline) == std::cv_status::timeout) return false; } return true; } private: std::mutex mutex; std::condition_variable posted; unsigned int post_count{0}; // Note that a buffer_id may appear twice in the list as the client is // faster than the compositor and can produce a new frame before the // compositor has measured the previous submisson of the same buffer id. struct Submission { uint32_t buffer_id; uint32_t time; }; std::deque submissions; }; /* * Note: we're not aiming to check performance in terms of CPU or GPU time processing * the incoming buffers. Rather, we're checking that we don't have any intrinsic * latency introduced by the swapping algorithms. */ class IdCollectingDB : public mtd::NullDisplayBuffer { public: mir::geometry::Rectangle view_area() const override { return {{0,0}, {1920, 1080}}; } bool post_renderables_if_optimizable(mg::RenderableList const& renderables) override { //the surface will be the frontmost of the renderables if (!renderables.empty()) last = renderables.front()->buffer()->id(); return true; } mg::BufferID last_id() { return last; } private: mg::BufferID last{0}; }; class TimeTrackingGroup : public mtd::NullDisplaySyncGroup { public: TimeTrackingGroup(Stats& stats) : stats{stats} {} void for_each_display_buffer(std::function const& f) override { f(db); } void post() override { auto latency = stats.latency_for(db.last_id().as_value()); if (latency.is_set()) { std::lock_guard lock{mutex}; latency_list.push_back(latency.value()); if (latency.value() > max) max = latency.value(); } stats.post(); /* * Sleep a little to make the test more realistic. This way the * client will actually fill the buffer queue. If we don't do this, * then it's like having an infinite refresh rate and the measured * latency would never exceed 1.0. (LP: #1447947) */ std::this_thread::sleep_for(vblank_interval); } float average_latency() { std::lock_guard lock{mutex}; unsigned int sum {0}; for(auto& s : latency_list) sum += s; return static_cast(sum) / latency_list.size(); } void dump_latency() { FILE* file = stdout; // gtest seems to use this char const prefix[] = "[ debug ] "; unsigned const size = latency_list.size(); unsigned const cols = 10u; fprintf(file, "%s%u frames sampled, averaging %.1f frames latency\n", prefix, size, average_latency()); for (unsigned i = 0; i < size; ++i) { if ((i % cols) == 0) fprintf(file, "%s%2u:", prefix, i); fprintf(file, " %2d", latency_list[i]); if ((i % cols) == cols-1) fprintf(file, "\n"); } if (size % cols) fprintf(file, "\n"); } unsigned int max_latency() const { return max; } private: std::mutex mutex; std::vector latency_list; unsigned int max = 0; Stats& stats; IdCollectingDB db; }; struct TimeTrackingDisplay : mtd::NullDisplay { TimeTrackingDisplay(Stats& stats) : group{stats} { } void for_each_display_sync_group(std::function const& f) override { f(group); } TimeTrackingGroup group; }; struct ClientLatency : mtf::ConnectedClientHeadlessServer { void SetUp() override { preset_display(mt::fake_shared(display)); mtf::ConnectedClientHeadlessServer::SetUp(); auto del = [] (MirSurfaceSpec* spec) { mir_surface_spec_release(spec); }; std::unique_ptr spec( mir_connection_create_spec_for_normal_surface( connection, 100, 100, mir_pixel_format_abgr_8888), del); visible_surface = std::make_unique(spec.get()); surface = *visible_surface; } void TearDown() override { visible_surface.reset(); mtf::ConnectedClientHeadlessServer::TearDown(); } Stats stats; TimeTrackingDisplay display{stats}; unsigned int test_submissions{100}; // We still have a margin for error here. The client and server will // be scheduled somewhat unpredictably which affects results. Also // affecting results will be the first few frames before the buffer // quere is full (during which there will be no buffer latency). float const error_margin = 0.4f; std::unique_ptr visible_surface; MirSurface* surface; }; } TEST_F(ClientLatency, triple_buffered_client_has_less_than_two_frames_latency) { using namespace testing; auto stream = mir_surface_get_buffer_stream(surface); for(auto i = 0u; i < test_submissions; i++) { auto submission_id = mir_debug_surface_current_buffer_id(surface); stats.record_submission(submission_id); mir_buffer_stream_swap_buffers_sync(stream); } ASSERT_TRUE(stats.wait_for_posts(test_submissions, std::chrono::seconds(60))); // Note: Using the "early release" optimization without dynamic queue // scaling enabled makes the expected latency possibly up to // nbuffers instead of nbuffers-1. After dynamic queue scaling is // enabled, the average will be lower than this. float const expected_max_latency = expected_client_buffers; if (server.get_options()->get(mtd::logging_opt)) display.group.dump_latency(); auto observed_latency = display.group.average_latency(); EXPECT_THAT(observed_latency, Lt(expected_max_latency+error_margin)); } TEST_F(ClientLatency, latency_is_limited_to_nbuffers) { using namespace testing; auto stream = mir_surface_get_buffer_stream(surface); for(auto i = 0u; i < test_submissions; i++) { auto submission_id = mir_debug_surface_current_buffer_id(surface); stats.record_submission(submission_id); mir_buffer_stream_swap_buffers_sync(stream); } ASSERT_TRUE(stats.wait_for_posts(test_submissions, std::chrono::seconds(60))); auto max_latency = display.group.max_latency(); EXPECT_THAT(max_latency, Le(expected_client_buffers)); } TEST_F(ClientLatency, throttled_input_rate_yields_lower_latency) { using namespace testing; int const throttled_input_rate = refresh_rate - 1; std::chrono::microseconds const input_interval(1000000/throttled_input_rate); auto next_input_event = std::chrono::high_resolution_clock::now(); auto stream = mir_surface_get_buffer_stream(surface); for (auto i = 0u; i < test_submissions; i++) { std::this_thread::sleep_until(next_input_event); next_input_event += input_interval; auto submission_id = mir_debug_surface_current_buffer_id(surface); stats.record_submission(submission_id); mir_buffer_stream_swap_buffers_sync(stream); } ASSERT_TRUE(stats.wait_for_posts(test_submissions, std::chrono::seconds(60))); if (server.get_options()->get(mtd::logging_opt)) display.group.dump_latency(); // As the client is producing frames slower than the compositor consumes // them, the buffer queue never fills. So latency is low; float const observed_latency = display.group.average_latency(); EXPECT_THAT(observed_latency, Ge(0.0f)); EXPECT_THAT(observed_latency, Le(1.0f + error_margin)); } ./tests/acceptance-tests/test_server_shutdown.cpp0000644000015600001650000001605112676616125022510 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/fatal.h" #include "mir/server.h" #include "mir_test_framework/interprocess_client_server_test.h" #include "mir_test_framework/process.h" #include "mir_test_framework/temporary_environment_value.h" #include "mir_test_framework/executable_path.h" #include "mir/test/doubles/null_logger.h" #include namespace mt = mir::test; namespace mtf = mir_test_framework; namespace mtd = mt::doubles; using ServerShutdown = mtf::InterprocessClientServerTest; namespace { bool file_exists(std::string const& filename) { struct stat statbuf; return 0 == stat(filename.c_str(), &statbuf); } } TEST_F(ServerShutdown, normal_exit_removes_endpoint) { run_in_server([]{}); if (is_test_process()) { ASSERT_TRUE(file_exists(mir_test_socket)); stop_server(); EXPECT_FALSE(file_exists(mir_test_socket)); } } // Regression test for LP: #1528135 TEST(ServerShutdownWithException, clean_shutdown_on_plugin_construction_exception) { char const* argv = "ServerShutdownWithException"; mtf::TemporaryEnvironmentValue graphics_platform("MIR_SERVER_PLATFORM_GRAPHICS_LIB", mtf::server_platform("graphics-throw.so").c_str()); mtf::TemporaryEnvironmentValue input_platform("MIR_SERVER_PLATFORM_INPUT_LIB", mtf::server_platform("input-stub.so").c_str()); mir::Server server; server.add_configuration_option(mtd::logging_opt, mtd::logging_descr, false); server.set_command_line_handler([](int, char const* const*){}); server.set_command_line(0, &argv); server.apply_settings(); server.run(); } using ServerShutdownDeathTest = ServerShutdown; TEST_F(ServerShutdownDeathTest, abort_removes_endpoint) { mt::CrossProcessSync sync; run_in_server([&] { sync.wait_for_signal_ready_for(); abort(); }); if (is_test_process()) { ASSERT_TRUE(file_exists(mir_test_socket)); sync.signal_ready(); auto result = wait_for_shutdown_server_process(); EXPECT_EQ(mtf::TerminationReason::child_terminated_by_signal, result.reason); // Under valgrind the server process is reported as being terminated // by SIGKILL because of multithreading madness. // TODO: Investigate if we can do better than this workaround EXPECT_TRUE(result.signal == SIGABRT || result.signal == SIGKILL); EXPECT_FALSE(file_exists(mir_test_socket)); } } TEST_F(ServerShutdownDeathTest, fatal_error_abort_causes_abort_on_fatal_error) { // Change the fatal error strategy before starting the Mir server mir::FatalErrorStrategy on_error{mir::fatal_error_abort}; mt::CrossProcessSync sync; run_in_server([&] { sync.wait_for_signal_ready_for(); mir::fatal_error("Bang"); }); if (is_test_process()) { sync.signal_ready(); auto result = wait_for_shutdown_server_process(); EXPECT_EQ(mtf::TerminationReason::child_terminated_by_signal, result.reason); // Under valgrind the server process is reported as being terminated // by SIGKILL because of multithreading madness. // TODO: Investigate if we can do better than this workaround EXPECT_TRUE(result.signal == SIGABRT || result.signal == SIGKILL); } } TEST_F(ServerShutdownDeathTest, fatal_error_abort_removes_endpoint) { // Even fatal errors sometimes need to be caught for critical cleanup... mir::FatalErrorStrategy on_error{mir::fatal_error_abort}; mt::CrossProcessSync sync; run_in_server([&] { sync.wait_for_signal_ready_for(); mir::fatal_error("Bang"); }); if (is_test_process()) { ASSERT_TRUE(file_exists(mir_test_socket)); sync.signal_ready(); wait_for_shutdown_server_process(); EXPECT_FALSE(file_exists(mir_test_socket)); }; } TEST_F(ServerShutdownDeathTest, on_fatal_error_abort_option_causes_abort_on_fatal_error) { add_to_environment( "MIR_SERVER_ON_FATAL_ERROR_ABORT", ""); mt::CrossProcessSync sync; run_in_server([&] { sync.wait_for_signal_ready_for(); mir::fatal_error("Bang"); }); if (is_test_process()) { sync.signal_ready(); auto result = wait_for_shutdown_server_process(); EXPECT_EQ(mtf::TerminationReason::child_terminated_by_signal, result.reason); // Under valgrind the server process is reported as being terminated // by SIGKILL because of multithreading madness. // TODO: Investigate if we can do better than this workaround EXPECT_TRUE(result.signal == SIGABRT || result.signal == SIGKILL); } } TEST_F(ServerShutdownDeathTest, mir_fatal_error_during_init_removes_endpoint) { // Even fatal errors sometimes need to be caught for critical cleanup... add_to_environment("MIR_SERVER_FILE", mir_test_socket); server.add_init_callback([&] { mir::fatal_error("Bang"); }); server.apply_settings(); mt::CrossProcessSync sync; if (auto const pid = fork()) { auto const server_process = std::make_shared(pid); sync.signal_ready(); auto result = server_process->wait_for_termination(); EXPECT_EQ(mtf::TerminationReason::child_terminated_normally, result.reason); EXPECT_FALSE(file_exists(mir_test_socket)); } else { sync.wait_for_signal_ready_for(); server.run(); } } struct OnSignalDeathTest : ServerShutdown, ::testing::WithParamInterface {}; TEST_P(OnSignalDeathTest, removes_endpoint) { mt::CrossProcessSync sync; run_in_server([&] { sync.wait_for_signal_ready_for(); raise(GetParam()); }); if (is_test_process()) { ASSERT_TRUE(file_exists(mir_test_socket)); sync.signal_ready(); auto result = wait_for_shutdown_server_process(); EXPECT_EQ(mtf::TerminationReason::child_terminated_by_signal, result.reason); // Under valgrind the server process is reported as being terminated // by SIGKILL because of multithreading madness // TODO: Investigate if we can do better than this workaround EXPECT_TRUE(result.signal == GetParam() || result.signal == SIGKILL); EXPECT_FALSE(file_exists(mir_test_socket)); } } INSTANTIATE_TEST_CASE_P(ServerShutdown, OnSignalDeathTest, ::testing::Values(SIGQUIT, SIGABRT, SIGFPE, SIGSEGV, SIGBUS)); ./tests/acceptance-tests/test_nested_mir.cpp0000644000015600001650000013356212676616157021414 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/frontend/session_mediator_report.h" #include "mir/graphics/platform.h" #include "mir/graphics/cursor_image.h" #include "mir/input/cursor_images.h" #include "mir/graphics/display.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/display_configuration_policy.h" #include "mir/graphics/display_configuration_report.h" #include "mir/input/cursor_listener.h" #include "mir/cached_ptr.h" #include "mir/main_loop.h" #include "mir/scene/session_coordinator.h" #include "mir/scene/session.h" #include "mir/shell/display_configuration_controller.h" #include "mir/shell/host_lifecycle_event_listener.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/headless_nested_server_runner.h" #include "mir_test_framework/any_surface.h" #include "mir/test/wait_condition.h" #include "mir/test/spin_wait.h" #include "mir/test/display_config_matchers.h" #include "mir/test/doubles/fake_display.h" #include "mir/test/doubles/stub_cursor.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/signal.h" #include "mir/test/doubles/nested_mock_egl.h" #include "mir/test/fake_shared.h" #include #include namespace geom = mir::geometry; namespace mf = mir::frontend; namespace mg = mir::graphics; namespace msh = mir::shell; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; using namespace testing; using namespace std::chrono_literals; namespace { struct MockSessionMediatorReport : mf::SessionMediatorReport { MockSessionMediatorReport() { EXPECT_CALL(*this, session_connect_called(_)).Times(AnyNumber()); EXPECT_CALL(*this, session_disconnect_called(_)).Times(AnyNumber()); // These are not needed for the 1st test, but they will be soon EXPECT_CALL(*this, session_create_surface_called(_)).Times(AnyNumber()); EXPECT_CALL(*this, session_release_surface_called(_)).Times(AnyNumber()); EXPECT_CALL(*this, session_next_buffer_called(_)).Times(AnyNumber()); EXPECT_CALL(*this, session_submit_buffer_called(_)).Times(AnyNumber()); } MOCK_METHOD1(session_connect_called, void (std::string const&)); MOCK_METHOD1(session_create_surface_called, void (std::string const&)); MOCK_METHOD1(session_next_buffer_called, void (std::string const&)); MOCK_METHOD1(session_exchange_buffer_called, void (std::string const&)); MOCK_METHOD1(session_submit_buffer_called, void (std::string const&)); MOCK_METHOD1(session_allocate_buffers_called, void (std::string const&)); MOCK_METHOD1(session_release_buffers_called, void (std::string const&)); MOCK_METHOD1(session_release_surface_called, void (std::string const&)); MOCK_METHOD1(session_disconnect_called, void (std::string const&)); MOCK_METHOD2(session_start_prompt_session_called, void (std::string const&, pid_t)); MOCK_METHOD1(session_stop_prompt_session_called, void (std::string const&)); void session_configure_surface_called(std::string const&) override {}; void session_configure_surface_cursor_called(std::string const&) override {}; void session_configure_display_called(std::string const&) override {}; void session_set_base_display_configuration_called(std::string const&) override {}; void session_create_buffer_stream_called(std::string const&) override {} void session_release_buffer_stream_called(std::string const&) override {} void session_error(const std::string&, const char*, const std::string&) override {}; }; struct MockCursor : public mtd::StubCursor { MOCK_METHOD1(show, void(mg::CursorImage const&)); }; struct MockHostLifecycleEventListener : msh::HostLifecycleEventListener { MOCK_METHOD1(lifecycle_event_occurred, void (MirLifecycleState)); }; struct MockDisplayConfigurationReport : public mg::DisplayConfigurationReport { MOCK_METHOD1(initial_configuration, void (mg::DisplayConfiguration const& configuration)); MOCK_METHOD1(new_configuration, void (mg::DisplayConfiguration const& configuration)); }; std::vector const display_geometry { {{ 0, 0}, { 640, 480}}, {{640, 0}, {1920, 1080}} }; std::chrono::seconds const timeout{10}; // We can't rely on the test environment to have X cursors, so we provide some of our own auto const cursor_names = { // mir_disabled_cursor_name, mir_arrow_cursor_name, mir_busy_cursor_name, mir_caret_cursor_name, mir_default_cursor_name, mir_pointing_hand_cursor_name, mir_open_hand_cursor_name, mir_closed_hand_cursor_name, mir_horizontal_resize_cursor_name, mir_vertical_resize_cursor_name, mir_diagonal_resize_bottom_to_top_cursor_name, mir_diagonal_resize_top_to_bottom_cursor_name, mir_omnidirectional_resize_cursor_name, mir_vsplit_resize_cursor_name, mir_hsplit_resize_cursor_name, mir_crosshair_cursor_name }; int const cursor_size = 24; struct CursorImage : mg::CursorImage { CursorImage(char const* name) : id{std::find(begin(cursor_names), end(cursor_names), name) - begin(cursor_names)}, data{{uint8_t(id)}} { } void const* as_argb_8888() const { return data.data(); } geom::Size size() const { return {cursor_size, cursor_size}; } geom::Displacement hotspot() const { return {0, 0}; } ptrdiff_t id; std::array data; }; struct CursorImages : mir::input::CursorImages { public: std::shared_ptr image(std::string const& cursor_name, geom::Size const& /*size*/) { return std::make_shared(cursor_name.c_str()); } }; struct CursorWrapper : mg::Cursor { CursorWrapper(std::shared_ptr const& wrapped) : wrapped{wrapped} {} void show() override { if (!hidden) wrapped->show(); } void show(mg::CursorImage const& cursor_image) override { if (!hidden) wrapped->show(cursor_image); } void hide() override { wrapped->hide(); } void move_to(geom::Point position) override { wrapped->move_to(position); } void set_hidden(bool hidden) { this->hidden = hidden; if (hidden) hide(); else show(); } private: std::shared_ptr const wrapped; bool hidden{false}; }; struct MockDisplayConfigurationPolicy : mg::DisplayConfigurationPolicy { MOCK_METHOD1(apply_to, void (mg::DisplayConfiguration&)); }; struct MockDisplay : mtd::FakeDisplay { using mtd::FakeDisplay::FakeDisplay; MOCK_METHOD1(configure, void (mg::DisplayConfiguration const&)); }; class NestedMirRunner : public mtf::HeadlessNestedServerRunner { public: NestedMirRunner(std::string const& connection_string) : NestedMirRunner(connection_string, true) { start_server(); } virtual ~NestedMirRunner() { stop_server(); } std::shared_ptr the_mock_host_lifecycle_event_listener() { return mock_host_lifecycle_event_listener([] { return std::make_shared>(); }); } std::shared_ptr cursor_wrapper; virtual std::shared_ptr mock_display_configuration_policy() { return mock_display_configuration_policy_([this] { return std::make_shared>(); }); } protected: NestedMirRunner(std::string const& connection_string, bool) : mtf::HeadlessNestedServerRunner(connection_string) { server.override_the_host_lifecycle_event_listener([this] { return the_mock_host_lifecycle_event_listener(); }); server.wrap_cursor([&](std::shared_ptr const& wrapped) { return cursor_wrapper = std::make_shared(wrapped); }); server.override_the_cursor_images([] { return std::make_shared(); }); server.wrap_display_configuration_policy([this] (std::shared_ptr const&) { return mock_display_configuration_policy(); }); } private: mir::CachedPtr mock_host_lifecycle_event_listener; mir::CachedPtr mock_display_configuration_policy_; }; struct NestedServer : mtf::HeadlessInProcessServer { mtd::NestedMockEGL mock_egl; mtf::UsingStubClientPlatform using_stub_client_platform; std::shared_ptr mock_session_mediator_report; NiceMock display{display_geometry}; void SetUp() override { preset_display(mt::fake_shared(display)); server.override_the_session_mediator_report([this] { mock_session_mediator_report = std::make_shared>(); return mock_session_mediator_report; }); server.override_the_display_configuration_report([this] { return the_mock_display_configuration_report(); }); server.wrap_cursor([this](std::shared_ptr const&) { return the_mock_cursor(); }); mtf::HeadlessInProcessServer::SetUp(); } void trigger_lifecycle_event(MirLifecycleState const lifecycle_state) { auto const app = server.the_session_coordinator()->successor_of({}); EXPECT_TRUE(app != nullptr) << "Nested server not connected"; if (app) { app->set_lifecycle_state(lifecycle_state); } } std::shared_ptr the_mock_display_configuration_report() { return mock_display_configuration_report([] { return std::make_shared>(); }); } mir::CachedPtr mock_display_configuration_report; std::shared_ptr the_mock_cursor() { return mock_cursor([] { return std::make_shared>(); }); } mir::CachedPtr mock_cursor; void ignore_rebuild_of_egl_context() { InSequence context_lifecycle; EXPECT_CALL(mock_egl, eglCreateContext(_, _, _, _)).Times(AnyNumber()).WillRepeatedly(Return((EGLContext)this)); EXPECT_CALL(mock_egl, eglDestroyContext(_, _)).Times(AnyNumber()).WillRepeatedly(Return(EGL_TRUE)); } auto hw_display_config_for_unplug() -> std::shared_ptr { auto new_displays = display_geometry; new_displays.resize(1); return std::make_shared(new_displays); } auto hw_display_config_for_plugin() -> std::shared_ptr { auto new_displays = display_geometry; new_displays.push_back({{2560, 0}, { 640, 480}}); return std::make_shared(new_displays); } void change_display_configuration(NestedMirRunner& nested_mir, float scale, MirFormFactor form_factor) { auto const configurator = nested_mir.server.the_display_configuration_controller(); std::shared_ptr config = nested_mir.server.the_display()->configuration(); config->for_each_output([scale, form_factor] (mg::UserDisplayConfigurationOutput& output) { output.scale = scale; output.form_factor = form_factor; }); configurator->set_base_configuration(config); } bool wait_for_display_configuration_change(NestedMirRunner& nested_mir, float expected_scale, MirFormFactor expected_form_factor) { using namespace std::literals::chrono_literals; /* Now, because we have absolutely no idea when the call to set_base_configuration will *actually* * set the base configuration we get to poll configuration() until we see that it's actually changed. */ auto const end_time = std::chrono::steady_clock::now() + 10s; bool done{false}; while (!done && (std::chrono::steady_clock::now() < end_time)) { auto const new_config = nested_mir.server.the_display()->configuration(); new_config->for_each_output([&done, expected_scale, expected_form_factor] (auto const& output) { if (output.scale == expected_scale && output.form_factor == expected_form_factor) { done = true; } }); if (!done) std::this_thread::sleep_for(100ms); } return done; } }; struct Client { explicit Client(NestedMirRunner& nested_mir) : connection(mir_connect_sync(nested_mir.new_connection().c_str(), __PRETTY_FUNCTION__)) {} ~Client() { mir_connection_release(connection); } void update_display_configuration(void (*changer)(MirDisplayConfiguration* config)) { auto const configuration = mir_connection_create_display_config(connection); changer(configuration); mir_wait_for(mir_connection_apply_display_config(connection, configuration)); mir_display_config_destroy(configuration); } void update_display_configuration_applied_to(MockDisplay& display, void (*changer)(MirDisplayConfiguration* config)) { mt::WaitCondition initial_condition; auto const configuration = mir_connection_create_display_config(connection); changer(configuration); EXPECT_CALL(display, configure(Not(mt::DisplayConfigMatches(configuration)))).Times(AnyNumber()) .WillRepeatedly(InvokeWithoutArgs([] {})); EXPECT_CALL(display, configure(mt::DisplayConfigMatches(configuration))) .WillRepeatedly(InvokeWithoutArgs([&] { initial_condition.wake_up_everyone(); })); mir_wait_for(mir_connection_apply_display_config(connection, configuration)); initial_condition.wait_for_at_most_seconds(1); mir_display_config_destroy(configuration); Mock::VerifyAndClearExpectations(&display); ASSERT_TRUE(initial_condition.woken()); } MirConnection* const connection; }; struct ClientWithADisplayChangeCallback : virtual Client { ClientWithADisplayChangeCallback(NestedMirRunner& nested_mir, mir_display_config_callback callback, void* context) : Client(nested_mir) { mir_connection_set_display_config_change_callback(connection, callback, context); } }; struct ClientWithAPaintedSurface : virtual Client { ClientWithAPaintedSurface(NestedMirRunner& nested_mir) : Client(nested_mir), surface(mtf::make_any_surface(connection)) { mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); } ~ClientWithAPaintedSurface() { mir_surface_release_sync(surface); } void update_surface_spec(void (*changer)(MirSurfaceSpec* spec)) { auto const spec = mir_connection_create_spec_for_changes(connection); changer(spec); mir_surface_apply_spec(surface, spec); mir_surface_spec_release(spec); } MirSurface* const surface; }; struct ClientWithAPaintedSurfaceAndABufferStream : virtual Client, ClientWithAPaintedSurface { ClientWithAPaintedSurfaceAndABufferStream(NestedMirRunner& nested_mir) : Client(nested_mir), ClientWithAPaintedSurface(nested_mir), buffer_stream(mir_connection_create_buffer_stream_sync( connection, cursor_size, cursor_size, mir_pixel_format_argb_8888, mir_buffer_usage_software)) { mir_buffer_stream_swap_buffers_sync(buffer_stream); } ~ClientWithAPaintedSurfaceAndABufferStream() { mir_buffer_stream_release_sync(buffer_stream); } MirBufferStream* const buffer_stream; }; struct ClientWithADisplayChangeCallbackAndAPaintedSurface : virtual Client, ClientWithADisplayChangeCallback, ClientWithAPaintedSurface { ClientWithADisplayChangeCallbackAndAPaintedSurface(NestedMirRunner& nested_mir, mir_display_config_callback callback, void* context) : Client(nested_mir), ClientWithADisplayChangeCallback(nested_mir, callback, context), ClientWithAPaintedSurface(nested_mir) { } }; } TEST_F(NestedServer, nested_platform_connects_and_disconnects) { InSequence seq; EXPECT_CALL(*mock_session_mediator_report, session_connect_called(_)).Times(1); EXPECT_CALL(*mock_session_mediator_report, session_disconnect_called(_)).Times(1); NestedMirRunner{new_connection()}; } TEST_F(NestedServer, sees_expected_outputs) { NestedMirRunner nested_mir{new_connection()}; auto const display = nested_mir.server.the_display(); auto const display_config = display->configuration(); std::vector outputs; display_config->for_each_output([&] (mg::UserDisplayConfigurationOutput& output) { outputs.push_back( geom::Rectangle{ output.top_left, output.modes[output.current_mode_index].size}); }); EXPECT_THAT(outputs, ContainerEq(display_geometry)); } TEST_F(NestedServer, shell_sees_set_scaling_factor) { NestedMirRunner nested_mir{new_connection()}; constexpr float expected_scale{2.3f}; constexpr MirFormFactor expected_form_factor{mir_form_factor_tv}; change_display_configuration(nested_mir, expected_scale, expected_form_factor); auto const config_applied = wait_for_display_configuration_change(nested_mir, expected_scale, expected_form_factor); EXPECT_TRUE(config_applied); } TEST_F(NestedServer, client_sees_set_scaling_factor) { NestedMirRunner nested_mir{new_connection()}; constexpr float expected_scale{2.3f}; constexpr MirFormFactor expected_form_factor{mir_form_factor_tv}; change_display_configuration(nested_mir, expected_scale, expected_form_factor); auto const config_applied = wait_for_display_configuration_change(nested_mir, expected_scale, expected_form_factor); EXPECT_TRUE(config_applied); Client client{nested_mir}; auto spec = mir_connection_create_spec_for_normal_surface(client.connection, 800, 600, mir_pixel_format_abgr_8888); mt::Signal surface_event_received; mir_surface_spec_set_event_handler(spec, [](MirSurface*, MirEvent const* event, void* ctx) { if (mir_event_get_type(event) == mir_event_type_surface_output) { auto surface_event = mir_event_get_surface_output_event(event); EXPECT_THAT(mir_surface_output_event_get_form_factor(surface_event), Eq(expected_form_factor)); EXPECT_THAT(mir_surface_output_event_get_scale(surface_event), Eq(expected_scale)); auto signal = static_cast(ctx); signal->raise(); } }, &surface_event_received); auto surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); EXPECT_TRUE(surface_event_received.wait_for(30s)); mir_surface_release_sync(surface); } ////////////////////////////////////////////////////////////////// // TODO the following test was used in investigating lifetime issues. // TODO it may not have much long term value, but decide that later. TEST_F(NestedServer, on_exit_display_objects_should_be_destroyed) { std::weak_ptr my_display; { NestedMirRunner nested_mir{new_connection()}; my_display = nested_mir.server.the_display(); } EXPECT_FALSE(my_display.lock()) << "after run_mir() exits the display should be released"; } TEST_F(NestedServer, receives_lifecycle_events_from_host) { NestedMirRunner nested_mir{new_connection()}; mir::test::WaitCondition events_processed; InSequence seq; EXPECT_CALL(*(nested_mir.the_mock_host_lifecycle_event_listener()), lifecycle_event_occurred(mir_lifecycle_state_resumed)).Times(1); EXPECT_CALL(*(nested_mir.the_mock_host_lifecycle_event_listener()), lifecycle_event_occurred(mir_lifecycle_state_will_suspend)) .WillOnce(WakeUp(&events_processed)); EXPECT_CALL(*(nested_mir.the_mock_host_lifecycle_event_listener()), lifecycle_event_occurred(mir_lifecycle_connection_lost)).Times(AtMost(1)); trigger_lifecycle_event(mir_lifecycle_state_resumed); trigger_lifecycle_event(mir_lifecycle_state_will_suspend); events_processed.wait_for_at_most_seconds(5); } TEST_F(NestedServer, client_may_connect_to_nested_server_and_create_surface) { NestedMirRunner nested_mir{new_connection()}; ClientWithAPaintedSurface client(nested_mir); bool became_exposed_and_focused = mir::test::spin_wait_for_condition_or_timeout( [surface = client.surface] { return mir_surface_get_visibility(surface) == mir_surface_visibility_exposed && mir_surface_get_focus(surface) == mir_surface_focused; }, std::chrono::seconds{10}); EXPECT_TRUE(became_exposed_and_focused); } TEST_F(NestedServer, posts_when_scene_has_visible_changes) { // No post on surface creation EXPECT_CALL(*mock_session_mediator_report, session_submit_buffer_called(_)).Times(0); NestedMirRunner nested_mir{new_connection()}; auto const connection = mir_connect_sync(nested_mir.new_connection().c_str(), __PRETTY_FUNCTION__); auto const surface = mtf::make_any_surface(connection); // NB there is no synchronization to guarantee that a spurious post on surface creation will have // been seen by this point (although in testing it was invariably the case). However, any missed post // would be included in one of the later counts and cause a test failure. Mock::VerifyAndClearExpectations(mock_session_mediator_report.get()); // One post on each output when surface drawn { mt::WaitCondition wait; EXPECT_CALL(*mock_session_mediator_report, session_submit_buffer_called(_)).Times(2) .WillOnce(InvokeWithoutArgs([]{})) .WillOnce(InvokeWithoutArgs([&] { wait.wake_up_everyone(); })); mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); wait.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(mock_session_mediator_report.get()); } // One post on each output when surface released { mt::WaitCondition wait; EXPECT_CALL(*mock_session_mediator_report, session_submit_buffer_called(_)).Times(2) .WillOnce(InvokeWithoutArgs([]{})) .WillOnce(InvokeWithoutArgs([&] { wait.wake_up_everyone(); })); mir_surface_release_sync(surface); mir_connection_release(connection); wait.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(mock_session_mediator_report.get()); } // No post during shutdown EXPECT_CALL(*mock_session_mediator_report, session_submit_buffer_called(_)).Times(0); // Ignore other shutdown events EXPECT_CALL(*mock_session_mediator_report, session_release_surface_called(_)).Times(AnyNumber()); EXPECT_CALL(*mock_session_mediator_report, session_disconnect_called(_)).Times(AnyNumber()); } TEST_F(NestedServer, display_configuration_changes_are_forwarded_to_host) { NestedMirRunner nested_mir{new_connection()}; ClientWithAPaintedSurface client(nested_mir); ignore_rebuild_of_egl_context(); mt::WaitCondition condition; EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(_)) .WillRepeatedly(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); client.update_display_configuration( [](MirDisplayConfiguration* config) { config->outputs->used = false; }); condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); } TEST_F(NestedServer, display_orientation_changes_are_forwarded_to_host) { NestedMirRunner nested_mir{new_connection()}; ClientWithAPaintedSurface client(nested_mir); auto const configuration = mir_connection_create_display_config(client.connection); for (auto new_orientation : {mir_orientation_left, mir_orientation_right, mir_orientation_inverted, mir_orientation_normal, mir_orientation_inverted, mir_orientation_right, mir_orientation_left, mir_orientation_normal}) { // Allow for the egl context getting rebuilt as a side-effect each iteration ignore_rebuild_of_egl_context(); mt::WaitCondition condition; for(auto* output = configuration->outputs; output != configuration->outputs+configuration->num_outputs; ++ output) output->orientation = new_orientation; EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(mt::DisplayConfigMatches(configuration))) .WillRepeatedly(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); mir_wait_for(mir_connection_apply_display_config(client.connection, configuration)); condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); } mir_display_config_destroy(configuration); } TEST_F(NestedServer, animated_cursor_image_changes_are_forwarded_to_host) { int const frames = 10; NestedMirRunner nested_mir{new_connection()}; ClientWithAPaintedSurfaceAndABufferStream client(nested_mir); auto const mock_cursor = the_mock_cursor(); server.the_cursor_listener()->cursor_moved_to(489, 9); // TODO workaround for lp:1523621 // (I don't see a way to detect that the host has placed focus on "Mir nested display for output #1") std::this_thread::sleep_for(10ms); { mt::WaitCondition condition; EXPECT_CALL(*mock_cursor, show(_)).Times(1) .WillRepeatedly(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); auto conf = mir_cursor_configuration_from_buffer_stream(client.buffer_stream, 0, 0); mir_wait_for(mir_surface_configure_cursor(client.surface, conf)); mir_cursor_configuration_destroy(conf); condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(mock_cursor.get()); } for (int i = 0; i != frames; ++i) { mt::WaitCondition condition; EXPECT_CALL(*mock_cursor, show(_)).Times(1) .WillRepeatedly(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); mir_buffer_stream_swap_buffers_sync(client.buffer_stream); condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(mock_cursor.get()); } } TEST_F(NestedServer, named_cursor_image_changes_are_forwarded_to_host) { NestedMirRunner nested_mir{new_connection()}; ClientWithAPaintedSurface client(nested_mir); auto const mock_cursor = the_mock_cursor(); server.the_cursor_listener()->cursor_moved_to(489, 9); // TODO workaround for lp:1523621 // (I don't see a way to detect that the host has placed focus on "Mir nested display for output #1") std::this_thread::sleep_for(10ms); for (auto const name : cursor_names) { mt::WaitCondition condition; EXPECT_CALL(*mock_cursor, show(_)).Times(1) .WillOnce(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); auto const cursor = mir_cursor_configuration_from_name(name); mir_wait_for(mir_surface_configure_cursor(client.surface, cursor)); mir_cursor_configuration_destroy(cursor); condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(mock_cursor.get()); } } TEST_F(NestedServer, can_hide_the_host_cursor) { int const frames = 10; NestedMirRunner nested_mir{new_connection()}; ClientWithAPaintedSurfaceAndABufferStream client(nested_mir); auto const mock_cursor = the_mock_cursor(); server.the_cursor_listener()->cursor_moved_to(489, 9); // TODO workaround for lp:1523621 // (I don't see a way to detect that the host has placed focus on "Mir nested display for output #1") std::this_thread::sleep_for(10ms); { mt::WaitCondition condition; EXPECT_CALL(*mock_cursor, show(_)).Times(1) .WillRepeatedly(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); auto conf = mir_cursor_configuration_from_buffer_stream(client.buffer_stream, 0, 0); mir_wait_for(mir_surface_configure_cursor(client.surface, conf)); mir_cursor_configuration_destroy(conf); condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(mock_cursor.get()); } nested_mir.cursor_wrapper->set_hidden(true); EXPECT_CALL(*mock_cursor, show(_)).Times(0); for (int i = 0; i != frames; ++i) { mir_buffer_stream_swap_buffers_sync(client.buffer_stream); } // Need to verify before test server teardown deletes the // surface as the host cursor then reverts to default. Mock::VerifyAndClearExpectations(mock_cursor.get()); } TEST_F(NestedServer, applies_display_config_on_startup) { mt::WaitCondition condition; auto const expected_config = server.the_display()->configuration(); expected_config->for_each_output([](mg::UserDisplayConfigurationOutput& output) { output.orientation = mir_orientation_inverted;}); EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(mt::DisplayConfigMatches(std::ref(*expected_config)))) .WillRepeatedly(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); struct MyNestedMirRunner : NestedMirRunner { MyNestedMirRunner(std::string const& connection_string) : NestedMirRunner(connection_string, true) { start_server(); } std::shared_ptr mock_display_configuration_policy() override { auto result = std::make_unique(); EXPECT_CALL(*result, apply_to(_)).Times(AnyNumber()) .WillOnce(Invoke([](mg::DisplayConfiguration& config) { config.for_each_output([](mg::UserDisplayConfigurationOutput& output) { output.orientation = mir_orientation_inverted; }); })); return std::shared_ptr(std::move(result)); } } nested_mir{new_connection()}; ClientWithAPaintedSurface client(nested_mir); condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); EXPECT_TRUE(condition.woken()); } TEST_F(NestedServer, base_configuration_change_in_host_is_seen_in_nested) { NestedMirRunner nested_mir{new_connection()}; ClientWithAPaintedSurface client(nested_mir); auto const config_policy = nested_mir.mock_display_configuration_policy(); std::shared_ptr const new_config{server.the_display()->configuration()}; new_config->for_each_output([](mg::UserDisplayConfigurationOutput& output) { output.orientation = mir_orientation_inverted;}); mt::WaitCondition condition; EXPECT_CALL(*config_policy, apply_to(mt::DisplayConfigMatches(std::ref(*new_config)))) .WillOnce(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); server.the_display_configuration_controller()->set_base_configuration(new_config); condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(config_policy.get()); EXPECT_TRUE(condition.woken()); } // lp:1511798 TEST_F(NestedServer, display_configuration_reset_when_application_exits) { NestedMirRunner nested_mir{new_connection()}; ignore_rebuild_of_egl_context(); mt::WaitCondition condition; { ClientWithAPaintedSurface client(nested_mir); { mt::WaitCondition initial_condition; EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(_)) .WillRepeatedly(InvokeWithoutArgs([&] { initial_condition.wake_up_everyone(); })); client.update_display_configuration( [](MirDisplayConfiguration* config) { config->outputs->used = false; }); // Wait for initial config to be applied initial_condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); ASSERT_TRUE(initial_condition.woken()); } EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(_)) .WillRepeatedly(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); } condition.wait_for_at_most_seconds(1); EXPECT_TRUE(condition.woken()); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); } // lp:1511798 TEST_F(NestedServer, display_configuration_reset_when_nested_server_exits) { mt::WaitCondition condition; { NestedMirRunner nested_mir{new_connection()}; ignore_rebuild_of_egl_context(); ClientWithAPaintedSurface client(nested_mir); std::shared_ptr const new_config{nested_mir.server.the_display()->configuration()}; new_config->for_each_output([](mg::UserDisplayConfigurationOutput& output) { output.orientation = mir_orientation_inverted;}); mt::WaitCondition initial_condition; EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(_)) .WillRepeatedly(InvokeWithoutArgs([&] { initial_condition.wake_up_everyone(); })); nested_mir.server.the_display_configuration_controller()->set_base_configuration(new_config); // Wait for initial config to be applied initial_condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); ASSERT_TRUE(initial_condition.woken()); EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(_)) .WillRepeatedly(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); } condition.wait_for_at_most_seconds(1); EXPECT_TRUE(condition.woken()); } TEST_F(NestedServer, when_monitor_unplugs_client_is_notified_of_new_display_configuration) { NestedMirRunner nested_mir{new_connection()}; ignore_rebuild_of_egl_context(); mt::WaitCondition client_config_changed; ClientWithADisplayChangeCallbackAndAPaintedSurface client( nested_mir, [](MirConnection*, void* context) { static_cast(context)->wake_up_everyone(); }, &client_config_changed); auto const new_config = hw_display_config_for_unplug(); display.emit_configuration_change_event(new_config); client_config_changed.wait_for_at_most_seconds(1); ASSERT_TRUE(client_config_changed.woken()); auto const configuration = mir_connection_create_display_config(client.connection); EXPECT_THAT(configuration, mt::DisplayConfigMatches(*new_config)); mir_display_config_destroy(configuration); } TEST_F(NestedServer, given_nested_server_set_base_display_configuration_when_monitor_unplugs_configuration_is_reset) { NestedMirRunner nested_mir{new_connection()}; ignore_rebuild_of_egl_context(); { std::shared_ptr const initial_config{nested_mir.server.the_display()->configuration()}; initial_config->for_each_output([](mg::UserDisplayConfigurationOutput& output) { output.orientation = mir_orientation_inverted;}); mt::WaitCondition initial_condition; EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(_)) .WillRepeatedly(InvokeWithoutArgs([&] { initial_condition.wake_up_everyone(); })); nested_mir.server.the_display_configuration_controller()->set_base_configuration(initial_config); // Wait for initial config to be applied initial_condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); ASSERT_TRUE(initial_condition.woken()); } ClientWithAPaintedSurface client(nested_mir); auto const expect_config = hw_display_config_for_unplug(); mt::WaitCondition condition; EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(mt::DisplayConfigMatches(*expect_config))) .WillOnce(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); display.emit_configuration_change_event(expect_config); condition.wait_for_at_most_seconds(1); EXPECT_TRUE(condition.woken()); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); } // TODO this test needs some core changes before it will pass. C.f. lp:1522802 // Specifically, ms::MediatingDisplayChanger::configure_for_hardware_change() // doesn't reset the session config of the active client (even though it // clears it from MediatingDisplayChanger::config_map). // This is intentional, to avoid redundant reconfigurations, but we should // handle the case of a client failing to apply a new config. TEST_F(NestedServer, DISABLED_given_client_set_display_configuration_when_monitor_unplugs_configuration_is_reset) { NestedMirRunner nested_mir{new_connection()}; ignore_rebuild_of_egl_context(); ClientWithAPaintedSurface client(nested_mir); client.update_display_configuration_applied_to(display, [](MirDisplayConfiguration* config) { config->outputs->used = false; }); auto const expect_config = hw_display_config_for_unplug(); mt::WaitCondition condition; EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(mt::DisplayConfigMatches(*expect_config))) .WillOnce(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); display.emit_configuration_change_event(expect_config); condition.wait_for_at_most_seconds(1); EXPECT_TRUE(condition.woken()); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); } TEST_F(NestedServer, when_monitor_plugged_in_client_is_notified_of_new_display_configuration) { NestedMirRunner nested_mir{new_connection()}; ignore_rebuild_of_egl_context(); mt::WaitCondition client_config_changed; ClientWithADisplayChangeCallbackAndAPaintedSurface client( nested_mir, [](MirConnection*, void* context) { static_cast(context)->wake_up_everyone(); }, &client_config_changed); auto const new_config = hw_display_config_for_plugin(); display.emit_configuration_change_event(new_config); client_config_changed.wait_for_at_most_seconds(1); ASSERT_TRUE(client_config_changed.woken()); // The default layout policy (for cloned displays) will be applied by the MediatingDisplayChanger. // So set the expectation to match mtd::StubDisplayConfig expected_config(*new_config); expected_config.for_each_output([](mg::UserDisplayConfigurationOutput& output) { output.top_left = {0, 0}; }); auto const configuration = mir_connection_create_display_config(client.connection); EXPECT_THAT(configuration, mt::DisplayConfigMatches(expected_config)); mir_display_config_destroy(configuration); } TEST_F(NestedServer, given_nested_server_set_base_display_configuration_when_monitor_plugged_in_configuration_is_reset) { NestedMirRunner nested_mir{new_connection()}; ignore_rebuild_of_egl_context(); { std::shared_ptr const initial_config{nested_mir.server.the_display()->configuration()}; initial_config->for_each_output([](mg::UserDisplayConfigurationOutput& output) { output.orientation = mir_orientation_inverted;}); mt::WaitCondition initial_condition; EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(_)) .WillRepeatedly(InvokeWithoutArgs([&] { initial_condition.wake_up_everyone(); })); nested_mir.server.the_display_configuration_controller()->set_base_configuration(initial_config); // Wait for initial config to be applied initial_condition.wait_for_at_most_seconds(1); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); ASSERT_TRUE(initial_condition.woken()); } ClientWithAPaintedSurface client(nested_mir); auto const new_config = hw_display_config_for_plugin(); // The default layout policy (for cloned displays) will be applied by the MediatingDisplayChanger. // So set the expectation to match mtd::StubDisplayConfig expected_config(*new_config); expected_config.for_each_output([](mg::UserDisplayConfigurationOutput& output) { output.top_left = {0, 0}; }); mt::WaitCondition condition; EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(mt::DisplayConfigMatches(expected_config))) .WillOnce(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); display.emit_configuration_change_event(new_config); condition.wait_for_at_most_seconds(1); EXPECT_TRUE(condition.woken()); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); } // TODO this test needs some core changes before it will pass. C.f. lp:1522802 // Specifically, ms::MediatingDisplayChanger::configure_for_hardware_change() // doesn't reset the session config of the active client (even though it // clears it from MediatingDisplayChanger::config_map). // This is intentional, to avoid redundant reconfigurations, but we should // handle the case of a client failing to apply a new config. TEST_F(NestedServer, DISABLED_given_client_set_display_configuration_when_monitor_plugged_in_configuration_is_reset) { NestedMirRunner nested_mir{new_connection()}; ignore_rebuild_of_egl_context(); ClientWithAPaintedSurface client(nested_mir); client.update_display_configuration_applied_to(display, [](MirDisplayConfiguration* config) { config->outputs->used = false; }); auto const new_config = hw_display_config_for_plugin(); // The default layout policy (for cloned displays) will be applied by the MediatingDisplayChanger. // So set the expectation to match mtd::StubDisplayConfig expected_config(*new_config); expected_config.for_each_output([](mg::UserDisplayConfigurationOutput& output) { output.top_left = {0, 0}; }); mt::WaitCondition condition; EXPECT_CALL(*the_mock_display_configuration_report(), new_configuration(mt::DisplayConfigMatches(expected_config))) .WillOnce(InvokeWithoutArgs([&] { condition.wake_up_everyone(); })); display.emit_configuration_change_event(new_config); condition.wait_for_at_most_seconds(1); EXPECT_TRUE(condition.woken()); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); } TEST_F(NestedServer, given_client_set_display_configuration_when_monitor_unplugs_client_is_notified_of_new_display_configuration) { NestedMirRunner nested_mir{new_connection()}; ignore_rebuild_of_egl_context(); mt::WaitCondition condition; ClientWithADisplayChangeCallbackAndAPaintedSurface client( nested_mir, [](MirConnection*, void* context) { static_cast(context)->wake_up_everyone(); }, &condition); client.update_display_configuration_applied_to(display, [](MirDisplayConfiguration* config) { config->outputs->used = false; }); auto const new_config = hw_display_config_for_unplug(); display.emit_configuration_change_event(new_config); condition.wait_for_at_most_seconds(1); EXPECT_TRUE(condition.woken()); auto const configuration = mir_connection_create_display_config(client.connection); EXPECT_THAT(configuration, mt::DisplayConfigMatches(*new_config)); mir_display_config_destroy(configuration); Mock::VerifyAndClearExpectations(the_mock_display_configuration_report().get()); } TEST_F(NestedServer, given_client_set_display_configuration_when_monitor_unplugs_client_can_set_display_configuration) { NestedMirRunner nested_mir{new_connection()}; ignore_rebuild_of_egl_context(); mt::WaitCondition client_config_changed; ClientWithADisplayChangeCallbackAndAPaintedSurface client( nested_mir, [](MirConnection*, void* context) { static_cast(context)->wake_up_everyone(); }, &client_config_changed); client.update_display_configuration_applied_to(display, [](MirDisplayConfiguration* config) { config->outputs->used = mir_orientation_inverted; }); auto const new_hw_config = hw_display_config_for_unplug(); auto const expected_config = std::make_shared(*new_hw_config); expected_config->for_each_output([](mg::UserDisplayConfigurationOutput& output) { output.orientation = mir_orientation_inverted; }); mt::WaitCondition host_config_change; EXPECT_CALL(display, configure(Not(mt::DisplayConfigMatches(*expected_config)))).Times(AnyNumber()) .WillRepeatedly(InvokeWithoutArgs([] {})); EXPECT_CALL(display, configure(mt::DisplayConfigMatches(*expected_config))) .WillRepeatedly(InvokeWithoutArgs([&] { host_config_change.wake_up_everyone(); })); display.emit_configuration_change_event(new_hw_config); client_config_changed.wait_for_at_most_seconds(1); if (client_config_changed.woken()) { client.update_display_configuration( [](MirDisplayConfiguration* config) { config->outputs->orientation = mir_orientation_inverted; }); } host_config_change.wait_for_at_most_seconds(1); EXPECT_TRUE(host_config_change.woken()); Mock::VerifyAndClearExpectations(&display); } ./tests/acceptance-tests/test_input_device_hub.cpp0000644000015600001650000000546312676616125022570 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/input/input_device_info.h" #include "mir/input/input_device_observer.h" #include "mir/input/input_device_hub.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/fake_input_device.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir/test/wait_condition.h" #include "mir/test/fake_shared.h" #include #include namespace mi = mir::input; namespace mt = mir::test; namespace mtf = mir_test_framework; namespace { struct MockInputDeviceObserver : public mi::InputDeviceObserver { MOCK_METHOD1(device_added, void(std::shared_ptr const& device)); MOCK_METHOD1(device_changed, void(std::shared_ptr const& device)); MOCK_METHOD1(device_removed, void(std::shared_ptr const& device)); MOCK_METHOD0(changes_complete, void()); }; struct TestInputDeviceHub : mtf::HeadlessInProcessServer { MockInputDeviceObserver observer; mt::WaitCondition observer_registered; mt::WaitCondition callbacks_received; std::shared_ptr keep_on_living; }; } TEST_F(TestInputDeviceHub, calls_observers_with_changes_complete_on_registry) { using namespace testing; EXPECT_CALL(observer, changes_complete()) .WillOnce(mt::WakeUp(&observer_registered)); server.the_input_device_hub()->add_observer(mt::fake_shared(observer)); observer_registered.wait_for_at_most_seconds(4); } TEST_F(TestInputDeviceHub, notifies_input_device_observer_about_available_devices) { using namespace testing; InSequence seq; EXPECT_CALL(observer, changes_complete()) .WillOnce(mt::WakeUp(&observer_registered)); EXPECT_CALL(observer, device_added(_)); EXPECT_CALL(observer, changes_complete()) .WillOnce(mt::WakeUp(&callbacks_received)); server.the_input_device_hub()->add_observer(mt::fake_shared(observer)); observer_registered.wait_for_at_most_seconds(4); keep_on_living = mtf::add_fake_input_device(mi::InputDeviceInfo{"keyboard", "keyboard-uid", mir::input::DeviceCapability::keyboard}); callbacks_received.wait_for_at_most_seconds(4); } ./tests/acceptance-tests/test_surface_morphing.cpp0000644000015600001650000003214312676616125022602 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/scene/null_surface_observer.h" #include "mir/scene/surface.h" #include "mir/test/doubles/wrap_shell_to_track_latest_surface.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir/test/fake_shared.h" #include "mir/test/signal.h" #include "mir_toolkit/common.h" #include namespace mf = mir::frontend; namespace mtf = mir_test_framework; namespace ms = mir::scene; namespace msh = mir::shell; namespace mt = mir::test; namespace mtd = mir::test::doubles; using namespace mir::geometry; using namespace testing; using namespace std::chrono_literals; namespace { class SurfaceHandle { public: explicit SurfaceHandle(MirSurface* surface) : surface{surface} {} ~SurfaceHandle() { if (surface) mir_surface_release_sync(surface); } operator MirSurface*() const { return surface; } SurfaceHandle(SurfaceHandle&& that) : surface{that.surface} { that.surface = nullptr; } private: SurfaceHandle(SurfaceHandle const&) = delete; MirSurface* surface; }; class MockSurfaceObserver : public ms::NullSurfaceObserver { public: MOCK_METHOD1(renamed, void(char const*)); MOCK_METHOD2(attrib_changed, void(MirSurfaceAttrib attrib, int value)); }; struct SurfaceMorphing : mtf::ConnectedClientHeadlessServer { SurfaceMorphing() { add_to_environment("MIR_SERVER_ENABLE_INPUT", "OFF"); } Rectangle const first_display {{ 0, 0}, {640, 480}}; Rectangle const second_display{{640, 0}, {640, 480}}; void SetUp() override { initial_display_layout({first_display, second_display}); server.wrap_shell([this](std::shared_ptr const& wrapped) { auto const msc = std::make_shared(wrapped); shell = msc; return msc; }); mtf::ConnectedClientHeadlessServer::SetUp(); init_pixel_format(); } void TearDown() override { shell.reset(); mtf::ConnectedClientHeadlessServer::TearDown(); } std::shared_ptr latest_shell_surface() const { auto const result = shell->latest_surface.lock(); // ASSERT_THAT(result, NotNull()); //<= doesn't compile!? EXPECT_THAT(result, NotNull()); return result; } template SurfaceHandle create_surface(Specifier const& specifier) const { auto const spec = mir_create_surface_spec(connection); specifier(spec); auto const surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); return SurfaceHandle{surface}; } template void change_surface(MirSurface* surface, Specifier const& specifier) { signal_change.reset(); auto const spec = mir_create_surface_spec(connection); specifier(spec); mir_surface_apply_spec(surface, spec); mir_surface_spec_release(spec); signal_change.wait_for(1s); } void wait_for_arbitrary_change(MirSurface* surface) { auto const new_title = __PRETTY_FUNCTION__; EXPECT_CALL(surface_observer, renamed(StrEq(new_title))). WillOnce(InvokeWithoutArgs([&]{ change_observed(); })); change_surface(surface, [&](MirSurfaceSpec* spec) { mir_surface_spec_set_name(spec, new_title); }); } NiceMock surface_observer; void change_observed() { signal_change.raise(); } MirPixelFormat pixel_format{mir_pixel_format_invalid}; static auto const width = 97; static auto const height= 101; private: std::shared_ptr shell; mt::Signal signal_change; void init_pixel_format() { unsigned int valid_formats{0}; mir_connection_get_available_surface_formats(connection, &pixel_format, 1, &valid_formats); } }; struct TypePair { MirSurfaceType from; MirSurfaceType to; friend std::ostream& operator<<(std::ostream& out, TypePair const& types) { return out << "from:" << types.from << ", to:" << types.to; } }; struct SurfaceMorphingCase : SurfaceMorphing, ::testing::WithParamInterface {}; using TargetWithoutParent = SurfaceMorphingCase; using TargetNeedingParent = SurfaceMorphingCase; using TargetMayHaveParent = SurfaceMorphingCase; } TEST_P(TargetWithoutParent, not_setting_parent_succeeds) { auto const old_type = GetParam().from; auto const new_type = GetParam().to; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, old_type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); latest_shell_surface()->add_observer(mt::fake_shared(surface_observer)); EXPECT_CALL(surface_observer, attrib_changed(mir_surface_attrib_type, new_type)). WillOnce(InvokeWithoutArgs([&] { change_observed(); })); change_surface(surface, [&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, new_type); }); } TEST_P(TargetWithoutParent, setting_parent_fails) { auto const old_type = GetParam().from; auto const new_type = GetParam().to; auto const parent = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, old_type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); latest_shell_surface()->add_observer(mt::fake_shared(surface_observer)); EXPECT_CALL(surface_observer, attrib_changed(mir_surface_attrib_type, new_type)). Times(0); change_surface(surface, [&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, new_type); mir_surface_spec_set_parent(spec, parent); // Don't wait for a notification we don't expect // We'll wait for another change change_observed(); }); wait_for_arbitrary_change(surface); } TEST_P(TargetNeedingParent, setting_parent_succeeds) { auto const old_type = GetParam().from; auto const new_type = GetParam().to; auto const parent = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, old_type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); latest_shell_surface()->add_observer(mt::fake_shared(surface_observer)); EXPECT_CALL(surface_observer, attrib_changed(mir_surface_attrib_type, new_type)). WillOnce(InvokeWithoutArgs([&] { change_observed(); })); change_surface(surface, [&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, new_type); mir_surface_spec_set_parent(spec, parent); }); } TEST_P(TargetNeedingParent, not_setting_parent_fails) { auto const old_type = GetParam().from; auto const new_type = GetParam().to; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, old_type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); latest_shell_surface()->add_observer(mt::fake_shared(surface_observer)); EXPECT_CALL(surface_observer, attrib_changed(mir_surface_attrib_type, new_type)). Times(0); change_surface(surface, [&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, new_type); // Don't wait for a notification we don't expect // We'll wait for another change change_observed(); }); wait_for_arbitrary_change(surface); } TEST_P(TargetMayHaveParent, setting_parent_succeeds) { auto const old_type = GetParam().from; auto const new_type = GetParam().to; auto const parent = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, old_type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); latest_shell_surface()->add_observer(mt::fake_shared(surface_observer)); EXPECT_CALL(surface_observer, attrib_changed(mir_surface_attrib_type, new_type)). WillOnce(InvokeWithoutArgs([&] { change_observed(); })); change_surface(surface, [&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, new_type); mir_surface_spec_set_parent(spec, parent); }); } TEST_P(TargetMayHaveParent, not_setting_parent_succeeds) { auto const old_type = GetParam().from; auto const new_type = GetParam().to; auto const parent = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, mir_surface_type_normal); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, old_type); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); latest_shell_surface()->add_observer(mt::fake_shared(surface_observer)); EXPECT_CALL(surface_observer, attrib_changed(mir_surface_attrib_type, new_type)). WillOnce(InvokeWithoutArgs([&] { change_observed(); })); change_surface(surface, [&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, new_type); }); } INSTANTIATE_TEST_CASE_P(SurfaceMorphing, TargetWithoutParent, Values( TypePair{mir_surface_type_normal, mir_surface_type_utility}, TypePair{mir_surface_type_utility, mir_surface_type_normal}, TypePair{mir_surface_type_dialog, mir_surface_type_utility}, TypePair{mir_surface_type_dialog, mir_surface_type_normal} )); INSTANTIATE_TEST_CASE_P(SurfaceMorphing, TargetNeedingParent, Values( TypePair{mir_surface_type_normal, mir_surface_type_satellite}, TypePair{mir_surface_type_utility, mir_surface_type_satellite}, TypePair{mir_surface_type_dialog, mir_surface_type_satellite} )); INSTANTIATE_TEST_CASE_P(SurfaceMorphing, TargetMayHaveParent, Values( TypePair{mir_surface_type_normal, mir_surface_type_dialog}, TypePair{mir_surface_type_utility, mir_surface_type_dialog} )); ./tests/acceptance-tests/server_configuration_options.cpp0000644000015600001650000001436512676616125024226 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir_test_framework/headless_test.h" #include "mir/options/option.h" #include #include #include #include using namespace testing; struct ServerConfigurationOptions : mir_test_framework::HeadlessTest { MOCK_METHOD1(command_line_handler, void(std::vector const&)); void SetUp() override { server.set_command_line_handler([this](int argc, char const* const* argv) { std::vector args; for (auto p = argv; p != argv+argc; ++p) { args.emplace_back(*p); } command_line_handler(args); }); server.add_configuration_option(test_config_key, "", mir::OptionType::string); clean_test_files(); add_to_environment(env_xdg_config_home, fake_xdg_config_home); add_to_environment(env_home, fake_home); add_to_environment(env_xdg_config_dirs, fake_xdg_config_dirs); } void TearDown() override { // In the event of failure leave test files around to aid debugging if(!HasFailure()) clean_test_files(); } static constexpr char const* const env_xdg_config_home = "XDG_CONFIG_HOME"; static constexpr char const* const env_home = "HOME"; static constexpr char const* const env_xdg_config_dirs = "XDG_CONFIG_DIRS"; static constexpr char const* const fake_xdg_config_home = "fake_xdg_config_home"; static constexpr char const* const fake_home = "fake_home"; static constexpr char const* const fake_home_config = "fake_home/.config"; static constexpr char const* const fake_xdg_config_dirs = "fake_xdg_config_dir0:fake_xdg_config_dir1"; static constexpr char const* const fake_xdg_config_dir0 = "fake_xdg_config_dir0"; static constexpr char const* const fake_xdg_config_dir1 = "fake_xdg_config_dir1"; static constexpr char const* const not_found = "not found"; std::string const config_filename{"test.config"}; static constexpr char const* const test_config_key = "config_dir"; void create_config_file_in(char const* dir) { mkdir(dir, 0700); auto const filename = dir + ('/' + config_filename); std::ofstream config(filename); config << test_config_key << '=' << dir << std::endl; } void remove_config_file_in(char const* dir) { remove((dir + ('/' + config_filename)).c_str()); remove(dir); } void clean_test_files() { remove_config_file_in(fake_xdg_config_dir0); remove_config_file_in(fake_xdg_config_dir1); remove_config_file_in(fake_xdg_config_home); remove_config_file_in(fake_home_config); remove(fake_home); } }; TEST_F(ServerConfigurationOptions, unknown_command_line_options_are_passed_to_handler) { const int argc = 10; char const* argv[argc] = { __PRETTY_FUNCTION__, "--enable-input", "no", "--hello", "-f", "test_file", "world", "--offscreen", "--answer", "42" }; server.set_command_line(argc, argv); EXPECT_CALL(*this, command_line_handler( ElementsAre(StrEq("--hello"), StrEq("world"), StrEq("--answer"), StrEq("42")))); server.apply_settings(); } TEST_F(ServerConfigurationOptions, are_read_from_xdg_config_home) { create_config_file_in(fake_xdg_config_home); server.set_config_filename(config_filename); server.apply_settings(); auto const options = server.get_options(); EXPECT_THAT(options->get(test_config_key, not_found), StrEq(fake_xdg_config_home)); } TEST_F(ServerConfigurationOptions, are_read_from_home_config_file) { mkdir(fake_home, 0700); create_config_file_in(fake_home_config); // $HOME is only used if $XDG_CONFIG_HOME isn't set add_to_environment(env_xdg_config_home, nullptr); server.set_config_filename(config_filename); server.apply_settings(); auto const options = server.get_options(); EXPECT_THAT(options->get(test_config_key, not_found), StrEq(fake_home_config)); } TEST_F(ServerConfigurationOptions, are_read_from_xdg_config_dir0_config_file) { create_config_file_in(fake_xdg_config_dir0); server.set_config_filename(config_filename); server.apply_settings(); auto const options = server.get_options(); EXPECT_THAT(options->get(test_config_key, not_found), StrEq(fake_xdg_config_dir0)); } TEST_F(ServerConfigurationOptions, are_read_from_xdg_config_dir1_config_file) { create_config_file_in(fake_xdg_config_dir1); server.set_config_filename(config_filename); server.apply_settings(); auto const options = server.get_options(); EXPECT_THAT(options->get(test_config_key, not_found), StrEq(fake_xdg_config_dir1)); } TEST_F(ServerConfigurationOptions, are_read_from_xdg_config_dir0_before_xdg_config_dir1) { create_config_file_in(fake_xdg_config_dir0); create_config_file_in(fake_xdg_config_dir1); server.set_config_filename(config_filename); server.apply_settings(); auto const options = server.get_options(); EXPECT_THAT(options->get(test_config_key, not_found), StrEq(fake_xdg_config_dir0)); } TEST_F(ServerConfigurationOptions, are_read_from_xdg_config_home_before_xdg_config_dirs) { create_config_file_in(fake_xdg_config_home); create_config_file_in(fake_xdg_config_dir0); create_config_file_in(fake_xdg_config_dir1); server.set_config_filename(config_filename); server.apply_settings(); auto const options = server.get_options(); EXPECT_THAT(options->get(test_config_key, not_found), StrEq(fake_xdg_config_home)); } ./tests/acceptance-tests/test_client_screencast.cpp0000644000015600001650000000652112676616125022740 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/frontend/session_credentials.h" #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/mir_screencast.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir/test/doubles/stub_session_authorizer.h" #include "mir/test/fake_shared.h" #include #include namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mf = mir::frontend; namespace mt = mir::test; namespace { struct MockSessionAuthorizer : public mtd::StubSessionAuthorizer { MOCK_METHOD1(screencast_is_allowed, bool(mf::SessionCredentials const&)); }; struct Screencast : mtf::HeadlessInProcessServer { MirScreencastParameters default_screencast_params { {0, 0, 1, 1}, 1, 1, mir_pixel_format_abgr_8888}; MockSessionAuthorizer mock_authorizer; void SetUp() override { server.override_the_session_authorizer([this] { return mt::fake_shared(mock_authorizer); }); mtf::HeadlessInProcessServer::SetUp(); } }; } // TODO test case(s) showing screencast works. lp:1396681 TEST_F(Screencast, with_invalid_connection_fails) { using namespace testing; auto screencast = mir_connection_create_screencast_sync(nullptr, &default_screencast_params); ASSERT_EQ(nullptr, screencast); } TEST_F(Screencast, with_invalid_params_fails) { using namespace testing; EXPECT_CALL(mock_authorizer, screencast_is_allowed(_)) .WillOnce(Return(true)); auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); MirScreencastParameters params = default_screencast_params; params.width = params.height = 0; auto screencast = mir_connection_create_screencast_sync(connection, ¶ms); ASSERT_EQ(nullptr, screencast); params = default_screencast_params; params.region.width = params.region.height = 0; screencast = mir_connection_create_screencast_sync(connection, ¶ms); ASSERT_EQ(nullptr, screencast); params = default_screencast_params; params.pixel_format = mir_pixel_format_invalid; screencast = mir_connection_create_screencast_sync(connection, ¶ms); ASSERT_EQ(nullptr, screencast); mir_connection_release(connection); } TEST_F(Screencast, when_unauthorized_fails) { using namespace testing; EXPECT_CALL(mock_authorizer, screencast_is_allowed(_)) .WillOnce(Return(false)); auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto screencast = mir_connection_create_screencast_sync(connection, &default_screencast_params); ASSERT_EQ(nullptr, screencast); mir_connection_release(connection); } ./tests/acceptance-tests/test_client_scaling.cpp0000644000015600001650000001310712676616157022231 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_toolkit/mir_client_library.h" #include "mir/geometry/size.h" #include "mir/compositor/scene_element.h" #include "mir/graphics/renderable.h" #include "mir/graphics/buffer.h" #include "mir/graphics/cursor.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/test/doubles/null_display_buffer_compositor_factory.h" #include "mir/test/signal.h" #include #include namespace mg = mir::graphics; namespace mtf = mir_test_framework; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mc = mir::compositor; namespace geom = mir::geometry; using namespace testing; namespace { struct SizeEntry { mc::CompositorID id; std::vector physical_sizes; std::vector composited_sizes; }; class SizeWatcher { public: void note_renderable_sizes(mc::CompositorID id, mc::SceneElementSequence const& seq) { std::lock_guard lk(mutex); SizeEntry entry{id, {}, {}}; for( auto const& element : seq) { entry.composited_sizes.push_back(element->renderable()->screen_position().size); entry.physical_sizes.push_back(element->renderable()->buffer()->size()); } entries.emplace_back(std::move(entry)); cv.notify_all(); } std::vector size_entries() { std::lock_guard lk(mutex); return entries; } void wait_for_an_empty_composition() { std::unique_lock lk(mutex); auto pred = [this] { return !entries.empty() && entries.back().physical_sizes.empty(); }; if (!cv.wait_for(lk, std::chrono::seconds(2), pred)) throw std::runtime_error("timeout waiting for empty composition"); } void wait_until_entries_number_at_least(unsigned int number, std::chrono::seconds timeout) { std::unique_lock lk(mutex); if (!cv.wait_for(lk, timeout, [this, number] { return entries.size() >= number; })) throw std::runtime_error("timeout waiting for a certain number of entries"); } void clear_record() { std::unique_lock lk(mutex); entries.clear(); } private: std::mutex mutex; std::condition_variable cv; std::vector entries; }; class SizeWatchingDBCompositorFactory : public mc::DisplayBufferCompositorFactory { public: SizeWatchingDBCompositorFactory(std::shared_ptr const& watch) : watch(watch) { } auto create_compositor_for(mg::DisplayBuffer&) -> std::unique_ptr override { return std::make_unique(watch); } private: struct NullDisplayBufferCompositor : mc::DisplayBufferCompositor { NullDisplayBufferCompositor(std::shared_ptr const& watch) : watch(watch) { } void composite(mc::SceneElementSequence&& seq) { watch->note_renderable_sizes(this, seq); std::this_thread::yield(); } std::shared_ptr const watch; }; std::shared_ptr const watch; }; struct SurfaceScaling : mtf::ConnectedClientWithASurface { SurfaceScaling() : watch(std::make_shared()) { } void SetUp() override { server.override_the_display_buffer_compositor_factory([this] { return std::make_shared(watch); }); ConnectedClientWithASurface::SetUp(); server.the_cursor()->hide(); watch->wait_for_an_empty_composition(); watch->clear_record(); } std::shared_ptr const watch; }; } TEST_F(SurfaceScaling, compositor_sees_size_different_when_scaled) { //FIXME: need an ABI break to get this to work for NBS. Temporarily disabled for a // no ABI break release. auto nbuffers_opt = getenv("MIR_SERVER_NBUFFERS"); if (nbuffers_opt && !strcmp(nbuffers_opt, "0")) { SUCCEED() << "temporarily disabled test for MIR_SERVER_NBUFFERS=0"; return; } using namespace std::literals::chrono_literals; auto scale = 2.0f; auto stream = mir_surface_get_buffer_stream(surface); mir_buffer_stream_set_scale(stream, scale); mir_buffer_stream_swap_buffers_sync(stream); //submits scaled size mir_buffer_stream_swap_buffers_sync(stream); watch->wait_until_entries_number_at_least(2, 5s); auto entries = watch->size_entries(); ASSERT_THAT(entries, SizeIs(Ge(1))); bool an_entry_with_differing_size {false}; for(auto &entry : entries) { if (entry.physical_sizes.empty() || entry.composited_sizes.empty()) continue; if (entry.composited_sizes.front() != entry.physical_sizes.front()) an_entry_with_differing_size = true; } EXPECT_TRUE(an_entry_with_differing_size); } ./tests/acceptance-tests/test_surface_placement.cpp0000644000015600001650000005041312676616125022727 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/events/event_builders.h" #include "mir/scene/surface.h" #include "mir/test/doubles/wrap_shell_to_track_latest_surface.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir/test/fake_shared.h" #include "mir/test/signal.h" #include #include namespace mev = mir::events; namespace mf = mir::frontend; namespace mtf = mir_test_framework; namespace ms = mir::scene; namespace msh = mir::shell; namespace mt = mir::test; namespace mtd = mir::test::doubles; using namespace mir::geometry; using namespace testing; namespace { struct SurfacePlacement : mtf::ConnectedClientHeadlessServer { SurfacePlacement() { add_to_environment("MIR_SERVER_ENABLE_INPUT", "OFF"); } Rectangle const first_display {{ 0, 0}, {640, 480}}; Rectangle const second_display{{640, 0}, {640, 480}}; // limit to cascade step (don't hard code title bar height) Displacement const max_cascade{20, 20}; void SetUp() override { initial_display_layout({first_display, second_display}); server.wrap_shell([this](std::shared_ptr const& wrapped) { auto const msc = std::make_shared(wrapped); shell = msc; return msc; }); mtf::ConnectedClientHeadlessServer::SetUp(); init_pixel_format(); make_active(first_display); } void TearDown() override { shell.reset(); mtf::ConnectedClientHeadlessServer::TearDown(); } std::shared_ptr latest_shell_surface() const { auto const result = shell->latest_surface.lock(); // ASSERT_THAT(result, NotNull()); //<= doesn't compile!? EXPECT_THAT(result, NotNull()); return result; } template MirSurface* create_normal_surface(int width, int height, Specifier const& specifier) const { auto const spec = mir_connection_create_spec_for_normal_surface( connection, width, height, pixel_format); specifier(spec); auto const surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); return surface; } template MirSurface* create_surface(Specifier const& specifier) const { auto const spec = mir_create_surface_spec(connection); specifier(spec); auto const surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); return surface; } MirSurface* create_normal_surface(int width, int height) const { return create_normal_surface(width, height, [](MirSurfaceSpec*){}); } void make_active(Rectangle const& display) { auto const click_position = display.top_left + 0.5*as_displacement(display.size); MirInputDeviceId const device_id{7}; auto const modifiers = mir_input_event_modifier_none; auto const depressed_buttons = mir_pointer_button_primary; auto const x_axis_value = click_position.x.as_float(); auto const y_axis_value = click_position.y.as_float(); auto const hscroll_value = 0.0; auto const vscroll_value = 0.0; auto const action = mir_pointer_action_button_down; auto const relative_x_value = 0.0; auto const relative_y_value = 0.0; auto const click_event = mev::make_event(device_id, std::chrono::nanoseconds(1), std::vector{}, modifiers, action, depressed_buttons, x_axis_value, y_axis_value, hscroll_value, vscroll_value, relative_x_value, relative_y_value); server.the_shell()->handle(*click_event); } MirPixelFormat pixel_format{mir_pixel_format_invalid}; private: std::shared_ptr shell; void init_pixel_format() { unsigned int valid_formats{0}; MirPixelFormat pixel_formats[mir_pixel_formats] = { mir_pixel_format_invalid }; mir_connection_get_available_surface_formats(connection, pixel_formats, mir_pixel_formats, &valid_formats); //select an 8 bit opaque format if we can for (auto i = 0u; i < valid_formats; i++) { if (pixel_formats[i] == mir_pixel_format_xbgr_8888 || pixel_formats[i] == mir_pixel_format_xrgb_8888) { pixel_format = pixel_formats[i]; break; } } } }; } // Optically centered in a space means: // // o horizontally centered, and positioned vertically such that the top margin // is half the bottom margin (vertical centering would look too low, and // would allow little room for cascading), unless this would leave any // part of the window off-screen or in shell space; // // o otherwise, as close as possible to that position without any part of the // window being off-screen or in shell space, if possible; // // o otherwise, as close as possible to that position while keeping all of its // title bar thickness in non-shell space. (For example, a dialog that is // taller than the screen should be positioned immediately below any menu // bar or shell panel at the top of the screen.) TEST_F(SurfacePlacement, small_window_is_optically_centered_on_first_display) { auto const width = 59; auto const height= 61; auto const geometric_centre = first_display.top_left + 0.5*(as_displacement(first_display.size) - Displacement{width, height}); auto const optically_centred = geometric_centre - DeltaY{(first_display.size.height.as_int()-height)/6}; auto const surface = create_normal_surface(width, height); auto const shell_surface = latest_shell_surface(); ASSERT_THAT(shell_surface, NotNull()); // Compiles here EXPECT_THAT(shell_surface->top_left(), Eq(optically_centred)); EXPECT_THAT(shell_surface->size(), Eq(Size{width, height})); mir_surface_release_sync(surface); } TEST_F(SurfacePlacement, which_second_display_acive_small_window_is_optically_centered_on_it) { auto const width = 59; auto const height= 61; make_active(second_display); auto const geometric_centre = second_display.top_left + 0.5*(as_displacement(second_display.size) - Displacement{width, height}); auto const optically_centred = geometric_centre - DeltaY{(second_display.size.height.as_int()-height)/6}; auto const surface = create_normal_surface(width, height); auto const shell_surface = latest_shell_surface(); ASSERT_THAT(shell_surface, NotNull()); // Compiles here EXPECT_THAT(shell_surface->top_left(), Eq(optically_centred)); EXPECT_THAT(shell_surface->size(), Eq(Size{width, height})); mir_surface_release_sync(surface); } TEST_F(SurfacePlacement, medium_window_fitted_onto_first_display) { auto const width = first_display.size.width.as_int(); auto const height= first_display.size.height.as_int(); auto const surface = create_normal_surface(width, height); auto const shell_surface = latest_shell_surface(); EXPECT_THAT(shell_surface->top_left(), Eq(first_display.top_left)); EXPECT_THAT(shell_surface->size(), Eq(Size{width, height})); EXPECT_THAT(shell_surface->size(), Eq(first_display.size)); mir_surface_release_sync(surface); } TEST_F(SurfacePlacement, big_window_keeps_top_on_first_display) { auto const width = 2*first_display.size.width.as_int(); auto const height= 2*first_display.size.height.as_int(); auto const surface = create_normal_surface(width, height); auto const shell_surface = latest_shell_surface(); EXPECT_THAT(shell_surface->top_left(), Eq(Point{-width/4, 0})); EXPECT_THAT(shell_surface->size(), Eq(Size{width, height})); mir_surface_release_sync(surface); } TEST_F(SurfacePlacement, second_window_is_on_same_display_as_first) { auto const width = 67; auto const height= 71; auto const surface1 = create_normal_surface(width, height); auto const shell_surface1 = latest_shell_surface(); shell_surface1->move_to(second_display.top_left); auto const surface2 = create_normal_surface(width, height); auto const shell_surface2 = latest_shell_surface(); EXPECT_TRUE(second_display.contains({shell_surface2->input_bounds()})); mir_surface_release_sync(surface1); mir_surface_release_sync(surface2); } // Cascaded, horizontally and/or vertically, relative to another window means: // // o if vertically, positioned one standard title-bar height lower than the // other window, and if horizontally, positioned one standard title-bar // height to the right if in an LTR language, or one standard title-bar // height to the left in an RTL language — unless the resulting position // would leave any part of the window off-screen or in shell space; // // o otherwise, positioned the same way, and shrunk the minimum amount // required to avoid extending off-screen or into shell space — unless this // would require making it smaller than its minimum width/height (for // example, if the window is not resizable at all); // // o otherwise, placed at the top left (LTR) or top right (RTL) of the // display’s biggest non-shell space. TEST_F(SurfacePlacement, second_window_is_cascaded_wrt_first) { auto const width = 73; auto const height= 79; auto const surface1 = create_normal_surface(width, height); auto const shell_surface1 = latest_shell_surface(); auto const surface2 = create_normal_surface(width, height); auto const shell_surface2 = latest_shell_surface(); EXPECT_THAT(shell_surface2->top_left().x, Gt(shell_surface1->top_left().x)); EXPECT_THAT(shell_surface2->top_left().y, Gt(shell_surface1->top_left().y)); EXPECT_THAT(shell_surface2->top_left().x, Lt((shell_surface1->top_left()+max_cascade).x)); EXPECT_THAT(shell_surface2->top_left().y, Lt((shell_surface1->top_left()+max_cascade).y)); EXPECT_THAT(shell_surface2->size(), Eq(Size{width, height})); mir_surface_release_sync(surface1); mir_surface_release_sync(surface2); } // This is what is currently in the spec, but I think it's wrong TEST_F(SurfacePlacement, DISABLED_medium_second_window_is_sized_when_cascaded_wrt_first) { auto const width = first_display.size.width.as_int(); auto const height= first_display.size.height.as_int(); auto const surface1 = create_normal_surface(width, height); auto const shell_surface1 = latest_shell_surface(); auto const surface2 = create_normal_surface(width, height); auto const shell_surface2 = latest_shell_surface(); EXPECT_THAT(shell_surface2->top_left().x, Gt(shell_surface1->top_left().x)); EXPECT_THAT(shell_surface2->top_left().y, Gt(shell_surface1->top_left().y)); EXPECT_THAT(shell_surface2->top_left().x, Lt((shell_surface1->top_left()+max_cascade).x)); EXPECT_THAT(shell_surface2->top_left().y, Lt((shell_surface1->top_left()+max_cascade).y)); EXPECT_TRUE(first_display.contains({shell_surface2->input_bounds()})); EXPECT_THAT(shell_surface2->size(), Ne(Size{width, height})); mir_surface_release_sync(surface1); mir_surface_release_sync(surface2); } // This is not what is currently in the spec, but I think it's right TEST_F(SurfacePlacement, medium_second_window_is_cascaded_wrt_first) { auto const width = first_display.size.width.as_int(); auto const height= first_display.size.height.as_int(); auto const surface1 = create_normal_surface(width, height); auto const shell_surface1 = latest_shell_surface(); auto const surface2 = create_normal_surface(width, height); auto const shell_surface2 = latest_shell_surface(); EXPECT_THAT(shell_surface2->top_left().x, Gt(shell_surface1->top_left().x)); EXPECT_THAT(shell_surface2->top_left().y, Gt(shell_surface1->top_left().y)); EXPECT_THAT(shell_surface2->top_left().x, Lt((shell_surface1->top_left()+max_cascade).x)); EXPECT_THAT(shell_surface2->top_left().y, Lt((shell_surface1->top_left()+max_cascade).y)); EXPECT_TRUE(first_display.overlaps({shell_surface2->input_bounds()})); EXPECT_THAT(shell_surface2->size(), Eq(Size{width, height})); mir_surface_release_sync(surface1); mir_surface_release_sync(surface2); } TEST_F(SurfacePlacement, fullscreen_surface_is_sized_to_display) { auto const surface = create_normal_surface(10, 10, [](MirSurfaceSpec* spec) { mir_surface_spec_set_state(spec, mir_surface_state_fullscreen); }); auto const shell_surface = latest_shell_surface(); EXPECT_THAT(shell_surface->top_left(), Eq(first_display.top_left)); EXPECT_THAT(shell_surface->size(), Eq(first_display.size)); mir_surface_release_sync(surface); } TEST_F(SurfacePlacement, maximized_surface_is_sized_to_display) { auto const surface = create_normal_surface(10, 10, [](MirSurfaceSpec* spec) { mir_surface_spec_set_state(spec, mir_surface_state_maximized); }); auto const shell_surface = latest_shell_surface(); EXPECT_THAT(shell_surface->top_left().x, Eq(first_display.top_left.x)); // Allow for a titlebar... EXPECT_THAT(shell_surface->top_left().y, Lt((first_display.top_left+max_cascade).y)); auto const expected_size = as_size(as_displacement(first_display.size) + (first_display.top_left - shell_surface->top_left())); EXPECT_THAT(shell_surface->size(), Eq(expected_size)); mir_surface_release_sync(surface); } TEST_F(SurfacePlacement, horizmaximized_surface_is_sized_to_display) { auto const surface = create_normal_surface(10, 10, [](MirSurfaceSpec* spec) { mir_surface_spec_set_state(spec, mir_surface_state_horizmaximized); }); auto const shell_surface = latest_shell_surface(); EXPECT_THAT(shell_surface->top_left().x, Eq(first_display.top_left.x)); // Allow for a titlebar... EXPECT_THAT(shell_surface->top_left().y, Gt((first_display.top_left+max_cascade).y)); EXPECT_THAT(shell_surface->size().height, Eq(Height{10})); EXPECT_THAT(shell_surface->size().width, Eq(first_display.size.width)); mir_surface_release_sync(surface); } TEST_F(SurfacePlacement, vertmaximized_surface_is_sized_to_display) { auto const surface = create_normal_surface(10, 10, [](MirSurfaceSpec* spec) { mir_surface_spec_set_state(spec, mir_surface_state_vertmaximized); }); auto const shell_surface = latest_shell_surface(); EXPECT_THAT(shell_surface->top_left().x, Gt(first_display.top_left.x)); // Allow for a titlebar... EXPECT_THAT(shell_surface->top_left().y, Lt((first_display.top_left+max_cascade).y)); Size const expected_size{10, first_display.size.height + (first_display.top_left.y-shell_surface->top_left().y)}; EXPECT_THAT(shell_surface->size(), Eq(expected_size)); mir_surface_release_sync(surface); } TEST_F(SurfacePlacement, fullscreen_on_output_1_surface_is_sized_to_first_display) { auto const surface = create_normal_surface(10, 10, [](MirSurfaceSpec* spec) { mir_surface_spec_set_fullscreen_on_output(spec, 1); }); auto const shell_surface = latest_shell_surface(); EXPECT_THAT(shell_surface->top_left(), Eq(first_display.top_left)); EXPECT_THAT(shell_surface->size(), Eq(first_display.size)); mir_surface_release_sync(surface); } TEST_F(SurfacePlacement, fullscreen_on_output_2_surface_is_sized_to_second_display) { auto const surface = create_normal_surface(10, 10, [](MirSurfaceSpec* spec) { mir_surface_spec_set_fullscreen_on_output(spec, 2); }); auto const shell_surface = latest_shell_surface(); EXPECT_THAT(shell_surface->top_left(), Eq(second_display.top_left)); EXPECT_THAT(shell_surface->size(), Eq(second_display.size)); mir_surface_release_sync(surface); } struct UnparentedSurface : SurfacePlacement, ::testing::WithParamInterface {}; TEST_P(UnparentedSurface, small_window_is_optically_centered_on_first_display) { auto const width = 83; auto const height= 89; auto const geometric_centre = first_display.top_left + 0.5*(as_displacement(first_display.size) - Displacement{width, height}); auto const optically_centred = geometric_centre - DeltaY{(first_display.size.height.as_int()-height)/6}; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, GetParam()); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); }); auto const shell_surface = latest_shell_surface(); ASSERT_THAT(shell_surface, NotNull()); // Compiles here EXPECT_THAT(shell_surface->top_left(), Eq(optically_centred)); EXPECT_THAT(shell_surface->size(), Eq(Size{width, height})); mir_surface_release_sync(surface); } INSTANTIATE_TEST_CASE_P(SurfacePlacement, UnparentedSurface, ::testing::Values( mir_surface_type_normal, mir_surface_type_utility, mir_surface_type_dialog, mir_surface_type_freestyle)); // Parented dialog or parented freestyle window // // For convenience, these types are referred to here as “parented dialogs”. // // o If a newly-opened parented dialog is the same as a previous dialog with // the same parent window, and it has user-customized position, then: // o … // // o Otherwise, if the dialog is not the same as any previous dialog for the // same parent window, and/or it does not have user-customized position: // o It should be optically centered relative to its parent, unless this // would overlap or cover the title bar of the parent. // o Otherwise, it should be cascaded vertically (but not horizontally) // relative to its parent, unless, this would cause at least part of // it to extend into shell space. // // o Otherwise (resorting to the original plan) it should be optically centered // relative to its parent // TODO tests for this struct ParentedSurface : SurfacePlacement, ::testing::WithParamInterface {}; TEST_P(ParentedSurface, small_window_is_optically_centered_on_parent) { auto const parent = create_normal_surface(256, 256); auto const shell_parent = latest_shell_surface(); auto const width = 97; auto const height= 101; auto const geometric_centre = shell_parent->top_left() + 0.5*(as_displacement(shell_parent->size()) - Displacement{width, height}); auto const optically_centred = geometric_centre - DeltaY{(shell_parent->size().height.as_int()-height)/6}; auto const surface = create_surface([&](MirSurfaceSpec* spec) { mir_surface_spec_set_type(spec, GetParam()); mir_surface_spec_set_width(spec, width); mir_surface_spec_set_height(spec, height); mir_surface_spec_set_pixel_format(spec, pixel_format); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); mir_surface_spec_set_parent(spec, parent); }); auto const shell_surface = latest_shell_surface(); ASSERT_THAT(shell_surface, NotNull()); // Compiles here EXPECT_THAT(shell_surface->top_left(), Eq(optically_centred)); EXPECT_THAT(shell_surface->size(), Eq(Size{width, height})); mir_surface_release_sync(surface); mir_surface_release_sync(parent); } INSTANTIATE_TEST_CASE_P(SurfacePlacement, ParentedSurface, ::testing::Values( mir_surface_type_dialog, mir_surface_type_satellite, mir_surface_type_popover, mir_surface_type_gloss, mir_surface_type_tip, mir_surface_type_freestyle)); ./tests/acceptance-tests/test_client_cookie.cpp0000644000015600001650000001633112676616157022064 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Brandon Schaefer */ #include "mir/input/input_device_info.h" #include "mir_test_framework/fake_input_device.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/test/spin_wait.h" #include "mir/test/wait_condition.h" #include "mir/cookie/authority.h" #include "mir_test_framework/visible_surface.h" #include "boost/throw_exception.hpp" #include #include namespace mtf = mir_test_framework; namespace mt = mir::test; namespace mi = mir::input; namespace mis = mir::input::synthesis; namespace { std::chrono::seconds const max_wait{4}; void cookie_capturing_callback(MirSurface*, MirEvent const* ev, void* ctx); } class ClientCookies : public mtf::ConnectedClientHeadlessServer { public: ClientCookies() { server.override_the_cookie_authority([this] () { return mir::cookie::Authority::create_saving(cookie_secret); }); } void SetUp() override { mtf::ConnectedClientHeadlessServer::SetUp(); // Need fullscreen for the cursor events auto const spec = mir_connection_create_spec_for_normal_surface( connection, 100, 100, mir_pixel_format_abgr_8888); mir_surface_spec_set_fullscreen_on_output(spec, 1); mir_surface_spec_set_event_handler(spec, &cookie_capturing_callback, this); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); ready_to_accept_events.wait_for_at_most_seconds(max_wait); if (!ready_to_accept_events.woken()) BOOST_THROW_EXCEPTION(std::runtime_error("Timeout waiting for surface to become focused and exposed")); } std::unique_ptr fake_keyboard{ mtf::add_fake_input_device(mi::InputDeviceInfo{"keyboard", "keyboard-uid" , mi::DeviceCapability::keyboard}) }; std::unique_ptr fake_pointer{ mtf::add_fake_input_device(mi::InputDeviceInfo{"mouse", "mouse-uid" , mi::DeviceCapability::pointer}) }; std::unique_ptr fake_touch_screen{ mtf::add_fake_input_device(mi::InputDeviceInfo{ "touch screen", "touch-screen-uid", mi::DeviceCapability::touchscreen | mi::DeviceCapability::multitouch}) }; mir::test::WaitCondition ready_to_accept_events; std::vector> out_cookies; std::vector cookie_secret; size_t event_count{0}; mutable std::mutex mutex; bool exposed{false}; bool focused{false}; MirSurface* surface; }; namespace { void cookie_capturing_callback(MirSurface*, MirEvent const* ev, void* ctx) { auto const event_type = mir_event_get_type(ev); auto client_cookie = static_cast(ctx); if (event_type == mir_event_type_surface) { auto event = mir_event_get_surface_event(ev); auto const attrib = mir_surface_event_get_attribute(event); auto const value = mir_surface_event_get_attribute_value(event); std::lock_guard lk(client_cookie->mutex); if (attrib == mir_surface_attrib_visibility && value == mir_surface_visibility_exposed) { client_cookie->exposed = true; } if (attrib == mir_surface_attrib_focus && value == mir_surface_focused) { client_cookie->focused = true; } if (client_cookie->exposed && client_cookie->focused) client_cookie->ready_to_accept_events.wake_up_everyone(); } else if (event_type == mir_event_type_input) { auto const* iev = mir_event_get_input_event(ev); std::lock_guard lk(client_cookie->mutex); if (mir_input_event_has_cookie(iev)) { auto cookie = mir_input_event_get_cookie(iev); size_t size = mir_cookie_buffer_size(cookie); std::vector cookie_bytes(size); mir_cookie_to_buffer(cookie, cookie_bytes.data(), size); mir_cookie_release(cookie); client_cookie->out_cookies.push_back(cookie_bytes); } client_cookie->event_count++; } } bool wait_for_n_events(size_t n, ClientCookies* client_cookie) { bool all_events = mt::spin_wait_for_condition_or_timeout( [&n, &client_cookie] { std::lock_guard lk(client_cookie->mutex); return client_cookie->event_count >= n; }, std::chrono::seconds{max_wait}); EXPECT_TRUE(all_events); return all_events; } } TEST_F(ClientCookies, keyboard_events_have_cookies) { fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_M)); int events = 1; if (wait_for_n_events(events, this)) { std::lock_guard lk(mutex); ASSERT_FALSE(out_cookies.empty()); auto authority = mir::cookie::Authority::create_from(cookie_secret); EXPECT_NO_THROW(authority->make_cookie(out_cookies.back())); } } TEST_F(ClientCookies, pointer_motion_events_do_not_have_cookies) { // with movement generates 2 events fake_pointer->emit_event(mis::a_pointer_event().with_movement(1, 1)); int events = 2; if (wait_for_n_events(events, this)) { std::lock_guard lk(mutex); EXPECT_EQ(event_count, events); EXPECT_TRUE(out_cookies.empty()); } } TEST_F(ClientCookies, pointer_click_events_have_cookies) { fake_pointer->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); fake_pointer->emit_event(mis::a_button_up_event().of_button(BTN_LEFT)); int events = 2; if (wait_for_n_events(events, this)) { std::lock_guard lk(mutex); ASSERT_FALSE(out_cookies.empty()); auto authority = mir::cookie::Authority::create_from(cookie_secret); EXPECT_NO_THROW(authority->make_cookie(out_cookies.back())); } } TEST_F(ClientCookies, touch_motion_events_do_not_have_cookies) { fake_touch_screen->emit_event( mis::a_touch_event() .at_position({0, 0}) ); fake_touch_screen->emit_event( mis::a_touch_event() .with_action(mis::TouchParameters::Action::Move) .at_position({1, 1}) ); int events = 1; if (wait_for_n_events(events, this)) { std::lock_guard lk(mutex); EXPECT_GE(event_count, events); EXPECT_EQ(out_cookies.size(), 1); } } ./tests/acceptance-tests/test_client_platform_operation.cpp0000644000015600001650000001171412676616125024512 0ustar jenkinsjenkins#include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir_test_framework/stub_graphics_platform_operation.h" #include "mir/test/pipe.h" #include #include #include #include namespace mtf = mir_test_framework; namespace { void assign_reply(MirConnection*, MirPlatformMessage* reply, void* context) { auto message = static_cast(context); *message = reply; } struct ClientPlatformOperation : mtf::ConnectedClientHeadlessServer { MirPlatformMessage* platform_operation_add(int num1, int num2) { return platform_operation_add({num1,num2}); } MirPlatformMessage* incorrect_platform_operation_add() { return platform_operation_add({7}); } MirPlatformMessage* platform_operation_add(std::vector const& nums) { auto const request = mir_platform_message_create(add_opcode); mir_platform_message_set_data(request, nums.data(), sizeof(int) * nums.size()); MirPlatformMessage* reply; auto const platform_op_done = mir_connection_platform_operation( connection, request, assign_reply, &reply); mir_wait_for(platform_op_done); mir_platform_message_release(request); return reply; } MirPlatformMessage* platform_operation_echo_fd(int fd) { unsigned int const echo_fd_opcode = static_cast(mtf::StubGraphicsPlatformOperation::echo_fd); auto const request = mir_platform_message_create(echo_fd_opcode); mir_platform_message_set_fds(request, &fd, 1); MirPlatformMessage* reply; auto const platform_op_done = mir_connection_platform_operation( connection, request, assign_reply, &reply); mir_wait_for(platform_op_done); mir_platform_message_release(request); return reply; } unsigned int const add_opcode = static_cast(mtf::StubGraphicsPlatformOperation::add); }; MATCHER_P(MessageDataAsIntsEq, v, "") { using namespace testing; auto msg_data = mir_platform_message_get_data(arg); if (msg_data.size % sizeof(int) != 0) throw std::runtime_error("Data is not an array of ints"); std::vector data(msg_data.size / sizeof(int)); memcpy(data.data(), msg_data.data, msg_data.size); return v == data; } MATCHER(MessageDataIsEmpty, "") { auto const msg_data = mir_platform_message_get_data(arg); return msg_data.size == 0 && msg_data.data == nullptr; } MATCHER_P(MessageOpcodeEq, opcode, "") { auto const msg_opcode = mir_platform_message_get_opcode(arg); return msg_opcode == opcode; } MATCHER(MessageFdsIsEmpty, "") { auto const msg_fds = mir_platform_message_get_fds(arg); return msg_fds.num_fds == 0 && msg_fds.fds == nullptr; } } TEST_F(ClientPlatformOperation, exchanges_data_items_with_platform) { using namespace testing; int const num1 = 7; int const num2 = 11; auto const reply = platform_operation_add(num1, num2); EXPECT_THAT(reply, MessageDataAsIntsEq(std::vector{num1 + num2})); mir_platform_message_release(reply); } TEST_F(ClientPlatformOperation, does_not_set_connection_error_message_on_success) { using namespace testing; auto const reply = platform_operation_add(7, 11); EXPECT_THAT(mir_connection_get_error_message(connection), StrEq("")); mir_platform_message_release(reply); } TEST_F(ClientPlatformOperation, reply_has_opcode_of_request) { using namespace testing; auto const reply = platform_operation_add(7, 11); EXPECT_THAT(reply, MessageOpcodeEq(add_opcode)); mir_platform_message_release(reply); } TEST_F(ClientPlatformOperation, returns_empty_reply_on_error) { using namespace testing; auto const reply = incorrect_platform_operation_add(); EXPECT_THAT(reply, MessageDataIsEmpty()); EXPECT_THAT(reply, MessageFdsIsEmpty()); mir_platform_message_release(reply); } TEST_F(ClientPlatformOperation, sets_connection_error_message_on_error) { using namespace testing; auto const reply = incorrect_platform_operation_add(); EXPECT_THAT(mir_connection_get_error_message(connection), StrNe("")); mir_platform_message_release(reply); } TEST_F(ClientPlatformOperation, exchanges_fd_items_with_platform) { using namespace testing; char const sent_char{'#'}; mir::test::Pipe pipe; EXPECT_THAT(write(pipe.write_fd(), &sent_char, 1), Eq(1)); auto const reply = platform_operation_echo_fd(pipe.read_fd()); EXPECT_THAT(reply, MessageDataIsEmpty()); auto const reply_fds = mir_platform_message_get_fds(reply); EXPECT_THAT(reply_fds.num_fds, Eq(1)); auto const reply_fd = reply_fds.fds[0]; EXPECT_THAT(reply_fd, Ne(pipe.read_fd())); char reply_char{0}; EXPECT_THAT(read(reply_fd, &reply_char, 1), Eq(1)); EXPECT_THAT(reply_char, Eq(sent_char)); close(reply_fd); mir_platform_message_release(reply); } ./tests/acceptance-tests/server_signal_handling.cpp0000644000015600001650000000573212676616125022723 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/server.h" #include "mir_test_framework/interprocess_client_server_test.h" #include "mir/test/cross_process_sync.h" #include #include #include #include namespace mt = mir::test; namespace mtf = mir_test_framework; namespace { struct ServerSignal : mtf::InterprocessClientServerTest { std::chrono::seconds const timeout{60}; MOCK_CONST_METHOD1(terminate_handler, void(int)); mt::CrossProcessSync cleanup_done; void SetUp() override { init_server([&] { server.set_terminator([&](int signal) { terminate_handler(signal); server.stop(); }); server.add_emergency_cleanup([&] { cleanup_done.signal_ready(); }); }); } }; } TEST_F(ServerSignal, terminate_handler_is_called_for_SIGTERM) { run_in_server([&] { EXPECT_CALL(*this, terminate_handler(SIGTERM)); kill(getpid(), SIGTERM); wait_for_server_exit(); }); } TEST_F(ServerSignal, terminate_handler_is_called_for_SIGINT) { run_in_server([&] { EXPECT_CALL(*this, terminate_handler(SIGINT)); kill(getpid(), SIGINT); wait_for_server_exit(); }); } struct AbortDeathTest : ServerSignal, ::testing::WithParamInterface {}; TEST_P(AbortDeathTest, cleanup_handler_is_called_for) { expect_server_signalled(GetParam()); run_in_server([&]{ kill(getpid(), GetParam()); }); cleanup_done.wait_for_signal_ready_for(timeout); } INSTANTIATE_TEST_CASE_P(ServerSignal, AbortDeathTest, ::testing::Values(SIGQUIT, SIGABRT, SIGFPE, SIGSEGV, SIGBUS)); using ServerSignalDeathTest = ServerSignal; TEST_F(ServerSignalDeathTest, multiple_cleanup_handlers_are_called) { const int multiple = 5; expect_server_signalled(SIGABRT); mt::CrossProcessSync more_cleanup[multiple]; init_server([&] { for (auto& cleanup : more_cleanup) server.add_emergency_cleanup([&] { cleanup.signal_ready(); }); }); run_in_server([&]{ kill(getpid(), SIGABRT); }); cleanup_done.wait_for_signal_ready_for(timeout); for (auto& cleanup : more_cleanup) cleanup.wait_for_signal_ready_for(timeout); } ./tests/acceptance-tests/test_client_library.cpp0000644000015600001650000010667112676616125022261 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Guest */ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/stub_platform_helpers.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/any_surface.h" #include "mir/test/validity_matchers.h" #include "src/include/common/mir/protobuf/protocol_version.h" #include "mir_protobuf.pb.h" #ifdef ANDROID /* * MirNativeBuffer for Android is defined opaquely, but we now depend on * it having width and height fields, for all platforms. So need definition... */ #include // for ANativeWindowBuffer AKA MirNativeBuffer #endif #include #include #include #include #include #include #include #include #include #include namespace mf = mir::frontend; namespace mc = mir::compositor; namespace mtf = mir_test_framework; namespace { struct ClientLibrary : mtf::HeadlessInProcessServer { std::set surfaces; MirConnection* connection = nullptr; MirSurface* surface = nullptr; std::atomic buffers{0}; static void connection_callback(MirConnection* connection, void* context) { ClientLibrary* config = reinterpret_cast(context); config->connected(connection); } static void create_surface_callback(MirSurface* surface, void* context) { ClientLibrary* config = reinterpret_cast(context); config->surface_created(surface); } static void next_buffer_callback(MirBufferStream* bs, void* context) { ClientLibrary* config = reinterpret_cast(context); config->next_buffer(bs); } static void release_surface_callback(MirSurface* surface, void* context) { ClientLibrary* config = reinterpret_cast(context); config->surface_released(surface); } virtual void connected(MirConnection* new_connection) { connection = new_connection; } virtual void surface_created(MirSurface* new_surface) { surfaces.insert(new_surface); surface = new_surface; } virtual void next_buffer(MirBufferStream*) { ++buffers; } void surface_released(MirSurface* old_surface) { surfaces.erase(old_surface); surface = NULL; } MirSurface* any_surface() { return *surfaces.begin(); } size_t current_surface_count() { return surfaces.size(); } static void nosey_thread(MirSurface *surf) { for (int i = 0; i < 10; i++) { mir_wait_for_one(mir_surface_set_state(surf, mir_surface_state_maximized)); mir_wait_for_one(mir_surface_set_state(surf, mir_surface_state_restored)); mir_wait_for_one(mir_surface_set_state(surf, mir_surface_state_fullscreen)); mir_wait_for_one(mir_surface_set_state(surf, mir_surface_state_minimized)); } } mtf::UsingStubClientPlatform using_stub_client_platform; }; auto const* const protocol_version_override = "MIR_CLIENT_TEST_OVERRRIDE_PROTOCOL_VERSION"; } using namespace testing; TEST_F(ClientLibrary, client_library_connects_and_disconnects) { MirWaitHandle* wh = mir_connect(new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, this); EXPECT_THAT(wh, NotNull()); mir_wait_for(wh); ASSERT_THAT(connection, NotNull()); EXPECT_TRUE(mir_connection_is_valid(connection)); EXPECT_THAT(mir_connection_get_error_message(connection), StrEq("")); mir_connection_release(connection); } TEST_F(ClientLibrary, synchronous_connection) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_THAT(connection, NotNull()); EXPECT_TRUE(mir_connection_is_valid(connection)); EXPECT_THAT(mir_connection_get_error_message(connection), StrEq("")); mir_connection_release(connection); } TEST_F(ClientLibrary, connects_when_protobuf_protocol_oldest_supported) { std::ostringstream buffer; buffer << mir::protobuf::oldest_compatible_protocol_version(); add_to_environment(protocol_version_override, buffer.str().c_str()); connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_THAT(connection, NotNull()); EXPECT_TRUE(mir_connection_is_valid(connection)); EXPECT_THAT(mir_connection_get_error_message(connection), StrEq("")); mir_connection_release(connection); } TEST_F(ClientLibrary, reports_error_when_protobuf_protocol_obsolete) { std::ostringstream buffer; buffer << (mir::protobuf::oldest_compatible_protocol_version() - 1); add_to_environment(protocol_version_override, buffer.str().c_str()); connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_THAT(connection, NotNull()); EXPECT_FALSE(mir_connection_is_valid(connection)); EXPECT_THAT(mir_connection_get_error_message(connection), HasSubstr("not accepted by server")); mir_connection_release(connection); } TEST_F(ClientLibrary, reports_error_when_protobuf_protocol_too_new) { std::ostringstream buffer; buffer << mir::protobuf::current_protocol_version() + 1; add_to_environment(protocol_version_override, buffer.str().c_str()); connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_THAT(connection, NotNull()); EXPECT_FALSE(mir_connection_is_valid(connection)); EXPECT_THAT(mir_connection_get_error_message(connection), HasSubstr("not accepted by server")); mir_connection_release(connection); } TEST_F(ClientLibrary, reports_error_when_protobuf_protocol_much_too_new) { std::ostringstream buffer; buffer << mir::protobuf::next_incompatible_protocol_version(); add_to_environment(protocol_version_override, buffer.str().c_str()); connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_THAT(connection, NotNull()); EXPECT_FALSE(mir_connection_is_valid(connection)); EXPECT_THAT(mir_connection_get_error_message(connection), HasSubstr("not accepted by server")); mir_connection_release(connection); } TEST_F(ClientLibrary, creates_surface) { mir_wait_for(mir_connect(new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, this)); int request_width = 640, request_height = 480; MirPixelFormat request_format = mir_pixel_format_abgr_8888; MirBufferUsage request_buffer_usage = mir_buffer_usage_hardware; auto spec = mir_connection_create_spec_for_normal_surface(connection, request_width, request_height, request_format); mir_surface_spec_set_buffer_usage(spec, request_buffer_usage); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); ASSERT_THAT(surface, NotNull()); EXPECT_TRUE(mir_surface_is_valid(surface)); EXPECT_THAT(mir_surface_get_error_message(surface), StrEq("")); MirSurfaceParameters response_params; mir_surface_get_parameters(surface, &response_params); EXPECT_EQ(request_width, response_params.width); EXPECT_EQ(request_height, response_params.height); EXPECT_EQ(request_format, response_params.pixel_format); EXPECT_EQ(request_buffer_usage, response_params.buffer_usage); mir_wait_for(mir_surface_release( surface, release_surface_callback, this)); mir_connection_release(connection); } TEST_F(ClientLibrary, can_set_surface_state) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto const spec = mir_connection_create_spec_for_normal_surface( connection, 640, 480, mir_pixel_format_abgr_8888); mir_wait_for(mir_surface_create(spec, create_surface_callback, this)); mir_surface_spec_release(spec); EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_restored)); mir_wait_for(mir_surface_set_state(surface, mir_surface_state_fullscreen)); EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_fullscreen)); mir_wait_for(mir_surface_set_state(surface, static_cast(999))); EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_fullscreen)); mir_wait_for(mir_surface_set_state(surface, mir_surface_state_horizmaximized)); EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_horizmaximized)); mir_wait_for(mir_surface_set_state(surface, static_cast(888))); EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_horizmaximized)); // Stress-test synchronization logic with some flooding for (int i = 0; i < 100; i++) { mir_surface_set_state(surface, mir_surface_state_maximized); mir_surface_set_state(surface, mir_surface_state_restored); mir_wait_for(mir_surface_set_state(surface, mir_surface_state_fullscreen)); ASSERT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_fullscreen)); } mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ClientLibrary, can_set_surface_min_width) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); int const width = 640; int const height = 480; auto const format = mir_pixel_format_abgr_8888; auto const spec = mir_connection_create_spec_for_normal_surface(connection, width, height, format); int const min_width = 480; mir_surface_spec_set_min_width(spec, min_width); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); EXPECT_THAT(surface, IsValid()); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ClientLibrary, can_set_surface_min_height) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); int const width = 640; int const height = 480; auto const format = mir_pixel_format_abgr_8888; auto const spec = mir_connection_create_spec_for_normal_surface(connection, width, height, format); int const min_height = 480; mir_surface_spec_set_min_height(spec, min_height); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); EXPECT_THAT(surface, IsValid()); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ClientLibrary, can_set_surface_max_width) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); int const width = 640; int const height = 480; auto const format = mir_pixel_format_abgr_8888; auto const spec = mir_connection_create_spec_for_normal_surface(connection, width, height, format); int const max_width = 1024; mir_surface_spec_set_max_width(spec, max_width); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); EXPECT_THAT(surface, IsValid()); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ClientLibrary, can_set_surface_max_height) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); int const width = 640; int const height = 480; auto const format = mir_pixel_format_abgr_8888; auto const spec = mir_connection_create_spec_for_normal_surface(connection, width, height, format); int const max_height = 1024; mir_surface_spec_set_max_height(spec, max_height); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); EXPECT_THAT(surface, IsValid()); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ClientLibrary, min_size_respected_when_placing_surface) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); int const width = 6400; int const height = 4800; auto const format = mir_pixel_format_abgr_8888; auto const spec = mir_connection_create_spec_for_normal_surface(connection, width, height, format); int const min_width = 4800; int const min_height = 3200; mir_surface_spec_set_min_width(spec, min_width); mir_surface_spec_set_min_height(spec, min_height); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); auto const buffer_stream = mir_surface_get_buffer_stream(surface); MirGraphicsRegion graphics_region; mir_buffer_stream_get_graphics_region(buffer_stream, &graphics_region); EXPECT_THAT(graphics_region.width, Ge(min_width)); EXPECT_THAT(graphics_region.height, Ge(min_height)); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ClientLibrary, receives_surface_dpi_value) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto const spec = mir_connection_create_spec_for_normal_surface( connection, 640, 480, mir_pixel_format_abgr_8888); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); // Expect zero (not wired up to detect the physical display yet) EXPECT_THAT(mir_surface_get_dpi(surface), Eq(0)); mir_surface_release_sync(surface); mir_connection_release(connection); } #if defined(MESA_KMS) || defined(MESA_X11) TEST_F(ClientLibrary, surface_scanout_flag_toggles) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto const spec = mir_connection_create_spec_for_normal_surface( connection, 1280, 1024, mir_pixel_format_abgr_8888); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); surface = mir_surface_create_sync(spec); MirNativeBuffer *native; auto bs = mir_surface_get_buffer_stream(surface); mir_buffer_stream_get_current_buffer(bs, &native); EXPECT_TRUE(native->flags & mir_buffer_flag_can_scanout); mir_buffer_stream_swap_buffers_sync(bs); EXPECT_TRUE(native->flags & mir_buffer_flag_can_scanout); mir_surface_release_sync(surface); mir_surface_spec_set_width(spec, 100); mir_surface_spec_set_height(spec, 100); surface = mir_surface_create_sync(spec); bs = mir_surface_get_buffer_stream(surface); mir_buffer_stream_get_current_buffer(bs, &native); EXPECT_FALSE(native->flags & mir_buffer_flag_can_scanout); mir_buffer_stream_swap_buffers_sync(bs); EXPECT_FALSE(native->flags & mir_buffer_flag_can_scanout); mir_surface_release_sync(surface); mir_surface_spec_set_width(spec, 800); mir_surface_spec_set_height(spec, 600); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_software); surface = mir_surface_create_sync(spec); bs = mir_surface_get_buffer_stream(surface); mir_buffer_stream_get_current_buffer(bs, &native); EXPECT_FALSE(native->flags & mir_buffer_flag_can_scanout); mir_buffer_stream_swap_buffers_sync(bs); EXPECT_FALSE(native->flags & mir_buffer_flag_can_scanout); mir_surface_release_sync(surface); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); surface = mir_surface_create_sync(spec); bs = mir_surface_get_buffer_stream(surface); mir_buffer_stream_get_current_buffer(bs, &native); EXPECT_TRUE(native->flags & mir_buffer_flag_can_scanout); mir_buffer_stream_swap_buffers_sync(bs); EXPECT_TRUE(native->flags & mir_buffer_flag_can_scanout); mir_surface_release_sync(surface); mir_surface_spec_release(spec); mir_connection_release(connection); } #endif #ifdef ANDROID // Mir's Android test infrastructure isn't quite ready for this yet. TEST_F(ClientLibrary, DISABLED_gets_buffer_dimensions) #else TEST_F(ClientLibrary, gets_buffer_dimensions) #endif { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto const spec = mir_connection_create_spec_for_normal_surface( connection, 0, 0, mir_pixel_format_abgr_8888); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); struct {int width, height;} const sizes[] = { {12, 34}, {56, 78}, {90, 21}, }; for (auto const& size : sizes) { mir_surface_spec_set_width(spec, size.width); mir_surface_spec_set_height(spec, size.height); surface = mir_surface_create_sync(spec); auto bs = mir_surface_get_buffer_stream(surface); MirNativeBuffer *native = NULL; mir_buffer_stream_get_current_buffer(bs, &native); ASSERT_THAT(native, NotNull()); EXPECT_THAT(native->width, Eq(size.width)); ASSERT_THAT(native->height, Eq(size.height)); mir_buffer_stream_swap_buffers_sync(bs); mir_buffer_stream_get_current_buffer(bs, &native); ASSERT_THAT(native, NotNull()); EXPECT_THAT(native->width, Eq(size.width)); ASSERT_THAT(native->height, Eq(size.height)); mir_surface_release_sync(surface); } mir_surface_spec_release(spec); mir_connection_release(connection); } TEST_F(ClientLibrary, creates_multiple_surfaces) { int const n_surfaces = 13; size_t old_surface_count = 0; mir_wait_for(mir_connect(new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, this)); auto const spec = mir_connection_create_spec_for_normal_surface( connection, 640, 480, mir_pixel_format_abgr_8888); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); for (int i = 0; i != n_surfaces; ++i) { old_surface_count = current_surface_count(); mir_wait_for(mir_surface_create(spec, create_surface_callback, this)); ASSERT_THAT(current_surface_count(), Eq(old_surface_count + 1)); } for (int i = 0; i != n_surfaces; ++i) { old_surface_count = current_surface_count(); ASSERT_THAT(old_surface_count, Ne(0u)); MirSurface * surface = any_surface(); mir_wait_for(mir_surface_release( surface, release_surface_callback, this)); ASSERT_THAT(current_surface_count(), Eq(old_surface_count - 1)); } mir_surface_spec_release(spec); mir_connection_release(connection); } TEST_F(ClientLibrary, client_library_accesses_and_advances_buffers) { mir_wait_for(mir_connect(new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, this)); surface = mtf::make_any_surface(connection); buffers = 0; mir_wait_for(mir_buffer_stream_swap_buffers(mir_surface_get_buffer_stream(surface), next_buffer_callback, this)); EXPECT_THAT(buffers, Eq(1)); mir_wait_for(mir_surface_release(surface, release_surface_callback, this)); ASSERT_THAT(surface, IsNull()); mir_connection_release(connection); } TEST_F(ClientLibrary, fully_synchronous_client) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); surface = mtf::make_any_surface(connection); mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); EXPECT_TRUE(mir_surface_is_valid(surface)); EXPECT_STREQ(mir_surface_get_error_message(surface), ""); mir_surface_release_sync(surface); EXPECT_TRUE(mir_connection_is_valid(connection)); EXPECT_STREQ("", mir_connection_get_error_message(connection)); mir_connection_release(connection); } TEST_F(ClientLibrary, highly_threaded_client) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); surface = mtf::make_any_surface(connection); std::thread a(nosey_thread, surface); std::thread b(nosey_thread, surface); std::thread c(nosey_thread, surface); a.join(); b.join(); c.join(); EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_minimized)); mir_surface_release_sync(surface); EXPECT_TRUE(mir_connection_is_valid(connection)); EXPECT_THAT(mir_connection_get_error_message(connection), StrEq("")); mir_connection_release(connection); } TEST_F(ClientLibrary, accesses_platform_package) { using namespace testing; mir_wait_for(mir_connect(new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, this)); MirPlatformPackage platform_package; ::memset(&platform_package, -1, sizeof(platform_package)); mir_connection_get_platform(connection, &platform_package); EXPECT_THAT(platform_package, mtf::IsStubPlatformPackage()); mir_connection_release(connection); } TEST_F(ClientLibrary, accesses_display_info) { mir_wait_for(mir_connect(new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, this)); auto configuration = mir_connection_create_display_config(connection); ASSERT_THAT(configuration, NotNull()); ASSERT_GT(configuration->num_outputs, 0u); ASSERT_THAT(configuration->outputs, NotNull()); for (auto i=0u; i < configuration->num_outputs; i++) { MirDisplayOutput* disp = &configuration->outputs[i]; ASSERT_THAT(disp, NotNull()); EXPECT_GE(disp->num_modes, disp->current_mode); EXPECT_GE(disp->num_output_formats, disp->current_format); } mir_display_config_destroy(configuration); mir_connection_release(connection); } TEST_F(ClientLibrary, MultiSurfaceClientTracksBufferFdsCorrectly) { mir_wait_for(mir_connect(new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, this)); auto const surf_one = mtf::make_any_surface(connection); auto const surf_two = mtf::make_any_surface(connection); ASSERT_THAT(surf_one, NotNull()); ASSERT_THAT(surf_two, NotNull()); buffers = 0; while (buffers < 1024) { mir_buffer_stream_swap_buffers_sync( mir_surface_get_buffer_stream(surf_one)); mir_buffer_stream_swap_buffers_sync( mir_surface_get_buffer_stream(surf_two)); buffers++; } /* We should not have any stray fds hanging around. Test this by trying to open a new one */ int canary_fd; canary_fd = open("/dev/null", O_RDONLY); ASSERT_THAT(canary_fd, Gt(0)) << "Failed to open canary file descriptor: "<< strerror(errno); EXPECT_THAT(canary_fd, Lt(1024)); close(canary_fd); mir_wait_for(mir_surface_release(surf_one, release_surface_callback, this)); mir_wait_for(mir_surface_release(surf_two, release_surface_callback, this)); ASSERT_THAT(current_surface_count(), testing::Eq(0)); mir_connection_release(connection); } /* TODO: Our stub platform support is a bit terrible. * * These acceptance tests accidentally work on mesa because the mesa client * platform doesn't validate any of its input and we don't touch anything that requires * syscalls. * * The Android client platform *does* care about its input, and so the fact that it's * trying to marshall stub buffers causes crashes. */ #ifdef ANDROID TEST_F(ClientLibrary, DISABLED_create_simple_normal_surface_from_spec) #else TEST_F(ClientLibrary, create_simple_normal_surface_from_spec) #endif { auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); int const width{800}, height{600}; MirPixelFormat const format{mir_pixel_format_bgr_888}; auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, width, height, format); auto surface = mir_surface_create_sync(surface_spec); mir_surface_spec_release(surface_spec); EXPECT_THAT(surface, IsValid()); MirNativeBuffer* native_buffer; mir_buffer_stream_get_current_buffer( mir_surface_get_buffer_stream(surface), &native_buffer); EXPECT_THAT(native_buffer->width, Eq(width)); EXPECT_THAT(native_buffer->height, Eq(height)); EXPECT_THAT(mir_surface_get_type(surface), Eq(mir_surface_type_normal)); mir_surface_release_sync(surface); mir_connection_release(connection); } #ifdef ANDROID TEST_F(ClientLibrary, DISABLED_create_simple_normal_surface_from_spec_async) #else TEST_F(ClientLibrary, create_simple_normal_surface_from_spec_async) #endif { auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); int const width{800}, height{600}; MirPixelFormat const format{mir_pixel_format_xbgr_8888}; auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, width, height, format); mir_wait_for(mir_surface_create(surface_spec, create_surface_callback, this)); mir_surface_spec_release(surface_spec); EXPECT_THAT(surface, IsValid()); MirNativeBuffer* native_buffer; mir_buffer_stream_get_current_buffer( mir_surface_get_buffer_stream(surface), &native_buffer); EXPECT_THAT(native_buffer->width, Eq(width)); EXPECT_THAT(native_buffer->height, Eq(height)); EXPECT_THAT(mir_surface_get_type(surface), Eq(mir_surface_type_normal)); mir_surface_release_sync(surface); mir_connection_release(connection); } #ifdef ANDROID TEST_F(ClientLibrary, DISABLED_can_specify_all_normal_surface_parameters_from_spec) #else TEST_F(ClientLibrary, can_specify_all_normal_surface_parameters_from_spec) #endif { using namespace testing; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, 800, 600, mir_pixel_format_bgr_888); char const* name = "The magnificent Dandy Warhols"; mir_surface_spec_set_name(surface_spec, name); int const width{999}, height{555}; mir_surface_spec_set_width(surface_spec, width); mir_surface_spec_set_height(surface_spec, height); MirPixelFormat const pixel_format{mir_pixel_format_argb_8888}; mir_surface_spec_set_pixel_format(surface_spec, pixel_format); MirBufferUsage const buffer_usage{mir_buffer_usage_hardware}; mir_surface_spec_set_buffer_usage(surface_spec, buffer_usage); auto surface = mir_surface_create_sync(surface_spec); mir_surface_spec_release(surface_spec); EXPECT_THAT(surface, IsValid()); mir_surface_release_sync(surface); mir_connection_release(connection); } #ifdef ANDROID TEST_F(ClientLibrary, DISABLED_set_fullscreen_on_output_makes_fullscreen_surface) #else TEST_F(ClientLibrary, set_fullscreen_on_output_makes_fullscreen_surface) #endif { using namespace testing; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, 780, 555, mir_pixel_format_xbgr_8888); // We need to specify a valid output id, so we need to find which ones are valid... auto configuration = mir_connection_create_display_config(connection); ASSERT_THAT(configuration->num_outputs, Ge(1)); auto const requested_output = configuration->outputs[0]; mir_surface_spec_set_fullscreen_on_output(surface_spec, requested_output.output_id); auto surface = mir_surface_create_sync(surface_spec); mir_surface_spec_release(surface_spec); EXPECT_THAT(surface, IsValid()); MirNativeBuffer* native_buffer; mir_buffer_stream_get_current_buffer( mir_surface_get_buffer_stream(surface), &native_buffer); EXPECT_THAT(native_buffer->width, Eq(requested_output.modes[requested_output.current_mode].horizontal_resolution)); EXPECT_THAT(native_buffer->height, Eq(requested_output.modes[requested_output.current_mode].vertical_resolution)); // TODO: This is racy. Fix in subsequent "send all the things on construction" branch // EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_fullscreen)); mir_surface_release_sync(surface); mir_display_config_destroy(configuration); mir_connection_release(connection); } /* * We don't (yet) use a stub client platform, so can't rely on its behaviour * in these tests. * * At the moment, enabling them will either spuriously pass (hardware buffer, mesa) * or crash (everything else). */ TEST_F(ClientLibrary, DISABLED_can_create_buffer_usage_hardware_surface) { using namespace testing; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, 800, 600, mir_pixel_format_bgr_888); MirBufferUsage const buffer_usage{mir_buffer_usage_hardware}; mir_surface_spec_set_buffer_usage(surface_spec, buffer_usage); auto surface = mir_surface_create_sync(surface_spec); mir_surface_spec_release(surface_spec); EXPECT_THAT(surface, IsValid()); MirNativeBuffer* native_buffer; // We use the fact that our stub client platform returns NULL if asked for a native // buffer on a surface with mir_buffer_usage_software set. mir_buffer_stream_get_current_buffer( mir_surface_get_buffer_stream(surface), &native_buffer); EXPECT_THAT(native_buffer, Not(Eq(nullptr))); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ClientLibrary, DISABLED_can_create_buffer_usage_software_surface) { using namespace testing; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, 800, 600, mir_pixel_format_bgr_888); MirBufferUsage const buffer_usage{mir_buffer_usage_software}; mir_surface_spec_set_buffer_usage(surface_spec, buffer_usage); auto surface = mir_surface_create_sync(surface_spec); mir_surface_spec_release(surface_spec); EXPECT_THAT(surface, IsValid()); MirGraphicsRegion graphics_region; // We use the fact that our stub client platform returns a NULL vaddr if // asked to map a hardware buffer. mir_buffer_stream_get_graphics_region( mir_surface_get_buffer_stream(surface), &graphics_region); EXPECT_THAT(graphics_region.vaddr, Not(Eq(nullptr))); mir_surface_release_sync(surface); mir_connection_release(connection); } namespace { void dummy_event_handler_one(MirSurface*, MirEvent const*, void*) { } void dummy_event_handler_two(MirSurface*, MirEvent const*, void*) { } } /* * Regression test for LP: 1438160 */ TEST_F(ClientLibrary, can_change_event_delegate) { using namespace testing; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, 800, 600, mir_pixel_format_argb_8888); auto surface = mir_surface_create_sync(surface_spec); mir_surface_spec_release(surface_spec); ASSERT_THAT(surface, IsValid()); /* TODO: When provide-event-fd lands, change this into a better test that actually * tests that the correct event handler is called. * * Without manual dispatch, it's racy to try and test that. */ mir_surface_set_event_handler(surface, &dummy_event_handler_one, nullptr); mir_surface_set_event_handler(surface, &dummy_event_handler_two, nullptr); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ClientLibrary, can_get_persistent_surface_id) { auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, 800, 600, mir_pixel_format_argb_8888); auto surface = mir_surface_create_sync(surface_spec); mir_surface_spec_release(surface_spec); ASSERT_THAT(surface, IsValid()); auto surface_id = mir_surface_request_persistent_id_sync(surface); EXPECT_TRUE(mir_persistent_id_is_valid(surface_id)); mir_surface_release_sync(surface); mir_persistent_id_release(surface_id); mir_connection_release(connection); } TEST_F(ClientLibrary, input_method_can_specify_foreign_surface_id) { auto first_client = mir_connect_sync(new_connection().c_str(), "Regular Client"); auto surface_spec = mir_connection_create_spec_for_normal_surface(first_client, 800, 600, mir_pixel_format_argb_8888); auto main_surface = mir_surface_create_sync(surface_spec); mir_surface_spec_release(surface_spec); ASSERT_THAT(main_surface, IsValid()); auto main_surface_id = mir_surface_request_persistent_id_sync(main_surface); ASSERT_TRUE(mir_persistent_id_is_valid(main_surface_id)); // Serialise & deserialise the ID auto im_parent_id = mir_persistent_id_from_string(mir_persistent_id_as_string(main_surface_id)); auto im_client = mir_connect_sync(new_connection().c_str(), "IM Client"); surface_spec = mir_connection_create_spec_for_input_method(im_client, 200, 20, mir_pixel_format_argb_8888); MirRectangle attachment_rect { 200, 200, 10, 10 }; mir_surface_spec_attach_to_foreign_parent(surface_spec, im_parent_id, &attachment_rect, mir_edge_attachment_any); auto im_surface = mir_surface_create_sync(surface_spec); EXPECT_THAT(im_surface, IsValid()); mir_surface_spec_release(surface_spec); mir_persistent_id_release(main_surface_id); mir_persistent_id_release(im_parent_id); mir_surface_release_sync(main_surface); mir_surface_release_sync(im_surface); mir_connection_release(first_client); mir_connection_release(im_client); } TEST_F(ClientLibrary, creates_buffer_streams) { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_TRUE(mir_connection_is_valid(connection)); auto stream = mir_connection_create_buffer_stream_sync(connection, 640, 480, mir_pixel_format_abgr_8888, mir_buffer_usage_software); ASSERT_THAT(stream, NotNull()); EXPECT_TRUE(mir_buffer_stream_is_valid(stream)); EXPECT_THAT(mir_buffer_stream_get_error_message(stream), StrEq("")); mir_buffer_stream_release_sync(stream); mir_connection_release(connection); } ./tests/acceptance-tests/test_render_override.cpp0000644000015600001650000000772712676616125022437 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/compositor/display_buffer_compositor.h" #include "mir/compositor/display_buffer_compositor_factory.h" #include "mir/compositor/scene_element.h" #include "mir/compositor/compositor.h" #include "mir/graphics/renderable.h" #include #include #include #include namespace mtf = mir_test_framework; namespace geom = mir::geometry; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace { struct size_less { bool operator() (geom::Size const& a, geom::Size const& b) { return a.width < b.width && a.height < b.height; } }; struct CompositionTracker { bool wait_until_surface_is_rendered_with_size(geom::Size sz) { using namespace std::literals::chrono_literals; std::unique_lock lk(mutex); return cv.wait_for(lk, 4s, [this, sz] { return std::find(rendered_sizes.begin(), rendered_sizes.end(), sz) != rendered_sizes.end(); }); } void surface_rendered_with_size(geom::Size sz) { std::unique_lock lk(mutex); rendered_sizes.emplace(sz); cv.notify_all(); } private: std::set rendered_sizes; std::mutex mutex; std::condition_variable cv; }; struct CustomDBC : mc::DisplayBufferCompositor { CustomDBC(std::shared_ptr const& tracker) : tracker(tracker) { } void composite(mc::SceneElementSequence&& sequence) override { for(auto const& element : sequence) { auto renderable = element->renderable(); renderable->buffer(); //consume buffer to stop compositor from spinning tracker->surface_rendered_with_size(renderable->screen_position().size); } } private: std::shared_ptr const tracker; }; struct CustomDBCFactory : mc::DisplayBufferCompositorFactory { CustomDBCFactory(std::shared_ptr const& tracker) : tracker(tracker) { } std::unique_ptr create_compositor_for(mg::DisplayBuffer&) override { return std::make_unique(tracker); } private: std::shared_ptr const tracker; }; struct DisplayBufferCompositorOverride : mtf::ConnectedClientWithASurface { void SetUp() override { using namespace std::literals::chrono_literals; server.override_the_display_buffer_compositor_factory([this] { return std::make_shared(tracker); }); mtf::ConnectedClientWithASurface::SetUp(); } void TearDown() override { mtf::ConnectedClientWithASurface::TearDown(); } protected: std::shared_ptr const tracker{std::make_shared()}; }; } TEST_F(DisplayBufferCompositorOverride, composite_called_with_surface) { MirSurfaceParameters surface_params; mir_surface_get_parameters(surface, &surface_params); mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); EXPECT_TRUE(tracker->wait_until_surface_is_rendered_with_size({surface_params.width, surface_params.height})); } ./tests/acceptance-tests/test_symbols_required_by_mesa.cpp0000644000015600001650000000227412676616125024340 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include #include #include #include "mir_test_framework/executable_path.h" using namespace testing; namespace mtf = mir_test_framework; TEST(SymbolsRequiredByMesa, are_exported_by_client_platform_mesa) { auto const handle = dlopen(mtf::client_platform("mesa").c_str(), RTLD_LAZY); ASSERT_THAT(handle, NotNull()); auto const sym = dlsym(handle, "mir_client_mesa_egl_native_display_is_valid"); EXPECT_THAT(sym, NotNull()); dlclose(handle); } ./tests/acceptance-tests/test_session_mediator_report.cpp0000644000015600001650000001337612676616125024220 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/mir_prompt_session.h" #include "mir/frontend/session_mediator_report.h" #include "mir/test/fake_shared.h" #include "mir_test_framework/any_surface.h" #include "mir_test_framework/headless_in_process_server.h" #include #include namespace mf = mir::frontend; namespace mt = mir::test; namespace mtf = mir_test_framework; using testing::_; namespace { struct MockSessionMediatorReport : mf::SessionMediatorReport { MOCK_METHOD1(session_connect_called, void (std::string const&)); MOCK_METHOD1(session_create_surface_called, void (std::string const&)); MOCK_METHOD1(session_next_buffer_called, void (std::string const&)); MOCK_METHOD1(session_exchange_buffer_called, void (std::string const&)); MOCK_METHOD1(session_submit_buffer_called, void (std::string const&)); MOCK_METHOD1(session_allocate_buffers_called, void (std::string const&)); MOCK_METHOD1(session_release_buffers_called, void (std::string const&)); MOCK_METHOD1(session_release_surface_called, void (std::string const&)); MOCK_METHOD1(session_disconnect_called, void (std::string const&)); MOCK_METHOD2(session_start_prompt_session_called, void (std::string const&, pid_t)); MOCK_METHOD1(session_stop_prompt_session_called, void (std::string const&)); MOCK_METHOD1(session_create_buffer_stream_called, void (std::string const& app_name)); MOCK_METHOD1(session_release_buffer_stream_called, void (std::string const& app_name)); void session_configure_surface_called(std::string const&) override {}; void session_configure_surface_cursor_called(std::string const&) override {}; void session_configure_display_called(std::string const&) override {}; void session_set_base_display_configuration_called(std::string const&) override {}; void session_error(const std::string&, const char*, const std::string&) override {}; }; void null_prompt_session_state_change_callback( MirPromptSession* /*prompt_provider*/, MirPromptSessionState /*state*/, void* /*context*/) { } struct SessionMediatorReportTest : mtf::HeadlessInProcessServer { void SetUp() override { server.override_the_session_mediator_report( [this] { return mt::fake_shared(report); }); mtf::HeadlessInProcessServer::SetUp(); } void TearDown() override { if (connection) mir_connection_release(connection); HeadlessInProcessServer::TearDown(); } void connect_client() { connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); } void disconnect_client() { mir_connection_release(connection); connection = nullptr; } testing::NiceMock report; MirConnection* connection = nullptr; }; } TEST_F(SessionMediatorReportTest, session_connect_called) { EXPECT_CALL(report, session_connect_called(_)); connect_client(); } TEST_F(SessionMediatorReportTest, session_disconnect_called) { connect_client(); EXPECT_CALL(report, session_disconnect_called(_)); disconnect_client(); testing::Mock::VerifyAndClearExpectations(&report); } TEST_F(SessionMediatorReportTest, session_create_and_release_surface_called) { connect_client(); EXPECT_CALL(report, session_create_surface_called(_)); auto const surface = mtf::make_any_surface(connection); testing::Mock::VerifyAndClearExpectations(&report); EXPECT_CALL(report, session_release_surface_called(_)); mir_surface_release_sync(surface); testing::Mock::VerifyAndClearExpectations(&report); } TEST_F(SessionMediatorReportTest, session_exchange_buffer_called) { EXPECT_CALL(report, session_submit_buffer_called(_)); connect_client(); auto const surface = mtf::make_any_surface(connection); auto const buffer_stream = mir_surface_get_buffer_stream(surface); mir_buffer_stream_swap_buffers_sync(buffer_stream); mir_surface_release_sync(surface); } TEST_F(SessionMediatorReportTest, session_start_and_stop_prompt_session_called) { connect_client(); EXPECT_CALL(report, session_start_prompt_session_called(_, _)); auto const prompt_session = mir_connection_create_prompt_session_sync( connection, getpid(), null_prompt_session_state_change_callback, nullptr); testing::Mock::VerifyAndClearExpectations(&report); EXPECT_CALL(report, session_stop_prompt_session_called(_)); mir_prompt_session_release_sync(prompt_session); testing::Mock::VerifyAndClearExpectations(&report); } TEST_F(SessionMediatorReportTest, session_create_and_release_buffer_stream_called) { connect_client(); EXPECT_CALL(report, session_create_buffer_stream_called(_)); auto const buffer_stream = mir_connection_create_buffer_stream_sync(connection, 640, 480, mir_pixel_format_abgr_8888, mir_buffer_usage_software); testing::Mock::VerifyAndClearExpectations(&report); EXPECT_CALL(report, session_release_buffer_stream_called(_)); mir_buffer_stream_release_sync(buffer_stream); testing::Mock::VerifyAndClearExpectations(&report); } ./tests/acceptance-tests/test_server_disconnect.cpp0000644000015600001650000001406312676616125022767 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/interprocess_client_server_test.h" #include "mir/test/cross_process_sync.h" #include "mir_test_framework/process.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/any_surface.h" #include "mir/test/cross_process_action.h" #include #include #include #include namespace mtf = mir_test_framework; namespace mt = mir::test; using namespace testing; namespace { struct ServerDisconnect : mtf::InterprocessClientServerTest { void SetUp() override { mtf::InterprocessClientServerTest::SetUp(); run_in_server([]{}); } mtf::UsingStubClientPlatform using_stub_client_platform; }; struct MockEventHandler { MOCK_METHOD1(handle, void(MirLifecycleState transition)); static void handle(MirConnection*, MirLifecycleState transition, void* event_handler) { static_cast(event_handler)->handle(transition); } }; void null_lifecycle_callback(MirConnection*, MirLifecycleState, void*) { } } TEST_F(ServerDisconnect, is_detected_by_client) { if (is_test_process()) { MockEventHandler mock_event_handler; auto connection = mir_connect_sync(mtf::test_socket_file().c_str() , __PRETTY_FUNCTION__); mir_connection_set_lifecycle_event_callback(connection, &MockEventHandler::handle, &mock_event_handler); auto surface = mtf::make_any_surface(connection); std::atomic signalled(false); EXPECT_CALL(mock_event_handler, handle(mir_lifecycle_connection_lost)).Times(1). WillOnce(testing::InvokeWithoutArgs([&] { signalled.store(true); })); using clock = std::chrono::high_resolution_clock; auto const time_limit = clock::now() + std::chrono::seconds(2); auto const server_stopper = std::async(std::launch::async, [this] { stop_server(); }); while (!signalled.load() && clock::now() < time_limit) { mir_buffer_stream_swap_buffers_sync( mir_surface_get_buffer_stream(surface)); } mir_surface_release_sync(surface); mir_connection_release(connection); } } TEST_F(ServerDisconnect, doesnt_stop_client_calling_API_functions) { mt::CrossProcessAction connect; mt::CrossProcessAction create_surface; mt::CrossProcessAction configure_display; mt::CrossProcessAction disconnect; auto const client = new_client_process([&] { MirConnection* connection{nullptr}; connect.exec([&] { connection = mir_connect_sync(mtf::test_socket_file().c_str() , __PRETTY_FUNCTION__); EXPECT_TRUE(mir_connection_is_valid(connection)); /* * Set a null callback to avoid killing the process * (default callback raises SIGHUP). */ mir_connection_set_lifecycle_event_callback(connection, null_lifecycle_callback, nullptr); }); MirSurface* surf; create_surface.exec([&] { surf = mtf::make_any_surface(connection); }); configure_display.exec([&] { auto config = mir_connection_create_display_config(connection); mir_wait_for(mir_connection_apply_display_config(connection, config)); mir_display_config_destroy(config); }); disconnect.exec([&] { mir_surface_release_sync(surf); mir_connection_release(connection); }); }); if (is_test_process()) { connect(); stop_server(); /* While trying to create a surface the connection break will be detected */ create_surface(); /* Trying to configure the display shouldn't block */ configure_display(); /* Trying to disconnect at this point shouldn't block */ disconnect(); EXPECT_THAT(client->wait_for_termination().exit_code, Eq(EXIT_SUCCESS)); } } using ServerDisconnectDeathTest = ServerDisconnect; TEST_F(ServerDisconnectDeathTest, causes_client_to_terminate_by_default) { mt::CrossProcessAction connect; mt::CrossProcessSync create_surface_sync; auto const client = new_client_process([&] { MirConnection* connection{nullptr}; connect.exec([&] { connection = mir_connect_sync(mtf::test_socket_file().c_str() , __PRETTY_FUNCTION__); EXPECT_TRUE(mir_connection_is_valid(connection)); }); create_surface_sync.wait_for_signal_ready_for(); mtf::make_any_surface(connection); mir_connection_release(connection); }); if (is_test_process()) { connect(); stop_server(); /* * While trying to create a surface the connection break will be detected * and the client should self-terminate. */ create_surface_sync.signal_ready(); auto const client_result = client->wait_for_termination(); EXPECT_EQ(mtf::TerminationReason::child_terminated_by_signal, client_result.reason); int sig = client_result.signal; EXPECT_TRUE(sig == SIGHUP || sig == SIGKILL /* (Valgrind) */); } } ./tests/acceptance-tests/test_custom_input_dispatcher.cpp0000644000015600001650000000711312676616125024205 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/input/input_device_info.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/fake_input_device.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir/test/doubles/mock_input_dispatcher.h" #include "mir/test/wait_condition.h" #include "mir/test/fake_shared.h" #include "mir/test/event_matchers.h" #include "mir/test/event_factory.h" #include #include #include namespace mi = mir::input; namespace mt = mir::test; namespace mis = mir::input::synthesis; namespace mt = mir::test; namespace mtd = mt::doubles; namespace mtf = mir_test_framework; namespace { struct TestCustomInputDispatcher : mtf::HeadlessInProcessServer { testing::NiceMock input_dispatcher; void SetUp() { using namespace ::testing; EXPECT_CALL(input_dispatcher, dispatch(mt::InputDeviceConfigurationChangedEvent())).Times(AnyNumber()); EXPECT_CALL(input_dispatcher, dispatch(mt::InputDeviceResetEvent())).Times(AnyNumber()); } std::unique_ptr fake_keyboard{ mtf::add_fake_input_device(mi::InputDeviceInfo{"keyboard", "keyboard-uid" , mi::DeviceCapability::keyboard}) }; std::unique_ptr fake_pointer{ mtf::add_fake_input_device(mi::InputDeviceInfo{"mouse", "mouse-uid" , mi::DeviceCapability::pointer}) }; mir::test::WaitCondition all_keys_received; mir::test::WaitCondition all_pointer_events_received; }; } TEST_F(TestCustomInputDispatcher, gets_started_and_stopped) { server.override_the_input_dispatcher( [this]() { testing::InSequence seq; EXPECT_CALL(input_dispatcher, start()); EXPECT_CALL(input_dispatcher, stop()); return mt::fake_shared(input_dispatcher); }); start_server(); } TEST_F(TestCustomInputDispatcher, receives_input) { server.override_the_input_dispatcher([this](){return mt::fake_shared(input_dispatcher);}); start_server(); using namespace testing; // the order of those two occuring is not guranteed, since the input is simulated from // separate devices - if the sequence of events is required in a test, better use // just one device with the superset of the capabilities instead. EXPECT_CALL(input_dispatcher, dispatch(mt::PointerEventWithPosition(1, 1))).Times(1) .WillOnce(mt::ReturnFalseAndWakeUp(&all_pointer_events_received)); EXPECT_CALL(input_dispatcher, dispatch(mt::KeyDownEvent())).Times(1) .WillOnce(mt::ReturnFalseAndWakeUp(&all_keys_received)); fake_pointer->emit_event(mis::a_pointer_event().with_movement(1, 1)); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_M)); all_keys_received.wait_for_at_most_seconds(10); all_pointer_events_received.wait_for_at_most_seconds(10); } ./tests/acceptance-tests/throwback/0000755000015600001650000000000012676616160017464 5ustar jenkinsjenkins./tests/acceptance-tests/throwback/test_client_library_errors.cpp0000644000015600001650000003045712676616157025644 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/debug/surface.h" #include "src/include/client/mir/client_platform_factory.h" #include "src/include/client/mir/client_platform.h" #include "src/include/client/mir/client_buffer_factory.h" #include "mir/test/validity_matchers.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/using_client_platform.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/stub_client_connection_configuration.h" #include "mir_test_framework/any_surface.h" #include "mir/test/doubles/stub_client_buffer_factory.h" #include #include #include #include #include namespace mcl = mir::client; namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace { enum Method : uint64_t { none = 0, the_client_platform_factory = 1<<0, create_client_platform = 1<<1, create_egl_native_window = 1<<2, create_buffer_factory = 1<<3 }; std::string const exception_text{"Ducks!"}; template bool should_fail() { return (name & failure_set); } template class ConfigurableFailurePlatform : public mir::client::ClientPlatform { std::shared_ptr create_egl_native_window(mir::client::EGLNativeSurface *) { if (should_fail()) { BOOST_THROW_EXCEPTION(std::runtime_error{exception_text}); } return std::shared_ptr{}; } void populate(MirPlatformPackage&) const override { } MirPlatformMessage* platform_operation(MirPlatformMessage const*) override { return nullptr; } MirPlatformType platform_type() const { BOOST_THROW_EXCEPTION(std::runtime_error{exception_text}); return MirPlatformType::mir_platform_type_gbm; } std::shared_ptr create_buffer_factory() { if (should_fail()) { BOOST_THROW_EXCEPTION(std::runtime_error{exception_text}); } return std::make_shared(); } std::shared_ptr create_egl_native_display() { return std::shared_ptr{}; } MirNativeBuffer *convert_native_buffer(mir::graphics::NativeBuffer*) const { BOOST_THROW_EXCEPTION(std::runtime_error{exception_text}); return nullptr; } MirPixelFormat get_egl_pixel_format(EGLDisplay, EGLConfig) const override { return mir_pixel_format_invalid; } }; template class ConfigurableFailureFactory: public mir::client::ClientPlatformFactory { std::shared_ptr create_client_platform(mir::client::ClientContext* /*context*/) override { if (should_fail()) { BOOST_THROW_EXCEPTION(std::runtime_error{exception_text}); } return std::make_shared>(); } }; template class ConfigurableFailureConfiguration : public mtf::StubConnectionConfiguration { using mtf::StubConnectionConfiguration::StubConnectionConfiguration; std::shared_ptr the_client_platform_factory() override { if (should_fail()) { BOOST_THROW_EXCEPTION(std::runtime_error{exception_text}); } return std::make_shared>(); } }; } using ClientLibraryErrors = mtf::HeadlessInProcessServer; TEST_F(ClientLibraryErrors, exception_in_client_configuration_constructor_generates_error) { mtf::UsingClientPlatform> stubby; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_FALSE(mir_connection_is_valid(connection)); EXPECT_THAT(mir_connection_get_error_message(connection), testing::HasSubstr(exception_text)); mir_connection_release(connection); } TEST_F(ClientLibraryErrors, exception_in_platform_construction_generates_error) { mtf::UsingClientPlatform> stubby; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_FALSE(mir_connection_is_valid(connection)); EXPECT_THAT(mir_connection_get_error_message(connection), testing::HasSubstr(exception_text)); mir_connection_release(connection); } TEST_F(ClientLibraryErrors, connecting_to_garbage_socket_returns_appropriate_error) { using namespace testing; mtf::UsingStubClientPlatform stubby; auto connection = mir_connect_sync("garbage", __PRETTY_FUNCTION__); ASSERT_THAT(connection, NotNull()); char const* error = mir_connection_get_error_message(connection); if (std::strcmp("connect: No such file or directory", error) && std::strcmp("Can't find MIR server", error) && !std::strstr(error, "Failed to connect to server socket")) { FAIL() << error; } mir_connection_release(connection); } TEST_F(ClientLibraryErrors, create_surface_returns_error_object_on_failure) { mtf::UsingClientPlatform> stubby; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_THAT(connection, IsValid()); auto surface = mtf::make_any_surface(connection); ASSERT_NE(surface, nullptr); EXPECT_FALSE(mir_surface_is_valid(surface)); EXPECT_THAT(mir_surface_get_error_message(surface), testing::HasSubstr(exception_text)); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ClientLibraryErrors, create_buffer_stream_returns_error_object_on_failure) { mtf::UsingClientPlatform> stubby; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_THAT(connection, IsValid()); auto stream = mir_connection_create_buffer_stream_sync(connection, 640, 480, mir_pixel_format_abgr_8888, mir_buffer_usage_software); ASSERT_NE(stream, nullptr); EXPECT_FALSE(mir_buffer_stream_is_valid(stream)); EXPECT_THAT(mir_buffer_stream_get_error_message(stream), testing::HasSubstr(exception_text)); mir_buffer_stream_release_sync(stream); mir_connection_release(connection); } namespace { void recording_surface_callback(MirSurface*, void* ctx) { auto called = static_cast(ctx); *called = true; } } TEST_F(ClientLibraryErrors, surface_release_on_error_object_still_calls_callback) { mtf::UsingClientPlatform> stubby; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_THAT(connection, IsValid()); auto surface = mtf::make_any_surface(connection); ASSERT_NE(surface, nullptr); EXPECT_FALSE(mir_surface_is_valid(surface)); EXPECT_THAT(mir_surface_get_error_message(surface), testing::HasSubstr(exception_text)); bool callback_called{false}; mir_surface_release(surface, &recording_surface_callback, &callback_called); EXPECT_TRUE(callback_called); mir_connection_release(connection); } TEST_F(ClientLibraryErrors, create_surface_returns_error_object_on_failure_in_reply_processing) { mtf::UsingClientPlatform> stubby; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_THAT(connection, IsValid()); auto surface = mtf::make_any_surface(connection); ASSERT_NE(surface, nullptr); EXPECT_FALSE(mir_surface_is_valid(surface)); EXPECT_THAT(mir_surface_get_error_message(surface), testing::HasSubstr(exception_text)); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ClientLibraryErrors, passing_invalid_parent_id_to_surface_create) { using namespace testing; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_THAT(connection, IsValid()); // An ID that parses as valid, but doesn't correspond to any auto invalid_id = mir_persistent_id_from_string("05f223a2-39e5-48b9-9416-b0ce837351b6"); auto spec = mir_connection_create_spec_for_input_method(connection, 200, 200, mir_pixel_format_argb_8888); MirRectangle rect{ 100, 100, 10, 10 }; mir_surface_spec_attach_to_foreign_parent(spec, invalid_id, &rect, mir_edge_attachment_any); auto surface = mir_surface_create_sync(spec); EXPECT_THAT(surface, Not(IsValid())); EXPECT_THAT(mir_surface_get_error_message(surface), MatchesRegex(".*Lookup.*failed.*")); mir_persistent_id_release(invalid_id); mir_surface_spec_release(spec); mir_surface_release_sync(surface); mir_connection_release(connection); } using ClientLibraryErrorsDeathTest = ClientLibraryErrors; TEST_F(ClientLibraryErrorsDeathTest, creating_surface_on_garbage_connection_is_fatal) { mtf::UsingStubClientPlatform stubby; auto connection = mir_connect_sync("garbage", __PRETTY_FUNCTION__); ASSERT_FALSE(mir_connection_is_valid(connection)); EXPECT_DEATH( mtf::make_any_surface(connection), ""); mir_connection_release(connection); } TEST_F(ClientLibraryErrorsDeathTest, creating_surface_synchronosly_on_malconstructed_connection_is_fatal) { mtf::UsingClientPlatform> stubby; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_FALSE(mir_connection_is_valid(connection)); EXPECT_DEATH(mtf::make_any_surface(connection), ""); mir_connection_release(connection); } TEST_F(ClientLibraryErrorsDeathTest, creating_surface_synchronosly_on_invalid_connection_is_fatal) { mtf::UsingClientPlatform> stubby; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_FALSE(mir_connection_is_valid(connection)); EXPECT_DEATH(mtf::make_any_surface(connection), ""); mir_connection_release(connection); } TEST_F(ClientLibraryErrorsDeathTest, surface_spec_attaching_invalid_parent_id) { auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto spec = mir_connection_create_spec_for_input_method(connection, 100, 100, mir_pixel_format_argb_8888); MirRectangle rect{ 100, 100, 10, 10 }; EXPECT_DEATH(mir_surface_spec_attach_to_foreign_parent(spec, nullptr, &rect, mir_edge_attachment_any), ""); mir_connection_release(connection); } TEST_F(ClientLibraryErrorsDeathTest, surface_spec_attaching_invalid_rectangle) { auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto spec = mir_connection_create_spec_for_input_method(connection, 100, 100, mir_pixel_format_argb_8888); auto id = mir_persistent_id_from_string("fa69b2e9-d507-4005-be61-5068f40a5aec"); EXPECT_DEATH(mir_surface_spec_attach_to_foreign_parent(spec, id, nullptr, mir_edge_attachment_any), ""); mir_persistent_id_release(id); mir_connection_release(connection); } ./tests/acceptance-tests/throwback/test_shell_control_of_surface_configuration.cpp0000644000015600001650000000753312676616125031232 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/scene/surface.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/shell/canonical_window_manager.h" #include "mir_toolkit/mir_client_library.h" #include #include namespace ms = mir::scene; namespace msh = mir::shell; namespace mtf = mir_test_framework; using namespace ::testing; namespace { struct MockWindowManager : msh::CanonicalWindowManager { using msh::CanonicalWindowManager::CanonicalWindowManager; MOCK_METHOD4(set_surface_attribute, int(std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value)); int real_set_surface_attribute(std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) { return msh::CanonicalWindowManager::set_surface_attribute(session, surface, attrib, value); } }; struct ShellSurfaceConfiguration : mtf::ConnectedClientWithASurface { void SetUp() override { server.override_the_window_manager_builder([this] (msh::FocusController* focus_controller) -> std::shared_ptr { mock_window_manager = std::make_shared( focus_controller, server.the_shell_display_layout()); ON_CALL(*mock_window_manager, set_surface_attribute(_, _, _, _)) .WillByDefault(Invoke( mock_window_manager.get(), &MockWindowManager::real_set_surface_attribute)); EXPECT_CALL(*mock_window_manager, set_surface_attribute(_, _, Ne(mir_surface_attrib_state), _)) .Times(AnyNumber()); return mock_window_manager; }); mtf::ConnectedClientWithASurface::SetUp(); } std::shared_ptr mock_window_manager; }; } TEST_F(ShellSurfaceConfiguration, the_window_manager_is_notified_of_attribute_changes) { EXPECT_CALL(*mock_window_manager, set_surface_attribute(_, _, mir_surface_attrib_state, Eq(mir_surface_state_maximized))); mir_wait_for(mir_surface_set_state(surface, mir_surface_state_maximized)); EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_maximized)); } TEST_F(ShellSurfaceConfiguration, the_window_manager_may_interfere_with_attribute_changes) { auto const set_to_vertmax = [this]( std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int /*value*/) { return mock_window_manager->real_set_surface_attribute( session, surface, attrib, mir_surface_state_vertmaximized); }; EXPECT_CALL(*mock_window_manager, set_surface_attribute(_, _, mir_surface_attrib_state, Eq(mir_surface_state_maximized))) .WillOnce(Invoke(set_to_vertmax)); mir_wait_for(mir_surface_set_state(surface, mir_surface_state_maximized)); EXPECT_THAT(mir_surface_get_state(surface), Eq(mir_surface_state_vertmaximized)); } ./tests/acceptance-tests/throwback/CMakeLists.txt0000644000015600001650000000070212676616157022231 0ustar jenkinsjenkinsinclude_directories( ${CMAKE_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/src/include/server ${PROJECT_SOURCE_DIR}/src/include/client ${PROJECT_SOURCE_DIR}/tests/include ) add_library(acceptance-test-throwback OBJECT test_client_cursor_api.cpp # uses BasicWindowManager<> test_client_library_errors.cpp # uses UsingClientPlatform test_shell_control_of_surface_configuration.cpp # uses CanonicalWindowManager test_presentation_chain.cpp ) ./tests/acceptance-tests/throwback/test_presentation_chain.cpp0000644000015600001650000002627012676616157025121 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_toolkit/mir_presentation_chain.h" #include "mir_toolkit/mir_buffer.h" #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir/geometry/size.h" #include #include namespace mg = mir::graphics; namespace mtf = mir_test_framework; namespace mt = mir::test; namespace mc = mir::compositor; namespace geom = mir::geometry; using namespace testing; using namespace std::chrono_literals; namespace { struct Chain { Chain(Chain const&) = delete; Chain& operator=(Chain const&) = delete; Chain(MirConnection* connection) : chain(mir_connection_create_presentation_chain_sync(connection)) { } operator MirPresentationChain*() { return chain; } ~Chain() { mir_presentation_chain_release(chain); } private: MirPresentationChain* chain; }; class SurfaceWithChain { public: SurfaceWithChain(SurfaceWithChain const&) = delete; SurfaceWithChain& operator=(SurfaceWithChain const&) = delete; operator MirSurface*() { return surface; } Chain& chain() { return chain_; } ~SurfaceWithChain() { mir_surface_release_sync(surface); } protected: SurfaceWithChain(MirConnection* connection, std::function const& fn) : chain_(connection), surface(fn(chain_)) { } private: Chain chain_; MirSurface* surface; }; struct SurfaceWithChainFromStart : SurfaceWithChain { SurfaceWithChainFromStart(SurfaceWithChainFromStart const&) = delete; SurfaceWithChainFromStart& operator=(SurfaceWithChainFromStart const&) = delete; SurfaceWithChainFromStart(MirConnection* connection, geom::Size size, MirPixelFormat pf) : SurfaceWithChain(connection, std::bind(&SurfaceWithChainFromStart::create_surface, this, std::placeholders::_1, connection, size, pf)) { } private: MirSurface* create_surface(Chain& chain, MirConnection* connection, geom::Size size, MirPixelFormat pf) { auto spec = mir_connection_create_spec_for_normal_surface( connection, size.width.as_int(), size.height.as_int(), pf); mir_surface_spec_add_presentation_chain( spec, size.width.as_int(), size.height.as_int(), 0, 0, chain); auto surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); return surface; } }; struct SurfaceWithChainFromReassociation : SurfaceWithChain { SurfaceWithChainFromReassociation(SurfaceWithChainFromReassociation const&) = delete; SurfaceWithChainFromReassociation& operator=(SurfaceWithChainFromReassociation const&) = delete; SurfaceWithChainFromReassociation(MirConnection* connection, geom::Size size, MirPixelFormat pf) : SurfaceWithChain(connection, std::bind(&SurfaceWithChainFromReassociation::create_surface, this, std::placeholders::_1, connection, size, pf)) { } private: MirSurface* create_surface(Chain& chain, MirConnection* connection, geom::Size size, MirPixelFormat pf) { MirSurfaceSpec* spec = mir_connection_create_spec_for_normal_surface( connection, size.width.as_int(), size.height.as_int(), pf); auto surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); spec = mir_create_surface_spec(connection); mir_surface_spec_add_presentation_chain( spec, size.width.as_int(), size.height.as_int(), 0, 0, chain); mir_surface_apply_spec(surface, spec); mir_surface_spec_release(spec); return surface; } }; struct PresentationChain : mtf::ConnectedClientHeadlessServer { geom::Size const size {100, 20}; MirPixelFormat const pf = mir_pixel_format_abgr_8888; MirBufferUsage const usage = mir_buffer_usage_software; void SetUp() override { //test suite has to be run with the new semantics activated add_to_environment("MIR_SERVER_NBUFFERS", "0"); ConnectedClientHeadlessServer::SetUp(); } }; struct MirBufferSync { void buffer_available(MirBuffer* b) { std::unique_lock lk(mutex); callback_count++; buffer_ = b; available = true; cv.notify_all(); } bool wait_for_buffer(std::chrono::seconds timeout) { std::unique_lock lk(mutex); return cv.wait_for(lk, timeout, [this] { return available; }); } void unavailable() { std::unique_lock lk(mutex); available = false; } MirBuffer* buffer() { return buffer_; } private: std::mutex mutex; std::condition_variable cv; MirBuffer* buffer_ = nullptr; bool available = false; unsigned int callback_count = 0; }; void buffer_callback(MirPresentationChain*, MirBuffer* buffer, void* context) { auto sync = reinterpret_cast(context); sync->buffer_available(buffer); } } TEST_F(PresentationChain, allocation_calls_callback) { SurfaceWithChainFromStart surface(connection, size, pf); MirBufferSync context; mir_presentation_chain_allocate_buffer( surface.chain(), size.width.as_int(), size.height.as_int(), pf, usage, buffer_callback, &context); EXPECT_TRUE(context.wait_for_buffer(10s)); EXPECT_THAT(context.buffer(), Ne(nullptr)); } TEST_F(PresentationChain, has_native_buffer) { SurfaceWithChainFromStart surface(connection, size, pf); MirBufferSync context; mir_presentation_chain_allocate_buffer( surface.chain(), size.width.as_int(), size.height.as_int(), pf, usage, buffer_callback, &context); EXPECT_TRUE(context.wait_for_buffer(10s)); auto buffer = context.buffer(); EXPECT_THAT(context.buffer(), Ne(nullptr)); //the native type for the stub platform is nullptr EXPECT_THAT(mir_buffer_get_native_buffer(buffer, mir_none), Eq(nullptr)); } TEST_F(PresentationChain, has_native_fence) { SurfaceWithChainFromStart surface(connection, size, pf); MirBufferSync context; mir_presentation_chain_allocate_buffer( surface.chain(), size.width.as_int(), size.height.as_int(), pf, usage, buffer_callback, &context); EXPECT_TRUE(context.wait_for_buffer(10s)); auto buffer = context.buffer(); EXPECT_THAT(context.buffer(), Ne(nullptr)); //the native type for the stub platform is nullptr EXPECT_THAT(mir_buffer_get_fence(buffer), Eq(nullptr)); } TEST_F(PresentationChain, can_map_for_cpu_render) { SurfaceWithChainFromStart surface(connection, size, pf); MirBufferSync context; mir_presentation_chain_allocate_buffer( surface.chain(), size.width.as_int(), size.height.as_int(), pf, usage, buffer_callback, &context); EXPECT_TRUE(context.wait_for_buffer(10s)); auto buffer = context.buffer(); EXPECT_THAT(context.buffer(), Ne(nullptr)); auto region = mir_buffer_get_graphics_region(buffer, mir_none); //cast to int so gtest doesn't try to print a char* that isn't a string EXPECT_THAT(reinterpret_cast(region.vaddr), Ne(nullptr)); EXPECT_THAT(region.width, Eq(size.width.as_int())); EXPECT_THAT(region.height, Eq(size.height.as_int())); EXPECT_THAT(region.stride, Eq(size.width.as_int() * MIR_BYTES_PER_PIXEL(pf))); EXPECT_THAT(region.pixel_format, Eq(pf)); } //needs an ABI break to fix TEST_F(PresentationChain, DISABLED_submission_will_eventually_call_callback) { SurfaceWithChainFromStart surface(connection, size, pf); auto const num_buffers = 2u; std::array contexts; auto num_iterations = 50u; for(auto& context : contexts) { mir_presentation_chain_allocate_buffer( surface.chain(), size.width.as_int(), size.height.as_int(), pf, usage, buffer_callback, &context); ASSERT_TRUE(context.wait_for_buffer(10s)); ASSERT_THAT(context.buffer(), Ne(nullptr)); } for(auto i = 0u; i < num_iterations; i++) { mir_presentation_chain_submit_buffer(surface.chain(), contexts[i % num_buffers].buffer()); contexts[i % num_buffers].unavailable(); if (i != 0) ASSERT_TRUE(contexts[(i-1) % num_buffers].wait_for_buffer(10s)) << "iteration " << i; } } TEST_F(PresentationChain, submission_will_eventually_call_callback_reassociated) { SurfaceWithChainFromReassociation surface(connection, size, pf); auto const num_buffers = 2u; std::array contexts; auto num_iterations = 50u; for(auto& context : contexts) { mir_presentation_chain_allocate_buffer( surface.chain(), size.width.as_int(), size.height.as_int(), pf, usage, buffer_callback, &context); ASSERT_TRUE(context.wait_for_buffer(10s)); ASSERT_THAT(context.buffer(), Ne(nullptr)); } for(auto i = 0u; i < num_iterations; i++) { mir_presentation_chain_submit_buffer(surface.chain(), contexts[i % num_buffers].buffer()); contexts[i % num_buffers].unavailable(); if (i != 0) ASSERT_TRUE(contexts[(i-1) % num_buffers].wait_for_buffer(10s)) << "iteration " << i; } } TEST_F(PresentationChain, buffers_can_be_destroyed_before_theyre_returned) { SurfaceWithChainFromStart surface(connection, size, pf); MirBufferSync context; mir_presentation_chain_allocate_buffer( surface.chain(), size.width.as_int(), size.height.as_int(), pf, usage, buffer_callback, &context); ASSERT_TRUE(context.wait_for_buffer(10s)); ASSERT_THAT(context.buffer(), Ne(nullptr)); mir_presentation_chain_submit_buffer(surface.chain(), context.buffer()); mir_buffer_release(context.buffer()); } TEST_F(PresentationChain, can_access_basic_buffer_properties) { MirBufferSync context; geom::Width width { 32 }; geom::Height height { 33 }; auto format = mir_pixel_format_abgr_8888; auto usage = mir_buffer_usage_software; SurfaceWithChainFromStart surface(connection, size, pf); mir_presentation_chain_allocate_buffer( surface.chain(), width.as_int(), height.as_int(), format, usage, buffer_callback, &context); ASSERT_TRUE(context.wait_for_buffer(10s)); auto buffer = context.buffer(); EXPECT_THAT(mir_buffer_get_width(buffer), Eq(width.as_uint32_t())); EXPECT_THAT(mir_buffer_get_height(buffer), Eq(height.as_uint32_t())); EXPECT_THAT(mir_buffer_get_buffer_usage(buffer), Eq(usage)); EXPECT_THAT(mir_buffer_get_pixel_format(buffer), Eq(format)); } ./tests/acceptance-tests/throwback/test_client_cursor_api.cpp0000644000015600001650000003631012676616157024744 0ustar jenkinsjenkins/* * Copyright © 2013-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr * Alexandros Frantzis */ #include "mir/graphics/cursor.h" #include "mir/graphics/cursor_image.h" #include "mir/input/cursor_images.h" #include "mir/input/input_device_info.h" #include "mir_test_framework/in_process_server.h" #include "mir_test_framework/executable_path.h" #include "mir_test_framework/fake_input_device.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/declarative_placement_window_manage_policy.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/headless_nested_server_runner.h" #include "mir/test/doubles/mock_egl.h" #include "mir/test/fake_shared.h" #include "mir/test/spin_wait.h" #include "mir/test/wait_condition.h" #include "mir_toolkit/mir_client_library.h" #include #include namespace mg = mir::graphics; namespace mi = mir::input; namespace mis = mir::input::synthesis; namespace ms = mir::scene; namespace msh = mir::shell; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mt::doubles; namespace mtf = mir_test_framework; namespace { struct MockCursor : public mg::Cursor { MOCK_METHOD0(show, void()); MOCK_METHOD1(show, void(mg::CursorImage const&)); MOCK_METHOD0(hide, void()); // We are not interested in mocking the motion in these tests as we // generate it ourself. void move_to(geom::Point) override {} }; struct NamedCursorImage : public mg::CursorImage { NamedCursorImage(std::string const& name) : cursor_name(name) { } void const* as_argb_8888() const override { return nullptr; } geom::Size size() const override { return geom::Size{16, 16}; } geom::Displacement hotspot() const override { return geom::Displacement{0, 0}; } std::string const cursor_name; }; struct NamedCursorImages : public mi::CursorImages { std::shared_ptr image(std::string const& name, geom::Size const& /* size */) override { return std::make_shared(name); } }; bool cursor_is_named(mg::CursorImage const& i, std::string const& name) { auto image = dynamic_cast(&i); assert(image); return image->cursor_name == name; } MATCHER(DefaultCursorImage, "") { return cursor_is_named(arg, "default"); } MATCHER_P(CursorNamed, name, "") { return cursor_is_named(arg, name); } struct CursorClient { CursorClient(std::string const& connect_string, std::string const& client_name) : connect_string{connect_string}, client_name{client_name} { } virtual ~CursorClient() { teardown.wake_up_everyone(); if (client_thread.joinable()) client_thread.join(); } void run() { mir::test::WaitCondition setup_done; client_thread = std::thread{ [this,&setup_done] { connection = mir_connect_sync(connect_string.c_str(), client_name.c_str()); auto spec = mir_connection_create_spec_for_normal_surface(connection, 1, 1, mir_pixel_format_abgr_8888); mir_surface_spec_set_name(spec, client_name.c_str()); auto const surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); mir_buffer_stream_swap_buffers_sync( mir_surface_get_buffer_stream(surface)); wait_for_surface_to_become_focused_and_exposed(surface); setup_cursor(surface); setup_done.wake_up_everyone(); teardown.wait_for_at_most_seconds(10); mir_surface_release_sync(surface); mir_connection_release(connection); }}; setup_done.wait_for_at_most_seconds(5); } virtual void setup_cursor(MirSurface*) { } void wait_for_surface_to_become_focused_and_exposed(MirSurface* surface) { bool success = mt::spin_wait_for_condition_or_timeout( [surface] { return mir_surface_get_visibility(surface) == mir_surface_visibility_exposed && mir_surface_get_focus(surface) == mir_surface_focused; }, std::chrono::seconds{5}); if (!success) throw std::runtime_error("Timeout waiting for surface to become focused and exposed"); } std::string const connect_string; std::string const client_name; MirConnection* connection; std::thread client_thread; mir::test::WaitCondition teardown; }; struct DisabledCursorClient : CursorClient { using CursorClient::CursorClient; void setup_cursor(MirSurface* surface) override { auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name); mir_wait_for(mir_surface_configure_cursor(surface, conf)); mir_cursor_configuration_destroy(conf); } }; struct NamedCursorClient : CursorClient { NamedCursorClient( std::string const& connect_string, std::string const& client_name, std::string const& cursor_name) : CursorClient{connect_string, client_name}, cursor_name{cursor_name} { } void setup_cursor(MirSurface* surface) override { auto conf = mir_cursor_configuration_from_name(cursor_name.c_str()); mir_wait_for(mir_surface_configure_cursor(surface, conf)); mir_cursor_configuration_destroy(conf); } std::string const cursor_name; }; struct TestClientCursorAPI : mtf::HeadlessInProcessServer { // mtf::add_fake_input_device needs this library to be loaded each test, for the tests mtf::TemporaryEnvironmentValue input_lib{"MIR_SERVER_PLATFORM_INPUT_LIB", mtf::server_platform("input-stub.so").c_str()}; MockCursor cursor; mtf::SurfaceGeometries client_geometries; TestClientCursorAPI() { mock_egl.provide_egl_extensions(); mock_egl.provide_stub_platform_buffer_swapping(); server.wrap_cursor([this](std::shared_ptr const&) { return mt::fake_shared(cursor); }); server.override_the_cursor_images([]() { return std::make_shared(); }); server.override_the_window_manager_builder([this](msh::FocusController* focus_controller) { using PlacementWindowManager = msh::WindowManagerConstructor; return std::make_shared( focus_controller, client_geometries, server.the_shell_display_layout()); }); } void expect_client_shutdown() { using namespace testing; Mock::VerifyAndClearExpectations(&cursor); // Client shutdown EXPECT_CALL(cursor, show(_)).Times(AnyNumber()); EXPECT_CALL(cursor, hide()).Times(AnyNumber()); } std::string const client_name_1{"client-1"}; std::string const client_name_2{"client-2"}; std::string const client_cursor_1{"cursor-1"}; std::string const client_cursor_2{"cursor-2"}; mir::test::WaitCondition expectations_satisfied; mtf::UsingStubClientPlatform using_stub_client_platform; std::unique_ptr fake_mouse{ mtf::add_fake_input_device(mi::InputDeviceInfo{"mouse", "mouse-uid" , mi::DeviceCapability::pointer}) }; ::testing::NiceMock mock_egl; }; } // In this set we create a 1x1 client surface at the point (1,0). The client requests to disable the cursor // over this surface. Since the cursor starts at (0,0) we when we move the cursor by (1,0) thus causing it // to enter the bounds of the first surface, we should observe it being disabled. TEST_F(TestClientCursorAPI, client_may_disable_cursor_over_surface) { using namespace ::testing; client_geometries[client_name_1] = geom::Rectangle{{1, 0}, {1, 1}}; DisabledCursorClient client{new_connection(), client_name_1}; client.run(); EXPECT_CALL(cursor, hide()) .WillOnce(mt::WakeUp(&expectations_satisfied)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 0)); expectations_satisfied.wait_for_at_most_seconds(5); expect_client_shutdown(); } TEST_F(TestClientCursorAPI, cursor_restored_when_leaving_surface) { using namespace ::testing; client_geometries[client_name_1] = geom::Rectangle{{1, 0}, {1, 1}}; DisabledCursorClient client{new_connection(), client_name_1}; client.run(); InSequence seq; EXPECT_CALL(cursor, hide()); EXPECT_CALL(cursor, show(DefaultCursorImage())) .WillOnce(mt::WakeUp(&expectations_satisfied)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 0)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(2, 0)); expectations_satisfied.wait_for_at_most_seconds(5); expect_client_shutdown(); } TEST_F(TestClientCursorAPI, cursor_changed_when_crossing_surface_boundaries) { using namespace ::testing; client_geometries[client_name_1] = geom::Rectangle{{1, 0}, {1, 1}}; client_geometries[client_name_2] = geom::Rectangle{{2, 0}, {1, 1}}; NamedCursorClient client_1{new_connection(), client_name_1, client_cursor_1}; NamedCursorClient client_2{new_connection(), client_name_2, client_cursor_2}; client_1.run(); client_2.run(); InSequence seq; EXPECT_CALL(cursor, show(CursorNamed(client_cursor_1))); EXPECT_CALL(cursor, show(CursorNamed(client_cursor_2))) .WillOnce(mt::WakeUp(&expectations_satisfied)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 0)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 0)); expectations_satisfied.wait_for_at_most_seconds(5); expect_client_shutdown(); } TEST_F(TestClientCursorAPI, cursor_request_taken_from_top_surface) { using namespace ::testing; client_geometries[client_name_1] = geom::Rectangle{{1, 0}, {1, 1}}; client_geometries[client_name_2] = geom::Rectangle{{1, 0}, {1, 1}}; NamedCursorClient client_1{new_connection(), client_name_1, client_cursor_1}; NamedCursorClient client_2{new_connection(), client_name_2, client_cursor_2}; client_1.run(); client_2.run(); EXPECT_CALL(cursor, show(CursorNamed(client_cursor_2))) .WillOnce(mt::WakeUp(&expectations_satisfied)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 0)); expectations_satisfied.wait_for_at_most_seconds(5); expect_client_shutdown(); } TEST_F(TestClientCursorAPI, cursor_request_applied_without_cursor_motion) { using namespace ::testing; struct ChangingCursorClient : NamedCursorClient { using NamedCursorClient::NamedCursorClient; void setup_cursor(MirSurface* surface) override { auto conf1 = mir_cursor_configuration_from_name(cursor_name.c_str()); auto conf2 = mir_cursor_configuration_from_name(mir_disabled_cursor_name); mir_wait_for(mir_surface_configure_cursor(surface, conf1)); mir_wait_for(mir_surface_configure_cursor(surface, conf2)); mir_cursor_configuration_destroy(conf1); mir_cursor_configuration_destroy(conf2); } }; client_geometries[client_name_1] = geom::Rectangle{{0, 0}, {1, 1}}; ChangingCursorClient client{new_connection(), client_name_1, client_cursor_1}; InSequence seq; EXPECT_CALL(cursor, show(CursorNamed(client_cursor_1))); EXPECT_CALL(cursor, hide()) .WillOnce(mt::WakeUp(&expectations_satisfied)); client.run(); expectations_satisfied.wait_for_at_most_seconds(5); expect_client_shutdown(); } TEST_F(TestClientCursorAPI, cursor_request_applied_from_buffer_stream) { using namespace ::testing; static int hotspot_x = 1, hotspot_y = 1; struct BufferStreamClient : CursorClient { using CursorClient::CursorClient; void setup_cursor(MirSurface* surface) override { auto stream = mir_connection_create_buffer_stream_sync( connection, 24, 24, mir_pixel_format_argb_8888, mir_buffer_usage_software); auto conf = mir_cursor_configuration_from_buffer_stream(stream, hotspot_x, hotspot_y); mir_buffer_stream_swap_buffers_sync(stream); mir_wait_for(mir_surface_configure_cursor(surface, conf)); mir_cursor_configuration_destroy(conf); mir_buffer_stream_swap_buffers_sync(stream); mir_buffer_stream_swap_buffers_sync(stream); mir_buffer_stream_release_sync(stream); } }; client_geometries[client_name_1] = geom::Rectangle{{0, 0}, {1, 1}}; BufferStreamClient client{new_connection(), client_name_1}; { InSequence seq; EXPECT_CALL(cursor, show(_)).Times(2); EXPECT_CALL(cursor, show(_)).Times(1) .WillOnce(mt::WakeUp(&expectations_satisfied)); } client.run(); expectations_satisfied.wait_for_at_most_seconds(500); expect_client_shutdown(); } namespace { // The nested server fixture we use is using the 'CanonicalWindowManager' which will place // surfaces in the center...in order to ensure the surface appears under the cursor we use // the fullscreen state. struct FullscreenDisabledCursorClient : CursorClient { using CursorClient::CursorClient; void setup_cursor(MirSurface* surface) override { // Workaround race condition (lp:1525003). I've tried, but I've not // found a better way to ensure that the host Mir server is "ready" // for the test logic. - alan_g std::this_thread::sleep_for(std::chrono::milliseconds(20)); mir_surface_set_state(surface, mir_surface_state_fullscreen); auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name); mir_surface_configure_cursor(surface, conf); mir_cursor_configuration_destroy(conf); } }; } TEST_F(TestClientCursorAPI, cursor_passed_through_nested_server) { using namespace ::testing; mtf::HeadlessNestedServerRunner nested_mir(new_connection()); nested_mir.start_server(); EXPECT_CALL(cursor, hide()) .WillOnce(mt::WakeUp(&expectations_satisfied)); { // Ensure we finalize the client prior stopping the nested server FullscreenDisabledCursorClient client{nested_mir.new_connection(), client_name_1}; client.run(); expectations_satisfied.wait_for_at_most_seconds(60); expect_client_shutdown(); } nested_mir.stop_server(); } ./tests/acceptance-tests/test_client_input.cpp0000644000015600001650000010747712676616157021766 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/input/input_device_info.h" #include "mir/input/event_filter.h" #include "mir/input/composite_event_filter.h" #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/fake_input_device.h" #include "mir_test_framework/placement_applying_shell.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir_test_framework/temporary_environment_value.h" #include "mir/test/wait_condition.h" #include "mir/test/spin_wait.h" #include "mir/test/event_matchers.h" #include "mir/test/event_factory.h" #include "mir_toolkit/mir_client_library.h" #include "mir/events/event_builders.h" #include #include #include #include #include #include namespace mi = mir::input; namespace mt = mir::test; namespace ms = mir::scene; namespace mis = mir::input::synthesis; namespace mtf = mir_test_framework; namespace geom = mir::geometry; namespace { struct MockEventFilter : public mi::EventFilter { // Work around GMock wanting to know how to construct MirEvent MOCK_METHOD1(handle, bool(MirEvent const*)); bool handle(MirEvent const& ev) { handle(&ev); return true; } }; const int surface_width = 100; const int surface_height = 100; void null_event_handler(MirSurface*, MirEvent const*, void*) { } struct Client { MirSurface* surface{nullptr}; MOCK_METHOD1(handle_input, void(MirEvent const*)); MOCK_METHOD1(handle_keymap, void(MirEvent const*)); Client(std::string const& con, std::string const& name) { connection = mir_connect_sync(con.c_str(), name.c_str()); if (!mir_connection_is_valid(connection)) { BOOST_THROW_EXCEPTION( std::runtime_error{std::string{"Failed to connect to test server: "} + mir_connection_get_error_message(connection)}); } auto spec = mir_connection_create_spec_for_normal_surface(connection, surface_width, surface_height, mir_pixel_format_abgr_8888); mir_surface_spec_set_name(spec, name.c_str()); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); if (!mir_surface_is_valid(surface)) BOOST_THROW_EXCEPTION(std::runtime_error{std::string{"Failed creating a surface: "}+ mir_surface_get_error_message(surface)}); mir_surface_set_event_handler(surface, handle_event, this); mir_buffer_stream_swap_buffers_sync( mir_surface_get_buffer_stream(surface)); ready_to_accept_events.wait_for_at_most_seconds(4); if (!ready_to_accept_events.woken()) BOOST_THROW_EXCEPTION(std::runtime_error("Timeout waiting for surface to become focused and exposed")); } void handle_surface_event(MirSurfaceEvent const* event) { auto const attrib = mir_surface_event_get_attribute(event); auto const value = mir_surface_event_get_attribute_value(event); if (mir_surface_attrib_visibility == attrib && mir_surface_visibility_exposed == value) exposed = true; if (mir_surface_attrib_focus == attrib && mir_surface_focused == value) focused = true; if (exposed && focused) ready_to_accept_events.wake_up_everyone(); } static void handle_event(MirSurface*, MirEvent const* ev, void* context) { auto const client = static_cast(context); auto type = mir_event_get_type(ev); if (type == mir_event_type_surface) { auto surface_event = mir_event_get_surface_event(ev); client->handle_surface_event(surface_event); } if (type == mir_event_type_input) client->handle_input(ev); if (type == mir_event_type_keymap) client->handle_keymap(ev); } ~Client() { // Remove the event handler to avoid handling spurious events unrelated // to the tests (e.g. pointer leave events when the surface is destroyed), // which can cause test expectations to fail. mir_surface_set_event_handler(surface, null_event_handler, nullptr); mir_surface_release_sync(surface); mir_connection_release(connection); } MirConnection * connection; mir::test::WaitCondition ready_to_accept_events; mir::test::WaitCondition all_events_received; bool exposed = false; bool focused = false; }; struct TestClientInput : mtf::HeadlessInProcessServer { void SetUp() override { initial_display_layout({screen_geometry}); server.wrap_shell( [this](std::shared_ptr const& wrapped) { shell = std::make_shared(wrapped, input_regions, positions); return shell; }); HeadlessInProcessServer::SetUp(); positions[first] = geom::Rectangle{{0,0}, {surface_width, surface_height}}; } std::shared_ptr shell; std::string const keyboard_name = "keyboard"; std::string const keyboard_unique_id = "keyboard-uid"; std::string const mouse_name = "mouse"; std::string const mouse_unique_id = "mouse-uid"; std::string const touchscreen_name = "touchscreen"; std::string const touchscreen_unique_id = "touchscreen-uid"; std::unique_ptr fake_keyboard{mtf::add_fake_input_device( mi::InputDeviceInfo{keyboard_name, keyboard_unique_id, mi::DeviceCapability::keyboard})}; std::unique_ptr fake_mouse{ mtf::add_fake_input_device(mi::InputDeviceInfo{mouse_name, mouse_unique_id, mi::DeviceCapability::pointer})}; std::unique_ptr fake_touch_screen{mtf::add_fake_input_device( mi::InputDeviceInfo{touchscreen_name, touchscreen_unique_id, mi::DeviceCapability::touchscreen | mi::DeviceCapability::multitouch})}; std::string first{"first"}; std::string second{"second"}; mtf::ClientInputRegions input_regions; mtf::ClientPositions positions; geom::Rectangle screen_geometry{{0,0}, {1000,800}}; std::shared_ptr mock_event_filter = std::make_shared(); }; } using namespace ::testing; using namespace std::chrono_literals; TEST_F(TestClientInput, clients_receive_keys) { Client first_client(new_connection(), first); InSequence seq; EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_R)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_M)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_M)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_R)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_i)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_i)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_r)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_r)))).WillOnce( mt::WakeUp(&first_client.all_events_received)); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_RIGHTSHIFT)); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_M)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_M)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_RIGHTSHIFT)); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_I)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_I)); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_R)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_R)); first_client.all_events_received.wait_for_at_most_seconds(10); } TEST_F(TestClientInput, clients_receive_us_english_mapped_keys) { Client first_client(new_connection(), first); InSequence seq; EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_L)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_dollar)))) .WillOnce(mt::WakeUp(&first_client.all_events_received)); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_LEFTSHIFT)); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_4)); first_client.all_events_received.wait_for_at_most_seconds(10); } TEST_F(TestClientInput, clients_receive_pointer_inside_window_and_crossing_events) { positions[first] = geom::Rectangle{{0,0}, {surface_width, surface_height}}; Client first_client(new_connection(), first); // We should see the cursor enter InSequence seq; EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())); EXPECT_CALL(first_client, handle_input(mt::PointerEventWithPosition(surface_width - 1, surface_height - 1))); EXPECT_CALL(first_client, handle_input(mt::PointerLeaveEvent())) .WillOnce(mt::WakeUp(&first_client.all_events_received)); // But we should not receive an event for the second movement outside of our surface! fake_mouse->emit_event(mis::a_pointer_event().with_movement(surface_width - 1, surface_height - 1)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(2, 2)); first_client.all_events_received.wait_for_at_most_seconds(120); } TEST_F(TestClientInput, clients_receive_relative_pointer_events) { using namespace ::testing; mtf::TemporaryEnvironmentValue disable_batching("MIR_CLIENT_INPUT_RATE", "0"); positions[first] = geom::Rectangle{{0,0}, {surface_width, surface_height}}; Client first_client(new_connection(), first); InSequence seq; EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())); EXPECT_CALL(first_client, handle_input(AllOf(mt::PointerEventWithPosition(1, 1), mt::PointerEventWithDiff(1, 1)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::PointerEventWithPosition(2, 2), mt::PointerEventWithDiff(1, 1)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::PointerEventWithPosition(3, 3), mt::PointerEventWithDiff(1, 1)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::PointerEventWithPosition(2, 2), mt::PointerEventWithDiff(-1, -1)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::PointerEventWithPosition(1, 1), mt::PointerEventWithDiff(-1, -1)))); // Ensure we continue to receive relative moement even when absolute movement is constrained. EXPECT_CALL(first_client, handle_input(AllOf(mt::PointerEventWithPosition(0, 0), mt::PointerEventWithDiff(-1, -1)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::PointerEventWithPosition(0, 0), mt::PointerEventWithDiff(-1, -1)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::PointerEventWithPosition(0, 0), mt::PointerEventWithDiff(-1, -1)))) .WillOnce(mt::WakeUp(&first_client.all_events_received)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 1)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 1)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 1)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(-1, -1)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(-1, -1)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(-1, -1)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(-1, -1)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(-1, -1)); first_client.all_events_received.wait_for_at_most_seconds(120); } TEST_F(TestClientInput, clients_receive_button_events_inside_window) { Client first_client(new_connection(), first); EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())); // The cursor starts at (0, 0). EXPECT_CALL(first_client, handle_input(mt::ButtonDownEvent(0, 0))) .WillOnce(mt::WakeUp(&first_client.all_events_received)); fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); first_client.all_events_received.wait_for_at_most_seconds(10); } TEST_F(TestClientInput, clients_receive_many_button_events_inside_window) { Client first_client(new_connection(), first); // The cursor starts at (0, 0). InSequence seq; auto expect_buttons = [&](MirPointerButtons b) { EXPECT_CALL(first_client, handle_input(mt::ButtonsDown(0, 0, b))); }; MirPointerButtons buttons = mir_pointer_button_primary; EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())); expect_buttons(buttons); expect_buttons(buttons |= mir_pointer_button_secondary); expect_buttons(buttons |= mir_pointer_button_tertiary); expect_buttons(buttons |= mir_pointer_button_forward); expect_buttons(buttons |= mir_pointer_button_back); expect_buttons(buttons &= ~mir_pointer_button_back); expect_buttons(buttons &= ~mir_pointer_button_forward); expect_buttons(buttons &= ~mir_pointer_button_tertiary); expect_buttons(buttons &= ~mir_pointer_button_secondary); EXPECT_CALL(first_client, handle_input(mt::ButtonsDown(0, 0, 0))).WillOnce( mt::WakeUp(&first_client.all_events_received)); auto press_button = [&](int button) { fake_mouse->emit_event(mis::a_button_down_event().of_button(button).with_action(mis::EventAction::Down)); }; auto release_button = [&](int button) { fake_mouse->emit_event(mis::a_button_up_event().of_button(button).with_action(mis::EventAction::Up)); }; press_button(BTN_LEFT); press_button(BTN_RIGHT); press_button(BTN_MIDDLE); press_button(BTN_FORWARD); press_button(BTN_BACK); release_button(BTN_BACK); release_button(BTN_FORWARD); release_button(BTN_MIDDLE); release_button(BTN_RIGHT); release_button(BTN_LEFT); first_client.all_events_received.wait_for_at_most_seconds(10); } TEST_F(TestClientInput, multiple_clients_receive_pointer_inside_windows) { int const screen_width = screen_geometry.size.width.as_int(); int const screen_height = screen_geometry.size.height.as_int(); int const client_height = screen_height / 2; int const client_width = screen_width / 2; positions[first] = {{0, 0}, {client_width, client_height}}; positions[second] = {{client_width, client_height}, {client_width, client_height}}; Client first_client(new_connection(), first); Client second_client(new_connection(), second); { InSequence seq; EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())); EXPECT_CALL(first_client, handle_input(mt::PointerEventWithPosition(client_width - 1, client_height - 1))); EXPECT_CALL(first_client, handle_input(mt::PointerLeaveEvent())) .WillOnce(mt::WakeUp(&first_client.all_events_received)); } { InSequence seq; EXPECT_CALL(second_client, handle_input(mt::PointerEnterEvent())); EXPECT_CALL(second_client, handle_input(mt::PointerEventWithPosition(client_width - 1, client_height - 1))) .WillOnce(mt::WakeUp(&second_client.all_events_received)); } // In the bounds of the first surface fake_mouse->emit_event(mis::a_pointer_event().with_movement(client_width - 1, client_height - 1)); // In the bounds of the second surface fake_mouse->emit_event(mis::a_pointer_event().with_movement(client_width, client_height)); first_client.all_events_received.wait_for_at_most_seconds(2); second_client.all_events_received.wait_for_at_most_seconds(2); } TEST_F(TestClientInput, clients_do_not_receive_pointer_outside_input_region) { int const client_height = surface_height; int const client_width = surface_width; input_regions[first] = {{{0, 0}, {client_width - 80, client_height}}, {{client_width - 20, 0}, {client_width - 80, client_height}}}; Client first_client(new_connection(), first); EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())).Times(AnyNumber()); EXPECT_CALL(first_client, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber()); EXPECT_CALL(first_client, handle_input(mt::PointerMovementEvent())).Times(AnyNumber()); { // We should see two of the three button pairs. InSequence seq; EXPECT_CALL(first_client, handle_input(mt::ButtonDownEvent(1, 1))); EXPECT_CALL(first_client, handle_input(mt::ButtonUpEvent(1, 1))); EXPECT_CALL(first_client, handle_input(mt::ButtonDownEvent(99, 99))); EXPECT_CALL(first_client, handle_input(mt::ButtonUpEvent(99, 99))) .WillOnce(mt::WakeUp(&first_client.all_events_received)); } // First we will move the cursor in to the input region on the left side of // the window. We should see a click here. fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 1)); fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT)); // Now in to the dead zone in the center of the window. We should not see // a click here. fake_mouse->emit_event(mis::a_pointer_event().with_movement(49, 49)); fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT)); // Now in to the right edge of the window, in the right input region. // Again we should see a click. fake_mouse->emit_event(mis::a_pointer_event().with_movement(49, 49)); fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT)); first_client.all_events_received.wait_for_at_most_seconds(5); } TEST_F(TestClientInput, scene_obscure_motion_events_by_stacking) { auto smaller_geometry = screen_geometry; smaller_geometry.size.width = geom::Width{screen_geometry.size.width.as_uint32_t() / 2}; positions[first] = screen_geometry; positions[second] = smaller_geometry; Client first_client(new_connection(), first); Client second_client(new_connection(), second); EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())).Times(AnyNumber()); EXPECT_CALL(first_client, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber()); EXPECT_CALL(first_client, handle_input(mt::PointerMovementEvent())).Times(AnyNumber()); { // We should only see one button event sequence. InSequence seq; EXPECT_CALL(first_client, handle_input(mt::ButtonDownEvent(501, 1))); EXPECT_CALL(first_client, handle_input(mt::ButtonUpEvent(501, 1))) .WillOnce(mt::WakeUp(&first_client.all_events_received)); } EXPECT_CALL(second_client, handle_input(mt::PointerEnterEvent())).Times(AnyNumber()); EXPECT_CALL(second_client, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber()); EXPECT_CALL(second_client, handle_input(mt::PointerMovementEvent())).Times(AnyNumber()); { // Likewise we should only see one button sequence. InSequence seq; EXPECT_CALL(second_client, handle_input(mt::ButtonDownEvent(1, 1))); EXPECT_CALL(second_client, handle_input(mt::ButtonUpEvent(1, 1))) .WillOnce(mt::WakeUp(&second_client.all_events_received)); } // First we will move the cursor in to the region where client 2 obscures client 1 fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 1)); fake_mouse->emit_event( mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT)); // Now we move to the unobscured region of client 1 fake_mouse->emit_event(mis::a_pointer_event().with_movement(500, 0)); fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT)); first_client.all_events_received.wait_for_at_most_seconds(5); second_client.all_events_received.wait_for_at_most_seconds(5); } TEST_F(TestClientInput, hidden_clients_do_not_receive_pointer_events) { positions[second] = {{0,0}, {surface_width, surface_height}}; Client first_client(new_connection(), first); Client second_client(new_connection(), second); EXPECT_CALL(second_client, handle_input(mt::PointerEnterEvent())).Times(AnyNumber()); EXPECT_CALL(second_client, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber()); EXPECT_CALL(second_client, handle_input(mt::PointerEventWithPosition(1, 1))) .WillOnce(mt::WakeUp(&second_client.all_events_received)); EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())).Times(AnyNumber()); EXPECT_CALL(first_client, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber()); EXPECT_CALL(first_client, handle_input(mt::PointerEventWithPosition(2, 2))) .WillOnce(mt::WakeUp(&first_client.all_events_received)); // We send one event and then hide the surface on top before sending the next. // So we expect each of the two surfaces to receive one event fake_mouse->emit_event(mis::a_pointer_event().with_movement(1,1)); second_client.all_events_received.wait_for_at_most_seconds(2); server.the_shell()->focused_session()->hide(); fake_mouse->emit_event(mis::a_pointer_event().with_movement(1,1)); first_client.all_events_received.wait_for_at_most_seconds(2); } TEST_F(TestClientInput, clients_receive_pointer_within_coordinate_system_of_window) { int const screen_width = screen_geometry.size.width.as_int(); int const screen_height = screen_geometry.size.height.as_int(); int const client_height = screen_height / 2; int const client_width = screen_width / 2; positions[first] = {{screen_width / 2, screen_height / 2}, {client_width, client_height}}; Client first_client(new_connection(), first); InSequence seq; EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())); EXPECT_CALL(first_client, handle_input(mt::PointerEventWithPosition(80, 170))) .Times(AnyNumber()) .WillOnce(mt::WakeUp(&first_client.all_events_received)); server.the_shell()->focused_surface()->move_to(geom::Point{screen_width / 2 - 40, screen_height / 2 - 80}); fake_mouse->emit_event(mis::a_pointer_event().with_movement(screen_width / 2 + 40, screen_height / 2 + 90)); first_client.all_events_received.wait_for_at_most_seconds(2); } // TODO: Consider tests for more input devices with custom mapping (i.e. joysticks...) TEST_F(TestClientInput, usb_direct_input_devices_work) { float const minimum_touch = mtf::FakeInputDevice::minimum_touch_axis_value; float const maximum_touch = mtf::FakeInputDevice::maximum_touch_axis_value; auto const display_width = screen_geometry.size.width.as_float(); auto const display_height = screen_geometry.size.height.as_float(); // We place a click 10% in to the touchscreens space in both axis, // and a second at 0,0. Thus we expect to see a click at // .1*screen_width/height and a second at zero zero. float const abs_touch_x_1 = minimum_touch + (maximum_touch - minimum_touch) * 0.1f; float const abs_touch_y_1 = minimum_touch + (maximum_touch - minimum_touch) * 0.1f; float const abs_touch_x_2 = 0; float const abs_touch_y_2 = 0; float const expected_scale_x = display_width / (maximum_touch - minimum_touch + 1.0f); float const expected_scale_y = display_height / (maximum_touch - minimum_touch + 1.0f); float const expected_motion_x_1 = expected_scale_x * abs_touch_x_1; float const expected_motion_y_1 = expected_scale_y * abs_touch_y_1; float const expected_motion_x_2 = expected_scale_x * abs_touch_x_2; float const expected_motion_y_2 = expected_scale_y * abs_touch_y_2; positions[first] = screen_geometry; Client first_client(new_connection(), first); InSequence seq; EXPECT_CALL(first_client, handle_input( mt::TouchEvent(expected_motion_x_1, expected_motion_y_1))); EXPECT_CALL(first_client, handle_input( mt::TouchEventInDirection(expected_motion_x_1, expected_motion_y_1, expected_motion_x_2, expected_motion_y_2))) .Times(AnyNumber()) .WillOnce(mt::WakeUp(&first_client.all_events_received)); fake_touch_screen->emit_event(mis::a_touch_event() .at_position({abs_touch_x_1, abs_touch_y_1})); // Sleep here to trigger more failures (simulate slow machine) // TODO why would that cause failures?b std::this_thread::sleep_for(std::chrono::milliseconds(10)); fake_touch_screen->emit_event(mis::a_touch_event() .with_action(mis::TouchParameters::Action::Move) .at_position({abs_touch_x_2, abs_touch_y_2})); first_client.all_events_received.wait_for_at_most_seconds(2); } TEST_F(TestClientInput, send_mir_input_events_through_surface) { Client first_client(new_connection(), first); EXPECT_CALL(first_client, handle_input(mt::KeyDownEvent())) .WillOnce(mt::WakeUp(&first_client.all_events_received)); auto key_event = mir::events::make_event(MirInputDeviceId{0}, 0ns, std::vector{}, mir_keyboard_action_down, 0, KEY_M, mir_input_event_modifier_none); server.the_shell()->focused_surface()->consume(key_event.get()); first_client.all_events_received.wait_for_at_most_seconds(2); } TEST_F(TestClientInput, clients_receive_keymap_change_events) { Client first_client(new_connection(), first); std::string const model = "pc105"; std::string const layout = "dvorak"; MirInputDeviceId const id = 1; EXPECT_CALL(first_client, handle_keymap(mt::KeymapEventForDevice(id))) .Times(1) .WillOnce(mt::WakeUp(&first_client.all_events_received)); server.the_shell()->focused_surface()->set_keymap(id, model, layout, "", ""); first_client.all_events_received.wait_for_at_most_seconds(2); } TEST_F(TestClientInput, keymap_changes_change_keycode_received) { Client first_client(new_connection(), first); MirInputDeviceId const id = 1; std::string const model = "pc105"; std::string const layout = "us"; std::string const variant = "dvorak"; mt::WaitCondition first_event_received, client_sees_keymap_change; InSequence seq; EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_n)))); EXPECT_CALL(first_client, handle_input(mt::KeyUpEvent())) .WillOnce(mt::WakeUp(&first_event_received)); EXPECT_CALL(first_client, handle_keymap(mt::KeymapEventForDevice(id))) .WillOnce(mt::WakeUp(&client_sees_keymap_change)); EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_b)))); EXPECT_CALL(first_client, handle_input(mt::KeyUpEvent())) .WillOnce(mt::WakeUp(&first_client.all_events_received)); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_N)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_N)); first_event_received.wait_for_at_most_seconds(60); server.the_shell()->focused_surface()->set_keymap(id, model, layout, variant, ""); client_sees_keymap_change.wait_for_at_most_seconds(60); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_N)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_N)); first_client.all_events_received.wait_for_at_most_seconds(5); } TEST_F(TestClientInput, sends_no_wrong_keymaps_to_clients) { Client first_client(new_connection(), first); MirInputDeviceId const id = 1; std::string const model = "thargoid207"; std::string const layout = "polaris"; mt::WaitCondition first_event_received, client_sees_keymap_change; EXPECT_CALL(first_client, handle_keymap(mt::KeymapEventForDevice(id))).Times(0); EXPECT_THROW( {server.the_shell()->focused_surface()->set_keymap(id, model, layout, "", "");}, std::runtime_error); } TEST_F(TestClientInput, event_filter_may_consume_events) { std::shared_ptr mock_event_filter = std::make_shared(); server.the_composite_event_filter()->append(mock_event_filter); Client first_client(new_connection(), first); EXPECT_CALL(*mock_event_filter, handle(mt::InputConfigurationEvent())).Times(AnyNumber()).WillRepeatedly(Return(false)); InSequence seq; EXPECT_CALL(*mock_event_filter, handle(_)).WillOnce(Return(true)); EXPECT_CALL(*mock_event_filter, handle(_)).WillOnce( DoAll(mt::WakeUp(&first_client.all_events_received), Return(true))); // Since we handle the events in the filter the client should not receive them. EXPECT_CALL(first_client, handle_input(_)).Times(0); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_M)); fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_M)); first_client.all_events_received.wait_for_at_most_seconds(10); } namespace { struct TestClientInputKeyRepeat : public TestClientInput { TestClientInputKeyRepeat() : enable_key_repeat("MIR_SERVER_ENABLE_KEY_REPEAT", "true") { } mtf::TemporaryEnvironmentValue enable_key_repeat; }; } TEST_F(TestClientInputKeyRepeat, keys_are_repeated_to_clients) { using namespace testing; Client first_client(new_connection(), first); InSequence seq; EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_R)))); EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyRepeatEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_R)))).WillOnce(mt::WakeUp(&first_client.all_events_received)); // Extra repeats before we shut down. EXPECT_CALL(first_client, handle_input(mt::KeyRepeatEvent())).Times(AnyNumber()); fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_RIGHTSHIFT)); first_client.all_events_received.wait_for_at_most_seconds(10); } TEST_F(TestClientInput, pointer_events_pass_through_shaped_out_regions_of_client) { using namespace testing; positions[first] = {{0, 0}, {10, 10}}; Client client(new_connection(), first); MirRectangle input_rects[] = {{1, 1, 10, 10}}; auto spec = mir_connection_create_spec_for_changes(client.connection); mir_surface_spec_set_input_shape(spec, input_rects, 1); mir_surface_apply_spec(client.surface, spec); mir_surface_spec_release(spec); ASSERT_TRUE(shell->wait_for_modify_surface(std::chrono::seconds(5))); // We verify that we don't receive the first shaped out button event. EXPECT_CALL(client, handle_input(mt::PointerEnterEvent())); EXPECT_CALL(client, handle_input(mt::PointerEventWithPosition(1, 1))); EXPECT_CALL(client, handle_input(mt::ButtonDownEvent(1, 1))) .WillOnce(mt::WakeUp(&client.all_events_received)); fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down)); fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT).with_action(mis::EventAction::Up)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 1)); fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT)); client.all_events_received.wait_for_at_most_seconds(10); } MATCHER_P3(ADeviceMatches, name, unique_id, caps, "") { auto one_of_the_devices_matched = false; for (size_t i = 0, e = mir_input_config_device_count(arg); i != e; ++i) { auto dev = mir_input_config_get_device(arg, i); if (mir_input_device_get_name(dev) == name && mir_input_device_get_unique_id(dev) == unique_id && mir_input_device_get_capabilities(dev) == caps) one_of_the_devices_matched = true; } return one_of_the_devices_matched; } //Poll for the expected config to fix lp: #1555708. Client can't expect synchronization //with the server on the input config. TEST_F(TestClientInput, client_input_config_request_receives_all_attached_devices) { auto con = mir_connect_sync(new_connection().c_str(), first.c_str()); auto config = mir_connection_create_input_config(con); int limit = 10; int num_devices = 0; int expected_devices = 3; for(auto i = 0; i < limit; i++) { num_devices = mir_input_config_device_count(config); if (num_devices == expected_devices) break; std::this_thread::sleep_for(std::chrono::milliseconds(10)); mir_input_config_destroy(config); config = mir_connection_create_input_config(con); } ASSERT_THAT(mir_input_config_device_count(config), Eq(expected_devices)); EXPECT_THAT(config, ADeviceMatches(keyboard_name, keyboard_unique_id, mir_input_device_capability_keyboard)); EXPECT_THAT(config, ADeviceMatches(mouse_name, mouse_unique_id, mir_input_device_capability_pointer)); EXPECT_THAT(config, ADeviceMatches(touchscreen_name, touchscreen_unique_id, uint32_t(mir_input_device_capability_touchscreen | mir_input_device_capability_multitouch))); mir_input_config_destroy(config); mir_connection_release(con); } TEST_F(TestClientInput, callback_function_triggered_on_input_device_addition) { Client a_client(new_connection(), first); mt::WaitCondition callback_triggered; mir_connection_set_input_config_change_callback( a_client.connection, [](MirConnection*, void* cond) { static_cast(cond)->wake_up_everyone(); }, static_cast(&callback_triggered)); std::string const touchpad{"touchpad"}; std::string const touchpad_uid{"touchpad"}; std::unique_ptr fake_touchpad{mtf::add_fake_input_device( mi::InputDeviceInfo{touchpad, touchpad_uid, mi::DeviceCapability::touchpad | mi::DeviceCapability::pointer})}; callback_triggered.wait_for_at_most_seconds(1); EXPECT_THAT(callback_triggered.woken(), Eq(true)); auto config = mir_connection_create_input_config(a_client.connection); EXPECT_THAT(mir_input_config_device_count(config), Eq(4)); EXPECT_THAT(config, ADeviceMatches(touchpad, touchpad_uid, uint32_t(mir_input_device_capability_touchpad | mir_input_device_capability_pointer))); mir_input_config_destroy(config); } TEST_F(TestClientInput, callback_function_triggered_on_input_device_removal) { Client a_client(new_connection(), first); mt::WaitCondition callback_triggered; mir_connection_set_input_config_change_callback( a_client.connection, [](MirConnection*, void* cond) { static_cast(cond)->wake_up_everyone(); }, static_cast(&callback_triggered)); fake_keyboard->emit_device_removal(); callback_triggered.wait_for_at_most_seconds(1); EXPECT_THAT(callback_triggered.woken(), Eq(true)); auto config = mir_connection_create_input_config(a_client.connection); EXPECT_THAT(mir_input_config_device_count(config), Eq(2)); mir_input_config_destroy(config); } ./tests/acceptance-tests/test_surface_modifications.cpp0000644000015600001650000004636112676616125023616 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/events/event_builders.h" #include "mir/scene/surface.h" #include "mir/scene/null_surface_observer.h" #include "mir/test/doubles/wrap_shell_to_track_latest_surface.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/test/fake_shared.h" #include "mir/test/signal.h" #include #include namespace mev = mir::events; namespace mf = mir::frontend; namespace mtf = mir_test_framework; namespace ms = mir::scene; namespace msh = mir::shell; namespace mt = mir::test; namespace mtd = mir::test::doubles; using namespace mir::geometry; using namespace testing; namespace { class MockSurfaceObserver : public ms::NullSurfaceObserver { public: MOCK_METHOD1(renamed, void(char const*)); MOCK_METHOD1(resized_to, void(Size const& size)); MOCK_METHOD1(hidden_set_to, void(bool)); }; struct SurfaceModifications : mtf::ConnectedClientWithASurface { SurfaceModifications() { add_to_environment("MIR_SERVER_ENABLE_INPUT", "OFF"); } void SetUp() override { std::shared_ptr shell; server.wrap_shell([&](std::shared_ptr const& wrapped) { auto const msc = std::make_shared(wrapped); shell = msc; return msc; }); mtf::ConnectedClientWithASurface::SetUp(); shell_surface = shell->latest_surface; auto const scene_surface = shell_surface.lock(); scene_surface->add_observer(mt::fake_shared(surface_observer)); // Swap buffers to ensure surface is visible for event based tests mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); } void generate_alt_click_at(Point const& click_position) { auto const modifiers = mir_input_event_modifier_alt; auto const x_axis_value = click_position.x.as_float(); auto const y_axis_value = click_position.y.as_float(); auto const hscroll_value = 0.0; auto const vscroll_value = 0.0; auto const action = mir_pointer_action_button_down; auto const relative_x_value = 0.0; auto const relative_y_value = 0.0; auto const click_event = mev::make_event(device_id, timestamp, cookie, modifiers, action, mir_pointer_button_tertiary, x_axis_value, y_axis_value, hscroll_value, vscroll_value, relative_x_value, relative_y_value); server.the_shell()->handle(*click_event); } void generate_alt_move_to(Point const& drag_position) { auto const modifiers = mir_input_event_modifier_alt; auto const x_axis_value = drag_position.x.as_float(); auto const y_axis_value = drag_position.y.as_float(); auto const hscroll_value = 0.0; auto const vscroll_value = 0.0; auto const action = mir_pointer_action_motion; auto const relative_x_value = 0.0; auto const relative_y_value = 0.0; auto const drag_event = mev::make_event(device_id, timestamp, cookie, modifiers, action, mir_pointer_button_tertiary, x_axis_value, y_axis_value, hscroll_value, vscroll_value, relative_x_value, relative_y_value); server.the_shell()->handle(*drag_event); } void ensure_server_has_processed_setup() { mt::Signal server_ready; auto const new_title = __PRETTY_FUNCTION__; EXPECT_CALL(surface_observer, renamed(StrEq(new_title))). WillOnce(InvokeWithoutArgs([&]{ server_ready.raise(); })); apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_name(spec, new_title); }); server_ready.wait(); } template void apply_changes(Specifier const& specifier) const { auto const spec = mir_connection_create_spec_for_changes(connection); specifier(spec); mir_surface_apply_spec(surface, spec); mir_surface_spec_release(spec); } MirInputDeviceId const device_id = MirInputDeviceId(7); std::chrono::nanoseconds const timestamp = std::chrono::nanoseconds(39); NiceMock surface_observer; std::weak_ptr shell_surface; std::vector cookie; }; MATCHER_P(WidthEq, value, "") { return Width(value) == arg.width; } MATCHER_P(HeightEq, value, "") { return Height(value) == arg.height; } struct StatePair { MirSurfaceState from; MirSurfaceState to; friend std::ostream& operator<<(std::ostream& out, StatePair const& types) { return out << "from:" << types.from << ", to:" << types.to; } }; bool is_visible(MirSurfaceState state) { switch (state) { case mir_surface_state_hidden: case mir_surface_state_minimized: return false; default: break; } return true; } struct SurfaceStateCase : SurfaceModifications, ::testing::WithParamInterface {}; struct SurfaceSpecStateCase : SurfaceModifications, ::testing::WithParamInterface {}; } TEST_F(SurfaceModifications, surface_spec_name_is_notified) { auto const new_title = __PRETTY_FUNCTION__; EXPECT_CALL(surface_observer, renamed(StrEq(new_title))); apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_name(spec, new_title); }); } TEST_F(SurfaceModifications, surface_spec_resize_is_notified) { auto const new_width = 5; auto const new_height = 7; EXPECT_CALL(surface_observer, resized_to(Size{new_width, new_height})); apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_width(spec, new_width); mir_surface_spec_set_height(spec, new_height); }); } TEST_F(SurfaceModifications, surface_spec_change_width_is_notified) { auto const new_width = 11; EXPECT_CALL(surface_observer, resized_to(WidthEq(new_width))); apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_width(spec, new_width); }); } TEST_F(SurfaceModifications, surface_spec_change_height_is_notified) { auto const new_height = 13; EXPECT_CALL(surface_observer, resized_to(HeightEq(new_height))); apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_height(spec, new_height); }); } TEST_F(SurfaceModifications, surface_spec_min_width_is_respected) { auto const min_width = 17; apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_min_width(spec, min_width); }); ensure_server_has_processed_setup(); auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; EXPECT_CALL(surface_observer, resized_to(WidthEq(min_width))); generate_alt_click_at(bottom_right); generate_alt_move_to(shell_surface->top_left()); } TEST_F(SurfaceModifications, surface_spec_min_height_is_respected) { auto const min_height = 19; apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_min_height(spec, min_height); }); ensure_server_has_processed_setup(); auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; EXPECT_CALL(surface_observer, resized_to(HeightEq(min_height))); generate_alt_click_at(bottom_right); generate_alt_move_to(shell_surface->top_left()); } TEST_F(SurfaceModifications, surface_spec_max_width_is_respected) { auto const max_width = 23; apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_max_width(spec, max_width); }); ensure_server_has_processed_setup(); auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; EXPECT_CALL(surface_observer, resized_to(WidthEq(max_width))); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaX(max_width)); } TEST_F(SurfaceModifications, surface_spec_max_height_is_respected) { auto const max_height = 29; apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_max_height(spec, max_height); }); ensure_server_has_processed_setup(); auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; EXPECT_CALL(surface_observer, resized_to(HeightEq(max_height))); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaY(max_height)); } TEST_F(SurfaceModifications, surface_spec_width_inc_is_respected) { auto const width_inc = 13; apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_width_increment(spec, width_inc); }); ensure_server_has_processed_setup(); auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaX(16)); EXPECT_TRUE(actual.width.as_int() % width_inc == 0); } TEST_F(SurfaceModifications, surface_spec_with_min_width_and_width_inc_is_respected) { auto const width_inc = 13; auto const min_width = 7; apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_width_increment(spec, width_inc); mir_surface_spec_set_min_width(spec, min_width); }); ensure_server_has_processed_setup(); auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaX(16)); EXPECT_TRUE((actual.width.as_int() - min_width) % width_inc == 0); } TEST_F(SurfaceModifications, surface_spec_height_inc_is_respected) { auto const height_inc = 13; apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_height_increment(spec, height_inc); }); ensure_server_has_processed_setup(); auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaY(16)); EXPECT_TRUE(actual.height.as_int() % height_inc == 0); } TEST_F(SurfaceModifications, surface_spec_with_min_height_and_height_inc_is_respected) { auto const height_inc = 13; auto const min_height = 7; apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_height_increment(spec, height_inc); mir_surface_spec_set_min_height(spec, min_height); }); ensure_server_has_processed_setup(); auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + DeltaY(16)); EXPECT_TRUE((actual.height.as_int() - min_height) % height_inc == 0); } TEST_F(SurfaceModifications, surface_spec_with_min_aspect_ratio_is_respected) { auto const aspect_width = 11; auto const aspect_height = 7; apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_min_aspect_ratio(spec, aspect_width, aspect_height); }); ensure_server_has_processed_setup(); auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; auto const bottom_left = shell_surface->input_bounds().bottom_left() + Displacement{1,-1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_left); EXPECT_THAT(actual.width.as_float()/actual.height.as_float(), Ge(float(aspect_width)/aspect_height)); } TEST_F(SurfaceModifications, surface_spec_with_max_aspect_ratio_is_respected) { auto const aspect_width = 7; auto const aspect_height = 11; apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_max_aspect_ratio(spec, aspect_width, aspect_height); }); ensure_server_has_processed_setup(); auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; auto const top_right = shell_surface->input_bounds().top_right() - Displacement{1,-1}; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).WillOnce(SaveArg<0>(&actual)); generate_alt_click_at(bottom_right); generate_alt_move_to(top_right); EXPECT_THAT(actual.width.as_float()/actual.height.as_float(), Le(float(aspect_width)/aspect_height)); } TEST_F(SurfaceModifications, surface_spec_with_fixed_aspect_ratio_and_size_range_is_respected) { auto const aspect_width = 11; auto const aspect_height = 7; auto const min_width = 10*aspect_width; auto const min_height = 10*aspect_height; auto const max_width = 20*aspect_width; auto const max_height = 20*aspect_height; auto const width_inc = 11; auto const height_inc = 7; Size actual; EXPECT_CALL(surface_observer, resized_to(_)).Times(AnyNumber()).WillRepeatedly(SaveArg<0>(&actual)); apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_min_aspect_ratio(spec, aspect_width, aspect_height); mir_surface_spec_set_max_aspect_ratio(spec, aspect_width, aspect_height); mir_surface_spec_set_min_height(spec, min_height); mir_surface_spec_set_min_width(spec, min_width); mir_surface_spec_set_max_height(spec, max_height); mir_surface_spec_set_max_width(spec, max_width); mir_surface_spec_set_width_increment(spec, width_inc); mir_surface_spec_set_height_increment(spec, height_inc); mir_surface_spec_set_height(spec, min_height); mir_surface_spec_set_width(spec, min_width); }); ensure_server_has_processed_setup(); auto const expected_aspect_ratio = FloatEq(float(aspect_width)/aspect_height); EXPECT_THAT(actual.width.as_float()/actual.height.as_float(), expected_aspect_ratio); EXPECT_THAT(actual, Eq(Size{min_width, min_height})); for (int delta = 1; delta != 20; ++delta) { auto const shell_surface = this->shell_surface.lock(); auto const bottom_right = shell_surface->input_bounds().bottom_right() - Displacement{1,1}; // Introduce small variation around "accurate" resize motion auto const jitter = Displacement{delta%2 ? +2 : -2, (delta/2)%2 ? +2 : -2}; auto const motion = Displacement{width_inc, height_inc} + jitter; generate_alt_click_at(bottom_right); generate_alt_move_to(bottom_right + motion); Size const expected_size{ std::min(max_width, min_width + delta*width_inc), std::min(max_height, min_height + delta*height_inc)}; EXPECT_THAT(actual.width.as_float()/actual.height.as_float(), expected_aspect_ratio); EXPECT_THAT(actual, Eq(expected_size)); }; } TEST_F(SurfaceModifications, surface_spec_state_affects_surface_visibility) { auto const new_state = mir_surface_state_hidden; EXPECT_CALL(surface_observer, hidden_set_to(true)); apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_state(spec, new_state); }); } TEST_P(SurfaceSpecStateCase, set_state_affects_surface_visibility) { auto const initial_state = GetParam().from; auto const new_state = GetParam().to; EXPECT_CALL(surface_observer, hidden_set_to(is_visible(initial_state))); EXPECT_CALL(surface_observer, hidden_set_to(is_visible(new_state))); apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_state(spec, initial_state); }); apply_changes([&](MirSurfaceSpec* spec) { mir_surface_spec_set_state(spec, new_state); }); } INSTANTIATE_TEST_CASE_P(SurfaceModifications, SurfaceSpecStateCase, Values( StatePair{mir_surface_state_hidden, mir_surface_state_restored}, StatePair{mir_surface_state_hidden, mir_surface_state_maximized}, StatePair{mir_surface_state_hidden, mir_surface_state_vertmaximized}, StatePair{mir_surface_state_hidden, mir_surface_state_horizmaximized}, StatePair{mir_surface_state_hidden, mir_surface_state_fullscreen}, StatePair{mir_surface_state_minimized, mir_surface_state_restored}, StatePair{mir_surface_state_minimized, mir_surface_state_maximized}, StatePair{mir_surface_state_minimized, mir_surface_state_vertmaximized}, StatePair{mir_surface_state_minimized, mir_surface_state_horizmaximized}, StatePair{mir_surface_state_minimized, mir_surface_state_fullscreen} )); TEST_P(SurfaceStateCase, set_state_affects_surface_visibility) { auto const initial_state = GetParam().from; auto const new_state = GetParam().to; EXPECT_CALL(surface_observer, hidden_set_to(is_visible(initial_state))); EXPECT_CALL(surface_observer, hidden_set_to(is_visible(new_state))); mir_wait_for(mir_surface_set_state(surface, initial_state)); mir_wait_for(mir_surface_set_state(surface, new_state)); } INSTANTIATE_TEST_CASE_P(SurfaceModifications, SurfaceStateCase, Values( StatePair{mir_surface_state_hidden, mir_surface_state_restored}, StatePair{mir_surface_state_hidden, mir_surface_state_maximized}, StatePair{mir_surface_state_hidden, mir_surface_state_vertmaximized}, StatePair{mir_surface_state_hidden, mir_surface_state_horizmaximized}, StatePair{mir_surface_state_hidden, mir_surface_state_fullscreen}, StatePair{mir_surface_state_minimized, mir_surface_state_restored}, StatePair{mir_surface_state_minimized, mir_surface_state_maximized}, StatePair{mir_surface_state_minimized, mir_surface_state_vertmaximized}, StatePair{mir_surface_state_minimized, mir_surface_state_horizmaximized}, StatePair{mir_surface_state_minimized, mir_surface_state_fullscreen} )); ./tests/acceptance-tests/test_client_surface_swap_buffers.cpp0000644000015600001650000000402012676616125024774 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/test/doubles/null_display_buffer_compositor_factory.h" #include "mir/test/signal.h" #include namespace mtf = mir_test_framework; namespace mt = mir::test; namespace mtd = mir::test::doubles; using namespace testing; namespace { void swap_buffers_callback(MirBufferStream*, void* ctx) { auto buffers_swapped = static_cast(ctx); buffers_swapped->raise(); } struct SurfaceSwapBuffers : mtf::ConnectedClientWithASurface { void SetUp() override { server.override_the_display_buffer_compositor_factory([] { return std::make_shared(); }); ConnectedClientWithASurface::SetUp(); } }; } TEST_F(SurfaceSwapBuffers, does_not_block_when_surface_is_not_composited) { for (int i = 0; i != 10; ++i) { mt::Signal buffers_swapped; mir_buffer_stream_swap_buffers(mir_surface_get_buffer_stream(surface), swap_buffers_callback, &buffers_swapped); /* * ASSERT instead of EXPECT, since if we continue we will block in future * mir client calls (e.g mir_connection_release). */ ASSERT_TRUE(buffers_swapped.wait_for(std::chrono::seconds{20})); } } ./tests/acceptance-tests/test_new_display_configuration.cpp0000644000015600001650000005724612676616157024534 0ustar jenkinsjenkins/* * Copyright © 2013-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis * Kevin DuBois * Christopher James Halse Rogers */ #include "mir/main_loop.h" #include "mir/frontend/session_authorizer.h" #include "mir/graphics/event_handler_register.h" #include "mir/shell/display_configuration_controller.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/test/doubles/null_platform.h" #include "mir/test/doubles/fake_display.h" #include "mir/test/doubles/null_display_sync_group.h" #include "mir/test/doubles/null_platform.h" #include "mir/test/display_config_matchers.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/stub_session_authorizer.h" #include "mir/test/fake_shared.h" #include "mir/test/pipe.h" #include "mir/test/wait_condition.h" #include "mir/test/signal.h" #include "mir_toolkit/mir_client_library.h" #include #include #include #include namespace mg = mir::graphics; namespace geom = mir::geometry; namespace mf = mir::frontend; namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mt = mir::test; using namespace testing; using namespace std::literals::chrono_literals; namespace { mtd::StubDisplayConfig stub_display_config; mtd::StubDisplayConfig changed_stub_display_config{1}; class MockDisplay : public mtd::FakeDisplay { public: MockDisplay(): mtd::FakeDisplay() { using namespace testing; ON_CALL(*this, configure(_)) .WillByDefault(Invoke( [this](mg::DisplayConfiguration const& new_config) { mtd::FakeDisplay::configure(new_config); })); } MOCK_METHOD1(configure, void(mg::DisplayConfiguration const&)); }; struct StubAuthorizer : mtd::StubSessionAuthorizer { bool configure_display_is_allowed(mf::SessionCredentials const&) override { return allow_configure_display; } bool set_base_display_configuration_is_allowed(mf::SessionCredentials const&) override { return allow_set_base_display_configuration; } std::atomic allow_configure_display{true}; std::atomic allow_set_base_display_configuration{true}; }; void wait_for_server_actions_to_finish(mir::ServerActionQueue& server_action_queue) { mt::WaitCondition last_action_done; server_action_queue.enqueue( &last_action_done, [&] { last_action_done.wake_up_everyone(); }); last_action_done.wait_for_at_most_seconds(5); } } struct DisplayConfigurationTest : mtf::ConnectedClientWithASurface { void SetUp() override { server.override_the_session_authorizer([this] { return mt::fake_shared(stub_authorizer); }); preset_display(mt::fake_shared(mock_display)); mtf::ConnectedClientWithASurface::SetUp(); } testing::NiceMock mock_display; StubAuthorizer stub_authorizer; }; TEST_F(DisplayConfigurationTest, display_configuration_reaches_client) { auto configuration = mir_connection_create_display_configuration(connection); EXPECT_THAT(configuration, mt::DisplayConfigMatches(std::cref(stub_display_config))); mir_display_config_release(configuration); } namespace { void display_change_handler(MirConnection* connection, void* context) { auto configuration = mir_connection_create_display_configuration(connection); EXPECT_THAT(configuration, mt::DisplayConfigMatches(std::cref(changed_stub_display_config))); mir_display_config_release(configuration); auto callback_called = static_cast(context); callback_called->raise(); } } TEST_F(DisplayConfigurationTest, hw_display_change_notification_reaches_all_clients) { mt::Signal callback_called; mir_connection_set_display_config_change_callback(connection, &display_change_handler, &callback_called); MirConnection* unsubscribed_connection = mir_connect_sync(new_connection().c_str(), "notifier"); mock_display.emit_configuration_change_event( mt::fake_shared(changed_stub_display_config)); EXPECT_TRUE(callback_called.wait_for(std::chrono::seconds{10})); // At this point, the message has gone out on the wire. since with unsubscribed_connection // we're emulating a client that is passively subscribed, we will just wait for the display // configuration to change and then will check the new config. auto config = mir_connection_create_display_configuration(unsubscribed_connection); while((unsigned)mir_display_config_get_num_outputs(config) != changed_stub_display_config.outputs.size()) { mir_display_config_release(config); std::this_thread::sleep_for(std::chrono::microseconds(500)); config = mir_connection_create_display_configuration(unsubscribed_connection); } EXPECT_THAT(config, mt::DisplayConfigMatches(std::cref(changed_stub_display_config))); mir_display_config_release(config); mir_connection_release(unsubscribed_connection); } namespace { struct SimpleClient { SimpleClient(std::string const& mir_test_socket) : mir_test_socket{mir_test_socket} {} void connect() { connection = mir_connect_sync(mir_test_socket.c_str(), __PRETTY_FUNCTION__); auto const spec = mir_connection_create_spec_for_normal_surface(connection, 100, 100, mir_pixel_format_abgr_8888); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); } void disconnect() { mir_surface_release_sync(surface); mir_connection_release(connection); } void disconnect_without_releasing_surface() { mir_connection_release(connection); } std::string mir_test_socket; MirConnection* connection{nullptr}; MirSurface* surface{nullptr}; }; struct DisplayClient : SimpleClient { using SimpleClient::SimpleClient; std::unique_ptr get_base_config() { return {mir_connection_create_display_configuration(connection), &mir_display_config_release}; } }; } namespace { struct DisplayConfigMatchingContext { std::function matcher; mt::Signal done; }; void new_display_config_matches(MirConnection* connection, void* ctx) { auto context = reinterpret_cast(ctx); auto config = mir_connection_create_display_configuration(connection); context->matcher(config); mir_display_config_release(config); context->done.raise(); } } TEST_F(DisplayConfigurationTest, shell_initiated_display_configuration_notifies_clients) { using namespace testing; // Create a new client for explicit lifetime handling. SimpleClient client{new_connection()}; client.connect(); std::shared_ptr new_conf; new_conf = server.the_display()->configuration(); new_conf->for_each_output([](mg::UserDisplayConfigurationOutput& output) { if (output.connected) { output.used = !output.used; } }); DisplayConfigMatchingContext context; context.matcher = [new_conf](MirDisplayConfig* conf) { EXPECT_THAT(conf, mt::DisplayConfigMatches(std::cref(*new_conf))); }; mir_connection_set_display_config_change_callback( client.connection, &new_display_config_matches, &context); server.the_display_configuration_controller()->set_base_configuration(new_conf); EXPECT_TRUE(context.done.wait_for(std::chrono::seconds{10})); EXPECT_THAT( *server.the_display()->configuration(), mt::DisplayConfigMatches(std::cref(*new_conf))); client.disconnect(); } struct DisplayPowerSetting : public DisplayConfigurationTest, public ::testing::WithParamInterface {}; struct DisplayOrientationSetting : public DisplayConfigurationTest, public ::testing::WithParamInterface {}; struct DisplayFormatSetting : public DisplayConfigurationTest, public ::testing::WithParamInterface {}; TEST_P(DisplayPowerSetting, can_get_power_mode) { using namespace testing; auto mode = GetParam(); std::shared_ptr server_config = server.the_display()->configuration(); server_config->for_each_output( [mode](mg::UserDisplayConfigurationOutput& output) { output.power_mode = mode; }); server.the_display_configuration_controller()->set_base_configuration(server_config); wait_for_server_actions_to_finish(*server.the_main_loop()); DisplayClient client{new_connection()}; client.connect(); auto client_config = client.get_base_config(); for (int i = 0; i < mir_display_config_get_num_outputs(client_config.get()); ++i) { auto output = mir_display_config_get_output(client_config.get(), i); EXPECT_THAT(mir_output_get_power_mode(output), Eq(mode)); } client.disconnect(); } TEST_P(DisplayOrientationSetting, can_get_orientation) { using namespace testing; auto orientation = GetParam(); std::shared_ptr server_config = server.the_display()->configuration(); server_config->for_each_output( [orientation](mg::UserDisplayConfigurationOutput& output) { output.orientation = orientation; }); mock_display.emit_configuration_change_event(server_config); mock_display.wait_for_configuration_change_handler(); DisplayClient client{new_connection()}; client.connect(); auto client_config = client.get_base_config(); for (int i = 0; i < mir_display_config_get_num_outputs(client_config.get()); ++i) { auto output = mir_display_config_get_output(client_config.get(), i); EXPECT_THAT(mir_output_get_orientation(output), Eq(orientation)); } client.disconnect(); } namespace { std::vector const formats{ mir_pixel_format_abgr_8888, mir_pixel_format_xbgr_8888, mir_pixel_format_argb_8888, mir_pixel_format_xrgb_8888, mir_pixel_format_bgr_888, mir_pixel_format_rgb_888, mir_pixel_format_rgb_565, mir_pixel_format_rgba_5551, mir_pixel_format_rgba_4444, }; } TEST_P(DisplayFormatSetting, can_get_all_output_format) { using namespace testing; auto format = GetParam(); mtd::StubDisplayConfig single_format_config(1, {format}); mock_display.emit_configuration_change_event(mt::fake_shared(single_format_config)); mock_display.wait_for_configuration_change_handler(); DisplayClient client{new_connection()}; client.connect(); auto client_config = client.get_base_config(); for (int i = 0; i < mir_display_config_get_num_outputs(client_config.get()); ++i) { auto output = mir_display_config_get_output(client_config.get(), i); EXPECT_THAT(mir_output_get_current_pixel_format(output), Eq(format)); } client.disconnect(); } INSTANTIATE_TEST_CASE_P(DisplayConfiguration, DisplayPowerSetting, Values(mir_power_mode_on, mir_power_mode_standby, mir_power_mode_suspend, mir_power_mode_off)); INSTANTIATE_TEST_CASE_P(DisplayConfiguration, DisplayFormatSetting, ValuesIn(formats)); TEST_F(DisplayConfigurationTest, client_received_configuration_matches_server_config) { mg::DisplayConfigurationMode hd{{1280, 720}, 60.0}; mg::DisplayConfigurationMode fhd{{1920, 1080}, 60.0}; mg::DisplayConfigurationMode proper_size{{1920, 1200}, 60.0}; mg::DisplayConfigurationMode retina{{3210, 2800}, 60.0}; mtd::StubDisplayConfigurationOutput tv{ mg::DisplayConfigurationOutputId{1}, {hd, fhd}, {mir_pixel_format_abgr_8888}}; mtd::StubDisplayConfigurationOutput monitor{ mg::DisplayConfigurationOutputId{2}, {hd, fhd, proper_size, retina}, {mir_pixel_format_abgr_8888}}; std::vector outputs{tv, monitor}; auto config = std::make_shared(outputs); mock_display.emit_configuration_change_event(config); mock_display.wait_for_configuration_change_handler(); DisplayClient client{new_connection()}; client.connect(); auto client_config = client.get_base_config(); auto server_config = server.the_display()->configuration(); EXPECT_THAT(client_config.get(), mt::DisplayConfigMatches(std::cref(*server_config))); client.disconnect(); } TEST_F(DisplayConfigurationTest, client_receives_correct_mode_information) { mg::DisplayConfigurationMode hd{{1280, 720}, 60.0}; mg::DisplayConfigurationMode fhd{{1920, 1080}, 60.0}; mg::DisplayConfigurationMode proper_size{{1920, 1200}, 60.0}; mg::DisplayConfigurationMode retina{{3210, 2800}, 60.0}; mg::DisplayConfigurationOutputId const id{2}; std::vector modes{hd, fhd, proper_size, retina}; mtd::StubDisplayConfigurationOutput monitor{ id, modes, {mir_pixel_format_abgr_8888}}; std::vector outputs{monitor}; mock_display.emit_configuration_change_event(std::make_shared(outputs)); mock_display.wait_for_configuration_change_handler(); DisplayClient client{new_connection()}; client.connect(); auto config = client.get_base_config(); ASSERT_THAT(mir_display_config_get_num_outputs(config.get()), Eq(1)); std::vector received_modes; auto output = mir_display_config_get_output(config.get(), 0); for (auto i = 0; i < mir_output_get_num_modes(output) ; ++i) { auto mode = mir_output_get_mode(output, i); auto width = mir_output_mode_get_width(mode); auto height = mir_output_mode_get_height(mode); auto refresh = mir_output_mode_get_refresh_rate(mode); received_modes.push_back(mg::DisplayConfigurationMode{{width, height}, refresh}); } EXPECT_THAT(received_modes, ContainerEq(modes)); client.disconnect(); } TEST_F(DisplayConfigurationTest, mode_width_and_height_are_independent_of_orientation) { mg::DisplayConfigurationMode hd{{1280, 720}, 60.0}; mg::DisplayConfigurationMode fhd{{1920, 1080}, 60.0}; mg::DisplayConfigurationMode proper_size{{1920, 1200}, 60.0}; mg::DisplayConfigurationMode retina{{3210, 2800}, 60.0}; mg::DisplayConfigurationOutputId const id{2}; std::vector modes{hd, fhd, proper_size, retina}; mtd::StubDisplayConfigurationOutput monitor{ id, modes, {mir_pixel_format_abgr_8888}}; std::vector outputs{monitor}; std::shared_ptr server_config; server_config = std::make_shared(outputs); mock_display.emit_configuration_change_event(server_config); mock_display.wait_for_configuration_change_handler(); DisplayClient client{new_connection()}; client.connect(); DisplayConfigMatchingContext context; context.matcher = [&server_config](MirDisplayConfig* conf) { EXPECT_THAT(conf, mt::DisplayConfigMatches(std::cref(*server_config))); }; mir_connection_set_display_config_change_callback( client.connection, &new_display_config_matches, &context); for (auto const orientation : {mir_orientation_normal, mir_orientation_left, mir_orientation_inverted, mir_orientation_right}) { server_config = server.the_display()->configuration(); server_config->for_each_output( [orientation](mg::UserDisplayConfigurationOutput& output) { output.orientation = orientation; }); server.the_display_configuration_controller()->set_base_configuration(server_config); EXPECT_TRUE(context.done.wait_for(std::chrono::seconds{10})); context.done.reset(); auto config = client.get_base_config(); ASSERT_THAT(mir_display_config_get_num_outputs(config.get()), Eq(1)); std::vector received_modes; auto output = mir_display_config_get_output(config.get(), 0); for (auto i = 0; i < mir_output_get_num_modes(output) ; ++i) { auto mode = mir_output_get_mode(output, i); auto width = mir_output_mode_get_width(mode); auto height = mir_output_mode_get_height(mode); auto refresh = mir_output_mode_get_refresh_rate(mode); received_modes.push_back(mg::DisplayConfigurationMode{{width, height}, refresh}); } EXPECT_THAT(received_modes, ContainerEq(modes)); } client.disconnect(); } TEST_F(DisplayConfigurationTest, output_position_is_independent_of_orientation) { std::array const positions = {{ mir::geometry::Point{-100, 10}, mir::geometry::Point{100, 10000}, mir::geometry::Point{-100, 10} }}; std::shared_ptr server_config = server.the_display()->configuration(); server_config->for_each_output( [position = positions.begin()](mg::UserDisplayConfigurationOutput& output) mutable { output.top_left = *position; ++position; }); DisplayClient client{new_connection()}; client.connect(); DisplayConfigMatchingContext context; context.matcher = [&server_config](MirDisplayConfig* conf) { EXPECT_THAT(conf, mt::DisplayConfigMatches(std::cref(*server_config))); }; mir_connection_set_display_config_change_callback( client.connection, &new_display_config_matches, &context); server.the_display_configuration_controller()->set_base_configuration(server_config); context.done.wait_for(std::chrono::seconds{10}); for (auto const orientation : {mir_orientation_normal, mir_orientation_left, mir_orientation_inverted, mir_orientation_right}) { server_config = server.the_display()->configuration(); server_config->for_each_output( [orientation](mg::UserDisplayConfigurationOutput& output) { output.orientation = orientation; }); context.done.reset(); server.the_display_configuration_controller()->set_base_configuration(server_config); EXPECT_TRUE(context.done.wait_for(std::chrono::seconds{10})); auto config = client.get_base_config(); auto position = positions.begin(); for (auto i = 0; i < mir_display_config_get_num_outputs(config.get()); ++i) { auto output = mir_display_config_get_output(config.get(), i); EXPECT_THAT(mir_output_get_position_x(output), Eq(position->x.as_int())); EXPECT_THAT(mir_output_get_position_y(output), Eq(position->y.as_int())); ++position; } } client.disconnect(); } TEST_F(DisplayConfigurationTest, client_receives_correct_output_positions) { std::array const positions = {{ mir::geometry::Point{-100, 10}, mir::geometry::Point{100, 10000}, mir::geometry::Point{-100, 10} }}; std::shared_ptr server_config = server.the_display()->configuration(); server_config->for_each_output( [position = positions.begin()](mg::UserDisplayConfigurationOutput& output) mutable { output.top_left = *position; ++position; }); DisplayClient client{new_connection()}; client.connect(); DisplayConfigMatchingContext context; context.matcher = [server_config](MirDisplayConfig* conf) { EXPECT_THAT(conf, mt::DisplayConfigMatches(std::cref(*server_config))); }; mir_connection_set_display_config_change_callback( client.connection, &new_display_config_matches, &context); server.the_display_configuration_controller()->set_base_configuration(server_config); EXPECT_TRUE(context.done.wait_for(std::chrono::seconds{10})); auto config = client.get_base_config(); auto position = positions.begin(); for (auto i = 0; i < mir_display_config_get_num_outputs(config.get()); ++i) { auto output = mir_display_config_get_output(config.get(), i); EXPECT_THAT(mir_output_get_position_x(output), Eq(position->x.as_int())); EXPECT_THAT(mir_output_get_position_y(output), Eq(position->y.as_int())); ++position; } client.disconnect(); } namespace { void signal_when_config_received(MirConnection* /*unused*/, void* ctx) { auto signal = reinterpret_cast(ctx); signal->raise(); } } TEST_F(DisplayConfigurationTest, client_sees_server_set_scale_factor) { std::shared_ptr current_config = server.the_display()->configuration(); current_config->for_each_output( [](mg::UserDisplayConfigurationOutput& output) { static int output_num{0}; output.scale = 1 + 0.25f * output_num; ++output_num; }); DisplayClient client{new_connection()}; client.connect(); mt::Signal configuration_received; mir_connection_set_display_config_change_callback( client.connection, &signal_when_config_received, &configuration_received); server.the_display_configuration_controller()->set_base_configuration(current_config); EXPECT_TRUE(configuration_received.wait_for(std::chrono::seconds{10})); auto client_config = client.get_base_config(); for (int i = 0; i < mir_display_config_get_num_outputs(client_config.get()); ++i) { auto output = mir_display_config_get_output(client_config.get(), i); EXPECT_THAT(mir_output_get_scale_factor(output), Eq(1 + 0.25 * i)); } client.disconnect(); } TEST_F(DisplayConfigurationTest, client_sees_server_set_form_factor) { std::array const form_factors = {{ mir_form_factor_monitor, mir_form_factor_projector, mir_form_factor_unknown }}; std::shared_ptr current_config = server.the_display()->configuration(); current_config->for_each_output( [&form_factors](mg::UserDisplayConfigurationOutput& output) { static int output_num{0}; output.form_factor = form_factors[output_num]; ++output_num; }); DisplayClient client{new_connection()}; client.connect(); mt::Signal configuration_received; mir_connection_set_display_config_change_callback( client.connection, &signal_when_config_received, &configuration_received); server.the_display_configuration_controller()->set_base_configuration(current_config); EXPECT_TRUE(configuration_received.wait_for(std::chrono::seconds{10})); auto client_config = client.get_base_config(); for (int i = 0; i < mir_display_config_get_num_outputs(client_config.get()); ++i) { auto output = mir_display_config_get_output(client_config.get(), i); EXPECT_THAT(mir_output_get_form_factor(output), Eq(form_factors[i])); } client.disconnect(); } ./tests/acceptance-tests/CMakeLists.txt0000644000015600001650000000503312676616157020247 0ustar jenkinsjenkinsinclude(CMakeDependentOption) include_directories( ${CMAKE_SOURCE_DIR} ) set( SOURCES server_configuration_wrapping.cpp server_configuration_options.cpp server_signal_handling.cpp test_application_not_responding_detection.cpp test_client_header_version.cpp test_client_input.cpp test_client_library.cpp test_client_library_callbacks.cpp test_client_library_old.cpp test_client_surface_events.cpp test_client_surface_swap_buffers.cpp test_command_line_handling.cpp test_client_surfaces.cpp test_client_logging.cpp test_custom_window_management.cpp test_custom_input_dispatcher.cpp test_server_shutdown.cpp test_client_focus_notification.cpp test_client_authorization.cpp test_mirblob.cpp test_nested_mir.cpp test_nested_input.cpp test_display_configuration.cpp test_surfaces_with_output_id.cpp test_server_disconnect.cpp test_prompt_session_client_api.cpp test_client_scaling.cpp test_client_screencast.cpp test_client_surface_visibility.cpp test_buffer_stream_arrangement.cpp test_client_with_custom_display_config_deadlock.cpp test_server_without_active_outputs.cpp test_server_startup.cpp test_debug_api.cpp test_unresponsive_client.cpp test_client_platform_operation.cpp test_input_device_hub.cpp test_latency.cpp test_render_override.cpp test_surface_modifications.cpp test_surface_placement.cpp test_surface_morphing.cpp test_surface_specification.cpp test_system_compositor_window_manager.cpp test_session_mediator_report.cpp test_surface_raise.cpp test_client_cookie.cpp test_new_display_configuration.cpp ) if (MIR_TEST_PLATFORM STREQUAL "mesa-kms" OR MIR_TEST_PLATFORM STREQUAL "mesa-x11") list(APPEND SOURCES test_symbols_required_by_mesa.cpp) endif() add_subdirectory(throwback) mir_add_wrapped_executable( mir_acceptance_tests ${SOURCES} $ ) mir_precompiled_header(mir_acceptance_tests ${CMAKE_CURRENT_SOURCE_DIR}/precompiled.hpp) add_dependencies(mir_acceptance_tests GMock) target_link_libraries(mir_acceptance_tests mir-test-assist mirclient mirclient-debug-extension mirserver ${CMAKE_THREAD_LIBS_INIT} # Link in pthread. ) CMAKE_DEPENDENT_OPTION( MIR_RUN_ACCEPTANCE_TESTS "Run acceptance tests as part of default testing" ON "MIR_BUILD_ACCEPTANCE_TESTS" OFF) if (MIR_RUN_ACCEPTANCE_TESTS) mir_discover_tests_with_fd_leak_detection(mir_acceptance_tests) mir_discover_tests_with_fd_leak_detection_and_env(mir_acceptance_tests "MIR_SERVER_NBUFFERS=0") endif (MIR_RUN_ACCEPTANCE_TESTS) ./tests/acceptance-tests/test_client_focus_notification.cpp0000644000015600001650000001115312676616125024470 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_toolkit/mir_client_library.h" #include "mir/test/wait_condition.h" #include "mir/test/event_matchers.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir_test_framework/process.h" #include "mir/test/cross_process_sync.h" #include #include namespace mt = mir::test; namespace mtf = mir_test_framework; using namespace ::testing; namespace { struct FocusSurface { FocusSurface(MirConnection* connection) : connection(connection) { auto spec = mir_connection_create_spec_for_normal_surface(connection, 100, 100, mir_pixel_format_abgr_8888); mir_surface_spec_set_event_handler(spec, FocusSurface::handle_event, this); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); } ~FocusSurface() { if (!released) release(); } static void handle_event(MirSurface* surface, MirEvent const* ev, void* context) { if (mir_event_type_surface == mir_event_get_type(ev)) { auto surface_ev = mir_event_get_surface_event(ev); if (mir_surface_attrib_focus == mir_surface_event_get_attribute(surface_ev)) { auto self = static_cast(context); self->log_focus_event(surface, static_cast(mir_surface_event_get_attribute_value(surface_ev))); } } } void log_focus_event(MirSurface*, MirSurfaceFocusState state) { std::lock_guard lk(mutex); focus_events.push_back(state); cv.notify_all(); } MirSurface* native_handle() const { return surface; } void release() { mir_surface_release_sync(surface); mir_connection_release(connection); released = true; } void expect_focus_event_sequence(std::vector const& seq) { std::unique_lock lk(mutex); if (!cv.wait_for(lk, timeout, [this, &seq] { return focus_events.size() >= seq.size(); })) { throw std::logic_error("timeout waiting for events"); } EXPECT_THAT(focus_events, ContainerEq(seq)); } private: std::mutex mutex; std::condition_variable cv; std::vector focus_events; bool released {false}; MirConnection* connection = nullptr; MirSurface* surface = nullptr; std::chrono::seconds timeout{5}; }; struct ClientFocusNotification : mtf::ConnectedClientHeadlessServer { std::unique_ptr make_surface() { auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_THAT(connection, NotNull()); return std::make_unique(connection); } }; } TEST_F(ClientFocusNotification, a_surface_is_notified_of_receiving_focus) { std::vector const focus_sequence = {mir_surface_focused, mir_surface_unfocused}; auto surface = make_surface(); surface->release(); surface->expect_focus_event_sequence(focus_sequence); } TEST_F(ClientFocusNotification, two_surfaces_are_notified_of_gaining_and_losing_focus) { std::vector const initial_focus = {mir_surface_focused}; std::vector const focus_sequence1 = {mir_surface_focused, mir_surface_unfocused, mir_surface_focused, mir_surface_unfocused}; std::vector const focus_sequence2 = {mir_surface_focused, mir_surface_unfocused}; auto surface1 = make_surface(); surface1->expect_focus_event_sequence(initial_focus); auto surface2 = make_surface(); surface2->release(); surface1->release(); surface1->expect_focus_event_sequence(focus_sequence1); surface2->expect_focus_event_sequence(focus_sequence2); } ./tests/acceptance-tests/test_client_logging.cpp0000644000015600001650000000603512676616157022241 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir/logging/logger.h" #include "mir/test/fake_shared.h" #include #include #include #include using namespace testing; using namespace mir_test_framework; namespace { class StringStreamLogger : public mir::logging::Logger { public: void log(mir::logging::Severity, std::string const& message, std::string const& component) override { out << "[StringStreamLogger] " << component << ": " << message << std::endl; } std::stringstream out; }; struct ClientLogging : ConnectedClientHeadlessServer { StringStreamLogger logger; void SetUp() override { add_to_environment("MIR_CLIENT_PERF_REPORT", "log"); ConnectedClientHeadlessServer::SetUp(); mir::logging::set_logger(mir::test::fake_shared(logger)); } }; } // namespace TEST_F(ClientLogging, reports_performance) { auto spec = mir_connection_create_spec_for_normal_surface( connection, 123, 456, mir_pixel_format_abgr_8888); ASSERT_THAT(spec, NotNull()); mir_surface_spec_set_name(spec, "Rumpelstiltskin"); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_software); auto surf = mir_surface_create_sync(spec); ASSERT_THAT(surf, NotNull()); mir_surface_spec_release(spec); int const target_fps = 10; int const nseconds = 3; auto bs = mir_surface_get_buffer_stream(surf); for (int s = 0; s < nseconds; ++s) { for (int f = 0; f < target_fps; ++f) mir_buffer_stream_swap_buffers_sync(bs); std::this_thread::sleep_for(std::chrono::seconds(1)); } int reports = 0; while (!logger.out.eof()) { std::string line; std::getline(logger.out, line); auto perf = line.find(" perf: "); if (perf != line.npos) { ++reports; char name[256]; float fps; int fields = sscanf(line.c_str() + perf, " perf: %255[^:]: %f FPS,", name, &fps); ASSERT_EQ(2, fields) << "Log line = {" << line << "}"; EXPECT_STREQ("Rumpelstiltskin", name); EXPECT_NEAR(target_fps, fps, 5.0f); } } EXPECT_THAT(reports, Ge(nseconds-1)); mir_surface_release_sync(surf); } ./tests/acceptance-tests/test_client_authorization.cpp0000644000015600001650000001275012676616125023507 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/frontend/session_credentials.h" #include "mir/frontend/session_authorizer.h" #include #include "mir/test/fake_shared.h" #include "mir_test_framework/interprocess_client_server_test.h" #include "mir/test/doubles/stub_session_authorizer.h" #include #include #include #include #include #include #include namespace mf = mir::frontend; namespace mt = mir::test; namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; using namespace ::testing; namespace { struct MockSessionAuthorizer : public mf::SessionAuthorizer { MOCK_METHOD1(connection_is_allowed, bool(mf::SessionCredentials const&)); MOCK_METHOD1(configure_display_is_allowed, bool(mf::SessionCredentials const&)); MOCK_METHOD1(set_base_display_configuration_is_allowed, bool(mf::SessionCredentials const&)); MOCK_METHOD1(screencast_is_allowed, bool(mf::SessionCredentials const&)); MOCK_METHOD1(prompt_session_is_allowed, bool(mf::SessionCredentials const&)); }; struct ClientCredsTestFixture : mtf::InterprocessClientServerTest { ClientCredsTestFixture() { shared_region = static_cast( mmap(NULL, sizeof(SharedRegion), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0)); sem_init(&shared_region->client_creds_set, 1, 0); } struct SharedRegion { sem_t client_creds_set; pid_t client_pid = -1; uid_t client_uid = -1; gid_t client_gid = -1; bool matches_client_process_creds(mf::SessionCredentials const& creds) { // A perfect match is perfect. But sometimes it's not a perfect // match and that's OK too. Because fakeroot (used in deb builds) // returns 0 unconditionally, even for non-root users. return client_pid == creds.pid() && (client_uid == 0 || client_uid == creds.uid()) && (client_gid == 0 || client_gid == creds.gid()); } bool wait_for_client_creds() { static time_t const timeout_seconds = 60; struct timespec abs_timeout = { time(NULL) + timeout_seconds, 0 }; return sem_timedwait(&client_creds_set, &abs_timeout) == 0; } void post_client_creds() { client_pid = getpid(); client_uid = geteuid(); client_gid = getegid(); sem_post(&client_creds_set); } }; SharedRegion* shared_region; MockSessionAuthorizer mock_authorizer; }; } TEST_F(ClientCredsTestFixture, session_authorizer_receives_pid_of_connecting_clients) { auto const matches_creds = [&](mf::SessionCredentials const& creds) { return shared_region->matches_client_process_creds(creds); }; init_server([&] { server.override_the_session_authorizer( [&] { return mt::fake_shared(mock_authorizer); }); EXPECT_CALL(mock_authorizer, connection_is_allowed(Truly(matches_creds))) .Times(1) .WillOnce(Return(true)); EXPECT_CALL(mock_authorizer, configure_display_is_allowed(Truly(matches_creds))) .Times(1) .WillOnce(Return(false)); EXPECT_CALL(mock_authorizer, screencast_is_allowed(Truly(matches_creds))) .Times(1) .WillOnce(Return(false)); EXPECT_CALL(mock_authorizer, prompt_session_is_allowed(Truly(matches_creds))) .Times(1) .WillOnce(Return(false)); }); run_in_server([&] { EXPECT_TRUE(shared_region->wait_for_client_creds()); }); run_in_client([&] { shared_region->post_client_creds(); auto const connection = mir_connect_sync(mir_test_socket, __PRETTY_FUNCTION__); ASSERT_TRUE(mir_connection_is_valid(connection)); mir_connection_release(connection); }); } // This test is also a regression test for https://bugs.launchpad.net/mir/+bug/1358191 TEST_F(ClientCredsTestFixture, authorizer_may_prevent_connection_of_clients) { init_server([&] { server.override_the_session_authorizer( [&] { return mt::fake_shared(mock_authorizer); }); EXPECT_CALL(mock_authorizer, connection_is_allowed(_)) .Times(1) .WillOnce(Return(false)); }); run_in_server([&]{}); run_in_client([&] { auto const connection = mir_connect_sync(mir_test_socket, __PRETTY_FUNCTION__); EXPECT_FALSE(mir_connection_is_valid(connection)); mir_connection_release(connection); }); } ./tests/acceptance-tests/test_mirblob.cpp0000644000015600001650000000365412676616125020702 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir_toolkit/mir_blob.h" #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/test/display_config_matchers.h" #include #include #include using MirBlobAPI = mir_test_framework::ConnectedClientWithASurface; using mir::test::DisplayConfigMatches; TEST_F(MirBlobAPI, can_serialize_display_configuration) { std::vector buffer; auto const save_display_config = mir_connection_create_display_config(connection); { auto const save_blob = mir_blob_from_display_configuration(save_display_config); buffer.resize(mir_blob_size(save_blob)); memcpy(buffer.data(), mir_blob_data(save_blob), buffer.size()); mir_blob_release(save_blob); } MirDisplayConfiguration* restore_display_config; { auto const restore_blob = mir_blob_onto_buffer(buffer.data(), buffer.size()); restore_display_config = mir_blob_to_display_configuration(restore_blob); mir_blob_release(restore_blob); } EXPECT_THAT(save_display_config, DisplayConfigMatches(restore_display_config)); mir_display_config_destroy(restore_display_config); mir_display_config_destroy(save_display_config); }./tests/acceptance-tests/test_surfaces_with_output_id.cpp0000644000015600001650000001577212676616125024222 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/mir_client_library.h" #include "mir/geometry/rectangle.h" #include "mir/scene/session.h" #include "mir/scene/surface.h" #include "mir/shell/shell_wrapper.h" #include "mir_test_framework/connected_client_headless_server.h" #include "mir_test_framework/any_surface.h" #include "mir_test_framework/visible_surface.h" #include "mir/test/validity_matchers.h" #include #include #include namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell; namespace mtf = mir_test_framework; namespace geom = mir::geometry; using namespace testing; namespace { struct RectangleCompare { bool operator()(geom::Rectangle const& rect1, geom::Rectangle const& rect2) { int x1 = rect1.top_left.x.as_int(); int y1 = rect1.top_left.y.as_int(); int w1 = rect1.size.width.as_int(); int h1 = rect1.size.height.as_int(); int x2 = rect2.top_left.x.as_int(); int y2 = rect2.top_left.y.as_int(); int w2 = rect2.size.width.as_int(); int h2 = rect2.size.height.as_int(); return std::tie(x1,y1,w1,h1) < std::tie(x2,y2,w2,h2); } }; class TrackingShell : public msh::ShellWrapper { public: using msh::ShellWrapper::ShellWrapper; mf::SurfaceId create_surface( std::shared_ptr const& session, ms::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override { auto const surface = msh::ShellWrapper::create_surface(session, params, sink); surfaces.push_back(session->surface(surface)); return surface; } std::vector server_surface_rectangles() { std::vector rects; for (auto const& surface : surfaces) { if (auto const ss = surface.lock()) { for (auto& renderable: ss->generate_renderables(this)) rects.push_back(renderable->screen_position()); } } return rects; } private: std::vector> surfaces; }; struct SurfacesWithOutputId : mtf::ConnectedClientHeadlessServer { void SetUp() override { server.wrap_shell([this] (std::shared_ptr const& wrapped) ->std::shared_ptr { tracking_shell = std::make_shared(wrapped); return tracking_shell; }); initial_display_layout(display_rects); mtf::ConnectedClientHeadlessServer::SetUp(); ASSERT_THAT(tracking_shell, NotNull()); config = mir_connection_create_display_config(connection); ASSERT_TRUE(config != NULL); } void TearDown() override { mir_display_config_destroy(config); tracking_shell.reset(); mtf::ConnectedClientHeadlessServer::TearDown(); } std::shared_ptr create_non_fullscreen_surface_for(MirDisplayOutput const& output) { auto const& mode = output.modes[output.current_mode]; auto del = [] (MirSurfaceSpec* spec) { mir_surface_spec_release(spec); }; std::unique_ptr spec( mir_connection_create_spec_for_normal_surface(connection, static_cast(mode.horizontal_resolution) - 1, static_cast(mode.vertical_resolution) + 1, mir_pixel_format_abgr_8888), del); mir_surface_spec_set_fullscreen_on_output(spec.get(), output.output_id); return std::make_shared(spec.get()); } std::shared_ptr create_fullscreen_surface_for(MirDisplayOutput const& output) { auto const& mode = output.modes[output.current_mode]; auto del = [] (MirSurfaceSpec* spec) { mir_surface_spec_release(spec); }; std::unique_ptr spec( mir_connection_create_spec_for_normal_surface(connection, static_cast(mode.horizontal_resolution), static_cast(mode.vertical_resolution), mir_pixel_format_abgr_8888), del); mir_surface_spec_set_fullscreen_on_output(spec.get(), output.output_id); return std::make_shared(spec.get()); } std::vector const display_rects{ {{0,0}, {800,600}}, {{800,600}, {200,400}}}; MirDisplayConfiguration* config; std::shared_ptr tracking_shell; }; } TEST_F(SurfacesWithOutputId, fullscreen_surfaces_are_placed_at_top_left_of_correct_output) { std::vector> surfaces; for (uint32_t n = 0; n < config->num_outputs; ++n) { auto surface = create_fullscreen_surface_for(config->outputs[n]); EXPECT_TRUE(mir_surface_is_valid(*surface)); surfaces.push_back(surface); } auto surface_rects = tracking_shell->server_surface_rectangles(); auto sorted_display_rects = display_rects; std::sort(sorted_display_rects.begin(), sorted_display_rects.end(), RectangleCompare()); std::sort(surface_rects.begin(), surface_rects.end(), RectangleCompare()); EXPECT_EQ(sorted_display_rects, surface_rects); } TEST_F(SurfacesWithOutputId, requested_size_is_ignored_in_favour_of_display_size) { using namespace testing; std::vector> expected_dimensions; std::vector> surfaces; for (uint32_t n = 0; n < config->num_outputs; ++n) { auto surface = create_non_fullscreen_surface_for(config->outputs[n]); EXPECT_TRUE(mir_surface_is_valid(*surface)); surfaces.push_back(surface); auto expected_mode = config->outputs[n].modes[config->outputs[n].current_mode]; expected_dimensions.push_back(std::pair{expected_mode.horizontal_resolution, expected_mode.vertical_resolution}); } auto surface_rects = tracking_shell->server_surface_rectangles(); auto display_rects = this->display_rects; std::sort(display_rects.begin(), display_rects.end(), RectangleCompare()); std::sort(surface_rects.begin(), surface_rects.end(), RectangleCompare()); EXPECT_EQ(display_rects, surface_rects); } ./tests/acceptance-tests/test_custom_window_management.cpp0000644000015600001650000002422212676616125024343 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/geometry/rectangle.h" #include "mir/scene/session.h" #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/headless_test.h" #include "mir/test/doubles/mock_window_manager.h" #include "mir/test/fake_shared.h" #include "mir/test/signal.h" #include #include namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell; namespace geom = mir::geometry; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; using namespace testing; using namespace std::literals::chrono_literals; namespace { MATCHER_P(WeakPtrEq, p, "") { return !arg.owner_before(p) && !p.owner_before(arg); } std::vector const display_geometry { {{ 0, 0}, { 640, 480}}, {{480, 0}, {1920, 1080}} }; using NiceMockWindowManager = NiceMock; struct Client { explicit Client(char const* connect_string) : connection{mir_connect_sync(connect_string, __PRETTY_FUNCTION__)} { } Client(Client&& source) : connection{nullptr} { std::swap(connection, source.connection); } auto surface_create() const -> MirSurface* { auto spec = mir_connection_create_spec_for_normal_surface( connection, 800, 600, mir_pixel_format_bgr_888); auto surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); return surface; } void disconnect() { if (connection) mir_connection_release(connection); connection = nullptr; } ~Client() noexcept { disconnect(); } MirConnection* connection{nullptr}; }; struct CustomWindowManagement : mtf::HeadlessTest { void SetUp() override { add_to_environment("MIR_SERVER_NO_FILE", ""); initial_display_layout(display_geometry); server.override_the_window_manager_builder([this] (msh::FocusController*) { return mt::fake_shared(window_manager); }); } void TearDown() override { stop_server(); } Client connect_client() { return Client(new_connection().c_str()); } NiceMockWindowManager window_manager; }; } TEST_F(CustomWindowManagement, display_layout_is_notified_on_startup) { for(auto const& rect : display_geometry) EXPECT_CALL(window_manager, add_display(rect)); start_server(); } TEST_F(CustomWindowManagement, display_layout_is_notified_on_shutdown) { start_server(); for(auto const& rect : display_geometry) EXPECT_CALL(window_manager, remove_display(rect)); } TEST_F(CustomWindowManagement, client_connect_adds_session) { start_server(); EXPECT_CALL(window_manager, add_session(_)); connect_client(); } TEST_F(CustomWindowManagement, client_disconnect_removes_session) { start_server(); auto client = connect_client(); EXPECT_CALL(window_manager, remove_session(_)); client.disconnect(); } TEST_F(CustomWindowManagement, surface_create_adds_surface) { start_server(); auto const client = connect_client(); EXPECT_CALL(window_manager, add_surface(_,_,_)); auto const surface = client.surface_create(); mir_surface_release_sync(surface); } TEST_F(CustomWindowManagement, surface_rename_modifies_surface) { auto const new_title = __PRETTY_FUNCTION__; start_server(); auto const client = connect_client(); auto const surface = client.surface_create(); EXPECT_CALL(window_manager, modify_surface(_,_,_)); auto const spec = mir_connection_create_spec_for_changes(client.connection); mir_surface_spec_set_name(spec, new_title); mir_surface_apply_spec(surface, spec); mir_surface_spec_release(spec); mir_surface_release_sync(surface); } TEST_F(CustomWindowManagement, surface_release_removes_surface) { start_server(); auto const client = connect_client(); auto const surface = client.surface_create(); EXPECT_CALL(window_manager, remove_surface(_,_)); mir_surface_release_sync(surface); Mock::VerifyAndClearExpectations(&window_manager); } TEST_F(CustomWindowManagement, client_disconnect_removes_unreleased_surfaces) { start_server(); auto client = connect_client(); client.surface_create(); EXPECT_CALL(window_manager, remove_surface(_,_)); client.disconnect(); } TEST_F(CustomWindowManagement, surface_is_associated_with_correct_client) { start_server(); const int no_of_clients = 17; std::weak_ptr session[no_of_clients]; std::vector client; client.reserve(no_of_clients); for (int i = 0; i != no_of_clients; ++i) { EXPECT_CALL(window_manager, add_session(_)).WillOnce(SaveArg<0>(&session[i])); client.emplace_back(connect_client()); } Mock::VerifyAndClearExpectations(&window_manager); for (int i = 0; i != no_of_clients; ++i) { EXPECT_CALL(window_manager, add_surface(WeakPtrEq(session[i]),_,_)); EXPECT_CALL(window_manager, remove_surface(WeakPtrEq(session[i]),_)); auto const surface = client[i].surface_create(); mir_surface_release_sync(surface); // verify expectations for each client in turn Mock::VerifyAndClearExpectations(&window_manager); } } TEST_F(CustomWindowManagement, state_change_requests_are_associated_with_correct_surface) { start_server(); auto const client = connect_client(); const int no_of_surfaces = 17; std::weak_ptr server_surface[no_of_surfaces]; MirSurface* client_surface[no_of_surfaces] = {}; for (int i = 0; i != no_of_surfaces; ++i) { auto const grab_server_surface = [i, &server_surface]( std::shared_ptr const& session, ms::SurfaceCreationParameters const& params, std::function const& session, ms::SurfaceCreationParameters const& params)> const& build) { auto const result = build(session, params); server_surface[i] = session->surface(result); return result; }; EXPECT_CALL(window_manager, add_surface(_,_,_)) .WillOnce(Invoke(grab_server_surface)); client_surface[i] = client.surface_create(); } for (int i = 0; i != no_of_surfaces; ++i) { // verify expectations for each surface in turn Mock::VerifyAndClearExpectations(&window_manager); mt::Signal received; EXPECT_CALL(window_manager, set_surface_attribute(_, WeakPtrEq(server_surface[i]), mir_surface_attrib_state,_)) .WillOnce(WithArg<3>(Invoke([&](int value) { received.raise(); return value; }))); mir_surface_set_state(client_surface[i], mir_surface_state_maximized); received.wait_for(400ms); } for (auto const surface : client_surface) mir_surface_release_sync(surface); } TEST_F(CustomWindowManagement, create_low_chrome_surface_from_spec) { start_server(); auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); int const width{800}, height{600}; MirPixelFormat const format{mir_pixel_format_bgr_888}; auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, width, height, format); mir_surface_spec_set_shell_chrome(surface_spec, mir_shell_chrome_low); auto const check_add_surface = []( std::shared_ptr const& session, ms::SurfaceCreationParameters const& params, std::function const& session, ms::SurfaceCreationParameters const& params)> const& build) { EXPECT_TRUE(params.shell_chrome.is_set()); return build(session, params); }; EXPECT_CALL(window_manager, add_surface(_,_,_)).WillOnce(Invoke(check_add_surface)); auto surface = mir_surface_create_sync(surface_spec); mir_surface_spec_release(surface_spec); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(CustomWindowManagement, apply_low_chrome_to_surface) { start_server(); auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); int const width{800}, height{600}; MirPixelFormat const format{mir_pixel_format_bgr_888}; auto surface_spec = mir_connection_create_spec_for_normal_surface(connection, width, height, format); auto surface = mir_surface_create_sync(surface_spec); mir_surface_spec_release(surface_spec); surface_spec = mir_connection_create_spec_for_changes(connection); mt::Signal received; mir_surface_spec_set_shell_chrome(surface_spec, mir_shell_chrome_low); auto const check_apply_surface = [&received]( std::shared_ptr const&, std::shared_ptr const&, msh::SurfaceSpecification const& spec) { EXPECT_TRUE(spec.shell_chrome.is_set()); received.raise(); }; EXPECT_CALL(window_manager, modify_surface(_,_,_)).WillOnce(Invoke(check_apply_surface)); mir_surface_apply_spec(surface, surface_spec); mir_surface_spec_release(surface_spec); EXPECT_TRUE(received.wait_for(400ms)); mir_surface_release_sync(surface); mir_connection_release(connection); } ./tests/acceptance-tests/test_buffer_stream_arrangement.cpp0000644000015600001650000002001512676616125024451 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/connected_client_with_a_surface.h" #include "mir/compositor/display_buffer_compositor.h" #include "mir/compositor/display_buffer_compositor_factory.h" #include "mir/compositor/scene_element.h" #include "mir/graphics/renderable.h" #include "mir/graphics/cursor.h" #include "mir/geometry/displacement.h" #include #include #include #include #include namespace mtf = mir_test_framework; namespace geom = mir::geometry; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace { struct RelativeRectangle { RelativeRectangle() = default; RelativeRectangle(geom::Displacement const& displacement, geom::Size const& size) : displacement{displacement}, size{size} { } geom::Displacement displacement; geom::Size size; }; bool operator==(RelativeRectangle const& a, RelativeRectangle const& b) { return (a.displacement == b.displacement) && (a.size == b.size); } MirPixelFormat an_available_format(MirConnection* connection) { using namespace testing; MirPixelFormat format{mir_pixel_format_invalid}; unsigned int valid_formats{0}; mir_connection_get_available_surface_formats(connection, &format, 1, &valid_formats); return format; } struct Stream { Stream(MirBufferStream* stream, geom::Point pt, geom::Size sz) : stream(stream), pos{pt}, stream_size{sz}, needs_release{false} { mir_buffer_stream_swap_buffers_sync(stream); } Stream(MirConnection* connection, geom::Rectangle rect) : stream(mir_connection_create_buffer_stream_sync( connection, rect.size.width.as_int(), rect.size.height.as_int(), an_available_format(connection), mir_buffer_usage_hardware)), pos{rect.top_left}, stream_size{rect.size}, needs_release{true} { mir_buffer_stream_swap_buffers_sync(stream); } ~Stream() { if (needs_release) mir_buffer_stream_release_sync(stream); } MirBufferStream* handle() const { return stream; } geom::Point position() { return pos; } geom::Size size() { return stream_size; } Stream(Stream const&) = delete; Stream& operator=(Stream const&) = delete; private: MirBufferStream* stream; geom::Point const pos; geom::Size stream_size; bool const needs_release; }; struct Ordering { void note_scene_element_sequence(mc::SceneElementSequence& sequence) { if (sequence.empty()) return; std::unique_lock lk(mutex); std::vector position; auto first_position = (*sequence.begin())->renderable()->screen_position().top_left; for (auto const& element : sequence) position.emplace_back( element->renderable()->screen_position().top_left - first_position, element->renderable()->screen_position().size); positions.push_back(position); cv.notify_all(); } template bool wait_for_positions_within( std::vector const& awaited_positions, std::chrono::duration duration) { std::unique_lock lk(mutex); return cv.wait_for(lk, duration, [this, awaited_positions] { for (auto& position : positions) if (position == awaited_positions) return true; positions.clear(); return false; }); } private: std::mutex mutex; std::condition_variable cv; std::vector> positions; }; struct OrderTrackingDBC : mc::DisplayBufferCompositor { OrderTrackingDBC( std::shared_ptr const& wrapped, std::shared_ptr const& ordering) : wrapped(wrapped), ordering(ordering) { } void composite(mc::SceneElementSequence&& scene_sequence) override { ordering->note_scene_element_sequence(scene_sequence); wrapped->composite(std::move(scene_sequence)); } std::shared_ptr const wrapped; std::shared_ptr const ordering; }; struct OrderTrackingDBCFactory : mc::DisplayBufferCompositorFactory { OrderTrackingDBCFactory( std::shared_ptr const& wrapped, std::shared_ptr const& ordering) : wrapped(wrapped), ordering(ordering) { } std::unique_ptr create_compositor_for(mg::DisplayBuffer& db) override { return std::make_unique(wrapped->create_compositor_for(db), ordering); } std::shared_ptr last_dbc{nullptr}; std::shared_ptr const wrapped; std::shared_ptr const ordering; }; struct BufferStreamArrangement : mtf::ConnectedClientWithASurface { void SetUp() override { ordering = std::make_shared(); server.wrap_display_buffer_compositor_factory( [this](std::shared_ptr const& wrapped) { order_tracker = std::make_shared(wrapped, ordering); return order_tracker; }); ConnectedClientWithASurface::SetUp(); server.the_cursor()->hide(); streams.emplace_back( std::make_unique(mir_surface_get_buffer_stream(surface), geom::Point{0,0}, surface_size)); int const additional_streams{3}; for (auto i = 0; i < additional_streams; i++) { geom::Size size{30 * i + 1, 40* i + 1}; geom::Point position{i * 2, i * 3}; streams.emplace_back(std::make_unique(connection, geom::Rectangle{position, size})); } } void TearDown() override { streams.clear(); ConnectedClientWithASurface::TearDown(); } std::shared_ptr ordering; std::shared_ptr order_tracker{nullptr}; std::vector> streams; }; } TEST_F(BufferStreamArrangement, arrangements_are_applied) { using namespace testing; std::vector infos(streams.size()); auto i = 0u; for (auto const& stream : streams) { infos[i++] = MirBufferStreamInfo{ stream->handle(), stream->position().x.as_int(), stream->position().y.as_int()}; } auto change_spec = mir_connection_create_spec_for_changes(connection); mir_surface_spec_set_streams(change_spec, infos.data(), infos.size()); mir_surface_apply_spec(surface, change_spec); mir_surface_spec_release(change_spec); std::vector positions; i = 0; for (auto& info : infos) { positions.emplace_back( geom::Displacement{info.displacement_x, info.displacement_y}, streams[i++]->size()); } //check that the compositor rendered correctly using namespace std::literals::chrono_literals; EXPECT_TRUE(ordering->wait_for_positions_within(positions, 5s)) << "timed out waiting to see the compositor post the streams in the right arrangement"; } ./tests/include/0000755000015600001650000000000012676616125013676 5ustar jenkinsjenkins./tests/include/mir/0000755000015600001650000000000012676616124014464 5ustar jenkinsjenkins./tests/include/mir/test/0000755000015600001650000000000012676616160015443 5ustar jenkinsjenkins./tests/include/mir/test/fake_clock.h0000644000015600001650000000423712676616125017704 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_FAKE_CLOCK_H_ #define MIR_TEST_FAKE_CLOCK_H_ #include #include #include namespace mir { namespace test { /** * @brief An invasive time source for Mocks/Stubs/Fakes that depend on timing */ class FakeClock { public: typedef std::chrono::nanoseconds duration; typedef duration::rep rep; typedef duration::period period; typedef std::chrono::time_point time_point; static constexpr bool is_steady = false; time_point now() const; FakeClock(); /** * \brief Advance the fake clock * \note Advancing by a negative duration will move the clock backwards */ template void advance_time(std::chrono::duration by) { advance_time_ns(std::chrono::duration_cast(by)); } /** * \brief Register an event callback when the time is changed * \param cb Function to call when the time is changed. * This function is called with the new time. * If the function returns false, it will no longer be called * on subsequent time changes. */ void register_time_change_callback(std::function cb); private: void advance_time_ns(std::chrono::nanoseconds by); std::chrono::nanoseconds current_time; std::list> callbacks; }; } } #endif // MIR_TEST_FAKE_CLOCK_H_ ./tests/include/mir/test/current_thread_name.h0000644000015600001650000000156512676616125021635 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_CURRENT_THREAD_NAME_H_ #define MIR_TEST_CURRENT_THREAD_NAME_H_ #include namespace mir { namespace test { std::string current_thread_name(); } } #endif ./tests/include/mir/test/as_render_target.h0000644000015600001650000000235012676616125021125 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_AS_RENDER_TARGET_H_ #define MIR_TEST_AS_RENDER_TARGET_H_ #include "mir/graphics/display_buffer.h" #include "mir/renderer/gl/render_target.h" #include namespace mir { namespace test { inline auto as_render_target(graphics::DisplayBuffer& display_buffer) { auto const render_target = dynamic_cast(display_buffer.native_display_buffer()); if (!render_target) throw std::logic_error{"DisplayBuffer doesn't support GL rendering"}; return render_target; } } } #endif ./tests/include/mir/test/test_protobuf_server.h0000644000015600001650000000270312676616125022104 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Alan Griffiths */ #ifndef MIR_TEST_TEST_PROTOBUF_SERVER_H_ #define MIR_TEST_TEST_PROTOBUF_SERVER_H_ #include namespace mir { namespace frontend { namespace detail { class DisplayServer; } class Connector; class ConnectorReport; } namespace test { struct TestProtobufServer { TestProtobufServer( std::string const& socket_name, std::shared_ptr const& tool); TestProtobufServer( std::string const& socket_name, std::shared_ptr const& tool, std::shared_ptr const& report); // "Server" side std::shared_ptr const comm; }; } } #endif /* MIR_TEST_TEST_PROTOBUF_SERVER_H_ */ ./tests/include/mir/test/fd_utils.h0000644000015600001650000000370512676616125017433 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_FD_UTILS_H_ #define MIR_TEST_FD_UTILS_H_ #include "mir/fd.h" #include #include #include namespace mir { namespace test { ::testing::AssertionResult std_call_succeeded(int retval); ::testing::AssertionResult fd_is_readable(mir::Fd const& fd); template ::testing::AssertionResult fd_becomes_readable(mir::Fd const& fd, std::chrono::duration timeout) { int timeout_ms = std::chrono::duration_cast(timeout).count(); pollfd readable; readable.events = POLLIN; readable.fd = fd; auto result = std_call_succeeded(poll(&readable, 1, timeout_ms)); if (result) { if (readable.revents & POLLERR) { return ::testing::AssertionFailure() << "error condition on fd"; } if (readable.revents & POLLNVAL) { return ::testing::AssertionFailure() << "fd is invalid"; } if (!(readable.revents & POLLIN)) { return ::testing::AssertionFailure() << "fd is not readable"; } return ::testing::AssertionSuccess(); } return result; } } } #endif // MIR_TEST_FD_UTILS_H_ ./tests/include/mir/test/input_devices_matcher.h0000644000015600001650000000341412676616157022170 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_INPUT_DEVICES_MATCHER_H_ #define MIR_TEST_INPUT_DEVICES_MATCHER_H_ #include "mir/input/device.h" #include #include #include namespace mir { namespace protobuf { static void PrintTo(mir::protobuf::InputDeviceInfo const &, ::std::ostream*) __attribute__ ((unused)); void PrintTo(mir::protobuf::InputDeviceInfo const&, ::std::ostream*) {} } namespace test { MATCHER_P(InputDevicesMatch, devices, "") { using std::begin; using std::end; if (distance(begin(devices), end(devices)) != distance(begin(arg), end(arg))) return false; return std::equal(begin(arg), end(arg), begin(devices), [](auto const& lhs, std::shared_ptr const& rhs) { return lhs.id() == rhs->id() && lhs.name() == rhs->name() && lhs.unique_id() == rhs->unique_id() && lhs.capabilities() == rhs->capabilities().value(); }); } } } #endif ./tests/include/mir/test/test_protobuf_client.h0000644000015600001650000000633112676616125022055 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Alan Griffiths */ #ifndef MIR_TEST_TEST_CLIENT_H_ #define MIR_TEST_TEST_CLIENT_H_ #include "mir_protobuf.pb.h" #include "mir/test/wait_condition.h" #include "src/client/rpc/mir_display_server.h" #include #include #include #include #include namespace mir { namespace client { namespace rpc {class MirBasicRpcChannel;}} namespace dispatch { class ThreadedDispatcher; } namespace test { namespace doubles { class MockRpcReport; } struct TestProtobufClient { TestProtobufClient(std::string socket_file, int timeout_ms); std::shared_ptr rpc_report; std::shared_ptr channel; std::shared_ptr eventloop; mir::client::rpc::DisplayServer display_server; mir::protobuf::ConnectParameters connect_parameters; mir::protobuf::SurfaceParameters surface_parameters; mir::protobuf::Surface surface; mir::protobuf::Void ignored; mir::protobuf::Connection connection; mir::protobuf::DisplayConfiguration disp_config; mir::protobuf::DisplayConfiguration disp_config_response; mir::protobuf::PromptSessionParameters prompt_session_parameters; mir::protobuf::Void prompt_session; MOCK_METHOD0(connect_done, void()); MOCK_METHOD0(create_surface_done, void()); MOCK_METHOD0(next_buffer_done, void()); MOCK_METHOD0(disconnect_done, void()); MOCK_METHOD0(display_configure_done, void()); void on_connect_done(); void on_create_surface_done(); void on_next_buffer_done(); void on_disconnect_done(); void on_configure_display_done(); void connect(); void disconnect(); void create_surface(); void next_buffer(); void configure_display(); void wait_for_connect_done(); void wait_for_create_surface(); void wait_for_next_buffer(); void wait_for_disconnect_done(); void wait_for_configure_display_done(); void wait_for_surface_count(int count); void wait_for(std::function const& predicate, std::string const& error_message); void signal_condition(bool& condition); void reset_condition(bool& condition); int const maxwait; bool connect_done_called; bool create_surface_called; bool next_buffer_called; bool exchange_buffer_called; bool disconnect_done_called; bool configure_display_done_called; int create_surface_done_count; std::mutex mutable guard; std::condition_variable cv; }; } } #endif /* MIR_TEST_TEST_CLIENT_H_ */ ./tests/include/mir/test/stub_server_tool.h0000644000015600001650000001021012676616125021207 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Alan Griffiths */ #ifndef MIR_TEST_STUB_SERVER_TOOL_H_ #define MIR_TEST_STUB_SERVER_TOOL_H_ #include "src/server/frontend/display_server.h" #include #include #include namespace mir { namespace test { struct StubServerTool : doubles::StubDisplayServer { virtual void create_surface( mir::protobuf::SurfaceParameters const* request, mir::protobuf::Surface* response, google::protobuf::Closure* done) override { response->mutable_id()->set_value(13); // TODO distinct numbers & tracking response->set_width(request->width()); response->set_height(request->height()); response->set_pixel_format(request->pixel_format()); response->mutable_buffer()->set_buffer_id(22); std::lock_guard lock(guard); surf_name = request->surface_name(); wait_condition.notify_one(); done->Run(); } virtual void next_buffer( mir::protobuf::SurfaceId const* /*request*/, mir::protobuf::Buffer* response, google::protobuf::Closure* done) override { response->set_buffer_id(22); std::lock_guard lock(guard); //FIXME: huh? What's the condition here? wait_condition.notify_one(); done->Run(); } virtual void release_surface( mir::protobuf::SurfaceId const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* done) override { done->Run(); } virtual void connect( mir::protobuf::ConnectParameters const* request, mir::protobuf::Connection* connect_msg, google::protobuf::Closure* done) override { { std::lock_guard lock(guard); app_name = request->application_name(); } // If you check out MirConnection::connected either the platform and display_configuration // have to be set or the error has to be set, otherwise we die and fail to callback. // // Since setting the error to "" means that mir_connection_is_valid will return false // with a confusing non-error-message, instead clear the error and set the platform et al. connect_msg->clear_error(); connect_msg->set_allocated_platform(new mir::protobuf::Platform{}); connect_msg->set_allocated_display_configuration(new mir::protobuf::DisplayConfiguration{}); done->Run(); } virtual void disconnect( mir::protobuf::Void const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* done) override { std::lock_guard lock(guard); //FIXME: huh? What's the condition here? wait_condition.notify_one(); done->Run(); } virtual void configure_display( mir::protobuf::DisplayConfiguration const*, mir::protobuf::DisplayConfiguration*, google::protobuf::Closure* done) override { done->Run(); } std::string application_name() const { std::lock_guard lock(guard); return app_name; } std::string surface_name() const { std::lock_guard lock(guard); return surf_name; } std::mutex mutable guard; std::string surf_name; std::condition_variable wait_condition; std::string app_name; }; } } #endif /* MIR_TEST_STUB_SERVER_TOOL_H_ */ ./tests/include/mir/test/doubles/0000755000015600001650000000000012676616160017100 5ustar jenkinsjenkins./tests/include/mir/test/doubles/mock_buffer_bundle.h0000644000015600001650000000452712676616125023075 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_BUFFER_BUNDLE_H_ #define MIR_TEST_DOUBLES_MOCK_BUFFER_BUNDLE_H_ #include "src/server/compositor/buffer_bundle.h" #include namespace mir { namespace test { namespace doubles { struct MockBufferBundle : public compositor::BufferBundle { public: MockBufferBundle() { ON_CALL(*this, buffers_ready_for_compositor(testing::_)) .WillByDefault(testing::Return(1)); ON_CALL(*this, properties()) .WillByDefault(testing::Return( graphics::BufferProperties{geometry::Size{1,1}, mir_pixel_format_abgr_8888, graphics::BufferUsage::hardware})); } MOCK_METHOD1(client_acquire, void(std::function)); MOCK_METHOD1(client_release, void(graphics::Buffer*)); MOCK_METHOD1(compositor_acquire, std::shared_ptr(void const*)); MOCK_METHOD1(compositor_release, void(std::shared_ptr const&)); MOCK_METHOD0(snapshot_acquire, std::shared_ptr()); MOCK_METHOD1(snapshot_release, void(std::shared_ptr const&)); MOCK_METHOD1(allow_framedropping, void(bool)); MOCK_CONST_METHOD0(properties, graphics::BufferProperties()); MOCK_METHOD0(force_client_abort, void()); MOCK_METHOD0(force_requests_to_complete, void()); MOCK_METHOD1(resize, void(const geometry::Size &)); MOCK_METHOD0(drop_old_buffers, void()); MOCK_CONST_METHOD1(buffers_ready_for_compositor, int(void const*)); int buffers_free_for_client() const override { return 1; } void drop_client_requests() override {} }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_BUFFER_BUNDLE_H_ */ ./tests/include/mir/test/doubles/stub_renderable_list_compositor.h0000644000015600001650000000235112676616125025724 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_RENDERABLE_LIST_COMPOSITOR_H_ #define MIR_TEST_DOUBLES_STUB_RENDERABLE_LIST_COMPOSITOR_H_ #include "src/platforms/android/server/hwc_fallback_gl_renderer.h" namespace mir { namespace test { namespace doubles { struct StubRenderableListCompositor : public graphics::android::RenderableListCompositor { void render( graphics::RenderableList const&, geometry::Displacement, graphics::android::SwappingGLContext const&) const { } }; } } } #endif // MIR_TEST_DOUBLES_STUB_RENDERABLE_LIST_COMPOSITOR_H_ ./tests/include/mir/test/doubles/null_video_devices.h0000644000015600001650000000220712676616125023115 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_VIDEO_DEVICES_H_ #define MIR_TEST_DOUBLES_NULL_VIDEO_DEVICES_H_ #include "src/platforms/mesa/video_devices.h" namespace mir { namespace test { namespace doubles { class NullVideoDevices : public graphics::mesa::VideoDevices { public: void register_change_handler( graphics::EventHandlerRegister&, std::function const&) { } }; } } } #endif /* MIR_TEST_DOUBLES_NULL_VIDEO_DEVICES_H_ */ ./tests/include/mir/test/doubles/mock_gl.h0000644000015600001650000001162012676616125020665 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_GL_H_ #define MIR_TEST_DOUBLES_MOCK_GL_H_ #include #include namespace mir { namespace test { namespace doubles { class MockGL { public: MockGL(); ~MockGL(); void provide_gles_extensions(); // Please keep these ordered by name MOCK_METHOD1(glActiveTexture, void(GLenum)); MOCK_METHOD2(glAttachShader, void(GLuint, GLuint)); MOCK_METHOD2(glBindBuffer, void(GLenum, GLuint)); MOCK_METHOD2(glBindFramebuffer, void(GLenum, GLuint)); MOCK_METHOD2(glBindRenderbuffer, void(GLenum, GLuint)); MOCK_METHOD2(glBindTexture, void(GLenum, GLuint)); MOCK_METHOD2(glBlendFunc, void(GLenum, GLenum)); MOCK_METHOD4(glBlendFuncSeparate, void(GLenum, GLenum, GLenum, GLenum)); MOCK_METHOD4(glBufferData, void(GLenum, GLsizeiptr, const GLvoid *, GLenum)); MOCK_METHOD1(glCheckFramebufferStatus, GLenum(GLenum)); MOCK_METHOD1(glClear, void(GLbitfield)); MOCK_METHOD4(glClearColor, void(GLclampf, GLclampf, GLclampf, GLclampf)); MOCK_METHOD4(glColorMask, void(GLboolean, GLboolean, GLboolean, GLboolean)); MOCK_METHOD1(glCompileShader, void(GLuint)); MOCK_METHOD0(glCreateProgram, GLuint()); MOCK_METHOD1(glCreateShader, GLuint(GLenum)); MOCK_METHOD2(glDeleteBuffers, void(GLsizei, const GLuint *)); MOCK_METHOD2(glDeleteFramebuffers, void(GLsizei, const GLuint *)); MOCK_METHOD2(glDeleteRenderbuffers, void(GLsizei, const GLuint *)); MOCK_METHOD1(glDeleteProgram, void(GLuint)); MOCK_METHOD1(glDeleteShader, void(GLuint)); MOCK_METHOD2(glDeleteTextures, void(GLsizei, const GLuint *)); MOCK_METHOD1(glDisable, void(GLenum)); MOCK_METHOD1(glDisableVertexAttribArray, void(GLuint)); MOCK_METHOD3(glDrawArrays, void(GLenum, GLint, GLsizei)); MOCK_METHOD1(glEnable, void(GLenum)); MOCK_METHOD1(glEnableVertexAttribArray, void(GLuint)); MOCK_METHOD0(glFinish, void()); MOCK_METHOD4(glFramebufferRenderbuffer, void(GLenum, GLenum, GLenum, GLuint)); MOCK_METHOD5(glFramebufferTexture2D, void(GLenum, GLenum, GLenum, GLuint, GLint)); MOCK_METHOD2(glGenBuffers, void(GLsizei, GLuint *)); MOCK_METHOD2(glGenFramebuffers, void(GLsizei, GLuint *)); MOCK_METHOD2(glGenRenderbuffers, void(GLsizei, GLuint*)); MOCK_METHOD2(glGenTextures, void(GLsizei, GLuint *)); MOCK_METHOD2(glGetAttribLocation, GLint(GLuint, const GLchar *)); MOCK_METHOD0(glGetError, GLenum()); MOCK_METHOD2(glGetIntegerv, void(GLenum, GLint*)); MOCK_METHOD4(glGetProgramInfoLog, void(GLuint, GLsizei, GLsizei *, GLchar *)); MOCK_METHOD3(glGetProgramiv, void(GLuint, GLenum, GLint *)); MOCK_METHOD4(glGetShaderInfoLog, void(GLuint, GLsizei, GLsizei *, GLchar *)); MOCK_METHOD3(glGetShaderiv, void(GLuint, GLenum, GLint *)); MOCK_METHOD1(glGetString, const GLubyte*(GLenum)); MOCK_METHOD2(glGetUniformLocation, GLint(GLuint, const GLchar *)); MOCK_METHOD1(glLinkProgram, void(GLuint)); MOCK_METHOD2(glPixelStorei, void(GLenum, GLint)); MOCK_METHOD7(glReadPixels, void(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid*)); MOCK_METHOD4(glRenderbufferStorage, void(GLenum, GLenum, GLsizei, GLsizei)); MOCK_METHOD4(glShaderSource, void(GLuint, GLsizei, const GLchar * const *, const GLint *)); MOCK_METHOD9(glTexImage2D, void(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum,const GLvoid*)); MOCK_METHOD3(glTexParameteri, void(GLenum, GLenum, GLenum)); MOCK_METHOD2(glUniform1f, void(GLint, GLfloat)); MOCK_METHOD3(glUniform2f, void(GLint, GLfloat, GLfloat)); MOCK_METHOD2(glUniform1i, void(GLint, GLint)); MOCK_METHOD4(glUniformMatrix4fv, void(GLuint, GLsizei, GLboolean, const GLfloat *)); MOCK_METHOD1(glUseProgram, void(GLuint)); MOCK_METHOD6(glVertexAttribPointer, void(GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid *)); MOCK_METHOD4(glViewport, void(GLint, GLint, GLsizei, GLsizei)); MOCK_METHOD1(glGenerateMipmap, void(GLenum target)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_GL_H_ */ ./tests/include/mir/test/doubles/mock_screencast.h0000644000015600001650000000265012676616125022420 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_SCREENCAST_H_ #define MIR_TEST_DOUBLES_MOCK_SCREENCAST_H_ #include "mir/frontend/screencast.h" #include namespace mir { namespace test { namespace doubles { class MockScreencast : public frontend::Screencast { public: MOCK_METHOD3(create_session, frontend::ScreencastSessionId( geometry::Rectangle const&, geometry::Size const&, MirPixelFormat)); MOCK_METHOD1(destroy_session, void(frontend::ScreencastSessionId)); MOCK_METHOD1(capture, std::shared_ptr( frontend::ScreencastSessionId)); }; } } } #endif /* MIR_TEST_DOUBLES_NULL_SCREENCAST_H_ */ ./tests/include/mir/test/doubles/mock_hwc_composer_device_1.h0000644000015600001650000001055412676616125024517 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_HWC_COMPOSER_DEVICE_1_H_ #define MIR_TEST_DOUBLES_MOCK_HWC_COMPOSER_DEVICE_1_H_ #include #include namespace mir { namespace test { namespace doubles { class MockHWCComposerDevice1 : public hwc_composer_device_1 { public: MockHWCComposerDevice1() { using namespace testing; //have mocked hw start with the external display off ON_CALL(*this, getDisplayConfigs_interface(_,HWC_DISPLAY_EXTERNAL,_,_)) .WillByDefault(Return(-1)); common.version = HWC_DEVICE_API_VERSION_1_1; registerProcs = hook_registerProcs; eventControl = hook_eventControl; set = hook_set; prepare = hook_prepare; blank = hook_blank; getDisplayConfigs = hook_getDisplayConfigs; getDisplayAttributes = hook_getDisplayAttributes; } static void hook_registerProcs(struct hwc_composer_device_1* mock_hwc, hwc_procs_t const* procs) { MockHWCComposerDevice1* mocker = static_cast(mock_hwc); mocker->registerProcs_interface(mock_hwc, procs); } static int hook_eventControl(struct hwc_composer_device_1* mock_hwc, int disp, int event, int enabled) { MockHWCComposerDevice1* mocker = static_cast(mock_hwc); return mocker->eventControl_interface(mock_hwc, disp, event, enabled); } static int hook_set(struct hwc_composer_device_1 *mock_hwc, size_t numDisplays, hwc_display_contents_1_t** displays) { MockHWCComposerDevice1* mocker = static_cast(mock_hwc); return mocker->set_interface(mock_hwc, numDisplays, displays); } static int hook_prepare(struct hwc_composer_device_1 *mock_hwc, size_t numDisplays, hwc_display_contents_1_t** displays) { MockHWCComposerDevice1* mocker = static_cast(mock_hwc); return mocker->prepare_interface(mock_hwc, numDisplays, displays); } static int hook_blank(struct hwc_composer_device_1 *mock_hwc, int disp, int blank) { MockHWCComposerDevice1* mocker = static_cast(mock_hwc); return mocker->blank_interface(mock_hwc, disp, blank); } static int hook_getDisplayConfigs(struct hwc_composer_device_1* mock_hwc, int disp, uint32_t* configs, size_t* numConfigs) { MockHWCComposerDevice1* mocker = static_cast(mock_hwc); return mocker->getDisplayConfigs_interface(mock_hwc, disp, configs, numConfigs); } static int hook_getDisplayAttributes(struct hwc_composer_device_1* mock_hwc, int disp, uint32_t config, const uint32_t* attributes, int32_t* values) { MockHWCComposerDevice1* mocker = static_cast(mock_hwc); return mocker->getDisplayAttributes_interface(mock_hwc, disp, config, attributes, values); } MOCK_METHOD2(registerProcs_interface, void(struct hwc_composer_device_1*, hwc_procs_t const*)); MOCK_METHOD4(eventControl_interface, int(struct hwc_composer_device_1* dev, int disp, int event, int enabled)); MOCK_METHOD3(set_interface, int(struct hwc_composer_device_1 *, size_t, hwc_display_contents_1_t**)); MOCK_METHOD3(prepare_interface, int(struct hwc_composer_device_1 *, size_t, hwc_display_contents_1_t**)); MOCK_METHOD3(blank_interface, int(struct hwc_composer_device_1 *, int, int)); MOCK_METHOD4(getDisplayConfigs_interface, int(struct hwc_composer_device_1*, int, uint32_t*, size_t*)); MOCK_METHOD5(getDisplayAttributes_interface, int(struct hwc_composer_device_1*, int, uint32_t, const uint32_t*, int32_t*)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_HWC_COMPOSER_DEVICE_1_H_ */ ./tests/include/mir/test/doubles/mock_timer.h0000644000015600001650000000262612676616125021411 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Alberto Aguirre */ #ifndef MIR_TEST_DOUBLES_MOCK_TIMER_H_ #define MIR_TEST_DOUBLES_MOCK_TIMER_H_ #include "mir/time/alarm_factory.h" #include "mir/test/fake_clock.h" #include namespace mir { namespace test { namespace doubles { class FakeTimer : public mir::time::AlarmFactory { public: FakeTimer(std::shared_ptr const& clock); std::unique_ptr create_alarm(std::function const& callback) override; std::unique_ptr create_alarm(std::shared_ptr const& callback) override; private: std::shared_ptr const clock; }; } } } #endif // MIR_TEST_DOUBLES_MOCK_TIMER_H_ ./tests/include/mir/test/doubles/mock_scene_session.h0000644000015600001650000000576612676616125023141 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_SCENE_SESSION_H_ #define MIR_TEST_DOUBLES_MOCK_SCENE_SESSION_H_ #include "mir/scene/session.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/graphics/display_configuration.h" #include namespace mir { namespace test { namespace doubles { struct MockSceneSession : public scene::Session { MOCK_METHOD2(create_surface, frontend::SurfaceId( scene::SurfaceCreationParameters const&, std::shared_ptr const&)); MOCK_METHOD1(destroy_surface, void(frontend::SurfaceId)); MOCK_CONST_METHOD1(get_surface, std::shared_ptr(frontend::SurfaceId)); MOCK_CONST_METHOD1(surface, std::shared_ptr(frontend::SurfaceId)); MOCK_CONST_METHOD1(surface_after, std::shared_ptr(std::shared_ptr const&)); MOCK_METHOD1(take_snapshot, void(scene::SnapshotCallback const&)); MOCK_CONST_METHOD0(default_surface, std::shared_ptr()); MOCK_CONST_METHOD0(name, std::string()); MOCK_CONST_METHOD0(process_id, pid_t()); MOCK_METHOD0(force_requests_to_complete, void()); MOCK_METHOD0(hide, void()); MOCK_METHOD0(show, void()); MOCK_METHOD1(send_display_config, void(graphics::DisplayConfiguration const&)); MOCK_METHOD1(send_input_device_change, void(std::vector> const&)); MOCK_METHOD3(configure_surface, int(frontend::SurfaceId, MirSurfaceAttrib, int)); MOCK_METHOD1(set_lifecycle_state, void(MirLifecycleState state)); MOCK_METHOD0(start_prompt_session, void()); MOCK_METHOD0(stop_prompt_session, void()); MOCK_METHOD0(suspend_prompt_session, void()); MOCK_METHOD0(resume_prompt_session, void()); MOCK_CONST_METHOD1(get_buffer_stream, std::shared_ptr(frontend::BufferStreamId)); MOCK_METHOD1(destroy_buffer_stream, void(frontend::BufferStreamId)); MOCK_METHOD1(create_buffer_stream, frontend::BufferStreamId(graphics::BufferProperties const&)); MOCK_METHOD2(configure_streams, void(scene::Surface&, std::vector const&)); MOCK_METHOD1(destroy_surface, void (std::weak_ptr const&)); }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_MOCK_SESSION_H_ ./tests/include/mir/test/doubles/mock_buffer.h0000644000015600001650000000463512676616125021544 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #ifndef MIR_TEST_DOUBLES_MOCK_BUFFER_H_ #define MIR_TEST_DOUBLES_MOCK_BUFFER_H_ #include "mir/graphics/buffer_basic.h" #include "mir/geometry/size.h" #include "mir/graphics/buffer_id.h" #include #include namespace mir { namespace test { namespace doubles { struct MockBuffer : public graphics::Buffer, public graphics::NativeBufferBase { public: MockBuffer() { using namespace testing; ON_CALL(*this, native_buffer_base()) .WillByDefault(Return(this)); } MockBuffer(geometry::Size size, geometry::Stride s, MirPixelFormat pf) : MockBuffer{} { using namespace testing; ON_CALL(*this, size()) .WillByDefault(Return(size)); ON_CALL(*this, stride()) .WillByDefault(Return(s)); ON_CALL(*this, pixel_format()) .WillByDefault(Return(pf)); ON_CALL(*this, id()) .WillByDefault(Return(graphics::BufferID{4})); ON_CALL(*this, native_buffer_handle()) .WillByDefault(Return(std::shared_ptr())); } MOCK_CONST_METHOD0(size, geometry::Size()); MOCK_CONST_METHOD0(stride, geometry::Stride()); MOCK_CONST_METHOD0(pixel_format, MirPixelFormat()); MOCK_CONST_METHOD0(native_buffer_handle, std::shared_ptr()); MOCK_CONST_METHOD0(id, graphics::BufferID()); MOCK_METHOD2(write, void(unsigned char const*, size_t)); MOCK_METHOD1(read, void(std::function const&)); MOCK_METHOD0(native_buffer_base, graphics::NativeBufferBase*()); MOCK_METHOD0(used_as_texture, void()); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_BUFFER_H_ ./tests/include/mir/test/doubles/mock_buffer_registrar.h0000644000015600001650000000263512676616125023624 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_BUFFER_REGISTRAR_H_ #define MIR_TEST_DOUBLES_MOCK_BUFFER_REGISTRAR_H_ #include "src/platforms/android/client/buffer_registrar.h" #include namespace mir { namespace geometry { class Rectangle; } namespace test { namespace doubles { struct MockBufferRegistrar : public client::android::BufferRegistrar { ~MockBufferRegistrar() noexcept {} MOCK_CONST_METHOD2(register_buffer, std::shared_ptr(MirBufferPackage const&, MirPixelFormat)); MOCK_METHOD2(secure_for_cpu, std::shared_ptr( std::shared_ptr const&, geometry::Rectangle const)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_BUFFER_REGISTRAR_H_ */ ./tests/include/mir/test/doubles/mock_input_sender.h0000644000015600001650000000210012676616125022753 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_SENDER_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_SENDER_H_ #include "mir/input/input_sender.h" #include namespace mir { namespace test { namespace doubles { struct MockInputSender : mir::input::InputSender { MOCK_METHOD2(send_event, void(MirEvent const& ev, std::shared_ptr const& channel)); }; } } } #endif ./tests/include/mir/test/doubles/mock_message_sender.h0000644000015600001650000000217312676616125023252 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_DOUBLES_MOCK_MESSAGE_SENDER_H_ #define MIR_TEST_DOUBLES_MOCK_MESSAGE_SENDER_H_ #include "src/server/frontend/message_sender.h" #include namespace mir { namespace test { namespace doubles { class MockMessageSender : public frontend::MessageSender { public: MOCK_METHOD3(send, void(char const*, size_t, frontend::FdSets const &)); }; } } } #endif //MIR_TEST_DOUBLES_MOCK_MESSAGE_SENDER_H_ ./tests/include/mir/test/doubles/stub_gl_buffer_allocator.h0000644000015600001650000000225112676616125024302 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_GL_BUFFER_ALLOCATOR_H_ #define MIR_TEST_DOUBLES_STUB_GL_BUFFER_ALLOCATOR_H_ #include "stub_buffer_allocator.h" #include "stub_gl_buffer.h" namespace mir { namespace test { namespace doubles { struct StubGLBufferAllocator : public StubBufferAllocator { std::shared_ptr alloc_buffer( graphics::BufferProperties const& properties) override { return std::make_shared(properties); } }; } } } #endif ./tests/include/mir/test/doubles/null_client_buffer.h0000644000015600001650000000400212676616157023114 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_CLIENT_BUFFER_H_ #define MIR_TEST_DOUBLES_NULL_CLIENT_BUFFER_H_ #include "mir/client_buffer.h" namespace mir { namespace test { namespace doubles { class NullClientBuffer : public client::ClientBuffer { public: NullClientBuffer() : NullClientBuffer(geometry::Size()) {} NullClientBuffer(geometry::Size sz) : sz(sz) { } std::shared_ptr secure_for_cpu_write() { return std::make_shared(); } geometry::Size size() const { return sz; } geometry::Stride stride() const { return geometry::Stride(); } MirPixelFormat pixel_format() const { return mir_pixel_format_invalid; } uint32_t age() const { return 0; } void increment_age() {} void mark_as_submitted() {} void update_from(MirBufferPackage const&) {} void fill_update_msg(MirBufferPackage&) {} std::shared_ptr native_buffer_handle() const { return nullptr; } geometry::Size sz; MirNativeBuffer* as_mir_native_buffer() const { return nullptr; } void set_fence(MirNativeFence*, MirBufferAccess) {} MirNativeFence* get_fence() const { return nullptr; } bool wait_fence(MirBufferAccess, std::chrono::nanoseconds) { return true; } }; } } } #endif /* MIR_TEST_DOUBLES_NULL_CLIENT_BUFFER_H_ */ ./tests/include/mir/test/doubles/mock_fence.h0000644000015600001650000000220412676616125021341 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_FENCE_H_ #define MIR_TEST_DOUBLES_MOCK_FENCE_H_ #include "fence.h" #include namespace mir { namespace test { namespace doubles { struct MockFence : public graphics::android::Fence { MOCK_METHOD0(wait, void()); MOCK_METHOD1(merge_with, void(graphics::android::NativeFence&)); MOCK_CONST_METHOD0(copy_native_handle, graphics::android::NativeFence()); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_FENCE_H_ */ ./tests/include/mir/test/doubles/mock_surface_stack.h0000644000015600001650000000271312676616125023103 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_MOCK_SURFACE_STACK_H_ #define MIR_TEST_DOUBLES_MOCK_SURFACE_STACK_H_ #include "mir/shell/surface_stack.h" #include "mir/scene/surface_creation_parameters.h" #include namespace mir { namespace test { namespace doubles { struct MockSurfaceStack : public shell::SurfaceStack { MOCK_METHOD1(raise, void(std::weak_ptr const&)); MOCK_METHOD1(raise, void(SurfaceSet const&)); MOCK_METHOD2(add_surface, void(std::shared_ptr const&, input::InputReceptionMode new_mode)); MOCK_METHOD1(remove_surface, void(std::weak_ptr const& surface)); MOCK_CONST_METHOD1(surface_at, std::shared_ptr(geometry::Point)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_SURFACE_STACK_H_ */ ./tests/include/mir/test/doubles/stub_gl_buffer.h0000644000015600001650000000220612676616125022242 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_GL_BUFFER_H_ #define MIR_TEST_DOUBLES_STUB_GL_BUFFER_H_ #include "stub_buffer.h" #include "mir/renderer/gl/texture_source.h" namespace mir { namespace test { namespace doubles { class StubGLBuffer : public StubBuffer, public renderer::gl::TextureSource { public: using StubBuffer::StubBuffer; void gl_bind_to_texture() {} void bind() {} void secure_for_render() {} }; } } } #endif ./tests/include/mir/test/doubles/mock_input_device_registry.h0000644000015600001650000000222312676616125024670 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_DEVICE_REGISTRY_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_DEVICE_REGISTRY_H_ #include "mir/input/input_device_registry.h" #include namespace mir { namespace test { namespace doubles { struct MockInputDeviceRegistry : input::InputDeviceRegistry { MOCK_METHOD1(add_device,void (std::shared_ptr const&)); MOCK_METHOD1(remove_device, void(std::shared_ptr const&)); }; } } } #endif ./tests/include/mir/test/doubles/stub_renderer.h0000644000015600001650000000302612676616125022116 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_TEST_DOUBLES_STUB_RENDERER_H_ #define MIR_TEST_DOUBLES_STUB_RENDERER_H_ #include "mir/compositor/renderer.h" #include "mir/graphics/renderable.h" #include namespace mir { namespace test { namespace doubles { class StubRenderer : public compositor::Renderer { public: void set_viewport(geometry::Rectangle const&) override { } void set_rotation(float) override { } void render(graphics::RenderableList const& renderables) const override { for (auto const& r : renderables) r->buffer(); // We need to consume a buffer to unblock client tests // Yield to reduce runtime under valgrind std::this_thread::yield(); } void suspend() override { } }; } // namespace doubles } // namespace test } // namespace mir #endif // MIR_TEST_DOUBLES_STUB_RENDERER_H_ ./tests/include/mir/test/doubles/mock_event_sink_factory.h0000644000015600001650000000251712676616125024164 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_DOUBLES_MOCK_EVENT_SINK_FACTORY_H_ #define MIR_TEST_DOUBLES_MOCK_EVENT_SINK_FACTORY_H_ #include "src/server/frontend/event_sink_factory.h" namespace mir { namespace test { namespace doubles { class MockEventSink; class MockEventSinkFactory : public frontend::EventSinkFactory { public: MockEventSinkFactory(); std::shared_ptr the_mock_sink(); std::unique_ptr create_sink(std::shared_ptr const&) override; private: std::shared_ptr const underlying_sink; }; } } } #endif //MIR_TEST_DOUBLES_MOCK_EVENT_SINK_FACTORY_H_ ./tests/include/mir/test/doubles/stub_input_surface.h0000644000015600001650000000342412676616125023161 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_STUB_INPUT_SURFACE_H_ #define MIR_TEST_DOUBLES_STUB_INPUT_SURFACE_H_ #include "mir/input/surface.h" #include namespace mir { namespace test { namespace doubles { struct StubInputSurface : public mir::input::Surface { StubInputSurface(std::shared_ptr const& channel) : channel(channel) { } StubInputSurface() : channel(nullptr) { } std::shared_ptr input_channel() const { return channel; } mir::input::InputReceptionMode reception_mode() const { return mir::input::InputReceptionMode::normal; } void consume(MirEvent const&) override {} std::string name() const { return {}; } mir::geometry::Rectangle input_bounds() const override { return {{},{}}; } bool input_area_contains(mir::geometry::Point const&) const { return false; } std::shared_ptr cursor_image() const { return nullptr; } std::shared_ptr const channel; }; } } } #endif /* MIR_TEST_DOUBLES_STUB_INPUT_SURFACE_H_ */ ./tests/include/mir/test/doubles/stub_buffer.h0000644000015600001650000001065512676616125021567 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_BUFFER_H_ #define MIR_TEST_DOUBLES_STUB_BUFFER_H_ #include "mir/graphics/buffer_basic.h" #include "mir/graphics/buffer_properties.h" #include "mir/geometry/size.h" #include "mir/graphics/buffer_id.h" #include #include namespace mir { namespace test { namespace doubles { class StubBuffer : public graphics::BufferBasic, public graphics::NativeBufferBase { public: StubBuffer() : StubBuffer{ create_native_buffer(), graphics::BufferProperties{ geometry::Size{}, mir_pixel_format_abgr_8888, graphics::BufferUsage::hardware}, geometry::Stride{}} { } StubBuffer(geometry::Size const& size) : StubBuffer{ create_native_buffer(), graphics::BufferProperties{ size, mir_pixel_format_abgr_8888, graphics::BufferUsage::hardware}, geometry::Stride{}} { } StubBuffer(std::shared_ptr const& native_buffer, geometry::Size const& size) : StubBuffer{ native_buffer, graphics::BufferProperties{ size, mir_pixel_format_abgr_8888, graphics::BufferUsage::hardware}, geometry::Stride{}} { } StubBuffer(std::shared_ptr const& native_buffer) : StubBuffer{native_buffer, {}} { } StubBuffer(graphics::BufferProperties const& properties) : StubBuffer{create_native_buffer(), properties, geometry::Stride{properties.size.width.as_int() * MIR_BYTES_PER_PIXEL(properties.format)}} { } StubBuffer(graphics::BufferID id) : native_buffer(create_native_buffer()), buf_size{}, buf_pixel_format{mir_pixel_format_abgr_8888}, buf_stride{}, buf_id{id} { } StubBuffer(std::shared_ptr const& native_buffer, graphics::BufferProperties const& properties, geometry::Stride stride) : native_buffer(native_buffer), buf_size{properties.size}, buf_pixel_format{properties.format}, buf_stride{stride}, buf_id{graphics::BufferBasic::id()} { } virtual graphics::BufferID id() const override { return buf_id; } virtual geometry::Size size() const override { return buf_size; } virtual geometry::Stride stride() const override { return buf_stride; } virtual MirPixelFormat pixel_format() const override { return buf_pixel_format; } virtual std::shared_ptr native_buffer_handle() const override { return native_buffer; } void write(unsigned char const* pixels, size_t len) override { if (pixels) written_pixels.assign(pixels, pixels + len); } void read(std::function const& do_with_pixels) override { if (written_pixels.size() == 0) { auto length = buf_size.width.as_int()*buf_size.height.as_int()*MIR_BYTES_PER_PIXEL(buf_pixel_format); written_pixels.resize(length); memset(written_pixels.data(), 0, length); } do_with_pixels(written_pixels.data()); } NativeBufferBase* native_buffer_base() override { return this; } std::shared_ptr const native_buffer; geometry::Size const buf_size; MirPixelFormat const buf_pixel_format; geometry::Stride const buf_stride; graphics::BufferID const buf_id; std::vector written_pixels; std::shared_ptr create_native_buffer(); }; } } } #endif /* MIR_TEST_DOUBLES_STUB_BUFFER_H_ */ ./tests/include/mir/test/doubles/mock_input_region.h0000644000015600001650000000241512676616125022767 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_REGION_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_REGION_H_ #include "mir/input/input_region.h" #include "mir/geometry/point.h" #include "mir/geometry/rectangle.h" #include namespace mir { namespace test { namespace doubles { class MockInputRegion : public input::InputRegion { public: MOCK_METHOD0(bounding_rectangle, geometry::Rectangle()); MOCK_METHOD1(set_input_rectangles, void(geometry::Rectangles const&)); MOCK_METHOD1(confine, void(geometry::Point&)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_INPUT_REGION_H_ */ ./tests/include/mir/test/doubles/mock_display_report.h0000644000015600001650000000326712676616125023333 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_DISPLAY_REPORT_H_ #define MIR_TEST_DOUBLES_MOCK_DISPLAY_REPORT_H_ #include "mir/graphics/display_report.h" #include namespace mir { namespace test { namespace doubles { class MockDisplayReport : public graphics::DisplayReport { public: MOCK_METHOD0(report_successful_setup_of_native_resources, void()); MOCK_METHOD0(report_successful_egl_make_current_on_construction, void()); MOCK_METHOD0(report_successful_egl_buffer_swap_on_construction, void()); MOCK_METHOD0(report_successful_drm_mode_set_crtc_on_construction, void()); MOCK_METHOD0(report_successful_display_construction, void()); MOCK_METHOD1(report_drm_master_failure, void(int)); MOCK_METHOD0(report_vt_switch_away_failure, void()); MOCK_METHOD0(report_vt_switch_back_failure, void()); MOCK_METHOD2(report_egl_configuration, void(EGLDisplay,EGLConfig)); MOCK_METHOD1(report_vsync, void(unsigned int)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_DISPLAY_REPORT_H_ */ ./tests/include/mir/test/doubles/mock_option.h0000644000015600001650000000233012676616125021571 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_OPTION_H_ #define MIR_TEST_DOUBLES_MOCK_OPTION_H_ #include "mir/options/option.h" #include namespace mir { namespace test { namespace doubles { struct MockOption : mir::options::Option { MOCK_CONST_METHOD1(is_set, bool(char const*)); MOCK_CONST_METHOD2(get, bool(char const*, bool)); MOCK_CONST_METHOD2(get, int(char const*, int)); MOCK_CONST_METHOD2(get, std::string(char const* name, char const*)); MOCK_CONST_METHOD1(get, boost::any const&(char const*)); }; } } } #endif ./tests/include/mir/test/doubles/stub_gl_program.h0000644000015600001650000000203112676616125022434 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_GL_PROGRAM_H_ #define MIR_TEST_DOUBLES_STUB_GL_PROGRAM_H_ #include "mir/gl/program.h" namespace mir { namespace test { namespace doubles { struct StubGLProgram : public gl::Program { operator GLuint() const override { return 7; } }; } } } // namespace mir #endif /* MIR_TEST_DOUBLES_STUB_GL_PROGRAM_H_ */ ./tests/include/mir/test/doubles/stub_input_sender.h0000644000015600001650000000206712676616125023013 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_STUB_INPUT_SENDER_H_ #define MIR_TEST_DOUBLES_STUB_INPUT_SENDER_H_ #include "mir/input/input_sender.h" namespace mir { namespace test { namespace doubles { struct StubInputSender : mir::input::InputSender { void send_event(MirEvent const& /*event*/, std::shared_ptr const& /*channel*/) override { } }; } } } #endif ./tests/include/mir/test/doubles/mock_client_buffer_factory.h0000644000015600001650000000243512676616125024625 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_MOCK_CLIENT_BUFFER_FACTORY_H_ #define MIR_TEST_DOUBLES_MOCK_CLIENT_BUFFER_FACTORY_H_ #include "mir/client_buffer_factory.h" #include #include namespace mir { namespace test { namespace doubles { struct MockClientBufferFactory : client::ClientBufferFactory { MOCK_METHOD3(create_buffer, std::shared_ptr( std::shared_ptr const& /*package*/, mir::geometry::Size /*size*/, MirPixelFormat /*pf*/)); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_CLIENT_BUFFER_FACTORY_H_ ./tests/include/mir/test/doubles/mock_display_buffer.h0000644000015600001650000000317312676616125023265 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_DISPLAY_BUFFER_H_ #define MIR_TEST_DOUBLES_MOCK_DISPLAY_BUFFER_H_ #include #include namespace mir { namespace test { namespace doubles { class MockDisplayBuffer : public graphics::DisplayBuffer, public graphics::NativeDisplayBuffer { public: MockDisplayBuffer() { using namespace testing; ON_CALL(*this, view_area()) .WillByDefault(Return(geometry::Rectangle{{0,0},{0,0}})); ON_CALL(*this, native_display_buffer()) .WillByDefault(Return(this)); } MOCK_CONST_METHOD0(view_area, geometry::Rectangle()); MOCK_METHOD1(post_renderables_if_optimizable, bool(graphics::RenderableList const&)); MOCK_CONST_METHOD0(orientation, MirOrientation()); MOCK_METHOD0(native_display_buffer, graphics::NativeDisplayBuffer*()); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_DISPLAY_BUFFER_H_ */ ./tests/include/mir/test/doubles/mock_input_sink.h0000644000015600001650000000224312676616125022447 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Cemil Azizoglu */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_SINK_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_SINK_H_ #include "mir/input/input_sink.h" #include namespace mir { namespace test { namespace doubles { struct MockInputSink : mir::input::InputSink { MOCK_METHOD1(handle_input, void(MirEvent&)); MOCK_METHOD1(confine_pointer, void(mir::geometry::Point&)); MOCK_CONST_METHOD0(bounding_rectangle, mir::geometry::Rectangle()); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_INPUT_SINK_H_ ./tests/include/mir/test/doubles/mock_session_listener.h0000644000015600001650000000303312676616125023652 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_MOCK_SESSION_LISTENER_H_ #define MIR_TEST_DOUBLES_MOCK_SESSION_LISTENER_H_ #include "mir/scene/session_listener.h" #include namespace mir { namespace test { namespace doubles { struct MockSessionListener : public scene::SessionListener { virtual ~MockSessionListener() noexcept(true) {} MOCK_METHOD1(starting, void(std::shared_ptr const&)); MOCK_METHOD1(stopping, void(std::shared_ptr const&)); MOCK_METHOD1(focused, void(std::shared_ptr const&)); MOCK_METHOD0(unfocused, void()); MOCK_METHOD2(surface_created, void(scene::Session&, std::shared_ptr const&)); MOCK_METHOD2(destroying_surface, void(scene::Session&, std::shared_ptr const&)); }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_MOCK_SESSION_LISTENER_H_ ./tests/include/mir/test/doubles/null_event_sink.h0000644000015600001650000000310412676616125022447 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_NULL_EVENT_SINK_H_ #define MIR_TEST_DOUBLES_NULL_EVENT_SINK_H_ #include "mir/frontend/event_sink.h" #include namespace mir { namespace frontend { class MessageSender; } namespace test { namespace doubles { struct NullEventSink : public frontend::EventSink { void handle_event(MirEvent const&) override {} void handle_lifecycle_event(MirLifecycleState) override {} void handle_display_config_change(graphics::DisplayConfiguration const&) override {} void send_ping(int32_t) override {} void send_buffer(frontend::BufferStreamId, graphics::Buffer&, graphics::BufferIpcMsgType) override {} void handle_input_device_change(std::vector> const&) override {}; }; std::unique_ptr null_sink_factory(std::shared_ptr const&); } } } #endif /* MIR_TEST_DOUBLES_NULL_EVENT_SINK_H_*/ ./tests/include/mir/test/doubles/fake_renderable.h0000644000015600001650000000476012676616125022352 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_TEST_DOUBLES_FAKE_RENDERABLE_H_ #define MIR_TEST_DOUBLES_FAKE_RENDERABLE_H_ #include "stub_buffer.h" #include "mir/graphics/renderable.h" #define GLM_FORCE_RADIANS #include #include namespace mir { namespace test { namespace doubles { class FakeRenderable : public graphics::Renderable { public: FakeRenderable(int x, int y, int width, int height) : FakeRenderable{geometry::Rectangle{{x,y},{width,height}}} { } FakeRenderable(geometry::Rectangle display_area) : FakeRenderable{display_area, 1.0f, true} { } FakeRenderable(geometry::Rectangle display_area, float opacity) : FakeRenderable{display_area, opacity, true} { } FakeRenderable(geometry::Rectangle display_area, float opacity, bool rectangular) : buf{std::make_shared()}, rect(display_area), opacity(opacity), rectangular(rectangular) { } ID id() const override { return this; } float alpha() const override { return opacity; } glm::mat4 transformation() const override { return glm::mat4(); } bool shaped() const override { return !rectangular; } void set_buffer(std::shared_ptr b) { buf = b; } std::shared_ptr buffer() const override { return buf; } geometry::Rectangle screen_position() const override { return rect; } private: std::shared_ptr buf; mir::geometry::Rectangle rect; float opacity; bool rectangular; }; } // namespace doubles } // namespace test } // namespace mir #endif // MIR_TEST_DOUBLES_FAKE_RENDERABLE_H_ ./tests/include/mir/test/doubles/mock_main_loop.h0000644000015600001650000000512112676616125022237 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_MAIN_LOOP_H_ #define MIR_TEST_DOUBLES_MOCK_MAIN_LOOP_H_ #include "mir/main_loop.h" #include "mir/test/gmock_fixes.h" #include namespace mir { namespace test { namespace doubles { class MockMainLoop : public MainLoop { public: ~MockMainLoop() noexcept {} void run() override {} void stop() override {} MOCK_METHOD2(register_signal_handler, void(std::initializer_list, std::function const&)); MOCK_METHOD2(register_signal_handler_module_ptr, void(std::initializer_list, std::function const&)); MOCK_METHOD3(register_fd_handler, void(std::initializer_list, void const*, std::function const&)); MOCK_METHOD3(register_fd_handler_module_ptr, void(std::initializer_list, void const*, std::function const&)); MOCK_METHOD1(unregister_fd_handler, void(void const*)); MOCK_METHOD2(enqueue, void(void const*, ServerAction const&)); MOCK_METHOD1(pause_processing_for,void (void const*)); MOCK_METHOD1(resume_processing_for,void (void const*)); MOCK_METHOD1(create_alarm, std::unique_ptr(std::function const& callback)); MOCK_METHOD1(create_alarm, std::unique_ptr(std::shared_ptr const& callback)); void register_signal_handler( std::initializer_list signals, mir::UniqueModulePtr> handler) { register_signal_handler_module_ptr(signals, *handler); } void register_fd_handler( std::initializer_list fds, void const* owner, mir::UniqueModulePtr> handler) { register_fd_handler_module_ptr(fds, owner, *handler); } }; } } } #endif ./tests/include/mir/test/doubles/mock_gbm.h0000644000015600001650000000745512676616125021043 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_GBM_H_ #define MIR_TEST_DOUBLES_MOCK_GBM_H_ #include #pragma GCC diagnostic push #pragma GCC diagnostic warning "-Wall" #include #pragma GCC diagnostic pop namespace mir { namespace test { namespace doubles { class FakeGBMResources { public: FakeGBMResources(); ~FakeGBMResources() = default; gbm_device *device; gbm_surface *surface; gbm_bo *bo; gbm_bo_handle bo_handle; }; class MockGBM { public: MockGBM(); ~MockGBM(); MOCK_METHOD1(gbm_create_device, struct gbm_device*(int fd)); MOCK_METHOD1(gbm_device_destroy, void(struct gbm_device *gbm)); MOCK_METHOD1(gbm_device_get_fd, int(struct gbm_device *gbm)); MOCK_METHOD3(gbm_device_is_format_supported, int(struct gbm_device *gbm, uint32_t format, uint32_t usage)); MOCK_METHOD5(gbm_surface_create, struct gbm_surface*(struct gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags)); MOCK_METHOD1(gbm_surface_destroy, void(struct gbm_surface *surface)); MOCK_METHOD1(gbm_surface_lock_front_buffer, struct gbm_bo*(struct gbm_surface *surface)); MOCK_METHOD2(gbm_surface_release_buffer, void(struct gbm_surface *surface, struct gbm_bo *bo)); MOCK_METHOD5(gbm_bo_create, struct gbm_bo*(struct gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags)); MOCK_METHOD1(gbm_bo_get_device, struct gbm_device*(struct gbm_bo *bo)); MOCK_METHOD1(gbm_bo_get_width, uint32_t(struct gbm_bo *bo)); MOCK_METHOD1(gbm_bo_get_height, uint32_t(struct gbm_bo *bo)); MOCK_METHOD1(gbm_bo_get_stride, uint32_t(struct gbm_bo *bo)); MOCK_METHOD1(gbm_bo_get_format, uint32_t(struct gbm_bo *bo)); MOCK_METHOD1(gbm_bo_get_handle, union gbm_bo_handle(struct gbm_bo *bo)); MOCK_METHOD3(gbm_bo_set_user_data, void(struct gbm_bo *bo, void *data, void (*destroy_user_data)(struct gbm_bo *, void *))); MOCK_METHOD1(gbm_bo_get_user_data, void*(struct gbm_bo *bo)); MOCK_METHOD3(gbm_bo_write, bool(struct gbm_bo *bo, const void *buf, size_t count)); MOCK_METHOD1(gbm_bo_destroy, void(struct gbm_bo *bo)); MOCK_METHOD4(gbm_bo_import, struct gbm_bo*(struct gbm_device*, uint32_t, void*, uint32_t)); FakeGBMResources fake_gbm; private: void on_gbm_bo_set_user_data(struct gbm_bo *bo, void *data, void (*destroy_user_data)(struct gbm_bo *, void *)) { destroyers.push_back(Destroyer{bo, data, destroy_user_data}); } struct Destroyer { struct gbm_bo *bo; void *data; void (*destroy_user_data)(struct gbm_bo *, void *); void operator()() const { destroy_user_data(bo, data); } }; std::vector destroyers; }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_GBM_H_ */ ./tests/include/mir/test/doubles/mock_window_handle_repository.h0000644000015600001650000000276212676616125025413 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "src/server/input/android/android_window_handle_repository.h" #include #ifndef MIR_TEST_DOUBLES_MOCK_WINDOW_HANDLE_REPOSITORY_H_ #define MIR_TEST_DOUBLES_MOCK_WINDOW_HANDLE_REPOSITORY_H_ namespace mir { namespace test { namespace doubles { struct MockWindowHandleRepository : public input::android::WindowHandleRepository { MockWindowHandleRepository() { using namespace testing; ON_CALL(*this, handle_for_channel(_)) .WillByDefault(Return(droidinput::sp())); } ~MockWindowHandleRepository() noexcept(true) {}; MOCK_METHOD1(handle_for_channel, droidinput::sp(std::shared_ptr const&)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_WINDOW_HANDLE_REPOSITORY_H_ */ ./tests/include/mir/test/doubles/mock_lockable_callback.h0000644000015600001650000000223712676616125023657 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #ifndef MIR_TEST_DOUBLES_MOCK_LOCKABLE_CALLBACK_H_ #define MIR_TEST_DOUBLES_MOCK_LOCKABLE_CALLBACK_H_ #include "mir/lockable_callback.h" #include namespace mir { namespace test { namespace doubles { class MockLockableCallback : public LockableCallback { public: ~MockLockableCallback() noexcept {} MOCK_METHOD0(functor, void()); MOCK_METHOD0(lock, void()); MOCK_METHOD0(unlock, void()); void operator()() override { functor(); } }; } } } #endif ./tests/include/mir/test/doubles/stub_cursor_listener.h0000644000015600001650000000206112676616125023530 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_STUB_CURSOR_LISTENER_H_ #define MIR_TEST_DOUBLES_STUB_CURSOR_LISTENER_H_ #include "mir/input/cursor_listener.h" namespace mir { namespace test { namespace doubles { struct StubCursorListener : public input::CursorListener { void cursor_moved_to(float, float) override {} }; } } } // namespace mir #endif /* MIR_TEST_DOUBLES_STUB_CURSOR_LISTENER_H_ */ ./tests/include/mir/test/doubles/mock_nested_context.h0000644000015600001650000000305612676616125023315 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_NESTED_CONTEXT_H_ #define MIR_TEST_DOUBLES_MOCK_NESTED_CONTEXT_H_ #include "mir/graphics/nested_context.h" #include "mir/graphics/platform_operation_message.h" namespace mir { namespace graphics { inline bool operator==(PlatformOperationMessage const& msg1, PlatformOperationMessage const& msg2) { return msg1.data == msg2.data && msg1.fds == msg2.fds; } inline bool operator!=(PlatformOperationMessage const& msg1, PlatformOperationMessage const& msg2) { return !(msg1 == msg2); } } namespace test { namespace doubles { struct MockNestedContext : graphics::NestedContext { MOCK_METHOD0(platform_fd_items, std::vector()); MOCK_METHOD2(platform_operation, graphics::PlatformOperationMessage( unsigned int, graphics::PlatformOperationMessage const&)); }; } } } #endif ./tests/include/mir/test/doubles/null_event_sink_factory.h0000644000015600001650000000224112676616125024177 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "src/server/frontend/event_sink_factory.h" #ifndef MIR_TEST_DOUBLES_NULL_EVENT_SINK_FACTORY_H_ #define MIR_TEST_DOUBLES_NULL_EVENT_SINK_FACTORY_H_ namespace mir { namespace test { namespace doubles { class NullEventSinkFactory : public frontend::EventSinkFactory { public: std::unique_ptr create_sink(std::shared_ptr const&) override; }; } } } #endif //MIR_TEST_DOUBLES_NULL_EVENT_SINK_FACTORY_H_ ./tests/include/mir/test/doubles/mock_server_status_listener.h0000644000015600001650000000223212676616125025100 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Ancell */ #ifndef MIR_TEST_DOUBLES_MOCK_SERVER_STATUS_LISTENER_H_ #define MIR_TEST_DOUBLES_MOCK_SERVER_STATUS_LISTENER_H_ #include "mir/server_status_listener.h" #include namespace mir { namespace test { namespace doubles { class MockServerStatusListener : public mir::ServerStatusListener { public: MOCK_METHOD0(paused, void()); MOCK_METHOD0(resumed, void()); MOCK_METHOD0(started, void()); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_SERVER_STATUS_LISTENER_H_ */ ./tests/include/mir/test/doubles/mock_x11.h0000644000015600001650000000630712676616157020707 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Cemil Azizoglu */ #ifndef MIR_TEST_DOUBLES_MOCK_X11_H_ #define MIR_TEST_DOUBLES_MOCK_X11_H_ #include #include #include namespace mir { namespace test { namespace doubles { class FakeX11Resources { public: FakeX11Resources(); ~FakeX11Resources() = default; Display *display; Window window; XVisualInfo visual_info; XEvent keypress_event_return = { 0 }; XEvent button_release_event_return = { 0 }; XEvent expose_event_return = { 0 }; XEvent focus_in_event_return = { 0 }; XEvent focus_out_event_return = { 0 }; XEvent vscroll_event_return = { 0 }; XEvent motion_event_return = { 0 }; int pending_events = 1; }; class MockX11 { public: MockX11(); ~MockX11(); MOCK_METHOD1(XOpenDisplay, Display*(const char*)); MOCK_METHOD1(XCloseDisplay, int(Display*)); MOCK_METHOD1(XPending, int(Display*)); MOCK_METHOD4(XGetVisualInfo, XVisualInfo*(Display*, long, XVisualInfo*, int*)); MOCK_METHOD4(XCreateColormap, Colormap(Display*, Window, Visual*, int)); /* Too long to mock, use wrapper instead. MOCK_METHOD12(XCreateWindow, Window(Display*, Window, int, int, unsigned int, unsigned int, unsigned int, int, unsigned int, Visual*, unsigned long, XSetWindowAttributes*)); */ MOCK_METHOD10(XCreateWindow_wrapper, Window(Display*, Window, unsigned int, unsigned int, unsigned int, int, unsigned int, Visual*, unsigned long, XSetWindowAttributes*)); MOCK_METHOD3(XSetNormalHints, int(Display*, Window, XSizeHints*)); MOCK_METHOD8(XSetStandardProperties, int(Display*, Window, const char*, const char*, Pixmap, char **, int, XSizeHints*)); MOCK_METHOD1(XFree, int(void*)); MOCK_METHOD2(XMapWindow, int(Display*, Window)); MOCK_METHOD2(XDestroyWindow, int(Display*, Window)); MOCK_METHOD1(XConnectionNumber, int(Display*)); MOCK_METHOD2(XNextEvent, int(Display*, XEvent*)); MOCK_METHOD5(XLookupString, int(XKeyEvent*, char*, int, KeySym*, XComposeStatus*)); MOCK_METHOD1(XRefreshKeyboardMapping, int(XMappingEvent*)); MOCK_METHOD1(XDefaultRootWindow, Window(Display*)); MOCK_METHOD6(XGrabKeyboard, int(Display*, Window, Bool, int, int, Time)); MOCK_METHOD2(XUngrabKeyboard, int(Display*, Time)); MOCK_METHOD4(XGetErrorText, int(Display*, int, char*, int )); MOCK_METHOD1(XSetErrorHandler, XErrorHandler(XErrorHandler)); MOCK_METHOD0(XInitThreads, Status()); MOCK_METHOD3(XSetWMHints, int(Display*, Window, XWMHints*)); FakeX11Resources fake_x11; }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_X11_H_ */ ./tests/include/mir/test/doubles/null_session_event_sink.h0000644000015600001650000000231312676616125024213 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_SESSION_EVENT_SINK_H_ #define MIR_TEST_DOUBLES_NULL_SESSION_EVENT_SINK_H_ #include "src/server/scene/session_event_sink.h" namespace mir { namespace test { namespace doubles { class NullSessionEventSink : public scene::SessionEventSink { public: void handle_focus_change(std::shared_ptr const&) {} void handle_no_focus() {} void handle_session_stopping(std::shared_ptr const&) {} }; } } } #endif /* MIR_TEST_DOUBLES_NULL_SESSION_EVENT_SINK_H_*/ ./tests/include/mir/test/doubles/mock_client_context.h0000644000015600001650000000241612676616125023310 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_CLIENT_CONTEXT_H_ #define MIR_TEST_DOUBLES_MOCK_CLIENT_CONTEXT_H_ #include "mir_toolkit/mir_client_library.h" #include "mir/client_context.h" #include namespace mir { namespace test { namespace doubles { struct MockClientContext : public client::ClientContext { MockClientContext() { using namespace testing; EXPECT_CALL(*this, populate_server_package(_)).Times(AtLeast(0)); } MOCK_METHOD1(populate_server_package, void(MirPlatformPackage&)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_CLIENT_CONTEXT_H_ */ ./tests/include/mir/test/doubles/null_emergency_cleanup.h0000644000015600001650000000223512676616125023773 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_EMERGENCY_CLEANUP_H_ #define MIR_TEST_DOUBLES_NULL_EMERGENCY_CLEANUP_H_ #include "mir/emergency_cleanup.h" namespace mir { namespace test { namespace doubles { class NullEmergencyCleanup : public EmergencyCleanup { public: void add(EmergencyCleanupHandler const&) override {} void add(ModuleEmergencyCleanupHandler) override {} void operator()() const override {} }; } } } #endif /* MIR_TEST_DOUBLES_NULL_EMERGENCY_CLEANUP_H_ */ ./tests/include/mir/test/doubles/stub_input_channel.h0000644000015600001650000000245512676616125023144 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_STUB_INPUT_CHANNEL_H_ #define MIR_TEST_DOUBLES_STUB_INPUT_CHANNEL_H_ #include "mir/input/input_channel.h" #include "mir/fd.h" namespace mir { namespace test { namespace doubles { struct StubInputChannel : public input::InputChannel { StubInputChannel(int fd) : input_fd(fd) { } StubInputChannel() : StubInputChannel(Fd::invalid) { } int client_fd() const override { return input_fd; } int server_fd() const override { return input_fd; } Fd input_fd; }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_STUB_INPUT_CHANNEL_H_ ./tests/include/mir/test/doubles/mock_gl_config.h0000644000015600001650000000213512676616125022213 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_GL_CONFIG_H_ #define MIR_TEST_DOUBLES_MOCK_GL_CONFIG_H_ #include "mir/graphics/gl_config.h" #include namespace mir { namespace test { namespace doubles { struct MockGLConfig : public graphics::GLConfig { MOCK_CONST_METHOD0(depth_buffer_bits, int()); MOCK_CONST_METHOD0(stencil_buffer_bits, int()); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_GL_CONFIG_H_ */ ./tests/include/mir/test/doubles/advanceable_clock.h0000644000015600001650000000307412676616125022656 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_ADVANCEABLE_CLOCK_H_ #define MIR_TEST_DOUBLES_ADVANCEABLE_CLOCK_H_ #include "mir/time/steady_clock.h" #include namespace mir { namespace test { namespace doubles { class AdvanceableClock : public mir::time::Clock { public: mir::time::Timestamp now() const override { std::lock_guard lock{clock_mutex}; return current_time; } mir::time::Duration min_wait_until(mir::time::Timestamp) const override { return mir::time::Duration{0}; } void advance_by(mir::time::Duration step) { std::lock_guard lock{clock_mutex}; current_time += step; } private: mutable std::mutex clock_mutex; mir::time::Timestamp current_time{ [] { mir::time::SteadyClock clock; return clock.now(); }() }; }; } } } #endif ./tests/include/mir/test/doubles/mock_display_changer.h0000644000015600001650000000305012676616125023415 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_DISPLAY_CHANGER_H_ #define MIR_TEST_DOUBLES_MOCK_DISPLAY_CHANGER_H_ #include "null_display_changer.h" #include namespace mir { namespace test { namespace doubles { class MockDisplayChanger : public NullDisplayChanger { public: MOCK_METHOD0(base_configuration, std::shared_ptr()); MOCK_METHOD2(configure, void(std::shared_ptr const&, std::shared_ptr const&)); MOCK_METHOD1(mock_set_base_configuration,void(graphics::DisplayConfiguration const&)); void set_base_configuration( std::shared_ptr const& config) { mock_set_base_configuration(*config); NullDisplayChanger::set_base_configuration(config); } }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_DISPLAY_CHANGER_H_ */ ./tests/include/mir/test/doubles/mock_hwc_layerlist.h0000644000015600001650000000232212676616125023133 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_HWC_LAYERLIST_H_ #define MIR_TEST_DOUBLES_MOCK_HWC_LAYERLIST_H_ #include "src/platforms/android/server/hwc_layerlist.h" #include namespace mir { namespace test { namespace doubles { struct MockHWCLayerList : public graphics::android::HWCLayerList { ~MockHWCLayerList() noexcept {} MOCK_CONST_METHOD0(native_list, hwc_display_contents_1_t*()); MOCK_METHOD1(set_fb_target, void(std::shared_ptr const&)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_HWC_LAYERLIST_H_ */ ./tests/include/mir/test/doubles/mock_android_hw.h0000644000015600001650000000416212676616125022404 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_ANDROID_HW_H_ #define MIR_TEST_DOUBLES_MOCK_ANDROID_HW_H_ #include "mir/test/doubles/mock_android_alloc_device.h" #include "mir/test/doubles/mock_hwc_composer_device_1.h" #include #include #include namespace mir { namespace test { namespace doubles { typedef struct hw_module_t hw_module; class HardwareModuleStub : public hw_module { public: HardwareModuleStub(hw_device_t& device); static int hw_open(const struct hw_module_t* module, const char*, struct hw_device_t** device); static int hw_close(struct hw_device_t*); hw_module_methods_t gr_methods; hw_device_t& mock_hw_device; }; class FailingHardwareModuleStub : public hw_module { public: FailingHardwareModuleStub(); static int hw_open(const struct hw_module_t* module, const char*, struct hw_device_t** device); static int hw_close(struct hw_device_t*); hw_module_methods_t gr_methods; }; class HardwareAccessMock { public: HardwareAccessMock(); ~HardwareAccessMock(); MOCK_METHOD2(hw_get_module, int(const char *id, const struct hw_module_t**)); bool open_count_matches_close(); std::shared_ptr mock_alloc_device; std::shared_ptr mock_hwc_device; std::shared_ptr mock_gralloc_module; std::shared_ptr mock_hwc_module; }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_ANDROID_HW_H_ */ ./tests/include/mir/test/doubles/mock_buffer_stream.h0000644000015600001650000000707112676616125023114 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_BUFFER_STREAM_H_ #define MIR_TEST_DOUBLES_MOCK_BUFFER_STREAM_H_ #include "mir/compositor/buffer_stream.h" #include "stub_buffer.h" #include namespace mir { namespace test { namespace doubles { struct MockBufferStream : public compositor::BufferStream { int buffers_ready_{0}; int buffers_ready(void const*) { if (buffers_ready_) return buffers_ready_--; return 0; } MockBufferStream() { ON_CALL(*this, buffers_ready_for_compositor(::testing::_)) .WillByDefault(testing::Invoke(this, &MockBufferStream::buffers_ready)); ON_CALL(*this, with_most_recent_buffer_do(testing::_)) .WillByDefault(testing::InvokeArgument<0>(*std::make_shared())); ON_CALL(*this, acquire_client_buffer(testing::_)) .WillByDefault(testing::InvokeArgument<0>(nullptr)); ON_CALL(*this, swap_buffers(testing::_, testing::_)) .WillByDefault(testing::InvokeArgument<1>(nullptr)); ON_CALL(*this, has_submitted_buffer()) .WillByDefault(testing::Return(true)); ON_CALL(*this, pixel_format()) .WillByDefault(testing::Return(mir_pixel_format_abgr_8888)); ON_CALL(*this, stream_size()) .WillByDefault(testing::Return(geometry::Size{0,0})); } MOCK_METHOD1(acquire_client_buffer, void(std::function)); MOCK_METHOD1(release_client_buffer, void(graphics::Buffer*)); MOCK_METHOD1(lock_compositor_buffer, std::shared_ptr(void const*)); MOCK_METHOD1(add_observer, void(std::shared_ptr const&)); MOCK_METHOD1(remove_observer, void(std::weak_ptr const&)); MOCK_METHOD0(get_stream_pixel_format, MirPixelFormat()); MOCK_METHOD0(stream_size, geometry::Size()); MOCK_METHOD1(resize, void(geometry::Size const&)); MOCK_METHOD0(force_client_completion, void()); MOCK_METHOD1(allow_framedropping, void(bool)); MOCK_METHOD0(force_requests_to_complete, void()); MOCK_CONST_METHOD1(buffers_ready_for_compositor, int(void const*)); MOCK_METHOD0(drop_old_buffers, void()); MOCK_METHOD0(drop_client_requests, void()); MOCK_METHOD2(swap_buffers, void(graphics::Buffer*, std::function)); MOCK_METHOD1(with_most_recent_buffer_do, void(std::function const&)); MOCK_CONST_METHOD0(pixel_format, MirPixelFormat()); MOCK_CONST_METHOD0(has_submitted_buffer, bool()); MOCK_METHOD1(allocate_buffer, graphics::BufferID(graphics::BufferProperties const&)); MOCK_METHOD1(remove_buffer, void(graphics::BufferID)); MOCK_METHOD2(with_buffer, void(graphics::BufferID, std::function const&)); MOCK_METHOD1(set_scale, void(float)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_BUFFER_STREAM_H_ */ ./tests/include/mir/test/doubles/null_pixel_buffer.h0000644000015600001650000000225612676616125022763 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_PIXEL_BUFFER_H_ #define MIR_TEST_DOUBLES_NULL_PIXEL_BUFFER_H_ #include "src/server/scene/pixel_buffer.h" namespace mir { namespace test { namespace doubles { struct NullPixelBuffer : public scene::PixelBuffer { void fill_from(graphics::Buffer&) {} void const* as_argb_8888() { return nullptr; } geometry::Size size() const { return {}; } geometry::Stride stride() const { return {}; } }; } } } #endif /* MIR_TEST_DOUBLES_NULL_PIXEL_BUFFER_H_ */ ./tests/include/mir/test/doubles/mock_frame_dropping_policy_factory.h0000644000015600001650000000524412676616125026372 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Alberto Aguirre */ #ifndef MIR_TEST_DOUBLES_MOCK_FRAME_DROPPING_POLICY_FACTORY_H_ #define MIR_TEST_DOUBLES_MOCK_FRAME_DROPPING_POLICY_FACTORY_H_ #include "mir/compositor/frame_dropping_policy_factory.h" #include "mir/compositor/frame_dropping_policy.h" #include "mir/test/gmock_fixes.h" #include #include #include #include namespace mc = mir::compositor; namespace mir { class LockableCallback; namespace test { namespace doubles { class MockFrameDroppingPolicyFactory; class MockFrameDroppingPolicy : public mc::FrameDroppingPolicy { public: MockFrameDroppingPolicy(std::shared_ptr const& callback, MockFrameDroppingPolicyFactory const* parent); ~MockFrameDroppingPolicy(); MOCK_METHOD0(swap_now_blocking, void(void)); MOCK_METHOD0(swap_unblocked, void(void)); void trigger(); private: friend class MockFrameDroppingPolicyFactory; void parent_destroyed(); std::shared_ptr callback; MockFrameDroppingPolicyFactory const* parent; }; class MockFrameDroppingPolicyFactory : public mc::FrameDroppingPolicyFactory { public: std::unique_ptr create_policy( std::shared_ptr const& callback) const override; ~MockFrameDroppingPolicyFactory(); void trigger_policies() const; private: friend class MockFrameDroppingPolicy; void policy_destroyed(MockFrameDroppingPolicy* policy) const; mutable std::unordered_set policies; }; class FrameDroppingPolicyFactoryMock : public mc::FrameDroppingPolicyFactory { public: MOCK_CONST_METHOD1(create_policy, std::unique_ptr( std::shared_ptr const& callback)); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_FRAME_DROPPING_POLICY_FACTORY_H_ ./tests/include/mir/test/doubles/mock_rpc_report.h0000644000015600001650000000442412676616125022446 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_RPC_REPORT_H_ #define MIR_TEST_DOUBLES_MOCK_RPC_REPORT_H_ #include "src/client/rpc/rpc_report.h" #include "mir_protobuf_wire.pb.h" #include namespace mir { namespace test { namespace doubles { class MockRpcReport : public mir::client::rpc::RpcReport { public: ~MockRpcReport() noexcept {} MOCK_METHOD1(invocation_requested, void(mir::protobuf::wire::Invocation const&)); MOCK_METHOD1(invocation_succeeded, void(mir::protobuf::wire::Invocation const&)); MOCK_METHOD2(invocation_failed, void(mir::protobuf::wire::Invocation const&, std::exception const&)); MOCK_METHOD1(result_receipt_succeeded, void(mir::protobuf::wire::Result const&)); MOCK_METHOD1(result_receipt_failed, void(std::exception const&)); MOCK_METHOD1(event_parsing_succeeded, void(MirEvent const&)); MOCK_METHOD1(event_parsing_failed, void(mir::protobuf::Event const&)); MOCK_METHOD1(orphaned_result, void(mir::protobuf::wire::Result const&)); MOCK_METHOD1(complete_response, void(mir::protobuf::wire::Result const&)); MOCK_METHOD2(result_processing_failed, void(mir::protobuf::wire::Result const&, std::exception const& ex)); MOCK_METHOD2(file_descriptors_received, void(google::protobuf::MessageLite const&, std::vector const&)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_RPC_REPORT_H_ */ ./tests/include/mir/test/doubles/mock_input_manager.h0000644000015600001650000000224512676616125023117 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_MANAGER_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_MANAGER_H_ #include "mir/input/input_manager.h" #include #include namespace mir { namespace test { namespace doubles { struct MockInputManager : public input::InputManager { MOCK_METHOD1(add_platform, void(std::shared_ptr const&)); MOCK_METHOD0(start, void()); MOCK_METHOD0(stop, void()); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_INPUT_MANAGER_H ./tests/include/mir/test/doubles/mock_interpreter_resource_cache.h0000644000015600001650000000256512676616125025670 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_INTERPRETER_RESOURCE_CACHE_H_ #define MIR_TEST_DOUBLES_MOCK_INTERPRETER_RESOURCE_CACHE_H_ #include "src/platforms/android/server/interpreter_resource_cache.h" namespace mir { namespace test { namespace doubles { struct MockInterpreterResourceCache : public graphics::android::InterpreterResourceCache { MOCK_METHOD2(store_buffer, void(std::shared_ptrconst&, std::shared_ptr const&)); MOCK_METHOD1(retrieve_buffer, std::shared_ptr(ANativeWindowBuffer*)); MOCK_METHOD2(update_native_fence, void(ANativeWindowBuffer*, int)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_INTERPRETER_RESOURCE_CACHE_H_ */ ./tests/include/mir/test/doubles/null_client_event_sink.h0000644000015600001650000000210312676616125024003 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Nick Dedekink */ #ifndef MIR_TEST_DOUBLES_NULL_CLIENT_EVENT_SINK_H_ #define MIR_TEST_DOUBLES_NULL_CLIENT_EVENT_SINK_H_ #include "src/client/event_sink.h" #include namespace mir { namespace test { namespace doubles { class NullClientEventSink : public client::EventSink { public: void handle_event(MirEvent const&) override {} }; } } } #endif /* MIR_TEST_DOUBLES_NULL_CLIENT_EVENT_SINK_H_ */ ./tests/include/mir/test/doubles/triggered_main_loop.h0000644000015600001650000000343412676616125023267 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_TRIGGERED_MAIN_LOOP_H_ #define MIR_TEST_DOUBLES_TRIGGERED_MAIN_LOOP_H_ #include "mir/test/doubles/mock_main_loop.h" #include namespace mir { namespace test { namespace doubles { class TriggeredMainLoop : public ::testing::NiceMock { public: using fd_callback = std::function; using signal_callback = std::function; using callback = std::function; void register_fd_handler(std::initializer_list fds, void const* owner, fd_callback const& handler) override; void unregister_fd_handler(void const* owner) override; std::unique_ptr create_alarm(callback const& call) override; void enqueue(void const* owner, ServerAction const& action) override; void trigger_pending_fds(); void fire_all_alarms(); void trigger_server_actions(); private: std::vector timeout_callbacks; struct Item { int fd; void const* owner; fd_callback callback; }; std::vector fd_callbacks; std::vector actions; }; } } } #endif ./tests/include/mir/test/doubles/mock_event_handler_register.h0000644000015600001650000000421612676616125025010 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_EVENT_HANDLER_REGISTER_H_ #define MIR_TEST_DOUBLES_MOCK_EVENT_HANDLER_REGISTER_H_ #include "mir/graphics/event_handler_register.h" #include namespace mir { namespace test { namespace doubles { class MockEventHandlerRegister : public graphics::EventHandlerRegister { public: MOCK_METHOD2(register_signal_handler, void(std::initializer_list, std::function const&)); MOCK_METHOD2(register_signal_handler_module_ptr, void(std::initializer_list, std::function const&)); MOCK_METHOD3(register_fd_handler, void(std::initializer_list, void const*, std::function const&)); MOCK_METHOD3(register_fd_handler_module_ptr, void(std::initializer_list, void const*, std::function const&)); MOCK_METHOD1(unregister_fd_handler, void(void const*)); void register_signal_handler( std::initializer_list sigs, mir::UniqueModulePtr> handler) { register_signal_handler_module_ptr(sigs, *handler); } void register_fd_handler( std::initializer_list fds, void const* owner, mir::UniqueModulePtr> handler) { register_fd_handler_module_ptr(fds, owner, *handler); } }; } } } #endif ./tests/include/mir/test/doubles/stub_touch_visualizer.h0000644000015600001650000000223312676616125023706 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_STUB_TOUCH_VISUALIZER_H_ #define MIR_TEST_DOUBLES_STUB_TOUCH_VISUALIZER_H_ #include "mir/input/touch_visualizer.h" namespace mir { namespace test { namespace doubles { struct StubTouchVisualizer : public input::TouchVisualizer { void visualize_touches(std::vector const&) override { } void enable() override { } void disable() override { } }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_STUB_TOUCH_VISUALIZER_H_ ./tests/include/mir/test/doubles/null_message_sender.h0000644000015600001650000000223212676616125023267 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_DOUBLES_NULL_MESSAGE_SENDER_H_ #define MIR_TEST_DOUBLES_NULL_MESSAGE_SENDER_H_ #include "src/server/frontend/message_sender.h" namespace mir { namespace test { namespace doubles { class NullMessageSender : public frontend::MessageSender { public: void send( char const* /*data*/, size_t /*length*/, frontend::FdSets const &/*fds*/) override { } }; } } } #endif //MIR_TEST_DOUBLES_NULL_MESSAGE_SENDER_H_ ./tests/include/mir/test/doubles/mock_gl_buffer.h0000644000015600001650000000226012676616125022216 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_GL_BUFFER_H_ #define MIR_TEST_DOUBLES_MOCK_GL_BUFFER_H_ #include "mock_buffer.h" #include "mir/renderer/gl/texture_source.h" namespace mir { namespace test { namespace doubles { struct MockGLBuffer : public MockBuffer, public renderer::gl::TextureSource { public: using MockBuffer::MockBuffer; MOCK_METHOD0(gl_bind_to_texture, void()); MOCK_METHOD0(secure_for_render, void()); MOCK_METHOD0(bind, void()); }; } } } #endif ./tests/include/mir/test/doubles/stub_ipc_factory.h0000644000015600001650000000375412676616125022622 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Alan Griffiths */ #ifndef MIR_TEST_DOUBLES_STUB_IPC_FACTORY_H_ #define MIR_TEST_DOUBLES_STUB_IPC_FACTORY_H_ #include "mir/test/fake_shared.h" #include "src/server/frontend/protobuf_ipc_factory.h" #include "src/server/frontend/resource_cache.h" namespace mir { namespace frontend { class SessionCredentials; } namespace test { namespace doubles { class StubIpcFactory : public frontend::ProtobufIpcFactory { public: StubIpcFactory(frontend::detail::DisplayServer& server) : server(fake_shared(server)), cache(std::make_shared()) { } std::shared_ptr make_ipc_server( mir::frontend::SessionCredentials const & /*creds*/, std::shared_ptr const& /*sink_factory*/, std::shared_ptr const& /*message_sender*/, mir::frontend::ConnectionContext const & /*connection_context*/) override { return server; } private: virtual std::shared_ptr resource_cache() override { return cache; } std::shared_ptr const server; std::shared_ptr const cache; }; } } } #endif /* MIR_TEST_DOUBLES_STUB_IPC_FACTORY_H_ */ ./tests/include/mir/test/doubles/stub_input_scene.h0000644000015600001650000000277612676616125022637 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_STUB_INPUT_SCENE_H_ #define MIR_TEST_DOUBLES_STUB_INPUT_SCENE_H_ #include "mir/input/scene.h" namespace mir { namespace test { namespace doubles { class StubInputScene : public input::Scene { void for_each(std::function const&)> const& ) override { } void add_observer(std::shared_ptr const& /* observer */) { } void remove_observer(std::weak_ptr const& /* observer */) { } void add_input_visualization(std::shared_ptr const& /* overlay */) { } void remove_input_visualization(std::weak_ptr const& /* overlay */) { } void emit_scene_changed() { } }; } } } // namespace mir #endif /* MIR_TEST_DOUBLES_STUB_INPUT_SCENE_H_ */ ./tests/include/mir/test/doubles/null_display_changer.h0000644000015600001650000000270712676616125023446 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_NULL_DISPLAY_CHANGER_H_ #define MIR_TEST_DOUBLES_NULL_DISPLAY_CHANGER_H_ #include "mir/frontend/display_changer.h" #include "mir/test/doubles/null_display_configuration.h" namespace mir { namespace test { namespace doubles { class NullDisplayChanger : public frontend::DisplayChanger { public: std::shared_ptr base_configuration() override { return std::make_shared(); } void configure(std::shared_ptr const&, std::shared_ptr const&) override { } void set_base_configuration(std::shared_ptr const&) override { } }; } } } #endif /* MIR_TEST_DOUBLES_NULL_DISPLAY_CHANGER_H_ */ ./tests/include/mir/test/doubles/fake_alarm_factory.h0000644000015600001650000000315112676616157023070 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_DOUBLES_FAKE_ALARM_FACTORY_H_ #define MIR_TEST_DOUBLES_FAKE_ALARM_FACTORY_H_ #include "mir/time/alarm_factory.h" #include "mir/time/alarm.h" #include "mir/time/clock.h" #include "mir/test/doubles/advanceable_clock.h" #include namespace mir { namespace test { namespace doubles { class FakeAlarmFactory : public time::AlarmFactory { public: FakeAlarmFactory(); std::unique_ptr create_alarm( std::function const& callback) override; std::unique_ptr create_alarm( std::shared_ptr const& callback) override; void advance_by(time::Duration step); void advance_smoothly_by(time::Duration step); int wakeup_count() const; private: class FakeAlarm; std::vector alarms; std::shared_ptr const clock; }; } } } #endif // MIR_TEST_DOUBLES_FAKE_ALARM_FACTORY_H_ ./tests/include/mir/test/doubles/mock_compositor_report.h0000644000015600001650000000335612676616125024063 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_TEST_DOUBLES_MOCK_COMPOSITOR_REPORT_H_ #define MIR_TEST_DOUBLES_MOCK_COMPOSITOR_REPORT_H_ #include "mir/compositor/compositor_report.h" #include namespace mir { namespace test { namespace doubles { class MockCompositorReport : public compositor::CompositorReport { public: MOCK_METHOD5(added_display, void(int,int,int,int, compositor::CompositorReport::SubCompositorId)); MOCK_METHOD1(began_frame, void(compositor::CompositorReport::SubCompositorId)); MOCK_METHOD2(renderables_in_frame, void(compositor::CompositorReport::SubCompositorId, graphics::RenderableList const&)); MOCK_METHOD1(rendered_frame, void(compositor::CompositorReport::SubCompositorId)); MOCK_METHOD1(finished_frame, void(compositor::CompositorReport::SubCompositorId)); MOCK_METHOD0(started, void()); MOCK_METHOD0(stopped, void()); MOCK_METHOD0(scheduled, void()); }; } // namespace doubles } // namespace test } // namespace mir #endif ./tests/include/mir/test/doubles/stub_client_buffer_factory.h0000644000015600001650000000241512676616125024647 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_CLIENT_BUFFER_FACTORY_H_ #define MIR_TEST_DOUBLES_STUB_CLIENT_BUFFER_FACTORY_H_ #include "src/include/client/mir/client_buffer_factory.h" #include "stub_client_buffer.h" namespace mir { namespace test { namespace doubles { struct StubClientBufferFactory : public client::ClientBufferFactory { std::shared_ptr create_buffer( std::shared_ptr const& package, geometry::Size size, MirPixelFormat pf) { return std::make_shared(package, size, pf); } }; } } } #endif ./tests/include/mir/test/doubles/stub_host_connection.h0000644000015600001650000000503012676616125023501 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_HOST_CONNECTION_H_ #define MIR_TEST_DOUBLES_STUB_HOST_CONNECTION_H_ #include "src/server/graphics/nested/host_connection.h" #include "src/server/graphics/nested/host_surface.h" #include "mir/graphics/platform_operation_message.h" namespace mir { namespace test { namespace doubles { class StubHostConnection : public graphics::nested::HostConnection { public: std::vector platform_fd_items() override { return {}; } EGLNativeDisplayType egl_native_display() override { return {}; } std::shared_ptr create_display_config() override { return std::shared_ptr{ new MirDisplayConfiguration{0, nullptr, 0, nullptr}}; } void set_display_config_change_callback(std::function const&) override { } void apply_display_config(MirDisplayConfiguration&) override {} std::shared_ptr create_surface( int /*width*/, int /*height*/, MirPixelFormat /*pf*/, char const* /*name*/, MirBufferUsage /*usage*/, uint32_t /*output_id*/) override { class NullHostSurface : public graphics::nested::HostSurface { public: EGLNativeWindowType egl_native_window() override { return {}; } void set_event_handler(mir_surface_event_callback, void*) override {} }; return std::make_shared(); } graphics::PlatformOperationMessage platform_operation( unsigned int, graphics::PlatformOperationMessage const&) override { return {{},{}}; } void set_cursor_image(graphics::CursorImage const&) { } void hide_cursor() { } auto graphics_platform_library() -> std::string { return {}; } }; } } } #endif /* MIR_TEST_DOUBLES_STUB_HOST_CONNECTION_H_ */ ./tests/include/mir/test/doubles/mock_compositor.h0000644000015600001650000000211612676616125022461 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_COMPOSITOR_H_ #define MIR_TEST_DOUBLES_MOCK_COMPOSITOR_H_ #include "mir/compositor/compositor.h" #include namespace mir { namespace test { namespace doubles { class MockCompositor : public compositor::Compositor { public: MOCK_METHOD0(start, void()); MOCK_METHOD0(stop, void()); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_COMPOSITOR_H_ */ ./tests/include/mir/test/doubles/fd_matcher.h0000644000015600001650000000221712676616125021350 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_FD_MATCHER_H_ #define MIR_TEST_DOUBLES_FD_MATCHER_H_ #include "mir/fd.h" #include #include #include namespace mir { namespace test { namespace doubles { MATCHER_P(RawFdMatcher, value, std::string("raw_fd does not match mir::Fd")) { return value == arg; } MATCHER(RawFdIsValid, std::string("raw_fd is not valid")) { return (fcntl(arg, F_GETFD) != -1); } } } } #endif /* MIR_TEST_DOUBLES_FD_MATCHER_H_ */ ./tests/include/mir/test/doubles/stub_buffer_allocator.h0000644000015600001650000000255512676616125023627 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_BUFFER_ALLOCATOR_H_ #define MIR_TEST_DOUBLES_STUB_BUFFER_ALLOCATOR_H_ #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/test/doubles/stub_buffer.h" #include #include namespace mir { namespace test { namespace doubles { struct StubBufferAllocator : public graphics::GraphicBufferAllocator { std::shared_ptr alloc_buffer( graphics::BufferProperties const& properties) { return std::make_shared(properties); } std::vector supported_pixel_formats() { return {}; } }; } } } #endif // MIR_TEST_DOUBLES_STUB_BUFFER_ALLOCATOR_H_ ./tests/include/mir/test/doubles/mock_buffer_ipc_message.h0000644000015600001650000000263112676616125024075 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_BUFFER_IPC_MESSAGE_H_ #define MIR_TEST_DOUBLES_MOCK_BUFFER_IPC_MESSAGE_H_ #include "mir/graphics/buffer_ipc_message.h" #include namespace mir { namespace test { namespace doubles { struct MockBufferIpcMessage : public graphics::BufferIpcMessage { ~MockBufferIpcMessage() noexcept {} MOCK_METHOD1(pack_fd, void(Fd const&)); MOCK_METHOD1(pack_data, void(int)); MOCK_METHOD1(pack_stride, void(geometry::Stride)); MOCK_METHOD1(pack_flags, void(unsigned int)); MOCK_METHOD1(pack_size, void(geometry::Size const&)); MOCK_METHOD0(fds, std::vector()); MOCK_METHOD0(data, std::vector()); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_BUFFER_IPC_MESSAGE_H_ */ ./tests/include/mir/test/doubles/mock_platform_ipc_operations.h0000644000015600001650000000351212676616125025206 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_PLATFORM_IPC_OPERATIONS_H_ #define MIR_TEST_DOUBLES_MOCK_PLATFORM_IPC_OPERATIONS_H_ #include "mir/graphics/platform_ipc_operations.h" #include "mir/graphics/platform_operation_message.h" #include "mir/graphics/buffer_ipc_message.h" #include namespace mir { namespace test { namespace doubles { struct MockPlatformIpcOperations : public graphics::PlatformIpcOperations { MockPlatformIpcOperations() { using namespace testing; ON_CALL(*this, connection_ipc_package()) .WillByDefault(Return(std::make_shared())); } MOCK_CONST_METHOD3(pack_buffer, void(graphics::BufferIpcMessage&, graphics::Buffer const&, graphics::BufferIpcMsgType)); MOCK_CONST_METHOD2(unpack_buffer, void(graphics::BufferIpcMessage&, graphics::Buffer const&)); MOCK_METHOD0(connection_ipc_package, std::shared_ptr()); MOCK_METHOD2(platform_operation, graphics::PlatformOperationMessage( unsigned int const, graphics::PlatformOperationMessage const&)); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_PLATFORM_IPC_OPERATIONS_H_ ./tests/include/mir/test/doubles/stub_driver_interpreter.h0000644000015600001650000000374512676616125024236 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_DRIVER_INTERPRETER_H_ #define MIR_TEST_DOUBLES_STUB_DRIVER_INTERPRETER_H_ #include "android_driver_interpreter.h" namespace mir { namespace test { namespace doubles { class StubDriverInterpreter : public graphics::android::AndroidDriverInterpreter { public: StubDriverInterpreter(mir::geometry::Size sz, int visual_id) : sz{sz}, visual_id{visual_id} { } StubDriverInterpreter() : StubDriverInterpreter(mir::geometry::Size{44,22}, 5) { } mir::graphics::NativeBuffer* driver_requests_buffer() override { return nullptr; } void driver_returns_buffer(ANativeWindowBuffer*, int) { } void dispatch_driver_request_format(int) override { } int driver_requests_info(int index) const override { if (index == NATIVE_WINDOW_WIDTH) return sz.width.as_uint32_t(); if (index == NATIVE_WINDOW_HEIGHT) return sz.height.as_uint32_t(); if (index == NATIVE_WINDOW_FORMAT) return visual_id; return 0; } void sync_to_display(bool) override { } void dispatch_driver_request_buffer_count(unsigned int) override { } private: mir::geometry::Size sz; int visual_id; }; } } } #endif /* MIR_TEST_DOUBLES_STUB_BUFFER_H_ */ ./tests/include/mir/test/doubles/mock_display.h0000644000015600001650000000424712676616125021737 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_DISPLAY_H_ #define MIR_TEST_DOUBLES_MOCK_DISPLAY_H_ #include "mir/graphics/display.h" #include "mir/graphics/gl_context.h" #include "mir/graphics/virtual_output.h" #include "mir/main_loop.h" #include "mir/test/gmock_fixes.h" #include namespace mir { namespace test { namespace doubles { struct MockDisplay : public graphics::Display { public: MOCK_METHOD1(for_each_display_sync_group, void (std::function const&)); MOCK_CONST_METHOD0(configuration, std::unique_ptr()); MOCK_METHOD1(configure, void(graphics::DisplayConfiguration const&)); MOCK_METHOD2(register_configuration_change_handler, void(graphics::EventHandlerRegister&, graphics::DisplayConfigurationChangeHandler const&)); MOCK_METHOD3(register_pause_resume_handlers, void(graphics::EventHandlerRegister&, graphics::DisplayPauseHandler const&, graphics::DisplayResumeHandler const&)); MOCK_METHOD0(pause, void()); MOCK_METHOD0(resume, void()); MOCK_METHOD1(create_hardware_cursor, std::shared_ptr(std::shared_ptr const&)); MOCK_METHOD0(create_gl_context, std::unique_ptr()); MOCK_METHOD2(create_virtual_output, std::unique_ptr(int, int)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_DISPLAY_H_ */ ./tests/include/mir/test/doubles/stub_android_native_buffer.h0000644000015600001650000000347212676616125024634 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_TEST_DOUBLES_STUB_ANDROID_NATIVE_BUFFER_H_ #define MIR_TEST_DOUBLES_STUB_ANDROID_NATIVE_BUFFER_H_ #include "src/platforms/android/include/native_buffer.h" #include "mir/geometry/size.h" namespace mir { namespace test { namespace doubles { struct StubAndroidNativeBuffer : public graphics::NativeBuffer { StubAndroidNativeBuffer() { } StubAndroidNativeBuffer(geometry::Size sz) { stub_anwb.width = sz.width.as_int(); stub_anwb.height = sz.height.as_int(); } auto anwb() const -> ANativeWindowBuffer* override { return const_cast(&stub_anwb); } auto handle() const -> buffer_handle_t override { return &native_handle; } auto copy_fence() const -> graphics::android::NativeFence override { return -1; } void ensure_available_for(graphics::android::BufferAccess) {} void update_usage(graphics::android::NativeFence&, graphics::android::BufferAccess) {} void lock_for_gpu() {}; void wait_for_unlock_by_gpu() {}; ANativeWindowBuffer stub_anwb; native_handle_t native_handle; }; } } } #endif /* MIR_TEST_DOUBLES_STUB_ANDROID_NATIVE_BUFFER_H_ */ ./tests/include/mir/test/doubles/mock_virtual_terminal.h0000644000015600001650000000245512676616125023652 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_VIRTUAL_TERMINAL_H_ #define MIR_TEST_DOUBLES_MOCK_VIRTUAL_TERMINAL_H_ #include "src/platforms/mesa/server/kms/virtual_terminal.h" #include namespace mir { namespace test { namespace doubles { class MockVirtualTerminal : public graphics::mesa::VirtualTerminal { public: MOCK_METHOD0(set_graphics_mode, void()); MOCK_METHOD3(register_switch_handlers, void(graphics::EventHandlerRegister&, std::function const&, std::function const&)); MOCK_METHOD0(restore, void()); }; } } } #endif ./tests/include/mir/test/doubles/mock_hwc_device_wrapper.h0000644000015600001650000000555112676616125024131 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_HWC_DEVICE_WRAPPER_H_ #define MIR_TEST_DOUBLES_MOCK_HWC_DEVICE_WRAPPER_H_ #include "src/platforms/android/server/hwc_wrapper.h" #include namespace mir { namespace test { namespace doubles { struct MockHWCDeviceWrapper : public graphics::android::HwcWrapper { MockHWCDeviceWrapper() { using namespace testing; using graphics::android::ConfigId; ON_CALL(*this, display_configs(_)) .WillByDefault(Return(std::vector{ConfigId{34}})); } MOCK_CONST_METHOD1(prepare, void(std::array const&)); MOCK_CONST_METHOD1(set, void(std::array const&)); MOCK_CONST_METHOD1(vsync_signal_on, void(graphics::android::DisplayName)); MOCK_CONST_METHOD1(vsync_signal_off, void(graphics::android::DisplayName)); MOCK_CONST_METHOD1(display_on, void(graphics::android::DisplayName)); MOCK_CONST_METHOD1(display_off, void(graphics::android::DisplayName)); MOCK_METHOD4(subscribe_to_events, void(void const*, std::function const&, std::function const&, std::function const&)); MOCK_METHOD1(unsubscribe_from_events_, void(void const*)); void unsubscribe_from_events(void const* id) noexcept { unsubscribe_from_events_(id); } MOCK_CONST_METHOD1(display_configs, std::vector(graphics::android::DisplayName)); MOCK_CONST_METHOD4(display_attributes, int( graphics::android::DisplayName, graphics::android::ConfigId, uint32_t const*, int32_t*)); MOCK_CONST_METHOD2(power_mode, void(graphics::android::DisplayName, graphics::android::PowerMode)); MOCK_CONST_METHOD1(has_active_config, bool(graphics::android::DisplayName)); MOCK_CONST_METHOD1(active_config_for, graphics::android::ConfigId(graphics::android::DisplayName)); MOCK_CONST_METHOD2(set_active_config, void(graphics::android::DisplayName name, graphics::android::ConfigId id)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_HWC_DEVICE_WRAPPER_H_ */ ./tests/include/mir/test/doubles/mock_renderable.h0000644000015600001650000000357512676616125022400 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_RENDERABLE_H_ #define MIR_TEST_DOUBLES_MOCK_RENDERABLE_H_ #include "mir/test/doubles/stub_buffer.h" #include #include namespace mir { namespace test { namespace doubles { struct MockRenderable : public graphics::Renderable { MockRenderable() { ON_CALL(*this, screen_position()) .WillByDefault(testing::Return(geometry::Rectangle{{},{}})); ON_CALL(*this, buffer()) .WillByDefault(testing::Return(std::make_shared())); ON_CALL(*this, alpha()) .WillByDefault(testing::Return(1.0f)); ON_CALL(*this, transformation()) .WillByDefault(testing::Return(glm::mat4{})); ON_CALL(*this, visible()) .WillByDefault(testing::Return(true)); } MOCK_CONST_METHOD0(id, ID()); MOCK_CONST_METHOD0(buffer, std::shared_ptr()); MOCK_CONST_METHOD0(screen_position, geometry::Rectangle()); MOCK_CONST_METHOD0(alpha, float()); MOCK_CONST_METHOD0(transformation, glm::mat4()); MOCK_CONST_METHOD0(visible, bool()); MOCK_CONST_METHOD0(shaped, bool()); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_RENDERABLE_H_ */ ./tests/include/mir/test/doubles/mock_hwc_report.h0000644000015600001650000000406612676616125022445 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_HWC_REPORT_H_ #define MIR_TEST_DOUBLES_MOCK_HWC_REPORT_H_ #include "src/platforms/android/server/hwc_report.h" #include namespace mir { namespace test { namespace doubles { struct MockHwcReport : public graphics::android::HwcReport { MOCK_CONST_METHOD1(report_list_submitted_to_prepare, void(std::array const& displays)); MOCK_CONST_METHOD1(report_prepare_done, void(std::array const& displays)); MOCK_CONST_METHOD1(report_set_list, void(std::array const& displays)); MOCK_CONST_METHOD1(report_set_done, void(std::array const& displays)); MOCK_CONST_METHOD1(report_overlay_optimization, void(graphics::android::OverlayOptimization)); MOCK_CONST_METHOD0(report_display_on, void()); MOCK_CONST_METHOD0(report_display_off, void()); MOCK_CONST_METHOD0(report_vsync_on, void()); MOCK_CONST_METHOD0(report_vsync_off, void()); MOCK_CONST_METHOD1(report_hwc_version, void(graphics::android::HwcVersion)); MOCK_CONST_METHOD0(report_legacy_fb_module, void()); MOCK_CONST_METHOD1(report_power_mode, void(graphics::android::PowerMode)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_HWC_REPORT_H_ */ ./tests/include/mir/test/doubles/mock_android_native_buffer.h0000644000015600001650000000412112676616125024600 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_ANDROID_NATIVE_BUFFER_H_ #define MIR_TEST_DOUBLES_MOCK_ANDROID_NATIVE_BUFFER_H_ #include "src/platforms/android/include/native_buffer.h" #include "mir/geometry/size.h" #include namespace mir { namespace test { namespace doubles { struct MockAndroidNativeBuffer : public graphics::NativeBuffer { MockAndroidNativeBuffer() { using namespace testing; ON_CALL(*this, anwb()) .WillByDefault(Return(&stub_anwb)); ON_CALL(*this, handle()) .WillByDefault(Return(&native_handle)); ON_CALL(*this, copy_fence()) .WillByDefault(Return(-1)); } MockAndroidNativeBuffer(geometry::Size sz) : MockAndroidNativeBuffer() { stub_anwb.width = sz.width.as_int(); stub_anwb.height = sz.height.as_int(); } MOCK_CONST_METHOD0(anwb, ANativeWindowBuffer*()); MOCK_CONST_METHOD0(handle, buffer_handle_t()); MOCK_CONST_METHOD0(copy_fence, graphics::android::NativeFence()); MOCK_METHOD1(ensure_available_for, void(graphics::android::BufferAccess)); MOCK_METHOD2(update_usage, void(graphics::android::NativeFence&, graphics::android::BufferAccess)); MOCK_METHOD0(lock_for_gpu, void()); MOCK_METHOD0(wait_for_unlock_by_gpu, void()); ANativeWindowBuffer stub_anwb; native_handle_t native_handle; }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_ANDROID_NATIVE_BUFFER_H_ */ ./tests/include/mir/test/doubles/mock_input_surface.h0000644000015600001650000000274612676616125023143 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_SURFACE_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_SURFACE_H_ #include "mir/input/surface.h" #include namespace mir { namespace test { namespace doubles { class MockInputSurface : public input::Surface { public: ~MockInputSurface() noexcept {} MOCK_CONST_METHOD0(name, std::string()); MOCK_CONST_METHOD0(input_bounds, geometry::Rectangle()); MOCK_CONST_METHOD1(input_area_contains, bool(geometry::Point const&)); MOCK_CONST_METHOD0(input_channel, std::shared_ptr()); MOCK_CONST_METHOD0(cursor_image, std::shared_ptr()); MOCK_CONST_METHOD0(reception_mode, input::InputReceptionMode()); MOCK_METHOD1(consume, void(MirEvent const*)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_INPUT_SURFACE_H_ */ ./tests/include/mir/test/doubles/stub_frame_dropping_policy_factory.h0000644000015600001650000000316412676616125026415 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Alberto Aguirre */ #ifndef MIR_TEST_DOUBLES_STUB_FRAME_DROPPING_POLICY_FACTORY_H_ #define MIR_TEST_DOUBLES_STUB_FRAME_DROPPING_POLICY_FACTORY_H_ #include "mir/compositor/frame_dropping_policy_factory.h" #include "mir/compositor/frame_dropping_policy.h" namespace mc = mir::compositor; namespace mir { namespace test { namespace doubles { class StubFrameDroppingPolicy : public mc::FrameDroppingPolicy { public: void swap_now_blocking() { } void swap_unblocked() { } }; class StubFrameDroppingPolicyFactory : public mc::FrameDroppingPolicyFactory { public: std::unique_ptr create_policy( std::shared_ptr const&) const override { return std::unique_ptr{new StubFrameDroppingPolicy}; } }; } } } #endif // TEST_DOUBLES_STUB_FRAME_DROPPING_POLICY_FACTORY_H_ ./tests/include/mir/test/doubles/stub_surface_factory.h0000644000015600001650000000273112676616125023471 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_SURFACE_FACTORY_H_ #define MIR_TEST_DOUBLES_STUB_SURFACE_FACTORY_H_ #include #include #include namespace mir { namespace test { namespace doubles { class StubSurfaceFactory : public scene::SurfaceFactory { public: std::shared_ptr create_surface( std::shared_ptr const&, scene::SurfaceCreationParameters const& params) override { using namespace testing; auto surface = std::make_shared>(); ON_CALL(*surface, size()).WillByDefault(Return(params.size)); return surface;; } }; } } } // namespace mir #endif /* MIR_TEST_DOUBLES_STUB_SURFACE_FACTORY_H_ */ ./tests/include/mir/test/doubles/mock_drm.h0000644000015600001650000001303012676616125021042 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_DRM_H_ #define MIR_TEST_DOUBLES_MOCK_DRM_H_ #include #include #include namespace mir { namespace geometry { struct Size; } namespace test { namespace doubles { class FakeDRMResources { public: FakeDRMResources(); ~FakeDRMResources(); int fd() const; int write_fd() const; drmModeRes* resources_ptr(); void add_crtc(uint32_t id, drmModeModeInfo mode); void add_encoder(uint32_t encoder_id, uint32_t crtc_id, uint32_t possible_crtcs_mask); void add_connector(uint32_t connector_id, uint32_t type, drmModeConnection connection, uint32_t encoder_id, std::vector& modes, std::vector& possible_encoder_ids, geometry::Size const& physical_size); void prepare(); void reset(); drmModeCrtc* find_crtc(uint32_t id); drmModeEncoder* find_encoder(uint32_t id); drmModeConnector* find_connector(uint32_t id); enum ModePreference {NormalMode, PreferredMode}; static drmModeModeInfo create_mode(uint16_t hdisplay, uint16_t vdisplay, uint32_t clock, uint16_t htotal, uint16_t vtotal, ModePreference preferred); private: int pipe_fds[2]; drmModeRes resources; std::vector crtcs; std::vector encoders; std::vector connectors; std::vector crtc_ids; std::vector encoder_ids; std::vector connector_ids; std::vector modes; std::vector modes_empty; std::vector connector_encoder_ids; }; class MockDRM { public: MockDRM(); ~MockDRM() noexcept; MOCK_METHOD3(open, int(char const* path, int flags, mode_t mode)); MOCK_METHOD2(drmOpen, int(const char *name, const char *busid)); MOCK_METHOD1(drmClose, int(int fd)); MOCK_METHOD3(drmIoctl, int(int fd, unsigned long request, void *arg)); MOCK_METHOD1(drmModeGetResources, drmModeResPtr(int fd)); MOCK_METHOD2(drmModeGetConnector, drmModeConnectorPtr(int fd, uint32_t connectorId)); MOCK_METHOD2(drmModeGetEncoder, drmModeEncoderPtr(int fd, uint32_t encoder_id)); MOCK_METHOD2(drmModeGetCrtc, drmModeCrtcPtr(int fd, uint32_t crtcId)); MOCK_METHOD8(drmModeSetCrtc, int(int fd, uint32_t crtcId, uint32_t bufferId, uint32_t x, uint32_t y, uint32_t *connectors, int count, drmModeModeInfoPtr mode)); MOCK_METHOD1(drmModeFreeResources, void(drmModeResPtr ptr)); MOCK_METHOD1(drmModeFreeConnector, void(drmModeConnectorPtr ptr)); MOCK_METHOD1(drmModeFreeEncoder, void(drmModeEncoderPtr ptr)); MOCK_METHOD1(drmModeFreeCrtc, void(drmModeCrtcPtr ptr)); MOCK_METHOD8(drmModeAddFB, int(int fd, uint32_t width, uint32_t height, uint8_t depth, uint8_t bpp, uint32_t pitch, uint32_t bo_handle, uint32_t *buf_id)); MOCK_METHOD9(drmModeAddFB2, int(int fd, uint32_t width, uint32_t height, uint32_t pixel_format, uint32_t bo_handles[4], uint32_t pitches[4], uint32_t offsets[4], uint32_t *buf_id, uint32_t flags)); MOCK_METHOD2(drmModeRmFB, int(int fd, uint32_t bufferId)); MOCK_METHOD5(drmModePageFlip, int(int fd, uint32_t crtc_id, uint32_t fb_id, uint32_t flags, void *user_data)); MOCK_METHOD2(drmHandleEvent, int(int fd, drmEventContextPtr evctx)); MOCK_METHOD3(drmGetCap, int(int fd, uint64_t capability, uint64_t *value)); MOCK_METHOD2(drmModeGetProperty, drmModePropertyPtr(int fd, uint32_t propertyId)); MOCK_METHOD1(drmModeFreeProperty, void(drmModePropertyPtr)); MOCK_METHOD4(drmModeConnectorSetProperty, int(int fd, uint32_t connector_id, uint32_t property_id, uint64_t value)); MOCK_METHOD2(drmGetMagic, int(int fd, drm_magic_t *magic)); MOCK_METHOD2(drmAuthMagic, int(int fd, drm_magic_t magic)); MOCK_METHOD4(drmPrimeHandleToFD, int(int fd, uint32_t handle, uint32_t flags, int *prime_fd)); MOCK_METHOD3(drmPrimeFDToHandle, int(int fd, int prime_fd, uint32_t *handle)); MOCK_METHOD1(drmSetMaster, int(int fd)); MOCK_METHOD1(drmDropMaster, int(int fd)); MOCK_METHOD5(drmModeSetCursor, int (int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height)); MOCK_METHOD4(drmModeMoveCursor,int (int fd, uint32_t crtcId, int x, int y)); MOCK_METHOD2(drmSetInterfaceVersion, int (int fd, drmSetVersion* sv)); MOCK_METHOD1(drmGetBusid, char* (int fd)); MOCK_METHOD1(drmFreeBusid, void (const char*)); FakeDRMResources fake_drm; }; } } } #endif /* MIR_TEST_DOUBLES_DRM_MOCK_H_ */ ./tests/include/mir/test/doubles/mock_coordinate_translator.h0000644000015600001650000000242712676616125024670 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_DOUBLES_MOCK_COORDINATE_TRANSLATOR_H_ #define MIR_TEST_DOUBLES_MOCK_COORDINATE_TRANSLATOR_H_ #include "src/server/frontend/coordinate_translator.h" #include namespace mir { namespace test { namespace doubles { class MockCoordinateTranslator : public mir::frontend::CoordinateTranslator { MOCK_METHOD3(surface_to_screen, geometry::Point(std::shared_ptr, uint32_t, uint32_t)); }; using StubCoordinateTranslator = testing::NiceMock; } } } #endif /* MIR_TEST_DOUBLES_MOCK_COORDINATE_TRANSLATOR_H_ */ ./tests/include/mir/test/doubles/null_prompt_session.h0000644000015600001650000000244612676616125023376 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Nick Dedekind */ #ifndef MIR_TEST_DOUBLES_NULL_PROMPT_SESSION_H_ #define MIR_TEST_DOUBLES_NULL_PROMPT_SESSION_H_ #include "mir/scene/prompt_session.h" namespace mir { namespace test { namespace doubles { class NullPromptSession : public scene::PromptSession { public: void start(std::shared_ptr const&) override { } void stop(std::shared_ptr const&) override { } void suspend(std::shared_ptr const&) override { } void resume(std::shared_ptr const&) override { } }; } } } #endif /* MIR_TEST_DOUBLES_NULL_PROMPT_SESSION_H_ */ ./tests/include/mir/test/doubles/mock_display_layout.h0000644000015600001650000000246512676616125023334 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_DISPLAY_LAYOUT_H_ #define MIR_TEST_DOUBLES_MOCK_DISPLAY_LAYOUT_H_ #include "mir/shell/display_layout.h" #include namespace mir { namespace test { namespace doubles { class MockDisplayLayout : public shell::DisplayLayout { public: MOCK_METHOD1(clip_to_output, void(geometry::Rectangle& rect)); MOCK_METHOD1(size_to_output, void(geometry::Rectangle& rect)); MOCK_METHOD2(place_in_output, void(graphics::DisplayConfigurationOutputId id, geometry::Rectangle& rect)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_DISPLAY_LAYOUT_H_ */ ./tests/include/mir/test/doubles/mock_egl_native_surface.h0000644000015600001650000000255112676616125024113 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_EGL_NATIVE_SURFACE_H_ #define MIR_TEST_DOUBLES_MOCK_EGL_NATIVE_SURFACE_H_ #include "mir/egl_native_surface.h" #include namespace mir { namespace test { namespace doubles { struct MockEGLNativeSurface : public client::EGLNativeSurface { MOCK_CONST_METHOD0(get_parameters, MirSurfaceParameters()); MOCK_METHOD0(get_current_buffer, std::shared_ptr()); MOCK_METHOD0(request_and_wait_for_next_buffer, void()); MOCK_METHOD2(request_and_wait_for_configure, void(MirSurfaceAttrib,int)); MOCK_METHOD1(set_buffer_cache_size, void(unsigned int)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_EGL_NATIVE_SURFACE_H_ */ ./tests/include/mir/test/doubles/stub_gl_config.h0000644000015600001650000000215312676616125022237 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_GL_CONFIG_H_ #define MIR_TEST_DOUBLES_STUB_GL_CONFIG_H_ #include "mir/graphics/gl_config.h" #include namespace mir { namespace test { namespace doubles { struct StubGLConfig : public graphics::GLConfig { int depth_buffer_bits() const override { return 0; } int stencil_buffer_bits() const override { return 0; } }; } } } #endif /* MIR_TEST_DOUBLES_STUB_GL_CONFIG_H_ */ ./tests/include/mir/test/doubles/fake_ipc_factory.h0000644000015600001650000000517612676616125022553 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FAKE_IPC_FACTORY_H_ #define MIR_TEST_FAKE_IPC_FACTORY_H_ #include "src/server/frontend/default_ipc_factory.h" #include namespace mir { namespace test { namespace doubles { class FakeIpcFactory : public frontend::DefaultIpcFactory { public: using frontend::DefaultIpcFactory::DefaultIpcFactory; std::shared_ptr make_default_mediator( std::shared_ptr const& shell, std::shared_ptr const& graphics_platform, std::shared_ptr const& changer, std::shared_ptr const& buffer_allocator, std::shared_ptr const& sm_report, std::shared_ptr const& sink, std::shared_ptr const& effective_screencast, frontend::ConnectionContext const& connection_context, std::shared_ptr const& cursor_images) { return frontend::DefaultIpcFactory::make_mediator( shell, graphics_platform, changer, buffer_allocator, sm_report, sink, effective_screencast, connection_context, cursor_images); } MOCK_METHOD9(make_mediator, std::shared_ptr( std::shared_ptr const& shell, std::shared_ptr const& graphics_platform, std::shared_ptr const& changer, std::shared_ptr const& buffer_allocator, std::shared_ptr const& sm_report, std::shared_ptr const& sink, std::shared_ptr const& effective_screencast, frontend::ConnectionContext const& connection_context, std::shared_ptr const& cursor_images)); }; } } } #endif /* MIR_TEST_FAKE_IPC_FACTORY_H_ */ ./tests/include/mir/test/doubles/mock_cursor_listener.h0000644000015600001650000000207212676616125023506 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_CURSOR_LISTENER_H #define MIR_TEST_DOUBLES_MOCK_CURSOR_LISTENER_H #include "mir/input/cursor_listener.h" #include namespace mir { namespace test { namespace doubles { struct MockCursorListener : public input::CursorListener { MOCK_METHOD2(cursor_moved_to, void(float, float)); ~MockCursorListener() noexcept {} }; } } } #endif ./tests/include/mir/test/doubles/stub_shell.h0000644000015600001650000000372512676616125021425 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_STUB_SHELL_H_ #define MIR_TEST_DOUBLES_STUB_SHELL_H_ #include "mir/frontend/shell.h" #include "mir/test/doubles/stub_session.h" namespace mir { namespace test { namespace doubles { struct StubShell : public frontend::Shell { StubShell() : stub_session(std::make_shared()) { } std::shared_ptr open_session(pid_t, std::string const& /* name */, std::shared_ptr const& /* sink */) override { return stub_session; } void close_session(std::shared_ptr const& /* session */) override { } std::shared_ptr start_prompt_session_for(std::shared_ptr const& /* session */, scene::PromptSessionCreationParameters const& /* params */) { return std::shared_ptr(); } void add_prompt_provider_for( std::shared_ptr const& /* prompt_session */, std::shared_ptr const& /* session */) { } void stop_prompt_session(std::shared_ptr const& /* prompt_session */) { } std::shared_ptr const stub_session; }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_STUB_SHELL_H_ ./tests/include/mir/test/doubles/stub_scene_element.h0000644000015600001650000000314612676616125023121 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_SCENE_ELEMENT_H_ #define MIR_TEST_DOUBLES_STUB_SCENE_ELEMENT_H_ #include "mir/compositor/scene_element.h" #include "mir/compositor/decoration.h" #include "stub_renderable.h" namespace mir { namespace test { namespace doubles { class StubSceneElement : public compositor::SceneElement { public: StubSceneElement(std::shared_ptr const& renderable) : renderable_{renderable} { } StubSceneElement() : StubSceneElement(std::make_shared()) { } std::shared_ptr renderable() const { return renderable_; } void rendered() override { } void occluded() override { } std::unique_ptr decoration() const override { return nullptr; } private: std::shared_ptr const renderable_; }; } } } #endif ./tests/include/mir/test/doubles/stub_session.h0000644000015600001650000000307012676616125021772 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_STUB_SESSION_H_ #define MIR_TEST_DOUBLES_STUB_SESSION_H_ #include "mir/frontend/session.h" namespace mir { namespace test { namespace doubles { struct StubSession : public frontend::Session { std::shared_ptr get_surface(frontend::SurfaceId /* surface */) const override { return std::shared_ptr(); } std::string name() const override { return std::string(); } std::shared_ptr get_buffer_stream(frontend::BufferStreamId) const override { return nullptr; } void destroy_buffer_stream(frontend::BufferStreamId) override { } frontend::BufferStreamId create_buffer_stream(graphics::BufferProperties const&) override { return frontend::BufferStreamId(); } }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_STUB_SESSION_H_ ./tests/include/mir/test/doubles/mock_framebuffer_bundle.h0000644000015600001650000000311112676616125024074 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_FRAMEBUFFER_BUNDLE_H_ #define MIR_TEST_DOUBLES_MOCK_FRAMEBUFFER_BUNDLE_H_ #include "src/platforms/android/server/framebuffer_bundle.h" #include "stub_buffer.h" #include namespace mir { namespace test { namespace doubles { struct MockFBBundle : public graphics::android::FramebufferBundle { MockFBBundle(geometry::Size sz) { ON_CALL(*this, last_rendered_buffer()) .WillByDefault(testing::Return(std::make_shared())); ON_CALL(*this, fb_size()) .WillByDefault(testing::Return(sz)); } MockFBBundle() : MockFBBundle({0,0}) { } MOCK_METHOD0(fb_size, geometry::Size()); MOCK_METHOD0(buffer_for_render, std::shared_ptr()); MOCK_METHOD0(last_rendered_buffer, std::shared_ptr()); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_FRAMEBUFFER_BUNDLE_H_ */ ./tests/include/mir/test/doubles/mock_fb_hal_device.h0000644000015600001650000000533712676616125023025 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_FB_HAL_DEVICE_H_ #define MIR_TEST_DOUBLES_MOCK_FB_HAL_DEVICE_H_ #include #include #include #include namespace mir { namespace test { namespace doubles { class MockFBHalDevice : public framebuffer_device_t { public: MockFBHalDevice(unsigned int const width, unsigned int const height, int const pf, int const numfbs, float dpi_x, float dpi_y) : framebuffer_device_t({ hw_device_t(), 0, width, height, 0, pf, dpi_x, dpi_y, 0.0f, 0, 1, numfbs, {0,0,0,0,0,0,0}, nullptr, nullptr,nullptr,nullptr, nullptr,nullptr, {0,0,0,0,0,0} }) { post = hook_post; setSwapInterval = hook_setSwapInterval; enableScreen = hook_enableScreen; } MockFBHalDevice() : MockFBHalDevice(1,1,1,1, 1.0f, 1.0f) { } static int hook_post(struct framebuffer_device_t* mock_fb, buffer_handle_t handle) { MockFBHalDevice* mocker = static_cast(mock_fb); return mocker->post_interface(mock_fb, handle); } static int hook_setSwapInterval(struct framebuffer_device_t* mock_fb, int interval) { MockFBHalDevice* mocker = static_cast(mock_fb); return mocker->setSwapInterval_interface(mock_fb, interval); } static int hook_enableScreen(struct framebuffer_device_t* mock_fb, int enable) { MockFBHalDevice* mocker = static_cast(mock_fb); return mocker->enableScreen_interface(mock_fb, enable); } MOCK_METHOD2(enableScreen_interface, int(struct framebuffer_device_t*, int)); MOCK_METHOD2(post_interface, int(struct framebuffer_device_t*, buffer_handle_t)); MOCK_METHOD2(setSwapInterval_interface, int(struct framebuffer_device_t*, int)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_FB_HAL_DEVICE_H_ */ ./tests/include/mir/test/doubles/stub_buffer_stream_factory.h0000644000015600001650000000301612676616125024662 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #ifndef MIR_TEST_DOUBLES_STUB_BUFFER_STREAM_FACTORY_H_ #define MIR_TEST_DOUBLES_STUB_BUFFER_STREAM_FACTORY_H_ #include "mir/scene/buffer_stream_factory.h" #include "stub_buffer_stream.h" namespace mir { namespace test { namespace doubles { struct StubBufferStreamFactory : public scene::BufferStreamFactory { std::shared_ptr create_buffer_stream( frontend::BufferStreamId i, std::shared_ptr const& s, int, graphics::BufferProperties const& p) { return create_buffer_stream(i, s, p); } std::shared_ptr create_buffer_stream( frontend::BufferStreamId, std::shared_ptr const&, graphics::BufferProperties const&) { return std::make_shared(); } }; } } } #endif // MIR_TEST_DOUBLES_STUB_BUFFER_STREAM_FACTORY_H_ ./tests/include/mir/test/doubles/mock_session.h0000644000015600001650000000262712676616125021755 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_MOCK_SESSION_H_ #define MIR_TEST_DOUBLES_MOCK_SESSION_H_ #include "mir/frontend/session.h" #include namespace mir { namespace test { namespace doubles { struct MockSession : public frontend::Session { MOCK_CONST_METHOD1(get_surface, std::shared_ptr(frontend::SurfaceId)); MOCK_CONST_METHOD1(get_buffer_stream, std::shared_ptr(frontend::BufferStreamId)); MOCK_METHOD1(create_buffer_stream, frontend::BufferStreamId(graphics::BufferProperties const&)); MOCK_METHOD1(destroy_buffer_stream, void(frontend::BufferStreamId)); MOCK_CONST_METHOD0(name, std::string()); }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_MOCK_SESSION_H_ ./tests/include/mir/test/doubles/mock_surface.h0000644000015600001650000000512712676616125021720 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_MOCK_SURFACE_H_ #define MIR_TEST_DOUBLES_MOCK_SURFACE_H_ #include "src/server/scene/basic_surface.h" #include "src/server/report/null_report_factory.h" #include "mock_buffer_stream.h" #include "stub_input_channel.h" // GMock wants to be able to construct MirEvent as it is passed by reference to consume #include "mir/events/event_private.h" #include namespace mir { namespace test { namespace doubles { struct MockSurface : public scene::BasicSurface { MockSurface() : scene::BasicSurface( {}, {{},{}}, true, std::make_shared>(), std::make_shared(), {}, {}, mir::report::null_scene_report()) { ON_CALL(*this, primary_buffer_stream()) .WillByDefault(testing::Return(std::make_shared>())); } ~MockSurface() noexcept {} MOCK_METHOD0(hide, void()); MOCK_METHOD0(show, void()); MOCK_CONST_METHOD0(visible, bool()); MOCK_METHOD0(force_requests_to_complete, void()); MOCK_METHOD0(advance_client_buffer, std::shared_ptr()); MOCK_CONST_METHOD0(size, geometry::Size()); MOCK_CONST_METHOD0(pixel_format, MirPixelFormat()); MOCK_CONST_METHOD0(supports_input, bool()); MOCK_CONST_METHOD0(client_input_fd, int()); MOCK_METHOD2(configure, int(MirSurfaceAttrib, int)); MOCK_METHOD1(add_observer, void(std::shared_ptr const&)); MOCK_METHOD1(remove_observer, void(std::weak_ptr const&)); MOCK_METHOD1(consume, void(MirEvent const*)); MOCK_CONST_METHOD0(primary_buffer_stream, std::shared_ptr()); MOCK_METHOD1(set_streams, void(std::list const&)); }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_MOCK_SURFACE_H_ ./tests/include/mir/test/doubles/mock_event_sink.h0000644000015600001650000000311212676616125022425 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_MOCK_EVENT_SINK_H_ #define MIR_TEST_DOUBLES_MOCK_EVENT_SINK_H_ #include "mir/frontend/event_sink.h" #include "mir/graphics/display_configuration.h" #include "mir/events/event_private.h" #include "mir/graphics/display_configuration.h" #include namespace mir { namespace test { namespace doubles { struct MockEventSink : public frontend::EventSink { MOCK_METHOD1(handle_event, void(MirEvent const&)); MOCK_METHOD1(handle_lifecycle_event, void(MirLifecycleState)); MOCK_METHOD1(handle_display_config_change, void(graphics::DisplayConfiguration const&)); MOCK_METHOD1(send_ping, void(int32_t)); MOCK_METHOD3(send_buffer, void(frontend::BufferStreamId, graphics::Buffer&, graphics::BufferIpcMsgType)); MOCK_METHOD1(handle_input_device_change, void(std::vector> const&)); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_EVENT_SINK_H_*/ ./tests/include/mir/test/doubles/stub_scene_surface.h0000644000015600001650000001006112676616125023112 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_STUB_SCENE_SURFACE_H_ #define MIR_TEST_DOUBLES_STUB_SCENE_SURFACE_H_ #include "mir/scene/surface.h" #include "mir/test/doubles/stub_input_channel.h" #include #include namespace mir { namespace test { namespace doubles { class StubSceneSurface : public mir::scene::Surface { public: std::shared_ptr channel; int fd; mir::input::InputReceptionMode input_mode{mir::input::InputReceptionMode::normal}; StubSceneSurface(int fd) : channel(std::make_shared(fd)), fd(fd) { } std::shared_ptr input_channel() const override { return channel; } mir::input::InputReceptionMode reception_mode() const override { return input_mode; } std::string name() const override { return {}; } geometry::Point top_left() const override { return {}; } geometry::Size client_size() const override { return {};} geometry::Size size() const override { return {}; } geometry::Rectangle input_bounds() const override { return {{},{}}; } bool input_area_contains(mir::geometry::Point const&) const override { return false; } void set_streams(std::list const&) override {} graphics::RenderableList generate_renderables(compositor::CompositorID) const override { return {}; } int buffers_ready_for_compositor(void const*) const override { return 0; } float alpha() const override { return 0.0f;} MirSurfaceType type() const override { return mir_surface_type_normal; } MirSurfaceState state() const override { return mir_surface_state_unknown; } void hide() override {} void show() override {} bool visible() const override { return true; } void move_to(geometry::Point const&) override {} void set_input_region(std::vector const&) override {} void resize(geometry::Size const&) override {} void set_transformation(glm::mat4 const&) override {} void set_alpha(float) override {} void set_orientation(MirOrientation) {} void add_observer(std::shared_ptr const&) override {} void remove_observer(std::weak_ptr const&) override {} void set_reception_mode(input::InputReceptionMode mode) override { input_mode = mode; } void consume(MirEvent const*) override {} void set_cursor_image(std::shared_ptr const& /* image */) {} std::shared_ptr cursor_image() const { return {}; } void request_client_surface_close() override {} bool supports_input() const override { return true;} int client_input_fd() const override { return fd;} int configure(MirSurfaceAttrib, int) override { return 0; } int query(MirSurfaceAttrib) const override { return 0; } void with_most_recent_buffer_do(std::function const&) {} std::shared_ptr parent() const override { return nullptr; } void set_keymap(MirInputDeviceId, std::string const&, std::string const&, std::string const&, std::string const&) override {} void set_cursor_stream(std::shared_ptr const&, geometry::Displacement const&) {} void rename(std::string const&) {} std::shared_ptr primary_buffer_stream() const override { return nullptr; } }; } } } #endif ./tests/include/mir/test/doubles/stub_gl_program_factory.h0000644000015600001650000000245712676616125024177 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_GL_PROGRAM_FACTORY_H_ #define MIR_TEST_DOUBLES_STUB_GL_PROGRAM_FACTORY_H_ #include "mir/gl/program_factory.h" #include "stub_gl_program.h" namespace mir { namespace test { namespace doubles { class StubGLProgramFactory : public gl::ProgramFactory { public: std::unique_ptr create_gl_program(std::string const&, std::string const&) const { return std::make_unique(); } std::unique_ptr create_texture_cache() const { return nullptr; } }; } } } // namespace mir #endif /* MIR_TEST_DOUBLES_STUB_GL_PROGRAM_FACTORY_H_ */ ./tests/include/mir/test/doubles/mock_input_platform.h0000644000015600001650000000213012676616125023322 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_PLATFORM_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_PLATFORM_H_ #include "mir/input/platform.h" #include namespace mir { namespace test { namespace doubles { struct MockInputPlatform : input::Platform { MOCK_METHOD0(dispatchable, std::shared_ptr()); MOCK_METHOD0(start, void()); MOCK_METHOD0(stop, void()); }; } } } #endif ./tests/include/mir/test/doubles/mock_touch_visualizer.h0000644000015600001650000000220612676616125023662 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_TOUCH_VISUALIZER_H_ #define MIR_TEST_DOUBLES_MOCK_TOUCH_VISUALIZER_H_ #include "mir/input/touch_visualizer.h" #include namespace mir { namespace test { namespace doubles { struct MockTouchVisualizer : public input::TouchVisualizer { MOCK_METHOD1(visualize_touches, void(std::vector const&)); MOCK_METHOD0(enable, void()); MOCK_METHOD0(disable, void()); }; } } } #endif ./tests/include/mir/test/doubles/null_virtual_terminal.h0000644000015600001650000000251112676616125023664 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_VIRTUAL_TERMINAL_H_ #define MIR_TEST_DOUBLES_NULL_VIRTUAL_TERMINAL_H_ #include "src/platforms/mesa/server/kms/virtual_terminal.h" namespace mir { namespace test { namespace doubles { class NullVirtualTerminal : public graphics::mesa::VirtualTerminal { public: void set_graphics_mode() override {} void register_switch_handlers(graphics::EventHandlerRegister&, std::function const&, std::function const&) override { } void restore() override {} }; } } } #endif /* MIR_TEST_DOUBLES_NULL_VIRTUAL_TERMINAL_H_ */ ./tests/include/mir/test/doubles/stub_renderable.h0000644000015600001650000000655112676616125022421 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_RENDERABLE_H_ #define MIR_TEST_DOUBLES_STUB_RENDERABLE_H_ #include "mir/test/doubles/stub_buffer.h" #include #include #define GLM_FORCE_RADIANS #define GLM_PRECISION_MEDIUMP_FLOAT #include #include #include namespace mir { namespace test { namespace doubles { class StubRenderable : public graphics::Renderable { public: StubRenderable(std::shared_ptr const& buffer, geometry::Rectangle const& rect) : rect(rect), stub_buffer(buffer) {} StubRenderable(std::shared_ptr const& buffer) : StubRenderable(buffer, {{},{}}) {} StubRenderable(geometry::Rectangle const& rect) : StubRenderable(make_stub_buffer(rect), rect) {} StubRenderable() : StubRenderable(make_stub_buffer({{},{}}), {{},{}}) {} void set_buffer(std::shared_ptr const& buffer) { stub_buffer = buffer; } ID id() const override { return this; } std::shared_ptr buffer() const override { return stub_buffer; } geometry::Rectangle screen_position() const override { return rect; } float alpha() const override { return 1.0f; } glm::mat4 transformation() const override { return trans; } bool shaped() const override { return false; } private: std::shared_ptr make_stub_buffer(geometry::Rectangle const& rect) { graphics::BufferProperties prop{ rect.size, mir_pixel_format_abgr_8888, graphics::BufferUsage::hardware}; return std::make_shared(prop); } glm::mat4 trans; geometry::Rectangle const rect; std::shared_ptr stub_buffer; }; struct StubTransformedRenderable : public StubRenderable { glm::mat4 transformation() const override { glm::mat4 transform(1.0); glm::vec3 vec(1.0, 0.0, 0.0); transform = glm::rotate(transform, 33.0f, vec); return transform; } }; //hopefully the alpha representation gets condensed at some point struct StubShapedRenderable : public StubRenderable { bool shaped() const override { return true; } }; struct StubTranslucentRenderable : public StubRenderable { bool shaped() const override { return true; } }; struct PlaneAlphaRenderable : public StubRenderable { float alpha() const override { //approx 99% alpha return 1.0f - ( 3.0f / 1024.0f ); } }; } } } #endif /* MIR_TEST_DOUBLES_STUB_RENDERABLE_H_ */ ./tests/include/mir/test/doubles/mock_udev_device.h0000644000015600001650000000242612676616125022551 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher Halse Rogers */ #ifndef MIR_TEST_DOUBLES_MOCK_UDEV_DEVICE_H_ #define MIR_TEST_DOUBLES_MOCK_UDEV_DEVICE_H_ #include "mir/udev/wrapper.h" #include namespace mir { namespace test { namespace doubles { class MockUdevDevice : public mir::udev::Device { public: MOCK_CONST_METHOD0(subsystem, char const*(void)); MOCK_CONST_METHOD0(devtype, char const*(void)); MOCK_CONST_METHOD0(devpath, char const*(void)); MOCK_CONST_METHOD0(devnode, char const*(void)); MOCK_CONST_METHOD1(property, char const*(char const*)); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_UDEV_DEVICE_H_ ./tests/include/mir/test/doubles/mock_frontend_surface.h0000644000015600001650000000477512676616125023627 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_FRONTEND_SURFACE_H_ #define MIR_TEST_DOUBLES_MOCK_FRONTEND_SURFACE_H_ #include "mir/frontend/surface.h" #include namespace mir { namespace test { namespace doubles { struct MockFrontendSurface : public frontend::Surface { MockFrontendSurface(std::shared_ptr const&, int input_fd) { using namespace testing; ON_CALL(*this, client_size()) .WillByDefault(Return(geometry::Size())); ON_CALL(*this, pixel_format()) .WillByDefault(Return(MirPixelFormat())); ON_CALL(*this, supports_input()) .WillByDefault(Return(true)); ON_CALL(*this, client_input_fd()) .WillByDefault(Return(input_fd)); } MockFrontendSurface(int input_fd) : MockFrontendSurface(nullptr, input_fd) { } ~MockFrontendSurface() noexcept {} MOCK_METHOD0(destroy, void()); MOCK_METHOD0(force_requests_to_complete, void()); MOCK_CONST_METHOD0(client_size, geometry::Size()); MOCK_CONST_METHOD0(pixel_format, MirPixelFormat()); MOCK_CONST_METHOD0(supports_input, bool()); MOCK_CONST_METHOD0(client_input_fd, int()); MOCK_METHOD1(set_cursor_image, void(std::shared_ptr const&)); MOCK_METHOD2(set_cursor_stream, void(std::shared_ptr const&, geometry::Displacement const&)); MOCK_METHOD2(configure, int(MirSurfaceAttrib, int)); MOCK_CONST_METHOD1(query, int(MirSurfaceAttrib)); MOCK_METHOD1(add_observer, void(std::shared_ptr const&)); MOCK_METHOD1(remove_observer, void(std::weak_ptr const&)); MOCK_CONST_METHOD0(primary_buffer_stream, std::shared_ptr()); }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_MOCK_FRONTEND_SURFACE_H_ ./tests/include/mir/test/doubles/mock_libinput.h0000644000015600001650000003171412676616157022124 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_LIBINPUT_H_ #define MIR_TEST_DOUBLES_MOCK_LIBINPUT_H_ #include "mir/dispatch/action_queue.h" #include #include namespace mir { namespace test { namespace doubles { class MockLibInput { public: MockLibInput(); ~MockLibInput() noexcept; void wake(); void setup_device(libinput_device* device, libinput_device_group* group, udev_device* u_dev, char const* name, unsigned int vendor, unsigned int product); libinput_event* setup_touch_event(libinput_device* dev, libinput_event_type type, uint64_t event_time, int slot, float x, float y, float major, float minor, float pressure); libinput_event* setup_touch_frame(libinput_device *dev, uint64_t event_time); libinput_event* setup_touch_up_event(libinput_device* dev, uint64_t event_time, int slot); libinput_event* setup_key_event(libinput_device* dev, uint64_t event_time, uint32_t key, libinput_key_state state); libinput_event* setup_pointer_event(libinput_device* dev, uint64_t event_time, float relatve_x, float relatve_y); libinput_event* setup_absolute_pointer_event(libinput_device* dev, uint64_t event_time, float x, float y); libinput_event* setup_button_event(libinput_device* dev, uint64_t event_time, int button, libinput_button_state state); libinput_event* setup_axis_event(libinput_device* dev, uint64_t event_time, double horizontal, double vertical); libinput_event* setup_finger_axis_event(libinput_device* dev, uint64_t event_time, double horizontal, double vertical); libinput_event* setup_device_add_event(libinput_device* dev); libinput_event* setup_device_remove_event(libinput_device* dev); MOCK_METHOD1(libinput_ref, libinput*(libinput*)); MOCK_METHOD1(libinput_unref, libinput*(libinput*)); MOCK_METHOD1(libinput_dispatch, int(libinput*)); MOCK_METHOD1(libinput_get_fd, int(libinput*)); MOCK_METHOD1(libinput_get_event, libinput_event*(libinput*)); MOCK_METHOD1(libinput_event_get_type, libinput_event_type(libinput_event*)); MOCK_METHOD1(libinput_event_destroy, void(libinput_event*)); MOCK_METHOD1(libinput_event_get_device, libinput_device*(libinput_event*)); MOCK_METHOD1(libinput_event_get_pointer_event, libinput_event_pointer*(libinput_event*)); MOCK_METHOD1(libinput_event_get_keyboard_event, libinput_event_keyboard*(libinput_event*)); MOCK_METHOD1(libinput_event_get_touch_event, libinput_event_touch*(libinput_event*)); MOCK_METHOD1(libinput_event_keyboard_get_time, uint32_t(libinput_event_keyboard*)); MOCK_METHOD1(libinput_event_keyboard_get_time_usec, uint64_t(libinput_event_keyboard*)); MOCK_METHOD1(libinput_event_keyboard_get_key, uint32_t(libinput_event_keyboard*)); MOCK_METHOD1(libinput_event_keyboard_get_key_state, libinput_key_state(libinput_event_keyboard*)); MOCK_METHOD1(libinput_event_keyboard_get_seat_key_count, uint32_t(libinput_event_keyboard*)); MOCK_METHOD1(libinput_event_pointer_get_time, uint32_t(libinput_event_pointer*)); MOCK_METHOD1(libinput_event_pointer_get_time_usec, uint64_t(libinput_event_pointer*)); MOCK_METHOD1(libinput_event_pointer_get_dx, double(libinput_event_pointer*)); MOCK_METHOD1(libinput_event_pointer_get_dy, double(libinput_event_pointer*)); MOCK_METHOD1(libinput_event_pointer_get_absolute_x, double(libinput_event_pointer*)); MOCK_METHOD1(libinput_event_pointer_get_absolute_y, double(libinput_event_pointer*)); MOCK_METHOD2(libinput_event_pointer_get_absolute_x_transformed, double(libinput_event_pointer*, uint32_t)); MOCK_METHOD2(libinput_event_pointer_get_absolute_y_transformed, double(libinput_event_pointer*, uint32_t)); MOCK_METHOD1(libinput_event_pointer_get_button, uint32_t(libinput_event_pointer*)); MOCK_METHOD1(libinput_event_pointer_get_button_state, libinput_button_state(libinput_event_pointer*)); MOCK_METHOD1(libinput_event_pointer_get_seat_button_count, uint32_t(libinput_event_pointer*)); MOCK_METHOD1(libinput_event_pointer_get_axis, libinput_pointer_axis(libinput_event_pointer*)); MOCK_METHOD2(libinput_event_pointer_get_axis_value, double(libinput_event_pointer*, libinput_pointer_axis)); MOCK_METHOD1(libinput_event_pointer_get_axis_source, libinput_pointer_axis_source(libinput_event_pointer*)); MOCK_METHOD2(libinput_event_pointer_get_axis_value_discrete, double(libinput_event_pointer*, libinput_pointer_axis)); MOCK_METHOD2(libinput_event_pointer_has_axis,int(libinput_event_pointer *,libinput_pointer_axis)); MOCK_METHOD1(libinput_event_touch_get_time, uint32_t(libinput_event_touch*)); MOCK_METHOD1(libinput_event_touch_get_time_usec, uint64_t(libinput_event_touch*)); MOCK_METHOD1(libinput_event_touch_get_slot, int32_t(libinput_event_touch*)); MOCK_METHOD1(libinput_event_touch_get_seat_slot, int32_t(libinput_event_touch*)); MOCK_METHOD1(libinput_event_touch_get_x, double(libinput_event_touch*)); MOCK_METHOD1(libinput_event_touch_get_y, double(libinput_event_touch*)); MOCK_METHOD2(libinput_event_touch_get_x_transformed, double(libinput_event_touch*, uint32_t)); MOCK_METHOD2(libinput_event_touch_get_y_transformed, double(libinput_event_touch*, uint32_t)); MOCK_METHOD1(libinput_event_touch_get_pressure, double(libinput_event_touch*)); MOCK_METHOD1(libinput_event_touch_get_minor, double(libinput_event_touch*)); MOCK_METHOD3(libinput_event_touch_get_minor_transformed, double(libinput_event_touch*, uint32_t, uint32_t)); MOCK_METHOD1(libinput_event_touch_get_major, double(libinput_event_touch*)); MOCK_METHOD3(libinput_event_touch_get_major_transformed, double(libinput_event_touch*, uint32_t, uint32_t)); MOCK_METHOD1(libinput_event_touch_get_orientation, double(libinput_event_touch*)); MOCK_METHOD3(libinput_udev_create_context, libinput*(const libinput_interface *, void*, struct udev* udev)); MOCK_METHOD2(libinput_udev_assign_seat, int(const libinput*, char const* seat)); MOCK_METHOD2(libinput_path_create_context, libinput*(const libinput_interface *, void*)); MOCK_METHOD2(libinput_path_add_device, libinput_device*(const libinput*, const char*)); MOCK_METHOD1(libinput_path_remove_device, void(libinput_device*)); MOCK_METHOD1(libinput_device_unref, libinput_device*(libinput_device*)); MOCK_METHOD1(libinput_device_ref, libinput_device*(libinput_device*)); MOCK_METHOD1(libinput_device_get_name, char const*(libinput_device*)); MOCK_METHOD1(libinput_device_get_udev_device, udev_device*(libinput_device*)); MOCK_METHOD1(libinput_device_get_id_product, unsigned int(libinput_device*)); MOCK_METHOD1(libinput_device_get_id_vendor, unsigned int(libinput_device*)); MOCK_METHOD1(libinput_device_get_sysname, char const*(libinput_device*)); MOCK_METHOD1(libinput_device_get_device_group, libinput_device_group*(libinput_device*)); MOCK_METHOD1(libinput_device_config_tap_get_finger_count, int(libinput_device*)); MOCK_METHOD2(libinput_device_config_tap_set_enabled, libinput_config_status(libinput_device*, libinput_config_tap_state enable)); MOCK_METHOD1(libinput_device_config_tap_get_enabled, libinput_config_tap_state(libinput_device*)); MOCK_METHOD1(libinput_device_config_tap_get_default_enabled, libinput_config_tap_state(libinput_device*)); MOCK_METHOD2(libinput_device_config_tap_set_drag_lock_enabled, libinput_config_status(libinput_device*, libinput_config_drag_lock_state enable)); MOCK_METHOD1(libinput_device_config_tap_get_drag_lock_enabled, libinput_config_drag_lock_state(libinput_device*)); MOCK_METHOD1(libinput_device_config_tap_get_default_drag_lock_enabled, libinput_config_drag_lock_state(libinput_device*)); MOCK_METHOD1(libinput_device_config_calibration_has_matrix, int(libinput_device*)); MOCK_METHOD2(libinput_device_config_calibration_set_matrix, libinput_config_status(libinput_device*, const float matrix[6])); MOCK_METHOD2(libinput_device_config_calibration_get_matrix, int(libinput_device*, float matrix[6])); MOCK_METHOD2(libinput_device_config_calibration_get_default_matrix, int(libinput_device*, float matrix[6])); MOCK_METHOD1(libinput_device_config_send_events_get_modes, uint32_t(libinput_device*)); MOCK_METHOD2(libinput_device_config_send_events_set_mode, libinput_config_status(libinput_device*, uint32_t mode)); MOCK_METHOD1(libinput_device_config_send_events_get_mode, uint32_t(libinput_device*)); MOCK_METHOD1(libinput_device_config_send_events_get_default_mode, uint32_t(libinput_device*)); MOCK_METHOD1(libinput_device_config_accel_is_available, int(libinput_device*)); MOCK_METHOD2(libinput_device_config_accel_set_speed, libinput_config_status(libinput_device*, double speed)); MOCK_METHOD1(libinput_device_config_accel_get_speed, double(libinput_device*)); MOCK_METHOD1(libinput_device_config_accel_get_default_speed, double(libinput_device*)); #if MIR_LIBINPUT_HAS_ACCEL_PROFILE MOCK_METHOD2(libinput_device_config_accel_set_profile, libinput_config_status(libinput_device*, libinput_config_accel_profile)); MOCK_METHOD1(libinput_device_config_accel_get_profile, libinput_config_accel_profile(libinput_device*)); #endif MOCK_METHOD1(libinput_device_config_scroll_has_natural_scroll, int(libinput_device*)); MOCK_METHOD2(libinput_device_config_scroll_set_natural_scroll_enabled, libinput_config_status(libinput_device*, int enable)); MOCK_METHOD1(libinput_device_config_scroll_get_natural_scroll_enabled, int(libinput_device*)); MOCK_METHOD1(libinput_device_config_scroll_get_default_natural_scroll_enabled, int(libinput_device*)); MOCK_METHOD1(libinput_device_config_left_handed_is_available, int(libinput_device*)); MOCK_METHOD2(libinput_device_config_left_handed_set, libinput_config_status(libinput_device*, int left_handed)); MOCK_METHOD1(libinput_device_config_left_handed_get, int(libinput_device*)); MOCK_METHOD1(libinput_device_config_left_handed_get_default, int(libinput_device*)); MOCK_METHOD1(libinput_device_config_click_get_methods, uint32_t(libinput_device*)); MOCK_METHOD2(libinput_device_config_click_set_method, libinput_config_status(libinput_device*, libinput_config_click_method method)); MOCK_METHOD1(libinput_device_config_click_get_method, libinput_config_click_method(libinput_device*)); MOCK_METHOD1(libinput_device_config_click_get_default_method, libinput_config_click_method(libinput_device*)); MOCK_METHOD1(libinput_device_config_middle_emulation_is_available, int(libinput_device*)); MOCK_METHOD2(libinput_device_config_middle_emulation_set_enabled, libinput_config_status(libinput_device*, libinput_config_middle_emulation_state enable)); MOCK_METHOD1(libinput_device_config_middle_emulation_get_enabled, libinput_config_middle_emulation_state(libinput_device*)); MOCK_METHOD1(libinput_device_config_middle_emulation_get_default_enabled, libinput_config_middle_emulation_state(libinput_device*)); MOCK_METHOD1(libinput_device_config_scroll_get_methods, uint32_t(libinput_device*)); MOCK_METHOD2(libinput_device_config_scroll_set_method, libinput_config_status(libinput_device*, libinput_config_scroll_method method)); MOCK_METHOD1(libinput_device_config_scroll_get_method, libinput_config_scroll_method(libinput_device*)); MOCK_METHOD1(libinput_device_config_scroll_get_default_method, libinput_config_scroll_method(libinput_device*)); MOCK_METHOD2(libinput_device_config_scroll_set_button, libinput_config_status(libinput_device*, uint32_t button)); MOCK_METHOD1(libinput_device_config_scroll_get_button, uint32_t(libinput_device*)); MOCK_METHOD1(libinput_device_config_scroll_get_default_button, uint32_t(libinput_device*)); MOCK_METHOD1(libinput_device_config_dwt_is_available, int(libinput_device*)); MOCK_METHOD2(libinput_device_config_dwt_set_enabled, libinput_config_status(libinput_device*, libinput_config_dwt_state enable)); MOCK_METHOD1(libinput_device_config_dwt_get_enabled, libinput_config_dwt_state(libinput_device*)); MOCK_METHOD1(libinput_device_config_dwt_get_default_enabled, libinput_config_dwt_state(libinput_device*)); template PtrT get_next_fake_ptr() { return reinterpret_cast(++last_fake_ptr); } std::vector events; private: dispatch::ActionQueue libinput_simulation_queue; unsigned int last_fake_ptr{0}; void push_back(libinput_event* event); }; } } } #endif ./tests/include/mir/test/doubles/mock_gl_display_buffer.h0000644000015600001650000000226312676616125023746 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_GL_DISPLAY_BUFFER_H_ #define MIR_TEST_DOUBLES_MOCK_GL_DISPLAY_BUFFER_H_ #include "mock_display_buffer.h" #include "mir/renderer/gl/render_target.h" namespace mir { namespace test { namespace doubles { class MockGLDisplayBuffer : public MockDisplayBuffer, public renderer::gl::RenderTarget { public: MOCK_METHOD0(make_current, void()); MOCK_METHOD0(release_current, void()); MOCK_METHOD0(swap_buffers, void()); }; } } } #endif ./tests/include/mir/test/doubles/mock_shell.h0000644000015600001650000000572012676616125021376 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_SHELL_H_ #define MIR_TEST_DOUBLES_SHELL_H_ #include "mir/frontend/shell.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/scene/prompt_session_creation_parameters.h" #include "mir/shell/surface_specification.h" #include namespace mir { namespace test { namespace doubles { struct MockShell : public frontend::Shell { MOCK_METHOD3(open_session, std::shared_ptr( pid_t client_pid, std::string const&, std::shared_ptr const&)); MOCK_METHOD1(close_session, void(std::shared_ptr const&)); MOCK_METHOD2(start_prompt_session_for, std::shared_ptr( std::shared_ptr const&, scene::PromptSessionCreationParameters const&)); MOCK_METHOD2(add_prompt_provider_for, void( std::shared_ptr const&, std::shared_ptr const&)); MOCK_METHOD1(stop_prompt_session, void(std::shared_ptr const&)); MOCK_METHOD3(create_surface, frontend::SurfaceId( std::shared_ptr const&, scene::SurfaceCreationParameters const& params, std::shared_ptr const&)); MOCK_METHOD3(modify_surface, void(std::shared_ptr const&, frontend::SurfaceId, shell::SurfaceSpecification const&)); MOCK_METHOD2(destroy_surface, void(std::shared_ptr const&, frontend::SurfaceId)); MOCK_METHOD2(persistent_id_for, std::string(std::shared_ptr const&, frontend::SurfaceId)); MOCK_METHOD1(surface_for_id, std::shared_ptr(std::string const&)); MOCK_METHOD4(set_surface_attribute, int( std::shared_ptr const& session, frontend::SurfaceId surface_id, MirSurfaceAttrib attrib, int value)); MOCK_METHOD3(get_surface_attribute, int(std::shared_ptr const& session, frontend::SurfaceId surface_id, MirSurfaceAttrib attrib)); MOCK_METHOD3(raise_surface, void(std::shared_ptr const& session, frontend::SurfaceId surface_id, uint64_t timestamp)); }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_SHELL_H_ ./tests/include/mir/test/doubles/null_snapshot_strategy.h0000644000015600001650000000223112676616125024063 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_SNAPSHOT_STRATEGY_H_ #define MIR_TEST_DOUBLES_NULL_SNAPSHOT_STRATEGY_H_ #include "src/server/scene/snapshot_strategy.h" namespace mir { namespace test { namespace doubles { struct NullSnapshotStrategy : public scene::SnapshotStrategy { void take_snapshot_of( std::shared_ptr const&, scene::SnapshotCallback const&) { } }; } } } #endif /* MIR_TEST_DOUBLES_NULL_SNAPSHOT_STRATEGY_H_ */ ./tests/include/mir/test/doubles/mock_display_device.h0000644000015600001650000000353612676616125023256 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_DISPLAY_DEVICE_H_ #define MIR_TEST_DOUBLES_MOCK_DISPLAY_DEVICE_H_ #include "mir/graphics/buffer.h" #include "src/platforms/android/server/display_device.h" #include "src/platforms/android/server/gl_context.h" #include "src/platforms/android/server/hwc_fallback_gl_renderer.h" #include "src/platforms/android/server/hwc_layerlist.h" #include namespace mir { namespace test { namespace doubles { class MockDisplayDevice : public graphics::android::DisplayDevice { public: MockDisplayDevice() { ON_CALL(*this, compatible_renderlist(testing::_)) .WillByDefault(testing::Return(true)); ON_CALL(*this, can_swap_buffers()) .WillByDefault(testing::Return(true)); } ~MockDisplayDevice() noexcept {} MOCK_METHOD0(content_cleared, void()); MOCK_METHOD1(commit, void(std::list const&)); MOCK_METHOD1(compatible_renderlist, bool( graphics::RenderableList const&)); MOCK_CONST_METHOD0(recommended_sleep, std::chrono::milliseconds()); MOCK_CONST_METHOD0(can_swap_buffers, bool()); }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_DISPLAY_DEVICE_H_ */ ./tests/include/mir/test/doubles/stub_display_builder.h0000644000015600001650000001224212676616125023463 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_DISPLAY_BUILDER_H_ #define MIR_TEST_DOUBLES_STUB_DISPLAY_BUILDER_H_ #include "src/platforms/android/server/framebuffer_bundle.h" #include "src/platforms/android/server/display_component_factory.h" #include "src/platforms/android/server/configurable_display_buffer.h" #include "src/platforms/android/server/hwc_configuration.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/mock_display_device.h" #include namespace mir { namespace test { namespace doubles { struct StubFramebufferBundle : public graphics::android::FramebufferBundle { geometry::Size fb_size() override { return {33, 34}; } std::shared_ptr buffer_for_render() { return nullptr; } std::shared_ptr last_rendered_buffer() { return nullptr; } }; struct MockHwcConfiguration : public graphics::android::HwcConfiguration { MockHwcConfiguration() { using namespace testing; StubDisplayConfig config({{true, true}, {false, false}}); ON_CALL(*this, subscribe_to_config_changes(_,_)).WillByDefault(Return(nullptr)); ON_CALL(*this, active_config_for(graphics::android::DisplayName::primary)) .WillByDefault(testing::Return(config.outputs[0])); ON_CALL(*this, active_config_for(graphics::android::DisplayName::external)) .WillByDefault(testing::Return(config.outputs[1])); } MOCK_METHOD2(power_mode, void(graphics::android::DisplayName, MirPowerMode)); MOCK_METHOD1(active_config_for, graphics::DisplayConfigurationOutput(graphics::android::DisplayName)); MOCK_METHOD2(subscribe_to_config_changes, graphics::android::ConfigChangeSubscription( std::function const&, std::function const&)); }; struct StubHwcConfiguration : public graphics::android::HwcConfiguration { void power_mode(graphics::android::DisplayName, MirPowerMode) override { } graphics::DisplayConfigurationOutput active_config_for(graphics::android::DisplayName name) override { bool connected{name == graphics::android::DisplayName::primary}; auto config = StubDisplayConfig({{connected, connected}}).outputs[0]; config.id = as_output_id(name); return config; } graphics::android::ConfigChangeSubscription subscribe_to_config_changes( std::function const&, std::function const&) override { return nullptr; } }; struct StubDisplayBuilder : public graphics::android::DisplayComponentFactory { StubDisplayBuilder(geometry::Size sz) : sz(sz), config{new StubHwcConfiguration} { } StubDisplayBuilder() : StubDisplayBuilder(geometry::Size{0,0}) { } std::shared_ptr the_buffer_allocator() { return nullptr; } std::unique_ptr create_layer_list() { return std::unique_ptr( new graphics::android::LayerList( std::make_shared(), {}, geometry::Displacement{0,0})); } std::unique_ptr create_framebuffers(graphics::DisplayConfigurationOutput const&) override { return std::unique_ptr(new StubFramebufferBundle()); } std::unique_ptr create_display_device() override { return std::unique_ptr(new testing::NiceMock()); } std::unique_ptr create_hwc_configuration() override { auto c = std::unique_ptr(new StubHwcConfiguration); std::swap(config, c); return std::move(c); } void with_next_config(std::function const& fn) { std::unique_ptr mock_config{ new testing::NiceMock()}; fn(*mock_config); config = std::move(mock_config); } std::unique_ptr create_command_stream_sync() { return std::make_unique(); } geometry::Size sz; std::unique_ptr config; }; } } } // namespace mir #endif /* MIR_TEST_DOUBLES_STUB_DISPLAY_BUILDER_H_ */ ./tests/include/mir/test/doubles/mock_scene.h0000644000015600001650000000337612676616125021371 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_TEST_DOUBLES_MOCK_SCENE_H_ #define MIR_TEST_DOUBLES_MOCK_SCENE_H_ #include "mir/compositor/scene.h" #include namespace mir { namespace test { namespace doubles { class MockScene : public compositor::Scene { public: MockScene() { ON_CALL(*this, scene_elements_for(testing::_)) .WillByDefault(testing::Return(compositor::SceneElementSequence{})); ON_CALL(*this, frames_pending(testing::_)) .WillByDefault(testing::Return(0)); } MOCK_METHOD1(scene_elements_for, compositor::SceneElementSequence(compositor::CompositorID)); MOCK_CONST_METHOD1(frames_pending, int(compositor::CompositorID)); MOCK_METHOD1(register_compositor, void(compositor::CompositorID)); MOCK_METHOD1(unregister_compositor, void(compositor::CompositorID)); MOCK_METHOD1(add_observer, void(std::shared_ptr const&)); MOCK_METHOD1(remove_observer, void(std::weak_ptr const&)); }; } // namespace doubles } // namespace test } // namespace mir #endif /* MIR_TEST_DOUBLES_MOCK_SCENE_H_ */ ./tests/include/mir/test/doubles/stub_scene.h0000644000015600001650000000307012676616125021404 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_STUB_SCENE_H_ #define MIR_TEST_DOUBLES_STUB_SCENE_H_ #include "mir/compositor/scene.h" #include namespace mir { namespace test { namespace doubles { class StubScene : public compositor::Scene { public: compositor::SceneElementSequence scene_elements_for(compositor::CompositorID) override { return {}; } int frames_pending(compositor::CompositorID) const override { return 0; } void register_compositor(compositor::CompositorID) override { } void unregister_compositor(compositor::CompositorID) override { } void add_observer(std::shared_ptr const&) override { } void remove_observer(std::weak_ptr const&) override { } }; } // namespace doubles } // namespace test } // namespace mir #endif /* MIR_TEST_DOUBLES_STUB_SCENE_H_ */ ./tests/include/mir/test/doubles/stub_swapping_gl_context.h0000644000015600001650000000302312676616125024363 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_SWAPPING_GL_CONTEXT_H_ #define MIR_TEST_DOUBLES_STUB_SWAPPING_GL_CONTEXT_H_ #include "src/platforms/android/server/gl_context.h" #include "stub_buffer.h" namespace mir { namespace test { namespace doubles { struct StubSwappingGLContext : public graphics::android::SwappingGLContext { StubSwappingGLContext(std::shared_ptr const& buffer) : buffer(buffer) { } StubSwappingGLContext() : StubSwappingGLContext(std::make_shared()) { } void make_current() const {} void release_current() const {} void swap_buffers() const {} std::shared_ptr last_rendered_buffer() const { return buffer; } private: std::shared_ptr const buffer; }; } } } #endif // MIR_TEST_DOUBLES_STUB_COMPOSITING_CRITERIA_H_ ./tests/include/mir/test/doubles/stub_input_targeter.h0000644000015600001650000000213712676616125023346 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_STUB_INPUT_TARGETER_H_ #define MIR_TEST_DOUBLES_STUB_INPUT_TARGETER_H_ #include "mir/shell/input_targeter.h" namespace mir { namespace test { namespace doubles { struct StubInputTargeter : public shell::InputTargeter { void set_focus(std::shared_ptr const&) { } void clear_focus() { } }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_STUB_INPUT_TARGETER_H_ ./tests/include/mir/test/doubles/mock_client_buffer.h0000644000015600001650000000355112676616157023103 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_CLIENT_BUFFER_H_ #define MIR_TEST_DOUBLES_MOCK_CLIENT_BUFFER_H_ #include "mir/client_buffer.h" #include #include namespace mir { namespace test { namespace doubles { struct MockClientBuffer : client::ClientBuffer { MOCK_METHOD0(secure_for_cpu_write, std::shared_ptr()); MOCK_CONST_METHOD0(size, geometry::Size()); MOCK_CONST_METHOD0(stride, geometry::Stride()); MOCK_CONST_METHOD0(pixel_format, MirPixelFormat()); MOCK_CONST_METHOD0(age, uint32_t()); MOCK_METHOD0(mark_as_submitted, void()); MOCK_METHOD0(increment_age, void()); MOCK_METHOD1(update_from, void(MirBufferPackage const&)); MOCK_METHOD1(fill_update_msg, void(MirBufferPackage&)); MOCK_CONST_METHOD0(native_buffer_handle, std::shared_ptr()); MOCK_CONST_METHOD0(as_mir_native_buffer, MirNativeBuffer*()); MOCK_METHOD2(set_fence, void(MirNativeFence*, MirBufferAccess)); MOCK_CONST_METHOD0(get_fence, MirNativeFence*()); MOCK_METHOD2(wait_fence, bool(MirBufferAccess, std::chrono::nanoseconds)); int age_{0}; }; } } } #endif // MIR_TEST_DOUBLES_MOCK_CLIENT_BUFFER_FACTORY_H_ ./tests/include/mir/test/doubles/stub_alarm.h0000644000015600001650000000242212676616125021403 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_DOUBLES_STUB_ALARM_H_ #define MIR_TEST_DOUBLES_STUB_ALARM_H_ #include "mir/time/alarm.h" namespace mir { namespace test { namespace doubles { class StubAlarm : public mir::time::Alarm { bool cancel() override { return false; } State state() const override { return cancelled; } bool reschedule_in(std::chrono::milliseconds) override { return false; } bool reschedule_for(mir::time::Timestamp) override { return false; } }; } } } #endif // MIR_TEST_DOUBLES_STUB_ALARM_H_ ./tests/include/mir/test/doubles/mock_input_device.h0000644000015600001650000000333012676616125022740 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_DEVICE_H #define MIR_TEST_DOUBLES_MOCK_INPUT_DEVICE_H #include "mir/input/input_device.h" #include "mir/input/input_device_info.h" // needed for fake device setup #include "mir/input/pointer_settings.h" #include "mir/input/touchpad_settings.h" #include namespace mir { namespace test { namespace doubles { struct MockInputDevice : input::InputDevice { MockInputDevice() = default; MockInputDevice(char const* name, char const* uid, input::DeviceCapabilities); MOCK_METHOD2(start, void(input::InputSink* destination, input::EventBuilder* builder)); MOCK_METHOD0(stop, void()); MOCK_METHOD0(get_device_info, input::InputDeviceInfo()); MOCK_CONST_METHOD0(get_pointer_settings, mir::optional_value()); MOCK_METHOD1(apply_settings, void(input::PointerSettings const&)); MOCK_CONST_METHOD0(get_touchpad_settings, mir::optional_value()); MOCK_METHOD1(apply_settings, void(input::TouchpadSettings const&)); }; } } } #endif ./tests/include/mir/test/doubles/mock_renderable_list_compositor.h0000644000015600001650000000236512676616125025705 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_RENDERABLE_LIST_COMPOSITOR_H_ #define MIR_TEST_DOUBLES_MOCK_RENDERABLE_LIST_COMPOSITOR_H_ #include "src/platforms/android/server/hwc_fallback_gl_renderer.h" #include namespace mir { namespace test { namespace doubles { struct MockRenderableListCompositor : public graphics::android::RenderableListCompositor { MOCK_CONST_METHOD3(render, void(graphics::RenderableList const&, geometry::Displacement, graphics::android::SwappingGLContext const&)); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_RENDERABLE_LIST_COMPOSITOR_H_ ./tests/include/mir/test/doubles/stub_gbm_native_buffer.h0000644000015600001650000000353112676616125023755 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_GBM_NATIVE_BUFFER_H_ #define MIR_TEST_DOUBLES_STUB_GBM_NATIVE_BUFFER_H_ #include "src/platforms/mesa/server/common/gbm_buffer.h" #include "mir/geometry/size.h" #include #include #include namespace mir { namespace test { namespace doubles { struct StubGBMNativeBuffer : public graphics::mesa::GBMNativeBuffer { StubGBMNativeBuffer(geometry::Size const& size, bool bypassable = true) { data_items = 4; //note: the client platform will only close the 1st fd, so lets not put more than one in here fd_items = 1; width = size.width.as_int(); height = size.height.as_int(); // Flags are defined in mir_native_buffer.h ... flags = bypassable ? mir_buffer_flag_can_scanout : 0; stride = 4390; bo = reinterpret_cast(&fake_bo); //gbm_bo is opaque, so test code shouldn't dereference. for(auto i = 0; i < data_items; i++) data[i] = i; fd[0] = open("/dev/null", 0); } ~StubGBMNativeBuffer() { close(fd[0]); } int fake_bo{0}; }; } } } #endif /* MIR_TEST_DOUBLES_STUB_GBM_NATIVE_BUFFER_H_ */ ./tests/include/mir/test/doubles/mock_input_seat.h0000644000015600001650000000230412676616157022442 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_SEAT_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_SEAT_H_ #include "mir/input/seat.h" #include namespace mir { namespace test { namespace doubles { struct MockInputSeat : input::Seat { MOCK_METHOD1(add_device, void(input::Device const& device)); MOCK_METHOD1(remove_device, void(input::Device const& device)); MOCK_METHOD1(dispatch_event, void(MirEvent& event)); MOCK_METHOD1(get_rectangle_for, geometry::Rectangle(input::Device const& dev)); }; } } } #endif ./tests/include/mir/test/doubles/mock_android_alloc_device.h0000644000015600001650000001026112676616125024374 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_ANDROID_ALLOC_DEVICE_H_ #define MIR_TEST_DOUBLES_MOCK_ANDROID_ALLOC_DEVICE_H_ #include #include namespace mir { namespace test { namespace doubles { struct MockGrallocModule : gralloc_module_t { MockGrallocModule() { lock = hook_lock; unlock = hook_unlock; } static int hook_lock( struct gralloc_module_t const* module, buffer_handle_t handle, int usage, int l, int t, int w, int h, void** vaddr) { auto module_mock = static_cast(module); return module_mock->lock_interface(module, handle, usage, l, t, w, h, vaddr); } static int hook_unlock( struct gralloc_module_t const* module, buffer_handle_t handle) { auto module_mock = static_cast(module); return module_mock->unlock_interface(module, handle); } MOCK_CONST_METHOD8(lock_interface, int(struct gralloc_module_t const*, buffer_handle_t, int, int, int, int, int, void**)); MOCK_CONST_METHOD2(unlock_interface, int(struct gralloc_module_t const*, buffer_handle_t)); }; class MockAllocDevice : public alloc_device_t { public: MockAllocDevice() { using namespace testing; buffer_handle = mock_generate_sane_android_handle(43, 22); fake_stride = 300; alloc = hook_alloc; free = hook_free; dump = hook_dump; ON_CALL(*this, alloc_interface(_,_,_,_,_,_,_)) .WillByDefault(DoAll( SetArgPointee<5>(buffer_handle), SetArgPointee<6>(fake_stride), Return(0))); ON_CALL(*this, free_interface(_,_)) .WillByDefault(Return(0)); } ~MockAllocDevice() { ::free((void*)buffer_handle); } static int hook_alloc(alloc_device_t* mock_alloc, int w, int h, int format, int usage, buffer_handle_t* handle, int* stride) { MockAllocDevice* mocker = static_cast(mock_alloc); return mocker->alloc_interface(mock_alloc, w, h, format, usage, handle, stride); } static int hook_free(alloc_device_t* mock_alloc, buffer_handle_t handle) { MockAllocDevice* mocker = static_cast(mock_alloc); return mocker->free_interface(mock_alloc, handle); } static void hook_dump(alloc_device_t* mock_alloc, char* buf, int buf_len) { MockAllocDevice* mocker = static_cast(mock_alloc); mocker->dump_interface(mock_alloc, buf, buf_len); } native_handle_t* mock_generate_sane_android_handle(int numFd, int numInt) { native_handle_t *handle; int total=numFd + numInt; int header_offset=3; handle = (native_handle_t*) malloc(sizeof(int) * (header_offset+ total)); handle->version = 0x389; handle->numFds = numFd; handle->numInts = numInt; for(int i=0; idata[i] = i*3; } return handle; } MOCK_METHOD7(alloc_interface, int(alloc_device_t*, int, int, int, int, buffer_handle_t*, int*)); MOCK_METHOD2(free_interface, int(alloc_device_t*, buffer_handle_t)); MOCK_METHOD3(dump_interface, int(alloc_device_t*, char*, int)); native_handle_t* buffer_handle; unsigned int fake_stride; }; } } } // namespace mir #endif /* MIR_TEST_DOUBLES_MOCK_ANDROID_ALLOC_DEVICE_H_ */ ./tests/include/mir/test/doubles/mock_client_buffer_stream.h0000644000015600001650000000436412676616125024454 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_MOCK_CLIENT_BUFFER_STREAM_H_ #define MIR_TEST_DOUBLES_MOCK_CLIENT_BUFFER_STREAM_H_ #include "src/client/client_buffer_stream.h" #include namespace mir { namespace test { namespace doubles { struct MockClientBufferStream : public client::ClientBufferStream { MOCK_METHOD2(release, MirWaitHandle*(mir_buffer_stream_callback, void*)); MOCK_CONST_METHOD0(get_parameters, MirSurfaceParameters()); MOCK_METHOD0(get_current_buffer, std::shared_ptr()); MOCK_METHOD0(get_current_buffer_id, uint32_t()); MOCK_METHOD0(egl_native_window, EGLNativeWindowType()); MOCK_METHOD1(next_buffer, MirWaitHandle*(std::function const&)); MOCK_METHOD0(secure_for_cpu_write, std::shared_ptr()); MOCK_CONST_METHOD0(swap_interval, int()); MOCK_METHOD1(set_swap_interval, MirWaitHandle*(int)); MOCK_METHOD0(platform_type, MirPlatformType(void)); MOCK_METHOD0(get_current_buffer_package, MirNativeBuffer*(void)); MOCK_METHOD0(get_create_wait_handle, MirWaitHandle*(void)); MOCK_CONST_METHOD0(rpc_id, frontend::BufferStreamId(void)); MOCK_CONST_METHOD0(valid, bool(void)); MOCK_METHOD1(buffer_available, void(mir::protobuf::Buffer const&)); MOCK_METHOD0(buffer_unavailable, void()); MOCK_METHOD1(set_size, void(geometry::Size)); MOCK_METHOD1(set_scale, MirWaitHandle*(float)); MOCK_CONST_METHOD0(get_error_message, char const*(void)); MOCK_CONST_METHOD0(connection, MirConnection*()); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_CLIENT_BUFFER_STREAM_H_ ./tests/include/mir/test/doubles/stub_gl_display_buffer.h0000644000015600001650000000235112676616125023770 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_GL_DISPLAY_BUFFER_H_ #define MIR_TEST_DOUBLES_STUB_GL_DISPLAY_BUFFER_H_ #include "mir/test/doubles/stub_display_buffer.h" #include "mir/renderer/gl/render_target.h" namespace mir { namespace test { namespace doubles { class StubGLDisplayBuffer : public StubDisplayBuffer, public renderer::gl::RenderTarget { public: using StubDisplayBuffer::StubDisplayBuffer; void make_current() override {} void release_current() override {} void swap_buffers() override {} }; } } } #endif ./tests/include/mir/test/doubles/null_prompt_session_manager.h0000644000015600001650000000453712676616125025073 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #ifndef MIR_SCENE_NULL_PROMPT_SESSION_MANAGER_H_ #define MIR_SCENE_NULL_PROMPT_SESSION_MANAGER_H_ #include "mir/scene/prompt_session_manager.h" namespace mir { namespace test { namespace doubles { class NullPromptSessionManager: public scene::PromptSessionManager { public: std::shared_ptr start_prompt_session_for(std::shared_ptr const&, scene::PromptSessionCreationParameters const&) const { return std::shared_ptr(); } void stop_prompt_session(std::shared_ptr const&) const { } void suspend_prompt_session(std::shared_ptr const&) const override { } void resume_prompt_session(std::shared_ptr const&) const override { } void add_prompt_provider(std::shared_ptr const&, std::shared_ptr const&) const { } void remove_session(std::shared_ptr const&) const { } std::shared_ptr application_for(std::shared_ptr const&) const { return std::shared_ptr(); } std::shared_ptr helper_for(std::shared_ptr const&) const { return std::shared_ptr(); } void for_each_provider_in(std::shared_ptr const&, std::function const&)> const&) const { } }; } } } #endif // MIR_SCENE_NULL_PROMPT_SESSION_MANAGER_H_ ./tests/include/mir/test/doubles/mock_protobuf_server.h0000644000015600001650000000521712676616125023516 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_PROTOBUF_SERVER_H_ #define MIR_TEST_DOUBLES_MOCK_PROTOBUF_SERVER_H_ #include "src/client/rpc/mir_display_server.h" #include namespace mir { namespace test { namespace doubles { ACTION(RunProtobufClosure) { arg2->Run(); } struct MockProtobufServer : public client::rpc::DisplayServer { MockProtobufServer() : client::rpc::DisplayServer(nullptr) { using namespace testing; ON_CALL(*this, configure_buffer_stream(_,_,_)) .WillByDefault(RunProtobufClosure()); ON_CALL(*this, submit_buffer(_,_,_)) .WillByDefault(RunProtobufClosure()); ON_CALL(*this, allocate_buffers(_,_,_)) .WillByDefault(DoAll(InvokeWithoutArgs([this]{ alloc_count++; }), RunProtobufClosure())); ON_CALL(*this, release_buffers(_,_,_)) .WillByDefault(RunProtobufClosure()); } MOCK_METHOD3(configure_buffer_stream, void( mir::protobuf::StreamConfiguration const*, protobuf::Void*, google::protobuf::Closure*)); MOCK_METHOD3(screencast_buffer, void( protobuf::ScreencastId const*, protobuf::Buffer*, google::protobuf::Closure*)); MOCK_METHOD3(allocate_buffers, void( mir::protobuf::BufferAllocation const*, mir::protobuf::Void*, google::protobuf::Closure*)); MOCK_METHOD3(release_buffers, void( mir::protobuf::BufferRelease const*, mir::protobuf::Void*, google::protobuf::Closure*)); MOCK_METHOD3(submit_buffer, void( protobuf::BufferRequest const*, protobuf::Void*, google::protobuf::Closure*)); MOCK_METHOD3(exchange_buffer, void( protobuf::BufferRequest const*, protobuf::Buffer*, google::protobuf::Closure*)); MOCK_METHOD3(create_buffer_stream, void( protobuf::BufferStreamParameters const*, protobuf::BufferStream*, google::protobuf::Closure*)); unsigned int alloc_count{0}; }; } } } #endif ./tests/include/mir/test/doubles/null_screencast.h0000644000015600001650000000254412676616125022443 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_SCREENCAST_H_ #define MIR_TEST_DOUBLES_NULL_SCREENCAST_H_ #include "mir/frontend/screencast.h" namespace mir { namespace test { namespace doubles { class NullScreencast : public frontend::Screencast { public: frontend::ScreencastSessionId create_session( geometry::Rectangle const&, geometry::Size const&, MirPixelFormat) { return frontend::ScreencastSessionId{1}; } void destroy_session(frontend::ScreencastSessionId) {} std::shared_ptr capture(frontend::ScreencastSessionId) { return nullptr; } }; } } } #endif /* MIR_TEST_DOUBLES_NULL_SCREENCAST_H_ */ ./tests/include/mir/test/doubles/stub_buffer_stream.h0000644000015600001650000000541712676616125023142 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_BUFFER_STREAM_H_ #define MIR_TEST_DOUBLES_NULL_BUFFER_STREAM_H_ #include #include #include "mir/test/current_thread_name.h" namespace mir { namespace test { namespace doubles { class StubBufferStream : public compositor::BufferStream { public: StubBufferStream() { stub_compositor_buffer = std::make_shared(); } std::shared_ptr lock_compositor_buffer(void const*) override { --nready; return stub_compositor_buffer; } geometry::Size stream_size() override { return geometry::Size(); } void resize(geometry::Size const&) override { } void force_requests_to_complete() override { } void allow_framedropping(bool) override { } int buffers_ready_for_compositor(void const*) const override { return nready; } void drop_old_buffers() override {} void swap_buffers(graphics::Buffer* b, std::function complete) override { if (b) ++nready; complete(&stub_client_buffer); } void with_most_recent_buffer_do(std::function const& fn) { thread_name = current_thread_name(); fn(*stub_compositor_buffer); } MirPixelFormat pixel_format() const { return mir_pixel_format_abgr_8888; } void add_observer(std::shared_ptr const&) {} void remove_observer(std::weak_ptr const&) {} bool has_submitted_buffer() const { return true; } graphics::BufferID allocate_buffer(graphics::BufferProperties const&) { return graphics::BufferID{}; } void remove_buffer(graphics::BufferID) {} void with_buffer(graphics::BufferID, std::function const&) {} void set_scale(float) {} StubBuffer stub_client_buffer; std::shared_ptr stub_compositor_buffer; int nready = 0; std::string thread_name; }; } } } // namespace mir #endif /* MIR_TEST_DOUBLES_NULL_BUFFER_STREAM_H_ */ ./tests/include/mir/test/doubles/mock_event_filter.h0000644000015600001650000000204612676616125022753 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voß */ #ifndef MIR_TEST_DOUBLES_MOCK_EVENT_FILTER_H_ #define MIR_TEST_DOUBLES_MOCK_EVENT_FILTER_H_ #include "mir/input/event_filter.h" #include namespace mir { namespace test { namespace doubles { struct MockEventFilter : public mir::input::EventFilter { MOCK_METHOD1(handle, bool(const MirEvent&)); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_EVENT_FILTER_H_ ./tests/include/mir/test/doubles/null_application_not_responding_detector.h0000644000015600001650000000302112676616125027604 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Christopher James Halse Rogers */ #ifndef MIR_TEST_DOUBLES_NULL_APPLICATION_NOT_RESPONDING_DETECTOR_H_ #define MIR_TEST_DOUBLES_NULL_APPLICATION_NOT_RESPONDING_DETECTOR_H_ #include "mir/scene/application_not_responding_detector.h" namespace mir { namespace test { namespace doubles { class NullANRDetector : public mir::scene::ApplicationNotRespondingDetector { public: void register_session(frontend::Session const*, std::function const&) override { } void unregister_session(frontend::Session const*) override { } void pong_received(frontend::Session const*) override { } void register_observer(std::shared_ptr const&) override { } void unregister_observer(std::shared_ptr const&) override { } }; } } } #endif // MIR_TEST_DOUBLES_NULL_APPLICATION_NOT_RESPONDING_DETECTOR_H_ ./tests/include/mir/test/doubles/mock_swapping_gl_context.h0000644000015600001650000000237012676616125024343 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_SWAPPING_GL_CONTEXT_H_ #define MIR_TEST_DOUBLES_MOCK_SWAPPING_GL_CONTEXT_H_ #include "src/platforms/android/server/gl_context.h" namespace mir { namespace test { namespace doubles { struct MockSwappingGLContext : public graphics::android::SwappingGLContext { MOCK_CONST_METHOD0(swap_buffers, void()); MOCK_CONST_METHOD0(make_current, void()); MOCK_CONST_METHOD0(release_current, void()); MOCK_CONST_METHOD0(last_rendered_buffer, std::shared_ptr()); }; } } } #endif // MIR_TEST_DOUBLES_MOCK_COMPOSITING_CRITERIA_H_ ./tests/include/mir/test/doubles/mock_input_targeter.h0000644000015600001650000000226412676616125023323 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_TARGETER_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_TARGETER_H_ #include "mir/shell/input_targeter.h" #include namespace mir { namespace test { namespace doubles { struct MockInputTargeter : public shell::InputTargeter { virtual ~MockInputTargeter() noexcept(true) {} MOCK_METHOD1(set_focus, void(std::shared_ptr const&)); MOCK_METHOD0(clear_focus, void()); }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_MOCK_INPUT_TARGETER_H_ ./tests/include/mir/test/doubles/mock_udev.h0000644000015600001650000000226712676616157021242 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_UDEV_H_ #define MIR_TEST_DOUBLES_MOCK_UDEV_H_ #include #include namespace mir { namespace test { namespace doubles { struct MockUdev { public: MockUdev(); ~MockUdev() noexcept; MOCK_METHOD1(udev_device_get_devnode, char const*(udev_device*)); MOCK_METHOD2(udev_device_get_property_value, char const*(udev_device* device, char const* property)); MOCK_METHOD1(udev_device_unref, udev_device*(udev_device* device)); }; } } } #endif ./tests/include/mir/test/doubles/stub_cmdstream_sync_factory.h0000644000015600001650000000232212676616125025050 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_CMDSTREAM_SYNC_FACTORY_H_ #define MIR_TEST_DOUBLES_STUB_CMDSTREAM_SYNC_FACTORY_H_ #include "src/platforms/android/server/cmdstream_sync_factory.h" namespace mir { namespace test { namespace doubles { struct StubCmdStreamSyncFactory : graphics::android::CommandStreamSyncFactory { std::unique_ptr create_command_stream_sync() override { return std::make_unique(); } }; } } } #endif /* MIR_TEST_DOUBLES_STUB_CMDSTREAM_SYNC_FACTORY_H_ */ ./tests/include/mir/test/doubles/stub_display_server.h0000644000015600001650000001342412676616125023346 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #ifndef MIR_TEST_DOUBLES_STUB_DISPLAY_SERVER_H_ #define MIR_TEST_DOUBLES_STUB_DISPLAY_SERVER_H_ #include "src/server/frontend/display_server.h" namespace mir { namespace test { namespace doubles { struct StubDisplayServer : public mir::frontend::detail::DisplayServer { void client_pid(int /*pid*/) {} void connect( mir::protobuf::ConnectParameters const* /*request*/, mir::protobuf::Connection* /*response*/, google::protobuf::Closure* /*done*/) {} void disconnect( mir::protobuf::Void const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void create_surface( mir::protobuf::SurfaceParameters const* /*request*/, mir::protobuf::Surface* /*response*/, google::protobuf::Closure* /*done*/) {} void modify_surface( mir::protobuf::SurfaceModifications const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void next_buffer( mir::protobuf::SurfaceId const* /*request*/, mir::protobuf::Buffer* /*response*/, google::protobuf::Closure* /*done*/) {} void release_surface( mir::protobuf::SurfaceId const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void platform_operation( mir::protobuf::PlatformOperationMessage const* /*request*/, mir::protobuf::PlatformOperationMessage* /*response*/, google::protobuf::Closure* /*done*/) {} void configure_surface( mir::protobuf::SurfaceSetting const* /*request*/, mir::protobuf::SurfaceSetting* /*response*/, google::protobuf::Closure* /*done*/) {} void configure_display( mir::protobuf::DisplayConfiguration const* /*request*/, mir::protobuf::DisplayConfiguration* /*response*/, google::protobuf::Closure* /*done*/) {} void set_base_display_configuration( mir::protobuf::DisplayConfiguration const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void create_screencast( mir::protobuf::ScreencastParameters const* /*request*/, mir::protobuf::Screencast* /*response*/, google::protobuf::Closure* /*done*/) {} void screencast_buffer( mir::protobuf::ScreencastId const* /*request*/, mir::protobuf::Buffer* /*response*/, google::protobuf::Closure* /*done*/) {} void release_screencast( mir::protobuf::ScreencastId const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void create_buffer_stream( mir::protobuf::BufferStreamParameters const* /*request*/, mir::protobuf::BufferStream* /*response*/, google::protobuf::Closure* /*done*/) {} void release_buffer_stream( mir::protobuf::BufferStreamId const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void configure_cursor( mir::protobuf::CursorSetting const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void new_fds_for_prompt_providers( mir::protobuf::SocketFDRequest const* /*request*/, mir::protobuf::SocketFD* /*response*/, google::protobuf::Closure* /*done*/) {} void start_prompt_session( mir::protobuf::PromptSessionParameters const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void stop_prompt_session( mir::protobuf::Void const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void exchange_buffer( mir::protobuf::BufferRequest const* /*request*/, mir::protobuf::Buffer* /*response*/, google::protobuf::Closure* /*done*/) {} void submit_buffer( mir::protobuf::BufferRequest const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void allocate_buffers( mir::protobuf::BufferAllocation const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void release_buffers( mir::protobuf::BufferRelease const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void request_persistent_surface_id( mir::protobuf::SurfaceId const* /*request*/, mir::protobuf::PersistentSurfaceId* /*response*/, google::protobuf::Closure* /*done*/) {} void pong( mir::protobuf::PingEvent const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void configure_buffer_stream( mir::protobuf::StreamConfiguration const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} void raise_surface( mir::protobuf::RaiseRequest const* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* /*done*/) {} }; } } } #endif ./tests/include/mir/test/doubles/stub_display.h0000644000015600001650000000521012676616125021752 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_DISPLAY_H_ #define MIR_TEST_DOUBLES_STUB_DISPLAY_H_ #include "mir/test/doubles/null_display.h" #include "mir/test/doubles/null_display_sync_group.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/geometry/rectangle.h" #include namespace mir { namespace test { namespace doubles { class StubDisplay : public NullDisplay { public: StubDisplay(std::vector const& output_rects) : output_rects(output_rects) { for (auto const& rect : output_rects) groups.emplace_back(new StubDisplaySyncGroup({rect})); } StubDisplay(unsigned int nbuffers) : StubDisplay(generate_stub_rects(nbuffers)) { } void configure(graphics::DisplayConfiguration const& new_config) override { auto current = begin(config.outputs); auto const end = std::end(config.outputs); new_config.for_each_output([¤t, end] (graphics::DisplayConfigurationOutput const& output) { if (current == end) abort(); *current++ = output; }); } void for_each_display_sync_group(std::function const& f) override { for (auto& group : groups) f(*group); } std::unique_ptr configuration() const override { return std::unique_ptr( new StubDisplayConfig(config) ); } std::vector const output_rects; private: StubDisplayConfig config{output_rects}; std::vector generate_stub_rects(unsigned int nbuffers) { std::vector rects; for (auto i = 0u; i < nbuffers; i++) rects.push_back(geometry::Rectangle{{0,0},{1,1}}); return rects; } std::vector> groups; }; } } } #endif ./tests/include/mir/test/doubles/mock_renderer.h0000644000015600001650000000234312676616125022073 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_MOCK_RENDERER_H_ #define MIR_TEST_DOUBLES_MOCK_RENDERER_H_ #include "mir/compositor/renderer.h" #include namespace mir { namespace test { namespace doubles { struct MockRenderer : public compositor::Renderer { MOCK_METHOD1(set_viewport, void(geometry::Rectangle const&)); MOCK_METHOD1(set_rotation, void(float)); MOCK_CONST_METHOD1(render, void(graphics::RenderableList const&)); MOCK_METHOD0(suspend, void()); ~MockRenderer() noexcept {} }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_RENDERER_H_ */ ./tests/include/mir/test/doubles/stub_client_buffer.h0000644000015600001650000000530012676616157023121 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_CLIENT_BUFFER_H_ #define MIR_TEST_DOUBLES_STUB_CLIENT_BUFFER_H_ #include "src/include/client/mir/client_buffer.h" #include namespace mir { namespace test { namespace doubles { struct StubClientBuffer : client::ClientBuffer { StubClientBuffer( std::shared_ptr const& package, geometry::Size size, MirPixelFormat pf) : package{package}, size_{size}, pf_{pf} { } ~StubClientBuffer() { for (int i = 0; i < package->fd_items; i++) ::close(package->fd[i]); } std::shared_ptr secure_for_cpu_write() override { auto raw = new client::MemoryRegion{ size().width, size().height, stride(), pixel_format(), std::shared_ptr(new char[stride().as_int()*size().height.as_int()], [](char arr[]) {delete[] arr;})}; return std::shared_ptr(raw); } geometry::Size size() const override { return size_; } geometry::Stride stride() const override { return geometry::Stride{package->stride}; } MirPixelFormat pixel_format() const override { return pf_; } uint32_t age() const override { return 0; } void increment_age() override {} void mark_as_submitted() override {} std::shared_ptr native_buffer_handle() const override { #ifndef ANDROID return package; #else return std::shared_ptr(); #endif } void update_from(MirBufferPackage const&) override {} void fill_update_msg(MirBufferPackage&) override{} MirNativeBuffer* as_mir_native_buffer() const { return nullptr; } void set_fence(MirNativeFence*, MirBufferAccess) {} MirNativeFence* get_fence() const { return nullptr; } bool wait_fence(MirBufferAccess, std::chrono::nanoseconds) { return true; } std::shared_ptr const package; geometry::Size size_; MirPixelFormat pf_; }; } } } #endif ./tests/include/mir/test/barrier.h0000644000015600001650000000302212676616125017240 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_BARRIER_H_ #define MIR_TEST_BARRIER_H_ #include #include namespace mir { namespace test { class Barrier { public: explicit Barrier(unsigned wait_threads) : wait_threads{wait_threads} {} void reset(unsigned threads) { std::unique_lock lock(mutex); wait_threads = threads; } void ready() { std::unique_lock lock(mutex); --wait_threads; cv.notify_all(); if (!cv.wait_for(lock, std::chrono::minutes(1), [&]{ return wait_threads == 0; })) throw std::runtime_error("Timeout"); } private: Barrier(Barrier const&) = default; Barrier& operator=(Barrier const&) = default; unsigned wait_threads; std::mutex mutex; std::condition_variable cv; }; } } #endif /* MIR_TEST_BARRIER_H_ */ ./tests/include/mir/test/test_dispatchable.h0000644000015600001650000000320712676616125021301 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_TEST_DISPATCHABLE_H_ #define MIR_TEST_TEST_DISPATCHABLE_H_ #include "mir/test/pipe.h" #include "mir/dispatch/dispatchable.h" #include namespace mir { namespace test { class TestDispatchable : public dispatch::Dispatchable { public: TestDispatchable(std::function const& target, dispatch::FdEvents relevant_events); TestDispatchable(std::function const& target); TestDispatchable(std::function const& target); mir::Fd watch_fd() const override; bool dispatch(dispatch::FdEvents events) override; dispatch::FdEvents relevant_events() const override; void trigger(); void untrigger(); void hangup(); private: mir::Fd read_fd; mir::Fd write_fd; std::function const target; dispatch::FdEvents const eventmask; }; } } #endif // MIR_TEST_TEST_DISPATCHABLE_H_ ./tests/include/mir/test/gmock_fixes.h0000644000015600001650000001312712676616125020117 0ustar jenkinsjenkins// // Copyright © 2012 Canonical Ltd. Copyright 2007, Google Inc. // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Author: wan@google.com (Zhanyong Wan) // Authored by: Alan Griffiths #ifndef MIR_TEST_GMOCK_FIXES_H_ #define MIR_TEST_GMOCK_FIXES_H_ #include #include namespace testing { namespace internal { template class ActionResultHolder> : public UntypedActionResultHolderBase { public: explicit ActionResultHolder(std::unique_ptr&& a_value) : value_(std::move(a_value)) {} // The compiler-generated copy constructor and assignment operator // are exactly what we need, so we don't need to define them. // Returns the held value and deletes this object. std::unique_ptr GetValueAndDelete() const { std::unique_ptr retval(std::move(value_)); delete this; return retval; } // Prints the held value as an action's result to os. virtual void PrintAsActionResult(::std::ostream* os) const { *os << "\n Returns: "; // T may be a reference type, so we don't use UniversalPrint(). UniversalPrinter>::Print(value_, os); } // Performs the given mock function's default action and returns the // result in a new-ed ActionResultHolder. template static ActionResultHolder* PerformDefaultAction( const FunctionMockerBase* func_mocker, const typename Function::ArgumentTuple& args, const string& call_description) { return new ActionResultHolder( func_mocker->PerformDefaultAction(args, call_description)); } // Performs the given action and returns the result in a new-ed // ActionResultHolder. template static ActionResultHolder* PerformAction(const Action& action, const typename Function::ArgumentTuple& args) { return new ActionResultHolder(action.Perform(args)); } private: std::unique_ptr mutable value_; // T could be a reference type, so = isn't supported. GTEST_DISALLOW_ASSIGN_(ActionResultHolder); }; } template class DefaultValue>> { public: // Unsets the default value for type T. static void Clear() {} // Returns true if the user has set the default value for type T. static bool IsSet() { return false; } // Returns true if T has a default return value set by the user or there // exists a built-in default value. static bool Exists() { return true; } // Returns the default value for type T if the user has set one; // otherwise returns the built-in default value if there is one; // otherwise aborts the process. static std::unique_ptr Get() { return std::unique_ptr(); } }; template class DefaultValue> { public: // Unsets the default value for type T. static void Clear() {} // Returns true if the user has set the default value for type T. static bool IsSet() { return false; } // Returns true if T has a default return value set by the user or there // exists a built-in default value. static bool Exists() { return true; } // Returns the default value for type T if the user has set one; // otherwise returns the built-in default value if there is one; // otherwise aborts the process. static std::unique_ptr Get() { return std::unique_ptr(nullptr, nullptr); } }; template class DefaultValue> { public: // Unsets the default value for type T. static void Clear() {} // Returns true if the user has set the default value for type T. static bool IsSet() { return false; } // Returns true if T has a default return value set by the user or there // exists a built-in default value. static bool Exists() { return true; } // Returns the default value for type T if the user has set one; // otherwise returns the built-in default value if there is one; // otherwise aborts the process. static std::unique_ptr Get() { return std::unique_ptr(); } }; } #endif /* MIR_TEST_GMOCK_FIXES_H_ */ ./tests/include/mir/test/wait_object.h0000644000015600001650000000275712676616125020122 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis * Andreas Pokorny */ #ifndef MIR_TEST_WAIT_OBJECT_H_ #define MIR_TEST_WAIT_OBJECT_H_ #include #include #include #include namespace mir { namespace test { class WaitObject { public: void notify_ready(); void wait_until_ready(); template void wait_until_ready(std::chrono::duration const& span) { std::unique_lock lock{mutex}; if (!cv.wait_for(lock, span, [this] { return ready; })) { BOOST_THROW_EXCEPTION(std::runtime_error("WaitObject timed out")); } } private: std::mutex mutex; std::condition_variable cv; bool ready = false; }; } } #endif ./tests/include/mir_test_framework/0000755000015600001650000000000012676616160017600 5ustar jenkinsjenkins./tests/include/mir_test_framework/testing_server_configuration.h0000644000015600001650000000350312676616157025752 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_TEST_TESTING_SERVER_CONFIGURATION_H_ #define MIR_TEST_TESTING_SERVER_CONFIGURATION_H_ #include "mir_test_framework/stubbed_server_configuration.h" #include "mir/test/cross_process_sync.h" namespace mir_test_framework { using namespace mir; class TestingServerConfiguration : public StubbedServerConfiguration { public: TestingServerConfiguration(); explicit TestingServerConfiguration(std::vector const& display_rects); ~TestingServerConfiguration(); // Code to run in server process virtual void exec(); // Code to run in server process after server starts virtual void on_start(); // Code to run in server process after server exits virtual void on_exit(); std::shared_ptr the_server_status_listener() override; virtual std::string the_socket_file() const override; using DefaultServerConfiguration::the_options; virtual void wait_for_server_start(); private: mir::test::CrossProcessSync server_started_sync; bool using_server_started_sync; }; std::string const& test_socket_file(); } #endif /* MIR_TEST_TESTING_SERVER_CONFIGURATION_H_ */ ./tests/include/mir_test_framework/stub_client_platform_factory.h0000644000015600001650000000364712676616157025737 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_FRAMEWORK_STUB_CLIENT_PLATFORM_FACTORY_ #define MIR_TEST_FRAMEWORK_STUB_CLIENT_PLATFORM_FACTORY_ #include "mir/client_platform_factory.h" #include "mir/client_platform.h" namespace mir_test_framework { struct StubClientPlatform : public mir::client::ClientPlatform { StubClientPlatform(mir::client::ClientContext* context); MirPlatformType platform_type() const; void populate(MirPlatformPackage& package) const; MirPlatformMessage* platform_operation(MirPlatformMessage const*) override; std::shared_ptr create_buffer_factory(); std::shared_ptr create_egl_native_window(mir::client::EGLNativeSurface* surface); std::shared_ptr create_egl_native_display(); MirNativeBuffer* convert_native_buffer(mir::graphics::NativeBuffer* buf) const; MirPixelFormat get_egl_pixel_format(EGLDisplay, EGLConfig) const override; mir::client::ClientContext* const context; }; struct StubClientPlatformFactory : public mir::client::ClientPlatformFactory { std::shared_ptr create_client_platform(mir::client::ClientContext* context) override; }; } #endif /* MIR_TEST_FRAMEWORK_STUB_CLIENT_PLATFORM_ */ ./tests/include/mir_test_framework/stub_input_platform.h0000644000015600001650000000423512676616125024056 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_FRAMEWORK_STUB_INPUT_PLATFORM_H_ #define MIR_TEST_FRAMEWORK_STUB_INPUT_PLATFORM_H_ #include "mir/input/platform.h" #include #include #include #include namespace mir { namespace dispatch { class ActionQueue; class MultiplexingDispatchable; } namespace input { class InputDevice; } } namespace mir_test_framework { class FakeInputDevice; class StubInputPlatform : public mir::input::Platform { public: explicit StubInputPlatform(std::shared_ptr const& input_device_registry); ~StubInputPlatform(); std::shared_ptr dispatchable() override; void start() override; void stop() override; static void add(std::shared_ptr const& dev); static void remove(std::shared_ptr const& dev); static void register_dispatchable(std::shared_ptr const& queue); static void unregister_dispatchable(std::shared_ptr const& queue); private: std::shared_ptr const platform_dispatchable; std::shared_ptr const platform_queue; std::shared_ptr const registry; static std::atomic stub_input_platform; static std::vector> device_store; static std::mutex device_store_guard; }; } #endif ./tests/include/mir_test_framework/deferred_in_process_server.h0000644000015600001650000000256612676616125025355 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_DEFERRED_IN_PROCESS_SERVER_H_ #define MIR_TEST_FRAMEWORK_DEFERRED_IN_PROCESS_SERVER_H_ #include "mir_test_framework/server_runner.h" #include namespace mir_test_framework { /** Fixture for running Mir server in test process. * The server startup is deferred until start_server() is called, to allows the * test code to initialize the server environment with expectations or stubs. */ struct DeferredInProcessServer : testing::Test, private ServerRunner { void TearDown() override { ServerRunner::stop_server(); } using ServerRunner::start_server; using ServerRunner::new_connection; }; } #endif /* MIR_TEST_FRAMEWORK_DEFERRED_IN_PROCESS_SERVER_H_ */ ./tests/include/mir_test_framework/input_testing_server_configuration.h0000644000015600001650000000351612676616125027170 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_INPUT_TESTING_SERVER_CONFIGURATION_H_ #define MIR_TEST_INPUT_TESTING_SERVER_CONFIGURATION_H_ #include "mir_test_framework/testing_server_configuration.h" #include "mir/geometry/rectangle.h" #include #include #include #include #include #include namespace mir_test_framework { class InputTestingServerConfiguration : public TestingServerConfiguration { public: InputTestingServerConfiguration(); explicit InputTestingServerConfiguration(std::vector const& display_rects); void on_start() override; void on_exit() override; std::shared_ptr the_input_manager() override; std::shared_ptr the_input_dispatcher() override; std::shared_ptr the_input_targeter() override; std::shared_ptr the_input_sender() override; protected: virtual void inject_input() = 0; void wait_until_client_appears(std::string const& surface_name); std::thread input_injection_thread; }; } #endif /* MIR_TEST_INPUT_TESTING_SERVER_CONFIGURATION_H_ */ ./tests/include/mir_test_framework/libinput_environment.h0000644000015600001650000000564312676616157024241 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_FRAMEWORK_LIBINPUT_ENVIRONMENT_H_ #define MIR_TEST_FRAMEWORK_LIBINPUT_ENVIRONMENT_H_ #include "mir/test/doubles/mock_udev.h" #include "mir/test/doubles/mock_libinput.h" #include #include namespace mir_test_framework { struct LibInputEnvironment { libinput *li_context{reinterpret_cast(0xFEBA)}; LibInputEnvironment(); /** * Creates a predefined libinput device selected via the given device_name. It will then * discoverable through udev and libinput. * \note This method will not queue a LIBINPUT_EVENT_DEVICE_ADDED event. */ libinput_device* setup_device(std::string const& device_name); /** * Creates a predefined libinput device selected via the given device_name. It will then * discoverable through udev and libinput. The device can be reached by processing the * queued LIBINPUT_EVENT_DEVICE_ADDED event. */ void add_standard_device(std::string const& device_name); /** * Removes the predefined device selected via the given device_name. */ void remove_standard_device(std::string const& device_name); testing::NiceMock mock_libinput; testing::NiceMock mock_udev; template PtrT to_fake_ptr(unsigned int number) { return reinterpret_cast(number); } template PtrT get_next_fake_ptr(Container const& container) { return to_fake_ptr(container.size()+1); } struct DeviceSetup { DeviceSetup() = default; explicit DeviceSetup(std::string const& path) : path(path) {} std::string path; std::unordered_map properties; }; std::unordered_map standard_devices; static std::string const synaptics_touchpad; static std::string const bluetooth_magic_trackpad; static std::string const usb_keyboard; static std::string const usb_mouse; static std::string const laptop_keyboard; static std::string const mtk_tpd; static std::string const usb_joystick; std::unordered_map available_devs; }; } #endif ./tests/include/mir_test_framework/basic_client_server_fixture.h0000644000015600001650000000411712676616125025530 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_BASIC_CLIENT_SERVER_FIXTURE_H_ #define MIR_TEST_FRAMEWORK_BASIC_CLIENT_SERVER_FIXTURE_H_ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/in_process_server.h" #include "mir_test_framework/using_stub_client_platform.h" #include "boost/throw_exception.hpp" namespace mir_test_framework { /// Test fixture to create a server in process and allocate a client connection. /// There are a lot of tests that could use this context. template struct BasicClientServerFixture : InProcessServer { TestServerConfiguration server_configuration; UsingStubClientPlatform using_stub_client_platform; mir::DefaultServerConfiguration& server_config() override { return server_configuration; } void SetUp() override { InProcessServer::SetUp(); connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); if (!mir_connection_is_valid(connection)) { BOOST_THROW_EXCEPTION( std::runtime_error{std::string{"Failed to connect to test server: "} + mir_connection_get_error_message(connection)}); } } void TearDown() override { mir_connection_release(connection); InProcessServer::TearDown(); } MirConnection* connection = nullptr; }; } #endif /* MIR_TEST_FRAMEWORK_BASIC_CLIENT_SERVER_FIXTURE_H_ */ ./tests/include/mir_test_framework/stub_client_connection_configuration.h0000644000015600001650000000232012676616125027430 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TESTING_STUB_CLIENT_CONNECTION_CONFIGURATION #define MIR_TESTING_STUB_CLIENT_CONNECTION_CONFIGURATION #include "src/client/default_connection_configuration.h" #include namespace mir_test_framework { struct StubConnectionConfiguration : public mir::client::DefaultConnectionConfiguration { StubConnectionConfiguration(std::string const& socket_file); std::shared_ptr the_client_platform_factory() override; }; } #endif /* MIR_TESTING_STUB_CLIENT_CONNECTION_CONFIGURATION */ ./tests/include/mir_test_framework/using_client_platform.h0000644000015600001650000000512612676616125024345 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_USING_CLIENT_PLATFORM_H #define MIR_TEST_FRAMEWORK_USING_CLIENT_PLATFORM_H #include #include #include "mir_test_framework/stub_client_connection_configuration.h" #include "src/client/mir_connection_api.h" namespace mcl = mir::client; namespace mir_test_framework { class StubMirConnectionAPI : public mcl::MirConnectionAPI { public: StubMirConnectionAPI( mcl::MirConnectionAPI* prev_api, mcl::ConfigurationFactory factory) : prev_api{prev_api}, factory{factory} { } MirWaitHandle* connect( mcl::ConfigurationFactory configuration, char const* socket_file, char const* name, mir_connected_callback callback, void* context) override; void release(MirConnection* connection) override; mcl::ConfigurationFactory configuration_factory() override; private: mcl::MirConnectionAPI* const prev_api; mcl::ConfigurationFactory factory; }; template class UsingClientPlatform { public: UsingClientPlatform() : prev_api{mir_connection_api_impl}, stub_api{ new StubMirConnectionAPI{prev_api, make_configuration_factory()}} { mir_connection_api_impl = stub_api.get(); } ~UsingClientPlatform() { mir_connection_api_impl = prev_api; } private: UsingClientPlatform(UsingClientPlatform const&) = delete; UsingClientPlatform operator=(UsingClientPlatform const&) = delete; mir::client::MirConnectionAPI* prev_api; std::unique_ptr stub_api; mcl::ConfigurationFactory make_configuration_factory() { return [](std::string const& socket) { return std::unique_ptr{ new ClientConfig{socket} }; }; } }; } #endif //MIR_TEST_FRAMEWORK_USING_CLIENT_PLATFORM_H ./tests/include/mir_test_framework/command_line_server_configuration.h0000644000015600001650000000223312676616125026714 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Josh Arenson */ #ifndef MIR_TEST_FRAMEWORK_COMMAND_LINE_SERVER_CONFIGURATION #define MIR_TEST_FRAMEWORK_COMMAND_LINE_SERVER_CONFIGURATION #include namespace mir { namespace options { class DefaultConfiguration; } class Server; } namespace mir_test_framework { auto configuration_from_commandline() -> std::shared_ptr; void configure_from_commandline(mir::Server& server); } #endif /* MIR_TEST_FRAMEWORK_COMMAND_LINE_SERVER_CONFIGURATION */ ./tests/include/mir_test_framework/udev_environment.h0000644000015600001650000000555412676616125023352 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Christopher James Halse Rogers */ #ifndef MIR_TESTING_UDEV_ENVIRONMENT #define MIR_TESTING_UDEV_ENVIRONMENT #include #include #include #include namespace mir_test_framework { class UdevEnvironment { public: UdevEnvironment(); ~UdevEnvironment() noexcept; std::string add_device(char const* subsystem, char const* name, char const* parent, std::initializer_list attributes, std::initializer_list properties); void remove_device(std::string const& device_path); void emit_device_changed(std::string const& device_path); /** * Add a device from the set of standard device traces * * Looks for a name.umockdev file, and adds a UMockDev device * from that description. * * If name.ioctl exists, it loads that ioctl script for the device * * @param name The unadorned filename of the device traces to add. */ void add_standard_device(std::string const& name); /** * Load an ioctl recording for a UMockdev device * * Looks for a name.ioctl file recorded with umockdev-record --ioctl * and adds it to the associated device in the testbed. * * The udev records for the device these ioctl records will be associated with * must already exist in the testbed * * @param name The unadorned filename for the ioctl records to add */ void load_device_ioctls(std::string const& name); /** * Load an evemu evdev recording for a UMockdev device * * Looks for a name.evemu file recorded with umockdev-record --evemu * (or evemu-record) and associates it with the udev device it was recorded from. * * The udev records for the device this recording is associated with * must already exist in the testbed * * @param name The unadorned filename for the ioctl records to add */ void load_device_evemu(std::string const& name); UMockdevTestbed *testbed; std::string const recordings_path; }; } #endif //MIR_TESTING_UDEV_ENVIRONMENT ./tests/include/mir_test_framework/stubbed_server_configuration.h0000644000015600001650000000422412676616157025726 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_STUBBED_SERVER_CONFIGURATION_H_ #define MIR_TEST_FRAMEWORK_STUBBED_SERVER_CONFIGURATION_H_ #include "mir/default_server_configuration.h" #include "mir/geometry/rectangle.h" #include namespace mir_test_framework { using namespace mir; /** * stubs out the graphics and input subsystems to avoid tests * failing where the hardware is not accessible */ class StubbedServerConfiguration : public DefaultServerConfiguration { public: StubbedServerConfiguration(); explicit StubbedServerConfiguration(std::vector const& display_rects); ~StubbedServerConfiguration(); std::shared_ptr the_graphics_platform() override; std::shared_ptr the_renderer_factory() override; // We override the_input_manager in the default server configuration // to avoid starting and stopping the full android input stack for tests // which do not leverage input. std::shared_ptr the_input_manager() override; std::shared_ptr the_input_targeter() override; std::shared_ptr the_input_sender() override; std::shared_ptr the_cursor() override; std::shared_ptr the_logger() override; private: std::shared_ptr graphics_platform; std::vector const display_rects; }; } #endif /* MIR_TEST_FRAMEWORK_STUBBED_SERVER_CONFIGURATION_H_ */ ./tests/include/mir_test_framework/client_platform_factory.h0000644000015600001650000000543712676616157024701 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_FRAMEWORK_CLIENT_PLATFORM_FACTORY_H_ #define MIR_TEST_FRAMEWORK_CLIENT_PLATFORM_FACTORY_H_ #include "mir/shared_library.h" #include "mir/client_platform_factory.h" #include "mir_test_framework/executable_path.h" #include "mir/test/doubles/mock_client_context.h" namespace mtd = mir::test::doubles; namespace mir_test_framework { std::shared_ptr platform_library; namespace { std::shared_ptr wrap_in_platform_library_cleanup( std::shared_ptr&& platform) { return std::shared_ptr{ platform.get(), [platform](mir::client::ClientPlatform*) mutable { platform.reset(); platform_library.reset(); } }; } } std::shared_ptr create_android_client_platform() { using namespace testing; mtd::MockClientContext ctx; ON_CALL(ctx, populate_server_package(_)) .WillByDefault(Invoke([](MirPlatformPackage& package) { ::memset(&package, 0, sizeof(package)); })); platform_library = std::make_shared(client_platform("android")); auto platform_factory = platform_library->load_function("create_client_platform"); return wrap_in_platform_library_cleanup(platform_factory(&ctx)); } std::shared_ptr create_mesa_client_platform( mir::client::ClientContext* client_context) { using namespace testing; platform_library = std::make_shared(client_platform("mesa")); auto platform_factory = platform_library->load_function("create_client_platform"); return wrap_in_platform_library_cleanup(platform_factory(client_context)); } std::shared_ptr get_platform_library() { if (!platform_library) { throw std::logic_error{"Must call one of create_*_client_platform() before calling get_platform_library()"}; } return platform_library; } } #endif // MIR_TEST_FRAMEWORK_CLIENT_PLATFORM_FACTORY_H_ ./tests/include/mir_test_framework/fake_input_server_configuration.h0000644000015600001650000000344512676616157026427 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr * Alexandros Frantzis */ #ifndef MIR_TEST_FAKE_INPUT_SERVER_CONFIGURATION_H_ #define MIR_TEST_FAKE_INPUT_SERVER_CONFIGURATION_H_ #include "mir_test_framework/testing_server_configuration.h" #include "mir_test_framework/temporary_environment_value.h" #include "mir_test_framework/executable_path.h" namespace mir_test_framework { class FakeInputServerConfiguration : public TestingServerConfiguration { public: FakeInputServerConfiguration(); explicit FakeInputServerConfiguration(std::vector const& display_rects); ~FakeInputServerConfiguration(); std::shared_ptr the_input_manager() override; std::shared_ptr the_input_dispatcher() override; std::shared_ptr the_input_targeter() override; std::shared_ptr the_input_sender() override; private: TemporaryEnvironmentValue input_lib{"MIR_SERVER_PLATFORM_INPUT_LIB", server_platform("input-stub.so").c_str()}; }; } #endif /* MIR_TEST_FAKE_INPUT_SERVER_CONFIGURATION_H_ */ ./tests/include/gmock_set_arg.h0000644000015600001650000000314312676616125016654 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef GMOCK_SET_ARG_H_ #define GMOCK_SET_ARG_H_ #include namespace testing { namespace internal { template class SetArgumentAction { public: // Constructs an action that sets the variable pointed to by the // N-th function argument to 'value'. explicit SetArgumentAction(const A& value) : value_(value) {} template void Perform(const ArgumentTuple& args) const { CompileAssertTypesEqual(); ::std::tr1::get(args) = value_; } private: const A value_; GTEST_DISALLOW_ASSIGN_(SetArgumentAction); }; } template PolymorphicAction< internal::SetArgumentAction< N, T, internal::IsAProtocolMessage::value> > SetArg(const T& x) { return MakePolymorphicAction(internal::SetArgumentAction< N, T, internal::IsAProtocolMessage::value>(x)); } } #endif /* GMOCK_SET_ARG_H_ */ ./tests/integration-tests/0000755000015600001650000000000012676616160015735 5ustar jenkinsjenkins./tests/integration-tests/test_surface_first_frame_sync.cpp0000644000015600001650000001604612676616125024555 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/compositor/compositor.h" #include "mir/compositor/display_buffer_compositor_factory.h" #include "mir/compositor/display_buffer_compositor.h" #include "mir/compositor/display_listener.h" #include "mir/compositor/renderer_factory.h" #include "mir/compositor/renderer.h" #include "mir/compositor/scene.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/display.h" #include "mir/scene/legacy_scene_change_notification.h" #include "mir/shell/shell.h" #include "mir/test/doubles/stub_renderer.h" #include "mir_test_framework/any_surface.h" #include "mir_test_framework/basic_client_server_fixture.h" #include "mir_test_framework/testing_server_configuration.h" #include "mir/test/spin_wait.h" #include "mir_toolkit/mir_client_library.h" #include #include #include #include namespace mt = mir::test; namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mg = mir::graphics; namespace mc = mir::compositor; namespace ms = mir::scene; namespace { class SynchronousCompositor : public mc::Compositor { public: SynchronousCompositor(std::shared_ptr const& display, std::shared_ptr const& display_listener, std::shared_ptr const& scene, std::shared_ptr const& dbc_factory) : display{display}, display_listener{display_listener}, scene{scene} { display->for_each_display_sync_group([this, &dbc_factory](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([this, &dbc_factory](mg::DisplayBuffer& display_buffer) { this->display_listener->add_display(display_buffer.view_area()); auto dbc = dbc_factory->create_compositor_for(display_buffer); this->scene->register_compositor(dbc.get()); display_buffer_compositor_map[&display_buffer] = std::move(dbc); }); }); auto notify = [this]() { this->display->for_each_display_sync_group([this](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([this](mg::DisplayBuffer& display_buffer) { auto& dbc = display_buffer_compositor_map[&display_buffer]; dbc->composite(this->scene->scene_elements_for(dbc.get())); }); group.post(); }); }; auto notify2 = [this, notify](int) { notify(); }; observer = std::make_shared(notify, notify2); } ~SynchronousCompositor() { for(auto& dbc : display_buffer_compositor_map) scene->unregister_compositor(dbc.second.get()); } void start() { scene->add_observer(observer); } void stop() { scene->remove_observer(observer); } private: std::shared_ptr const display; std::shared_ptr const display_listener; std::shared_ptr const scene; std::unordered_map> display_buffer_compositor_map; std::shared_ptr observer; }; class StubRenderer : public mtd::StubRenderer { public: StubRenderer(std::atomic& render_operations) : render_operations{render_operations} { } void render(mg::RenderableList const& renderables) const override { for (auto const& r : renderables) { r->buffer(); // We need to consume a buffer to unblock client tests ++render_operations; } } private: std::atomic& render_operations; }; struct ServerConfig : mtf::TestingServerConfiguration { std::shared_ptr the_renderer_factory() override { struct StubRendererFactory : mc::RendererFactory { StubRendererFactory(std::atomic& render_operations) : render_operations{render_operations} { } std::unique_ptr create_renderer_for(mg::DisplayBuffer&) { auto raw = new StubRenderer{render_operations}; return std::unique_ptr(raw); } std::atomic& render_operations; }; if (!stub_renderer_factory) stub_renderer_factory = std::make_shared(render_operations); return stub_renderer_factory; } std::shared_ptr the_compositor() override { if (!sync_compositor) { sync_compositor = std::make_shared( the_display(), the_shell(), the_scene(), the_display_buffer_compositor_factory()); } return sync_compositor; } std::atomic render_operations{0}; std::shared_ptr stub_renderer_factory; std::shared_ptr sync_compositor; }; struct SurfaceFirstFrameSync : mtf::BasicClientServerFixture { int number_of_executed_render_operations() { return server_configuration.render_operations; } }; } TEST_F(SurfaceFirstFrameSync, surface_not_rendered_until_buffer_is_pushed) { auto surface = mtf::make_any_surface(connection); ASSERT_TRUE(surface != NULL); EXPECT_TRUE(mir_surface_is_valid(surface)); EXPECT_STREQ(mir_surface_get_error_message(surface), ""); /* * This test uses a synchronous compositor (instead of the default * multi-threaded one) to ensure we don't get a false negative * for this expectation. In the multi-threaded case we can get a * a false negative if the compositor doesn't get the chance to run at * all before we reach this point. */ EXPECT_EQ(0, number_of_executed_render_operations()); mir_buffer_stream_swap_buffers_sync( mir_surface_get_buffer_stream(surface)); /* After submitting the buffer we should get some render operations */ mir::test::spin_wait_for_condition_or_timeout( [this] { return number_of_executed_render_operations() > 0; }, std::chrono::seconds{5}); mir_surface_release_sync(surface); } ./tests/integration-tests/test_surface_stack_with_compositor.cpp0000644000015600001650000002720712676616125025637 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/compositor/display_listener.h" #include "mir/compositor/renderer_factory.h" #include "mir/scene/surface_creation_parameters.h" #include "src/server/report/null_report_factory.h" #include "src/server/scene/surface_stack.h" #include "src/server/compositor/buffer_stream_surfaces.h" #include "src/server/scene/basic_surface.h" #include "src/server/compositor/default_display_buffer_compositor_factory.h" #include "src/server/compositor/multi_threaded_compositor.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/mock_buffer_stream.h" #include "mir/test/doubles/mock_buffer_bundle.h" #include "mir/test/doubles/null_display.h" #include "mir/test/doubles/stub_renderer.h" #include "mir/test/doubles/stub_display_buffer.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_input_sender.h" #include "mir/test/doubles/null_display_sync_group.h" #include #include #include #include namespace ms = mir::scene; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mr = mir::report; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { class StubRendererFactory : public mc::RendererFactory { public: std::unique_ptr create_renderer_for(mg::DisplayBuffer&) override { return std::unique_ptr(new mtd::StubRenderer); } }; struct CountingDisplaySyncGroup : public mtd::StubDisplaySyncGroup { CountingDisplaySyncGroup() : mtd::StubDisplaySyncGroup({100,100}) { } void post() override { increment_post_count(); } bool has_posted_at_least(unsigned int count, std::chrono::system_clock::time_point& timeout) { std::unique_lock lk(mutex); return count_cv.wait_until(lk, timeout, [this, &count](){ return post_count_ >= count; }); } private: void increment_post_count() { std::unique_lock lk(mutex); ++post_count_; count_cv.notify_all(); } std::mutex mutex; std::condition_variable count_cv; unsigned int post_count_{0}; }; struct StubDisplay : public mtd::NullDisplay { StubDisplay(mg::DisplaySyncGroup& primary, mg::DisplaySyncGroup& secondary) : primary(primary), secondary(secondary) { } void for_each_display_sync_group(std::function const& fn) override { fn(primary); fn(secondary); } private: mg::DisplaySyncGroup& primary; mg::DisplaySyncGroup& secondary; }; struct StubDisplayListener : mc::DisplayListener { virtual void add_display(geom::Rectangle const& /*area*/) override {} virtual void remove_display(geom::Rectangle const& /*area*/) override {} }; struct SurfaceStackCompositor : public testing::Test { SurfaceStackCompositor() : timeout{std::chrono::system_clock::now() + std::chrono::seconds(5)}, mock_buffer_stream(std::make_shared>()), stub_surface{std::make_shared( std::string("stub"), geom::Rectangle{{0,0},{1,1}}, false, std::make_shared(mock_buffer_stream), std::shared_ptr(), std::shared_ptr(), std::shared_ptr(), null_scene_report)} { using namespace testing; ON_CALL(*mock_buffer_stream, compositor_acquire(_)) .WillByDefault(Return(mt::fake_shared(stubbuf))); } std::shared_ptr null_scene_report{mr::null_scene_report()}; ms::SurfaceStack stack{null_scene_report}; std::shared_ptr null_comp_report{mr::null_compositor_report()}; StubRendererFactory renderer_factory; std::chrono::system_clock::time_point timeout; std::shared_ptr mock_buffer_stream; std::shared_ptr stub_surface; ms::SurfaceCreationParameters default_params; mtd::StubBuffer stubbuf{geom::Size{1,1}}; CountingDisplaySyncGroup stub_primary_db; CountingDisplaySyncGroup stub_secondary_db; StubDisplay stub_display{stub_primary_db, stub_secondary_db}; StubDisplayListener stub_display_listener; mc::DefaultDisplayBufferCompositorFactory dbc_factory{ mt::fake_shared(renderer_factory), null_comp_report}; }; std::chrono::milliseconds const default_delay{-1}; } TEST_F(SurfaceStackCompositor, composes_on_start_if_told_to_in_constructor) { mc::MultiThreadedCompositor mt_compositor( mt::fake_shared(stub_display), mt::fake_shared(stack), mt::fake_shared(dbc_factory), mt::fake_shared(stub_display_listener), null_comp_report, default_delay, true); mt_compositor.start(); EXPECT_TRUE(stub_primary_db.has_posted_at_least(1, timeout)); EXPECT_TRUE(stub_secondary_db.has_posted_at_least(1, timeout)); } TEST_F(SurfaceStackCompositor, does_not_composes_on_start_if_told_not_to_in_constructor) { mc::MultiThreadedCompositor mt_compositor( mt::fake_shared(stub_display), mt::fake_shared(stack), mt::fake_shared(dbc_factory), mt::fake_shared(stub_display_listener), null_comp_report, default_delay, false); mt_compositor.start(); EXPECT_TRUE(stub_primary_db.has_posted_at_least(0, timeout)); EXPECT_TRUE(stub_secondary_db.has_posted_at_least(0, timeout)); } TEST_F(SurfaceStackCompositor, swapping_a_surface_that_has_been_added_triggers_a_composition) { mc::MultiThreadedCompositor mt_compositor( mt::fake_shared(stub_display), mt::fake_shared(stack), mt::fake_shared(dbc_factory), mt::fake_shared(stub_display_listener), null_comp_report, default_delay, false); mt_compositor.start(); stack.add_surface(stub_surface, default_params.input_mode); stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){}); EXPECT_TRUE(stub_primary_db.has_posted_at_least(1, timeout)); EXPECT_TRUE(stub_secondary_db.has_posted_at_least(1, timeout)); } //test associated with lp:1290306, 1293896, 1294048, 1294051, 1294053 TEST_F(SurfaceStackCompositor, compositor_runs_until_all_surfaces_buffers_are_consumed) { using namespace testing; ON_CALL(*mock_buffer_stream, buffers_ready_for_compositor(_)) .WillByDefault(Return(5)); mc::MultiThreadedCompositor mt_compositor( mt::fake_shared(stub_display), mt::fake_shared(stack), mt::fake_shared(dbc_factory), mt::fake_shared(stub_display_listener), null_comp_report, default_delay, false); mt_compositor.start(); stack.add_surface(stub_surface, default_params.input_mode); stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){}); EXPECT_TRUE(stub_primary_db.has_posted_at_least(5, timeout)); EXPECT_TRUE(stub_secondary_db.has_posted_at_least(5, timeout)); } TEST_F(SurfaceStackCompositor, bypassed_compositor_runs_until_all_surfaces_buffers_are_consumed) { using namespace testing; ON_CALL(*mock_buffer_stream, buffers_ready_for_compositor(_)) .WillByDefault(Return(5)); stub_surface->resize(geom::Size{10,10}); stub_surface->move_to(geom::Point{0,0}); mc::MultiThreadedCompositor mt_compositor( mt::fake_shared(stub_display), mt::fake_shared(stack), mt::fake_shared(dbc_factory), mt::fake_shared(stub_display_listener), null_comp_report, default_delay, false); mt_compositor.start(); stack.add_surface(stub_surface, default_params.input_mode); stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){}); EXPECT_TRUE(stub_primary_db.has_posted_at_least(5, timeout)); EXPECT_TRUE(stub_secondary_db.has_posted_at_least(5, timeout)); } TEST_F(SurfaceStackCompositor, an_empty_scene_retriggers) { using namespace testing; ON_CALL(*mock_buffer_stream, buffers_ready_for_compositor(_)) .WillByDefault(Return(0)); mc::MultiThreadedCompositor mt_compositor( mt::fake_shared(stub_display), mt::fake_shared(stack), mt::fake_shared(dbc_factory), mt::fake_shared(stub_display_listener), null_comp_report, default_delay, false); mt_compositor.start(); stack.add_surface(stub_surface, default_params.input_mode); stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){}); EXPECT_TRUE(stub_primary_db.has_posted_at_least(1, timeout)); EXPECT_TRUE(stub_secondary_db.has_posted_at_least(1, timeout)); stack.remove_surface(stub_surface); EXPECT_TRUE(stub_primary_db.has_posted_at_least(2, timeout)); EXPECT_TRUE(stub_secondary_db.has_posted_at_least(2, timeout)); } TEST_F(SurfaceStackCompositor, moving_a_surface_triggers_composition) { stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){}); stack.add_surface(stub_surface, default_params.input_mode); mc::MultiThreadedCompositor mt_compositor( mt::fake_shared(stub_display), mt::fake_shared(stack), mt::fake_shared(dbc_factory), mt::fake_shared(stub_display_listener), null_comp_report, default_delay, false); mt_compositor.start(); stub_surface->move_to(geom::Point{1,1}); EXPECT_TRUE(stub_primary_db.has_posted_at_least(1, timeout)); EXPECT_TRUE(stub_secondary_db.has_posted_at_least(1, timeout)); } TEST_F(SurfaceStackCompositor, removing_a_surface_triggers_composition) { stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){}); stack.add_surface(stub_surface, default_params.input_mode); mc::MultiThreadedCompositor mt_compositor( mt::fake_shared(stub_display), mt::fake_shared(stack), mt::fake_shared(dbc_factory), mt::fake_shared(stub_display_listener), null_comp_report, default_delay, false); mt_compositor.start(); stack.remove_surface(stub_surface); EXPECT_TRUE(stub_primary_db.has_posted_at_least(1, timeout)); EXPECT_TRUE(stub_secondary_db.has_posted_at_least(1, timeout)); } TEST_F(SurfaceStackCompositor, buffer_updates_trigger_composition) { using namespace testing; ON_CALL(*mock_buffer_stream, buffers_ready_for_compositor(_)) .WillByDefault(testing::Return(1)); stack.add_surface(stub_surface, default_params.input_mode); stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){}); mc::MultiThreadedCompositor mt_compositor( mt::fake_shared(stub_display), mt::fake_shared(stack), mt::fake_shared(dbc_factory), mt::fake_shared(stub_display_listener), null_comp_report, default_delay, false); mt_compositor.start(); stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){}); EXPECT_TRUE(stub_primary_db.has_posted_at_least(1, timeout)); EXPECT_TRUE(stub_secondary_db.has_posted_at_least(1, timeout)); } ./tests/integration-tests/shell/0000755000015600001650000000000012676616126017046 5ustar jenkinsjenkins./tests/integration-tests/shell/test_session_lifecycle_event.cpp0000644000015600001650000000522412676616125025516 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Ricardo Mendoza */ #include "mir/scene/null_session_listener.h" #include "src/server/scene/application_session.h" #include "mir_test_framework/stubbed_server_configuration.h" #include "mir_test_framework/basic_client_server_fixture.h" #include "mir_test_framework/in_process_server.h" #include "mir_toolkit/mir_client_library.h" #include #include namespace ms = mir::scene; namespace mtf = mir_test_framework; namespace { struct MockStateHandler { MOCK_METHOD1(state_changed, void (MirLifecycleState)); }; void lifecycle_callback(MirConnection* /*connection*/, MirLifecycleState state, void* context) { auto const handler = static_cast(context); handler->state_changed(state); } class StubSessionListener : public ms::NullSessionListener { void stopping(std::shared_ptr const& session) { auto const app_session = std::static_pointer_cast(session); app_session->set_lifecycle_state(mir_lifecycle_state_will_suspend); } }; struct StubServerConfig : mtf::StubbedServerConfiguration { std::shared_ptr the_session_listener() override { return std::make_shared(); } }; struct LifecycleEventTest : mtf::InProcessServer { StubServerConfig server_configuration; mir::DefaultServerConfiguration& server_config() override { return server_configuration; } }; } TEST_F(LifecycleEventTest, lifecycle_event_test) { using namespace ::testing; auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto const handler = std::make_shared(); mir_connection_set_lifecycle_event_callback(connection, lifecycle_callback, handler.get()); EXPECT_CALL(*handler, state_changed(Eq(mir_lifecycle_state_will_suspend))).Times(1); EXPECT_CALL(*handler, state_changed(Eq(mir_lifecycle_connection_lost))).Times(AtMost(1)); mir_connection_release(connection); } ./tests/integration-tests/shell/CMakeLists.txt0000644000015600001650000000026212676616125021605 0ustar jenkinsjenkinslist( APPEND INTEGRATION_TESTS_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_session_lifecycle_event.cpp ) set( INTEGRATION_TESTS_SRCS ${INTEGRATION_TESTS_SRCS} PARENT_SCOPE) ./tests/integration-tests/compositor/0000755000015600001650000000000012676616126020135 5ustar jenkinsjenkins./tests/integration-tests/compositor/test_buffer_stream.cpp0000644000015600001650000002773412676616125024540 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "src/server/compositor/buffer_stream_surfaces.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "src/server/compositor/buffer_queue.h" #include "src/server/compositor/timeout_frame_dropping_policy_factory.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/mock_timer.h" #include "mir/test/signal.h" #include #include #include #include #include namespace mc = mir::compositor; namespace mg = mir::graphics; namespace mtd = mir::test::doubles; namespace mt =mir::test; namespace geom = mir::geometry; using namespace ::testing; namespace { struct BufferStreamSurfaces : mc::BufferStreamSurfaces { using mc::BufferStreamSurfaces::BufferStreamSurfaces; void acquire_client_buffer_async(mg::Buffer*& buffer, std::shared_ptr const& signal) { acquire_client_buffer( [signal, &buffer](mg::Buffer* new_buffer) { buffer = new_buffer; signal->raise(); }); } // Convenient functions to allow tests to be written in linear style mg::Buffer* acquire_client_buffer_blocking() { mg::Buffer* buffer = nullptr; auto signal = std::make_shared(); acquire_client_buffer_async(buffer, signal); signal->wait(); return buffer; } void swap_client_buffers_blocking(mg::Buffer*& buffer) { if (buffer) release_client_buffer(buffer); buffer = acquire_client_buffer_blocking(); } }; struct BufferStreamTest : public ::testing::Test { BufferStreamTest() : clock{std::make_shared()}, timer{std::make_shared(clock)}, frame_drop_timeout{1000}, nbuffers{3}, buffer_stream{create_bundle()} { } int buffers_free_for_client() const { return buffer_queue->buffers_free_for_client(); } std::shared_ptr create_bundle() { auto allocator = std::make_shared(); mg::BufferProperties properties{geom::Size{380, 210}, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware}; mc::TimeoutFrameDroppingPolicyFactory policy_factory{timer, frame_drop_timeout}; buffer_queue = std::make_shared(nbuffers, allocator, properties, policy_factory); return buffer_queue; } std::shared_ptr clock; std::shared_ptr timer; std::chrono::milliseconds const frame_drop_timeout; const int nbuffers; std::shared_ptr buffer_queue; BufferStreamSurfaces buffer_stream; }; } TEST_F(BufferStreamTest, gives_same_back_buffer_until_more_available) { mg::Buffer* client1 = buffer_stream.acquire_client_buffer_blocking(); auto client1_id = client1->id(); buffer_stream.release_client_buffer(client1); auto comp1 = buffer_stream.lock_compositor_buffer(nullptr); auto comp2 = buffer_stream.lock_compositor_buffer(nullptr); EXPECT_EQ(comp1->id(), comp2->id()); EXPECT_EQ(comp1->id(), client1_id); comp1.reset(); mg::Buffer* client2 = buffer_stream.acquire_client_buffer_blocking(); auto client2_id = client2->id(); buffer_stream.release_client_buffer(client2); auto comp3 = buffer_stream.lock_compositor_buffer(nullptr); EXPECT_NE(client1_id, comp3->id()); EXPECT_EQ(client2_id, comp3->id()); comp2.reset(); auto comp3_id = comp3->id(); comp3.reset(); auto comp4 = buffer_stream.lock_compositor_buffer(nullptr); EXPECT_EQ(comp3_id, comp4->id()); } TEST_F(BufferStreamTest, gives_all_monitors_the_same_buffer) { mg::Buffer* client_buffer{nullptr}; int const prefill = buffers_free_for_client(); for (int i = 0; i < prefill; ++i) buffer_stream.swap_client_buffers_blocking(client_buffer); auto first_monitor = buffer_stream.lock_compositor_buffer(0); auto first_compositor_id = first_monitor->id(); first_monitor.reset(); for (int m = 1; m <= 10; m++) { const void *th = reinterpret_cast(m); auto monitor = buffer_stream.lock_compositor_buffer(th); ASSERT_EQ(first_compositor_id, monitor->id()); } } TEST_F(BufferStreamTest, gives_different_back_buffer_asap) { buffer_stream.release_client_buffer(buffer_stream.acquire_client_buffer_blocking()); auto comp1 = buffer_stream.lock_compositor_buffer(nullptr); buffer_stream.release_client_buffer(buffer_stream.acquire_client_buffer_blocking()); auto comp2 = buffer_stream.lock_compositor_buffer(nullptr); EXPECT_NE(comp1->id(), comp2->id()); comp1.reset(); comp2.reset(); } TEST_F(BufferStreamTest, resize_affects_client_buffers_immediately) { auto old_size = buffer_stream.stream_size(); mg::Buffer* client = buffer_stream.acquire_client_buffer_blocking(); EXPECT_EQ(old_size, client->size()); buffer_stream.release_client_buffer(client); buffer_stream.lock_compositor_buffer(this); geom::Size const new_size { old_size.width.as_int() * 2, old_size.height.as_int() * 3 }; buffer_stream.resize(new_size); EXPECT_EQ(new_size, buffer_stream.stream_size()); client = buffer_stream.acquire_client_buffer_blocking(); EXPECT_EQ(new_size, client->size()); buffer_stream.release_client_buffer(client); buffer_stream.lock_compositor_buffer(this); buffer_stream.resize(old_size); EXPECT_EQ(old_size, buffer_stream.stream_size()); buffer_stream.lock_compositor_buffer(this); client = buffer_stream.acquire_client_buffer_blocking(); EXPECT_EQ(old_size, client->size()); buffer_stream.release_client_buffer(client); } TEST_F(BufferStreamTest, compositor_gets_resized_buffers) { auto old_size = buffer_stream.stream_size(); mg::Buffer* client = buffer_stream.acquire_client_buffer_blocking(); buffer_stream.release_client_buffer(client); geom::Size const new_size { old_size.width.as_int() * 2, old_size.height.as_int() * 3 }; buffer_stream.resize(new_size); EXPECT_EQ(new_size, buffer_stream.stream_size()); auto comp1 = buffer_stream.lock_compositor_buffer(nullptr); client = buffer_stream.acquire_client_buffer_blocking(); buffer_stream.release_client_buffer(client); EXPECT_EQ(old_size, comp1->size()); comp1.reset(); auto comp2 = buffer_stream.lock_compositor_buffer(nullptr); client = buffer_stream.acquire_client_buffer_blocking(); buffer_stream.release_client_buffer(client); EXPECT_EQ(new_size, comp2->size()); comp2.reset(); auto comp3 = buffer_stream.lock_compositor_buffer(nullptr); client = buffer_stream.acquire_client_buffer_blocking(); buffer_stream.release_client_buffer(client); EXPECT_EQ(new_size, comp3->size()); comp3.reset(); buffer_stream.resize(old_size); EXPECT_EQ(old_size, buffer_stream.stream_size()); auto comp4 = buffer_stream.lock_compositor_buffer(nullptr); // No client frames "drawn" since resize(old_size), so compositor gets new_size client = buffer_stream.acquire_client_buffer_blocking(); buffer_stream.release_client_buffer(client); EXPECT_EQ(new_size, comp4->size()); comp4.reset(); auto comp5 = buffer_stream.lock_compositor_buffer(nullptr); // Generate a new frame, which should be back to old_size now client = buffer_stream.acquire_client_buffer_blocking(); buffer_stream.release_client_buffer(client); EXPECT_EQ(old_size, comp5->size()); comp5.reset(); } TEST_F(BufferStreamTest, can_get_partly_released_back_buffer) { mg::Buffer* client = buffer_stream.acquire_client_buffer_blocking(); buffer_stream.release_client_buffer(client); int a, b, c; auto comp1 = buffer_stream.lock_compositor_buffer(&a); auto comp2 = buffer_stream.lock_compositor_buffer(&b); EXPECT_EQ(comp1->id(), comp2->id()); comp1.reset(); auto comp3 = buffer_stream.lock_compositor_buffer(&c); EXPECT_EQ(comp2->id(), comp3->id()); } namespace { void client_loop(mg::Buffer*& out_buffer, int nframes, BufferStreamSurfaces& stream) { for (int f = 0; f < nframes; f++) { stream.swap_client_buffers_blocking(out_buffer); ASSERT_NE(nullptr, out_buffer); std::this_thread::yield(); } } void compositor_loop(mc::BufferStream &stream, std::atomic &done) { while (!done.load()) { auto comp1 = stream.lock_compositor_buffer(nullptr); ASSERT_NE(nullptr, comp1); // Also stress test getting a second compositor buffer before yielding auto comp2 = stream.lock_compositor_buffer(nullptr); ASSERT_NE(nullptr, comp2); std::this_thread::yield(); comp1.reset(); comp2.reset(); } } void snapshot_loop(mc::BufferStream &stream, std::atomic &done) { while (!done.load()) { stream.with_most_recent_buffer_do([](mg::Buffer&){}); std::this_thread::yield(); } } } TEST_F(BufferStreamTest, stress_test_distinct_buffers) { // More would be good, but armhf takes too long const int num_snapshotters{2}; const int num_frames{200}; std::atomic done; done = false; mg::Buffer* out_buffer{nullptr}; // Ensure we've posted a buffer before we start the snapshot loop buffer_stream.swap_client_buffers_blocking(out_buffer); buffer_stream.swap_client_buffers_blocking(out_buffer); std::thread client(client_loop, std::ref(out_buffer), num_frames, std::ref(buffer_stream)); std::thread compositor(compositor_loop, std::ref(buffer_stream), std::ref(done)); std::vector> snapshotters; for (unsigned int i = 0; i < num_snapshotters; i++) { snapshotters.push_back( std::make_shared(snapshot_loop, std::ref(buffer_stream), std::ref(done))); } client.join(); done = true; buffer_stream.force_requests_to_complete(); compositor.join(); for (auto &s : snapshotters) s->join(); } TEST_F(BufferStreamTest, blocked_client_is_released_on_timeout) { using namespace testing; mg::Buffer* placeholder{nullptr}; // Grab all the buffers... int max = buffers_free_for_client(); for (int i = 0; i < max; ++i) { placeholder = buffer_stream.acquire_client_buffer_blocking(); buffer_stream.release_client_buffer(placeholder); } auto swap_completed = std::make_shared(); buffer_stream.acquire_client_buffer([swap_completed](mg::Buffer*) {swap_completed->raise();}); EXPECT_FALSE(swap_completed->raised()); clock->advance_time(frame_drop_timeout + std::chrono::milliseconds{1}); EXPECT_TRUE(swap_completed->wait_for(std::chrono::milliseconds{100})); } ./tests/integration-tests/compositor/test_synchronizer.cpp0000644000015600001650000000435212676616125024440 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "multithread_harness.h" #include namespace mt = mir::testing; namespace { void test_func(mt::SynchronizerSpawned* synchronizer, int* data) { *data = 1; synchronizer->child_enter_wait(); *data = 2; synchronizer->child_enter_wait(); } } TEST(Synchronizer, thread_stop_start) { int data = 0; mt::Synchronizer synchronizer; std::thread t1(test_func, &synchronizer, &data); synchronizer.ensure_child_is_waiting(); EXPECT_EQ(data, 1); synchronizer.activate_waiting_child(); synchronizer.ensure_child_is_waiting(); EXPECT_EQ(data, 2); synchronizer.activate_waiting_child(); t1.join(); } namespace { void test_func_pause (mt::SynchronizerSpawned* synchronizer, int* data) { bool wait_request; for(;;) { wait_request = synchronizer->child_check_wait_request(); *data = *data+1; if (wait_request) { if (synchronizer->child_enter_wait()) break; } std::this_thread::yield(); } } } TEST(Synchronizer, thread_pause_req) { int data = 0, old_data = 0; mt::Synchronizer synchronizer; std::thread t1(test_func_pause, &synchronizer, &data); synchronizer.ensure_child_is_waiting(); EXPECT_GT(data, old_data); old_data = data; synchronizer.activate_waiting_child(); synchronizer.ensure_child_is_waiting(); EXPECT_GT(data, old_data); synchronizer.activate_waiting_child(); synchronizer.ensure_child_is_waiting(); synchronizer.kill_thread(); synchronizer.activate_waiting_child(); t1.join(); } ./tests/integration-tests/compositor/multithread_harness.h0000644000015600001650000000551512676616125024360 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_MULTITHREAD_HARNESS_H_ #define MIR_TEST_MULTITHREAD_HARNESS_H_ #include #include #include namespace mir { namespace testing { /* interface for main/controller thread to synchronize system */ class SynchronizerController { public: virtual ~SynchronizerController() {} virtual void ensure_child_is_waiting() = 0; virtual void activate_waiting_child() = 0; virtual void kill_thread() = 0; }; /* interface for spawned threads to interact with main thread */ class SynchronizerSpawned { public: virtual ~SynchronizerSpawned() {} virtual bool child_enter_wait() = 0; virtual bool child_check_wait_request() = 0; }; class Synchronizer : public SynchronizerController, public SynchronizerSpawned { public: Synchronizer () : paused(false), pause_request(false), kill(false) { }; ~Synchronizer () { }; void ensure_child_is_waiting() { std::unique_lock lk(sync_mutex); pause_request = true; while (!paused) { cv.wait(lk); } pause_request = false; }; void activate_waiting_child() { std::unique_lock lk(sync_mutex); paused = false; cv.notify_all(); }; bool child_enter_wait() { std::unique_lock lk(sync_mutex); paused = true; cv.notify_all(); while (paused) { cv.wait(lk); } return kill; }; bool child_check_wait_request() { std::unique_lock lk(sync_mutex); return pause_request; }; void kill_thread() { std::unique_lock lk(sync_mutex); kill = true; }; private: std::condition_variable cv; std::mutex sync_mutex; bool paused; bool pause_request; bool kill; }; } } #endif /* MIR_TEST_MULTITHREAD_HARNESS_ */ ./tests/integration-tests/compositor/test_swapping_swappers.cpp0000644000015600001650000001235612676616125025462 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/stub_frame_dropping_policy_factory.h" #include "multithread_harness.h" #include "src/server/compositor/buffer_queue.h" #include "src/server/compositor/buffer_stream_surfaces.h" #include "mir/graphics/graphic_buffer_allocator.h" #include #include #include #include #include #include namespace mc = mir::compositor; namespace mg = mir::graphics; namespace mt = mir::testing; namespace geom = mir::geometry; namespace mtd = mir::test::doubles; namespace { struct SwapperSwappingStress : public ::testing::Test { void SetUp() { auto allocator = std::make_shared(); auto properties = mg::BufferProperties{geom::Size{380, 210}, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware}; mtd::StubFrameDroppingPolicyFactory policy_factory; switching_bundle = std::make_shared(3, allocator, properties, policy_factory); } std::shared_ptr switching_bundle; std::mutex acquire_mutex; // must live longer than our callback/lambda mg::Buffer* client_acquire_blocking( std::shared_ptr const& switching_bundle) { std::condition_variable cv; bool acquired = false; mg::Buffer* result; switching_bundle->client_acquire( [&](mg::Buffer* new_buffer) { std::unique_lock lock(acquire_mutex); result = new_buffer; acquired = true; cv.notify_one(); }); std::unique_lock lock(acquire_mutex); cv.wait(lock, [&]{ return acquired; }); return result; } }; } // namespace TEST_F(SwapperSwappingStress, swapper) { std::atomic_bool done(false); auto f = std::async(std::launch::async, [&] { for(auto i=0u; i < 400; i++) { auto b = client_acquire_blocking(switching_bundle); std::this_thread::yield(); switching_bundle->client_release(b); } done = true; }); auto g = std::async(std::launch::async, [&] { while (!done) { auto b = switching_bundle->compositor_acquire(0); std::this_thread::yield(); switching_bundle->compositor_release(b); } }); auto h = std::async(std::launch::async, [this] { for(auto i=0u; i < 100; i++) { switching_bundle->allow_framedropping(true); std::this_thread::yield(); switching_bundle->allow_framedropping(false); std::this_thread::yield(); } }); f.wait(); g.wait(); h.wait(); } TEST_F(SwapperSwappingStress, different_swapper_types) { std::atomic_bool done(false); auto f = std::async(std::launch::async, [&] { for(auto i=0u; i < 400; i++) { auto b = client_acquire_blocking(switching_bundle); std::this_thread::yield(); switching_bundle->client_release(b); } done = true; }); auto g = std::async(std::launch::async, [&] { while (!done) { auto b = switching_bundle->compositor_acquire(0); std::this_thread::yield(); switching_bundle->compositor_release(b); } }); auto h = std::async(std::launch::async, [this] { for(auto i=0u; i < 200; i++) { switching_bundle->allow_framedropping(true); std::this_thread::yield(); switching_bundle->allow_framedropping(false); std::this_thread::yield(); } }); f.wait(); g.wait(); h.wait(); } ./tests/integration-tests/compositor/CMakeLists.txt0000644000015600001650000000042512676616125022675 0ustar jenkinsjenkinslist( APPEND INTEGRATION_TESTS_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_swapping_swappers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_synchronizer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_stream.cpp ) set( INTEGRATION_TESTS_SRCS ${INTEGRATION_TESTS_SRCS} PARENT_SCOPE) ./tests/integration-tests/test_large_messages.cpp0000644000015600001650000000623012676616125022463 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_test_framework/in_process_server.h" #include "mir_test_framework/stubbed_server_configuration.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir/test/wait_object.h" #include "mir/test/fake_shared.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/null_display_changer.h" #include "mir_toolkit/mir_connection.h" #include namespace mtd = mir::test::doubles; namespace mt = mir::test; namespace mg = mir::graphics; namespace mf = mir::frontend; namespace { class LargeDisplayConfigChanger : public mtd::NullDisplayChanger { public: std::shared_ptr base_configuration() override { return mt::fake_shared(stub_display_config); } private: mtd::StubDisplayConfig stub_display_config{20}; }; struct LargeMessagesServerConfiguration : mir_test_framework::StubbedServerConfiguration { std::shared_ptr the_frontend_display_changer() override { return mt::fake_shared(large_display_config_changer); } LargeDisplayConfigChanger large_display_config_changer; }; struct LargeMessages : mir_test_framework::InProcessServer { LargeMessagesServerConfiguration large_messages_server_config; mir::DefaultServerConfiguration& server_config() override { return large_messages_server_config; } mir_test_framework::UsingStubClientPlatform using_stub_client_platform; }; struct ConnectionContext { mt::WaitObject connected; MirConnection* connection{nullptr}; }; void connection_callback(MirConnection* connection, void* context) { auto connection_context = static_cast(context); connection_context->connection = connection; connection_context->connected.notify_ready(); } } // Regression test for lp:1320187. Without a fix for that bug, connecting // to a server with a lot of information in the display configuration hangs // the client, because the server reply message is incomplete/corrupted. TEST_F(LargeMessages, connection_to_server_with_large_display_configuration_succeeds) { using namespace testing; ConnectionContext connection_context; mir_connect(new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, &connection_context); connection_context.connected.wait_until_ready(std::chrono::seconds{3}); EXPECT_NE(connection_context.connection, nullptr); mir_connection_release(connection_context.connection); } ./tests/integration-tests/test_surfaceloop.cpp0000644000015600001650000001722612676616125022033 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir_toolkit/mir_client_library.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/null_platform.h" #include "mir/test/doubles/null_display.h" #include "mir/test/doubles/null_display_sync_group.h" #include "mir/test/doubles/stub_display_buffer.h" #include "mir_test_framework/stubbed_server_configuration.h" #include "mir_test_framework/basic_client_server_fixture.h" #include #include #include #include #include #include "mir/test/gmock_fixes.h" namespace mc = mir::compositor; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace mf = mir::frontend; namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace { geom::Size const size{640, 480}; MirPixelFormat const format{mir_pixel_format_abgr_8888}; mg::BufferUsage const usage{mg::BufferUsage::hardware}; mg::BufferProperties const buffer_properties{size, format, usage}; class MockGraphicBufferAllocator : public mtd::StubBufferAllocator { public: MockGraphicBufferAllocator() { using testing::_; ON_CALL(*this, alloc_buffer(_)) .WillByDefault(testing::Invoke(this, &MockGraphicBufferAllocator::on_create_swapper)); } MOCK_METHOD1( alloc_buffer, std::shared_ptr (mg::BufferProperties const&)); std::shared_ptr on_create_swapper(mg::BufferProperties const&) { return std::make_shared(::buffer_properties); } ~MockGraphicBufferAllocator() noexcept {} }; class StubDisplay : public mtd::NullDisplay { public: StubDisplay() : display_sync_group{geom::Size{1600,1600}} { } void for_each_display_sync_group(std::function const& f) override { f(display_sync_group); } private: mtd::StubDisplaySyncGroup display_sync_group; }; struct SurfaceSync { void surface_created(MirSurface * new_surface) { std::unique_lock lock(guard); surface = new_surface; wait_condition.notify_all(); } void surface_released(MirSurface * /*released_surface*/) { std::unique_lock lock(guard); surface = NULL; wait_condition.notify_all(); } void wait_for_surface_create() { std::unique_lock lock(guard); wait_condition.wait(lock, [&]{ return !!surface; }); } void wait_for_surface_release() { std::unique_lock lock(guard); wait_condition.wait(lock, [&]{ return !surface; }); } std::mutex guard; std::condition_variable wait_condition; MirSurface * surface{nullptr}; }; void create_surface_callback(MirSurface* surface, void * context) { SurfaceSync* config = reinterpret_cast(context); config->surface_created(surface); } void release_surface_callback(MirSurface* surface, void * context) { SurfaceSync* config = reinterpret_cast(context); config->surface_released(surface); } void wait_for_surface_create(SurfaceSync* context) { context->wait_for_surface_create(); } void wait_for_surface_release(SurfaceSync* context) { context->wait_for_surface_release(); } struct BufferCounterConfig : mtf::StubbedServerConfiguration { class CountingStubBuffer : public mtd::StubBuffer { public: CountingStubBuffer() { std::lock_guard lock{buffers_mutex}; ++buffers_created; buffers_cv.notify_one(); } ~CountingStubBuffer() { std::lock_guard lock{buffers_mutex}; ++buffers_destroyed; buffers_cv.notify_one(); } static std::mutex buffers_mutex; static std::condition_variable buffers_cv; static int buffers_created; static int buffers_destroyed; }; class StubGraphicBufferAllocator : public mtd::StubBufferAllocator { public: std::shared_ptr alloc_buffer(mg::BufferProperties const&) override { return std::make_shared(); } }; class StubPlatform : public mtd::NullPlatform { public: mir::UniqueModulePtr create_buffer_allocator() override { return mir::make_module_ptr(); } mir::UniqueModulePtr create_display( std::shared_ptr const&, std::shared_ptr const&) override { return mir::make_module_ptr(); } }; std::shared_ptr the_graphics_platform() { if (!platform) platform = std::make_shared(); return platform; } std::shared_ptr platform; }; std::mutex BufferCounterConfig::CountingStubBuffer::buffers_mutex; std::condition_variable BufferCounterConfig::CountingStubBuffer::buffers_cv; int BufferCounterConfig::CountingStubBuffer::buffers_created; int BufferCounterConfig::CountingStubBuffer::buffers_destroyed; } struct SurfaceLoop : mtf::BasicClientServerFixture { static const int max_surface_count = 5; SurfaceSync ssync[max_surface_count]; MirSurfaceSpec* surface_spec; void SetUp() override { mtf::BasicClientServerFixture::SetUp(); surface_spec = mir_connection_create_spec_for_normal_surface( connection, 640, 480, mir_pixel_format_abgr_8888); } void TearDown() override { mir_surface_spec_release(surface_spec); mtf::BasicClientServerFixture::TearDown(); using Counter = BufferCounterConfig::CountingStubBuffer; std::chrono::seconds const long_enough{4}; auto const all_buffers_destroyed = []{ return Counter::buffers_created == Counter::buffers_destroyed; }; std::unique_lock lock{Counter::buffers_mutex}; EXPECT_TRUE( Counter::buffers_cv.wait_for(lock, long_enough, all_buffers_destroyed)); } }; TEST_F(SurfaceLoop, all_created_buffers_are_destroyed) { for (int i = 0; i != max_surface_count; ++i) mir_surface_create(surface_spec, create_surface_callback, ssync+i); for (int i = 0; i != max_surface_count; ++i) wait_for_surface_create(ssync+i); for (int i = 0; i != max_surface_count; ++i) mir_surface_release(ssync[i].surface, release_surface_callback, ssync+i); for (int i = 0; i != max_surface_count; ++i) wait_for_surface_release(ssync+i); } TEST_F(SurfaceLoop, all_created_buffers_are_destroyed_if_client_disconnects_without_releasing_surfaces) { for (int i = 0; i != max_surface_count; ++i) mir_surface_create(surface_spec, create_surface_callback, ssync+i); for (int i = 0; i != max_surface_count; ++i) wait_for_surface_create(ssync+i); } ./tests/integration-tests/test_test_framework.cpp0000644000015600001650000000671712676616125022550 0ustar jenkinsjenkins/* * Copyright © 2012, 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir_test_framework/testing_server_configuration.h" #include "mir_test_framework/in_process_server.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/any_surface.h" #include "mir/test/signal.h" #include "mir/test/auto_unblock_thread.h" #include "mir_toolkit/mir_client_library.h" #include #include #include #include #include #include namespace mf = mir::frontend; namespace mtf = mir_test_framework; namespace { struct DemoInProcessServer : mir_test_framework::InProcessServer { virtual mir::DefaultServerConfiguration& server_config() { return server_config_; } mir_test_framework::StubbedServerConfiguration server_config_; }; struct DemoInProcessServerWithStubClientPlatform : DemoInProcessServer { mir_test_framework::UsingStubClientPlatform using_stub_client_platform; }; } TEST_F(DemoInProcessServer, client_can_connect) { auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_TRUE(mir_connection_is_valid(connection)); mir_connection_release(connection); } namespace { unsigned count_fds() { unsigned count = 0; DIR* dir; EXPECT_TRUE(dir = opendir("/proc/self/fd/")); while (readdir(dir) != nullptr) count++; closedir(dir); return count; } } // Regression test for https://bugs.launchpad.net/mir/+bug/1395762 TEST_F(DemoInProcessServerWithStubClientPlatform, surface_creation_does_not_leak_fds) { mir::test::Signal connection_released; int fd_count_after_one_surface_lifetime = 0; mir::test::AutoJoinThread t{ [&] { auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); EXPECT_TRUE(mir_connection_is_valid(connection)); for (int i = 0; i < 16; ++i) { auto const surface = mtf::make_any_surface(connection); EXPECT_TRUE(mir_surface_is_valid(surface)); mir_surface_release_sync(surface); if (i == 0) { fd_count_after_one_surface_lifetime = count_fds(); } } mir_connection_release(connection); connection_released.raise(); }}; EXPECT_TRUE(connection_released.wait_for(std::chrono::seconds{480})) << "Client hung" << std::endl; // Investigation revealed we leak a differing number of fds (3, 0) on Mesa/Android over the // entire lifetime of the client library. So we verify here only that we don't leak any FDs beyond // those created up to the lifetime of the first surface. auto new_fd_count = count_fds(); EXPECT_LE(new_fd_count, fd_count_after_one_surface_lifetime); } ./tests/integration-tests/test_protobuf.cpp0000644000015600001650000002470712676616125021353 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "test_protobuf.pb.h" #include "src/client/rpc/mir_basic_rpc_channel.h" #include "mir_toolkit/mir_client_library.h" #include "mir/client/private.h" #include "mir/frontend/protobuf_message_sender.h" #include "mir/frontend/protobuf_connection_creator.h" #include "mir/frontend/template_protobuf_message_processor.h" #include "mir_test_framework/stubbed_server_configuration.h" #include "mir_test_framework/in_process_server.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir/test/doubles/null_platform_ipc_operations.h" #include #include #include namespace mf = mir::frontend; namespace mfd = mir::frontend::detail; namespace mtf = mir_test_framework; /*************************************************************************/ /*************************************************************************/ /* Note that the functionality demonstrated here relies on "detail" and */ /* is not guaranteed to be supported in future. */ /*************************************************************************/ /*************************************************************************/ namespace { struct DemoMirServer { MOCK_CONST_METHOD1(on_call, std::string(std::string)); DemoMirServer() { using namespace testing; ON_CALL(*this, on_call(_)).WillByDefault(Return("ok")); } void function( mir::protobuf::Parameters const* parameters, mir::protobuf::Result* response, google::protobuf::Closure* done) { response->set_value(on_call(parameters->name())); done->Run(); } }; struct AMirServer { AMirServer(std::shared_ptr const& channel) : channel{channel} { } void function( mir::protobuf::Parameters const* parameters, mir::protobuf::Result* response, google::protobuf::Closure* done) { channel->call_method(std::string(__func__), parameters, response, done); } std::shared_ptr channel; }; // using a global for easy access from tests and DemoMessageProcessor::dispatch() DemoMirServer* demo_mir_server; struct DemoMessageProcessor : mfd::MessageProcessor { DemoMessageProcessor( std::shared_ptr const& sender, std::shared_ptr const& wrapped) : sender(sender), wrapped(wrapped) {} void client_pid(int /*pid*/) override {} bool dispatch(mfd::Invocation const& invocation, std::vector const& fds) { if ("function" == invocation.method_name()) { mfd::invoke( this, demo_mir_server, &DemoMirServer::function, invocation); return true; } return wrapped->dispatch(invocation, fds); } void send_response(::google::protobuf::uint32 id, ::google::protobuf::MessageLite* response) { sender->send_response(id, response, {}); } std::shared_ptr const sender; std::shared_ptr const wrapped; }; struct DemoConnectionCreator : mf::ProtobufConnectionCreator { using ProtobufConnectionCreator::ProtobufConnectionCreator; MOCK_CONST_METHOD3(create_processor, std::shared_ptr( std::shared_ptr const& sender, std::shared_ptr const& display_server, std::shared_ptr const& report)); std::shared_ptr create_wrapped_processor( std::shared_ptr const& sender, std::shared_ptr const& display_server, std::shared_ptr const& report) const { auto const wrapped = mf::ProtobufConnectionCreator::create_processor( sender, display_server, report); return std::make_shared(sender, wrapped); } std::shared_ptr create_unwrapped_processor( std::shared_ptr const& sender, std::shared_ptr const& display_server, std::shared_ptr const& report) const { return mf::ProtobufConnectionCreator::create_processor( sender, display_server, report); } }; struct DemoServerConfiguration : mtf::StubbedServerConfiguration { std::shared_ptr the_connection_creator() override { return connection_creator([this] { return std::make_shared( new_ipc_factory(the_session_authorizer()), the_session_authorizer(), std::make_shared(), the_message_processor_report()); }); } }; struct DemoPrivateProtobuf : mtf::InProcessServer { mir::DefaultServerConfiguration& server_config() override { return my_server_config; } DemoServerConfiguration my_server_config; mtf::UsingStubClientPlatform using_stub_client_platform; std::shared_ptr demo_connection_creator; void SetUp() { ::demo_mir_server = &demo_mir_server; mtf::InProcessServer::SetUp(); demo_connection_creator = std::dynamic_pointer_cast(my_server_config.the_connection_creator()); using namespace testing; ASSERT_THAT(demo_connection_creator, NotNull()); ON_CALL(*demo_connection_creator, create_processor(_, _, _)) .WillByDefault(Invoke(demo_connection_creator.get(), &DemoConnectionCreator::create_unwrapped_processor)); } testing::NiceMock demo_mir_server; }; void callback(std::atomic* called_back) { called_back->store(true); } char const* const nothing_returned = "Nothing returned"; } TEST_F(DemoPrivateProtobuf, client_calls_server) { using namespace testing; EXPECT_CALL(*demo_connection_creator, create_processor(_, _, _)); auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_TRUE(mir_connection_is_valid(connection)); auto const rpc_channel = mir::client::the_rpc_channel(connection); using namespace mir::protobuf; using namespace google::protobuf; AMirServer server(rpc_channel); Parameters parameters; parameters.set_name(__PRETTY_FUNCTION__); Result result; result.set_error(nothing_returned); std::atomic called_back{false}; // After the call there's a race between the client releasing the connection // and the server dropping the connection. // If the latter wins we'll invoke the client's lifecycle_event_callback. // As the default callback kills the process with SIGHUP, we need to // replace it to ensure the test can continue. mir_connection_set_lifecycle_event_callback( connection, [](MirConnection*, MirLifecycleState, void*){}, nullptr); // Note: // As the default server won't recognise this call it drops the connection // resulting in a callback when the connection drops (but result being unchanged) server.function( ¶meters, &result, NewCallback(&callback, &called_back)); mir_connection_release(connection); EXPECT_TRUE(called_back); EXPECT_THAT(result.error(), Eq(nothing_returned)); } TEST_F(DemoPrivateProtobuf, wrapping_message_processor) { using namespace testing; EXPECT_CALL(*demo_connection_creator, create_processor(_, _, _)) .Times(1) .WillOnce(Invoke(demo_connection_creator.get(), &DemoConnectionCreator::create_wrapped_processor)); auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); mir_connection_release(connection); } TEST_F(DemoPrivateProtobuf, server_receives_function_call) { using namespace testing; EXPECT_CALL(*demo_connection_creator, create_processor(_, _, _)) .WillRepeatedly(Invoke(demo_connection_creator.get(), &DemoConnectionCreator::create_wrapped_processor)); auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_TRUE(mir_connection_is_valid(connection)); auto const rpc_channel = mir::client::the_rpc_channel(connection); using namespace mir::protobuf; using namespace google::protobuf; AMirServer server(rpc_channel); Parameters parameters; Result result; parameters.set_name(__PRETTY_FUNCTION__); EXPECT_CALL(demo_mir_server, on_call(__PRETTY_FUNCTION__)).Times(1); server.function(¶meters, &result, NewCallback([]{})); mir_connection_release(connection); } TEST_F(DemoPrivateProtobuf, client_receives_result) { using namespace testing; EXPECT_CALL(*demo_connection_creator, create_processor(_, _, _)) .WillRepeatedly(Invoke(demo_connection_creator.get(), &DemoConnectionCreator::create_wrapped_processor)); EXPECT_CALL(demo_mir_server, on_call(_)).WillRepeatedly(Return(__PRETTY_FUNCTION__)); auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_TRUE(mir_connection_is_valid(connection)); auto const rpc_channel = mir::client::the_rpc_channel(connection); using namespace mir::protobuf; using namespace google::protobuf; AMirServer server(rpc_channel); Parameters parameters; Result result; parameters.set_name(__PRETTY_FUNCTION__); server.function(¶meters, &result, NewCallback([]{})); mir_connection_release(connection); EXPECT_THAT(result.has_error(), Eq(false)); EXPECT_THAT(result.value(), Eq(__PRETTY_FUNCTION__)); } ./tests/integration-tests/test_protobuf.proto0000644000015600001650000000030012676616125021713 0ustar jenkinsjenkinsoption optimize_for = LITE_RUNTIME; package mir.protobuf; message Parameters { required string name = 1; } message Result { optional string error = 127; optional string value = 1; } ./tests/integration-tests/test_stale_frames.cpp0000644000015600001650000001422012676616125022145 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/debug/surface.h" #include "mir/compositor/compositor.h" #include "mir/compositor/renderer_factory.h" #include "mir/graphics/renderable.h" #include "mir/graphics/buffer.h" #include "mir/graphics/buffer_id.h" #include "mir_test_framework/stubbed_server_configuration.h" #include "mir_test_framework/basic_client_server_fixture.h" #include "mir_test_framework/any_surface.h" #include "mir/test/doubles/stub_renderer.h" #include #include #include #include namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { struct StubRenderer : mtd::StubRenderer { void render(mg::RenderableList const& renderables) const override { std::lock_guard lock{mutex}; for (auto const& r : renderables) rendered_buffers_.push_back(r->buffer()->id()); if (renderables.size() > 0) new_rendered_buffer_cv.notify_all(); } std::vector rendered_buffers() { std::lock_guard lock{mutex}; return rendered_buffers_; } std::vector wait_for_new_rendered_buffers() { std::unique_lock lock{mutex}; new_rendered_buffer_cv.wait_for( lock, std::chrono::seconds{2}, [this] { return rendered_buffers_.size() != 0; }); auto const rendered = std::move(rendered_buffers_); return rendered; } mutable std::mutex mutex; mutable std::condition_variable new_rendered_buffer_cv; mutable std::vector rendered_buffers_; }; class StubRendererFactory : public mc::RendererFactory { public: std::unique_ptr create_renderer_for( mg::DisplayBuffer&) override { std::lock_guard lock{mutex}; renderer_ = new StubRenderer(); renderer_created_cv.notify_all(); return std::unique_ptr{renderer_}; } StubRenderer* renderer() { std::unique_lock lock{mutex}; renderer_created_cv.wait_for( lock, std::chrono::seconds{2}, [this] { return renderer_ != nullptr; }); return renderer_; } void clear_renderer() { std::lock_guard lock{mutex}; renderer_ = nullptr; } std::mutex mutex; std::condition_variable renderer_created_cv; StubRenderer* renderer_ = nullptr; }; struct StubServerConfig : mtf::StubbedServerConfiguration { std::shared_ptr the_stub_renderer_factory() { return stub_renderer_factory( [] { return std::make_shared(); }); } std::shared_ptr the_renderer_factory() override { return the_stub_renderer_factory(); } mir::CachedPtr stub_renderer_factory; }; using BasicFixture = mtf::BasicClientServerFixture; struct StaleFrames : BasicFixture { void SetUp() { BasicFixture::SetUp(); client_create_surface(); } void TearDown() { mir_surface_release_sync(surface); BasicFixture::TearDown(); } void client_create_surface() { surface = mtf::make_any_surface(connection); ASSERT_TRUE(mir_surface_is_valid(surface)); } std::vector wait_for_new_rendered_buffers() { return server_configuration.the_stub_renderer_factory()->renderer()->wait_for_new_rendered_buffers(); } void stop_compositor() { server_configuration.the_compositor()->stop(); server_configuration.the_stub_renderer_factory()->clear_renderer(); } void start_compositor() { server_configuration.the_compositor()->start(); } MirSurface* surface; }; } TEST_F(StaleFrames, are_dropped_when_restarting_compositor) { using namespace testing; stop_compositor(); std::set stale_buffers; stale_buffers.emplace(mir_debug_surface_current_buffer_id(surface)); auto bs = mir_surface_get_buffer_stream(surface); mir_buffer_stream_swap_buffers_sync(bs); stale_buffers.emplace(mir_debug_surface_current_buffer_id(surface)); mir_buffer_stream_swap_buffers_sync(bs); EXPECT_THAT(stale_buffers.size(), Eq(2)); auto const fresh_buffer = mg::BufferID{mir_debug_surface_current_buffer_id(surface)}; mir_buffer_stream_swap_buffers_sync(bs); start_compositor(); // Note first stale buffer and fresh_buffer may be equal when defaulting to double buffers stale_buffers.erase(fresh_buffer); auto const new_buffers = wait_for_new_rendered_buffers(); ASSERT_THAT(new_buffers.size(), Eq(1)); EXPECT_THAT(stale_buffers, Not(Contains(new_buffers[0]))); } TEST_F(StaleFrames, only_fresh_frames_are_used_after_restarting_compositor) { using namespace testing; stop_compositor(); auto bs = mir_surface_get_buffer_stream(surface); mir_buffer_stream_swap_buffers_sync(bs); mir_buffer_stream_swap_buffers_sync(bs); auto const fresh_buffer = mg::BufferID{mir_debug_surface_current_buffer_id(surface)}; mir_buffer_stream_swap_buffers_sync(bs); start_compositor(); auto const new_buffers = wait_for_new_rendered_buffers(); ASSERT_THAT(new_buffers.size(), Eq(1)); EXPECT_THAT(new_buffers[0], Eq(fresh_buffer)); } ./tests/integration-tests/precompiled.hpp0000644000015600001650000000164012676616125020753 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_INTEGRATION_TESTS_PRECOMPILED_H_ #define MIR_INTEGRATION_TESTS_PRECOMPILED_H_ #include #include #include #include #include #include #endif ./tests/integration-tests/input/0000755000015600001650000000000012676616160017074 5ustar jenkinsjenkins./tests/integration-tests/input/test_single_seat_setup.cpp0000644000015600001650000004761412676616157024376 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "src/server/input/default_input_device_hub.h" #include "src/server/input/basic_seat.h" #include "mir/test/doubles/mock_input_device.h" #include "mir/test/doubles/mock_input_device_observer.h" #include "mir/test/doubles/mock_input_dispatcher.h" #include "mir/test/doubles/mock_input_region.h" #include "mir/test/doubles/mock_touch_visualizer.h" #include "mir/test/doubles/mock_cursor_listener.h" #include "mir/test/doubles/mock_event_sink.h" #include "mir/test/doubles/triggered_main_loop.h" #include "mir/test/event_matchers.h" #include "mir/test/fake_shared.h" #include "mir/dispatch/multiplexing_dispatchable.h" #include "mir/cookie/authority.h" #include "mir/graphics/buffer.h" #include "mir/input/device.h" #include "mir/input/device_capability.h" #include "mir/input/pointer_configuration.h" #include "mir/input/touchpad_configuration.h" #include "mir/input/touch_visualizer.h" #include "mir/input/input_device_info.h" #include "mir/geometry/rectangles.h" #include #include namespace mt = mir::test; namespace mtd = mt::doubles; namespace mi = mir::input; namespace geom = mir::geometry; using namespace std::literals::chrono_literals; using namespace ::testing; namespace mir { namespace input { void PrintTo(mir::input::InputDeviceInfo const &info, ::std::ostream* out) { *out << info.name << " " << info.unique_id << " " << info.capabilities.value(); } } } MATCHER_P(DeviceMatches, device_info, "") { return arg->name() == device_info.name && arg->unique_id() == device_info.unique_id; } struct SingleSeatInputDeviceHubSetup : ::testing::Test { mtd::TriggeredMainLoop observer_loop; NiceMock mock_dispatcher; NiceMock mock_region; NiceMock mock_sink; std::shared_ptr cookie_authority = mir::cookie::Authority::create(); NiceMock mock_cursor_listener; NiceMock mock_visualizer; mir::dispatch::MultiplexingDispatchable multiplexer; mi::BasicSeat seat{mt::fake_shared(mock_dispatcher), mt::fake_shared(mock_visualizer), mt::fake_shared(mock_cursor_listener), mt::fake_shared(mock_region)}; mi::DefaultInputDeviceHub hub{mt::fake_shared(mock_sink), mt::fake_shared(seat), mt::fake_shared(multiplexer), mt::fake_shared(observer_loop), cookie_authority}; NiceMock mock_observer; NiceMock device{"device","dev-1", mi::DeviceCapability::unknown}; NiceMock another_device{"another_device","dev-2", mi::DeviceCapability::keyboard}; NiceMock third_device{"third_device","dev-3", mi::DeviceCapability::keyboard}; NiceMock touchpad{"touchpad", "dev-4", mi::DeviceCapability::touchpad|mi::DeviceCapability::pointer}; std::chrono::nanoseconds arbitrary_timestamp; void capture_input_sink(NiceMock& dev, mi::InputSink*& sink, mi::EventBuilder*& builder) { ON_CALL(dev,start(_,_)) .WillByDefault(Invoke([&sink,&builder](mi::InputSink* input_sink, mi::EventBuilder* event_builder) { sink = input_sink; builder = event_builder; } )); } }; TEST_F(SingleSeatInputDeviceHubSetup, input_sink_posts_events_to_input_dispatcher) { mi::InputSink* sink; mi::EventBuilder* builder; std::shared_ptr handle; capture_input_sink(device, sink, builder); EXPECT_CALL(mock_observer,device_added(_)) .WillOnce(SaveArg<0>(&handle)); hub.add_observer(mt::fake_shared(mock_observer)); hub.add_device(mt::fake_shared(device)); observer_loop.trigger_server_actions(); auto event = builder->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_A); EXPECT_CALL(mock_dispatcher, dispatch(AllOf(mt::InputDeviceIdMatches(handle->id()), mt::MirKeyboardEventMatches(event.get())))); sink->handle_input(*event); } TEST_F(SingleSeatInputDeviceHubSetup, forwards_touch_spots_to_visualizer) { mi::InputSink* sink; mi::EventBuilder* builder; capture_input_sink(device, sink, builder); hub.add_device(mt::fake_shared(device)); observer_loop.trigger_server_actions(); auto touch_event_1 = builder->touch_event(arbitrary_timestamp); builder->add_touch(*touch_event_1, 0, mir_touch_action_down, mir_touch_tooltype_finger, 21.0f, 34.0f, 50.0f, 15.0f, 5.0f, 4.0f); auto touch_event_2 = builder->touch_event(arbitrary_timestamp); builder->add_touch(*touch_event_2, 0, mir_touch_action_change, mir_touch_tooltype_finger, 24.0f, 34.0f, 50.0f, 15.0f, 5.0f, 4.0f); builder->add_touch(*touch_event_2, 1, mir_touch_action_down, mir_touch_tooltype_finger, 60.0f, 34.0f, 50.0f, 15.0f, 5.0f, 4.0f); auto touch_event_3 = builder->touch_event(arbitrary_timestamp); builder->add_touch(*touch_event_3, 0, mir_touch_action_up, mir_touch_tooltype_finger, 24.0f, 34.0f, 50.0f, 15.0f, 5.0f, 4.0f); builder->add_touch(*touch_event_3, 1, mir_touch_action_change, mir_touch_tooltype_finger, 70.0f, 30.0f, 50.0f, 15.0f, 5.0f, 4.0f); auto touch_event_4 = builder->touch_event(arbitrary_timestamp); builder->add_touch(*touch_event_4, 1, mir_touch_action_up, mir_touch_tooltype_finger, 70.0f, 35.0f, 50.0f, 15.0f, 5.0f, 4.0f); using Spot = mi::TouchVisualizer::Spot; InSequence seq; EXPECT_CALL(mock_visualizer, visualize_touches(ElementsAreArray({Spot{{21,34}, 50}}))); EXPECT_CALL(mock_visualizer, visualize_touches(ElementsAreArray({Spot{{24,34}, 50}, Spot{{60,34}, 50}}))); EXPECT_CALL(mock_visualizer, visualize_touches(ElementsAreArray({Spot{{70,30}, 50}}))); EXPECT_CALL(mock_visualizer, visualize_touches(ElementsAreArray(std::vector()))); sink->handle_input(*touch_event_1); sink->handle_input(*touch_event_2); sink->handle_input(*touch_event_3); sink->handle_input(*touch_event_4); } TEST_F(SingleSeatInputDeviceHubSetup, tracks_pointer_position) { geom::Point first{10,10}, second{20,20}, third{10,30}; EXPECT_CALL(mock_region, confine(first)); EXPECT_CALL(mock_region, confine(second)); EXPECT_CALL(mock_region, confine(third)); mi::InputSink* sink; mi::EventBuilder* builder; capture_input_sink(device, sink, builder); hub.add_device(mt::fake_shared(device)); observer_loop.trigger_server_actions(); sink->handle_input( *builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f, 10.0f, 10.0f)); sink->handle_input( *builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f, 10.0f, 10.0f)); sink->handle_input( *builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f, -10.0f, 10.0f)); } TEST_F(SingleSeatInputDeviceHubSetup, confines_pointer_movement) { geom::Point confined_pos{10, 18}; ON_CALL(mock_region,confine(_)) .WillByDefault(SetArgReferee<0>(confined_pos)); EXPECT_CALL(mock_cursor_listener, cursor_moved_to(confined_pos.x.as_int(), confined_pos.y.as_int())).Times(2); mi::InputSink* sink; mi::EventBuilder* builder; capture_input_sink(device, sink, builder); hub.add_device(mt::fake_shared(device)); observer_loop.trigger_server_actions(); sink->handle_input( *builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f, 10.0f, 20.0f)); sink->handle_input( *builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f, 10.0f, 10.0f)); } TEST_F(SingleSeatInputDeviceHubSetup, forwards_pointer_updates_to_cursor_listener) { auto move_x = 12.0f, move_y = 14.0f; mi::InputSink* sink; mi::EventBuilder* builder; capture_input_sink(device, sink, builder); hub.add_device(mt::fake_shared(device)); auto event = builder->pointer_event(0ns, mir_pointer_action_motion, 0, 0.0f, 0.0f, move_x, move_y); EXPECT_CALL(mock_cursor_listener, cursor_moved_to(move_x, move_y)).Times(1); sink->handle_input(*event); } TEST_F(SingleSeatInputDeviceHubSetup, forwards_pointer_settings_to_input_device) { std::shared_ptr dev; ON_CALL(mock_observer, device_added(_)).WillByDefault(SaveArg<0>(&dev)); hub.add_observer(mt::fake_shared(mock_observer)); hub.add_device(mt::fake_shared(touchpad)); observer_loop.trigger_server_actions(); EXPECT_CALL(touchpad, apply_settings(Matcher(_))); auto conf = dev->pointer_configuration(); dev->apply_pointer_configuration(conf.value()); multiplexer.dispatch(mir::dispatch::FdEvent::readable); } TEST_F(SingleSeatInputDeviceHubSetup, forwards_touchpad_settings_to_input_device) { std::shared_ptr dev; ON_CALL(mock_observer, device_added(_)).WillByDefault(SaveArg<0>(&dev)); hub.add_observer(mt::fake_shared(mock_observer)); hub.add_device(mt::fake_shared(touchpad)); observer_loop.trigger_server_actions(); EXPECT_CALL(touchpad, apply_settings(Matcher(_))); auto conf = dev->touchpad_configuration(); dev->apply_touchpad_configuration(conf.value()); multiplexer.dispatch(mir::dispatch::FdEvent::readable); } TEST_F(SingleSeatInputDeviceHubSetup, input_sink_tracks_modifier) { mi::InputSink* key_board_sink; mi::EventBuilder* key_event_builder; std::shared_ptr key_handle; capture_input_sink(device, key_board_sink, key_event_builder); InSequence seq; EXPECT_CALL(mock_observer,device_added(_)) .WillOnce(SaveArg<0>(&key_handle)); hub.add_observer(mt::fake_shared(mock_observer)); hub.add_device(mt::fake_shared(device)); observer_loop.trigger_server_actions(); const MirInputEventModifiers shift_left = mir_input_event_modifier_shift_left | mir_input_event_modifier_shift; auto shift_down = key_event_builder->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_LEFTSHIFT); auto shift_up = key_event_builder->key_event(arbitrary_timestamp, mir_keyboard_action_up, 0, KEY_LEFTSHIFT); EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(shift_left))); EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(mir_input_event_modifier_none))); key_board_sink->handle_input(*shift_down); key_board_sink->handle_input(*shift_up); } TEST_F(SingleSeatInputDeviceHubSetup, input_sink_unifies_modifier_state_accross_devices) { mi::InputSink* mouse_sink; mi::EventBuilder* mouse_event_builder; mi::InputSink* key_board_sink; mi::EventBuilder* key_event_builder; std::shared_ptr mouse_handle; std::shared_ptr key_handle; capture_input_sink(device, mouse_sink, mouse_event_builder); capture_input_sink(another_device, key_board_sink, key_event_builder); InSequence seq; EXPECT_CALL(mock_observer,device_added(_)) .WillOnce(SaveArg<0>(&mouse_handle)); EXPECT_CALL(mock_observer,device_added(_)) .WillOnce(SaveArg<0>(&key_handle)); hub.add_observer(mt::fake_shared(mock_observer)); hub.add_device(mt::fake_shared(device)); hub.add_device(mt::fake_shared(another_device)); observer_loop.trigger_server_actions(); const MirInputEventModifiers r_alt_modifier = mir_input_event_modifier_alt_right | mir_input_event_modifier_alt; auto key = key_event_builder->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_RIGHTALT); auto motion = mouse_event_builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 12, 40); EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(r_alt_modifier))); EXPECT_CALL(mock_dispatcher, dispatch(mt::PointerEventWithModifiers(r_alt_modifier))); key_board_sink->handle_input(*key); mouse_sink->handle_input(*motion); EXPECT_THAT(key_handle->id(), Ne(mouse_handle->id())); } TEST_F(SingleSeatInputDeviceHubSetup, input_sink_reduces_modifier_state_accross_devices) { mi::InputSink* mouse_sink; mi::EventBuilder* mouse_event_builder; mi::InputSink* key_board_sink_1; mi::EventBuilder* key_event_builder_1; mi::InputSink* key_board_sink_2; mi::EventBuilder* key_event_builder_2; std::shared_ptr mouse_handle; std::shared_ptr key_handle_1; std::shared_ptr key_handle_2; capture_input_sink(device, mouse_sink, mouse_event_builder); capture_input_sink(another_device, key_board_sink_1, key_event_builder_1); capture_input_sink(third_device, key_board_sink_2, key_event_builder_2); InSequence seq; EXPECT_CALL(mock_observer, device_added(_)) .WillOnce(SaveArg<0>(&mouse_handle)); EXPECT_CALL(mock_observer, device_added(_)) .WillOnce(SaveArg<0>(&key_handle_1)); EXPECT_CALL(mock_observer, device_added(_)) .WillOnce(SaveArg<0>(&key_handle_2)); hub.add_observer(mt::fake_shared(mock_observer)); hub.add_device(mt::fake_shared(device)); hub.add_device(mt::fake_shared(another_device)); hub.add_device(mt::fake_shared(third_device)); const MirInputEventModifiers r_alt_modifier = mir_input_event_modifier_alt_right | mir_input_event_modifier_alt; const MirInputEventModifiers l_ctrl_modifier = mir_input_event_modifier_ctrl_left | mir_input_event_modifier_ctrl; const MirInputEventModifiers combined_modifier = r_alt_modifier | l_ctrl_modifier; observer_loop.trigger_server_actions(); auto alt_down = key_event_builder_1->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_RIGHTALT); auto ctrl_down = key_event_builder_2->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_LEFTCTRL); auto ctrl_up = key_event_builder_2->key_event(arbitrary_timestamp, mir_keyboard_action_up, 0, KEY_LEFTCTRL); auto motion_1 = mouse_event_builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 12, 40); auto motion_2 = mouse_event_builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 18, 10); EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(r_alt_modifier))); EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(l_ctrl_modifier))); EXPECT_CALL(mock_dispatcher, dispatch(mt::PointerEventWithModifiers(combined_modifier))); EXPECT_CALL(mock_dispatcher, dispatch(mt::KeyWithModifiers(mir_input_event_modifier_none))); EXPECT_CALL(mock_dispatcher, dispatch(mt::PointerEventWithModifiers(r_alt_modifier))); key_board_sink_1->handle_input(*alt_down); key_board_sink_2->handle_input(*ctrl_down); mouse_sink->handle_input(*motion_1); key_board_sink_2->handle_input(*ctrl_up); mouse_sink->handle_input(*motion_2); EXPECT_THAT(key_handle_1->id(), Ne(key_handle_2->id())); } TEST_F(SingleSeatInputDeviceHubSetup, tracks_a_single_cursor_position_from_multiple_pointing_devices) { mi::InputSink* mouse_sink_1; mi::EventBuilder* mouse_event_builder_1; mi::InputSink* mouse_sink_2; mi::EventBuilder* mouse_event_builder_2; capture_input_sink(device, mouse_sink_1, mouse_event_builder_1); capture_input_sink(another_device, mouse_sink_2, mouse_event_builder_2); hub.add_device(mt::fake_shared(device)); hub.add_device(mt::fake_shared(another_device)); observer_loop.trigger_server_actions(); auto motion_1 = mouse_event_builder_1->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 23, 20); auto motion_2 = mouse_event_builder_2->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 18, -10); auto motion_3 = mouse_event_builder_2->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 2, 10); EXPECT_CALL(mock_cursor_listener, cursor_moved_to(23, 20)); EXPECT_CALL(mock_cursor_listener, cursor_moved_to(41, 10)); EXPECT_CALL(mock_cursor_listener, cursor_moved_to(43, 20)); mouse_sink_1->handle_input(*motion_1); mouse_sink_2->handle_input(*motion_2); mouse_sink_1->handle_input(*motion_3); } TEST_F(SingleSeatInputDeviceHubSetup, tracks_a_single_button_state_from_multiple_pointing_devices) { int const x = 0, y = 0; MirPointerButtons no_buttons = 0; mi::InputSink* mouse_sink_1; mi::EventBuilder* mouse_event_builder_1; mi::InputSink* mouse_sink_2; mi::EventBuilder* mouse_event_builder_2; capture_input_sink(device, mouse_sink_1, mouse_event_builder_1); capture_input_sink(another_device, mouse_sink_2, mouse_event_builder_2); hub.add_device(mt::fake_shared(device)); hub.add_device(mt::fake_shared(another_device)); observer_loop.trigger_server_actions(); auto motion_1 = mouse_event_builder_1->pointer_event(arbitrary_timestamp, mir_pointer_action_button_down, mir_pointer_button_primary, 0, 0, 0, 0); auto motion_2 = mouse_event_builder_2->pointer_event(arbitrary_timestamp, mir_pointer_action_button_down, mir_pointer_button_secondary, 0, 0, 0, 0); auto motion_3 = mouse_event_builder_1->pointer_event(arbitrary_timestamp, mir_pointer_action_button_up, no_buttons, 0, 0, 0, 0); auto motion_4 = mouse_event_builder_2->pointer_event(arbitrary_timestamp, mir_pointer_action_button_up, no_buttons, 0, 0, 0, 0); InSequence seq; EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsDown(x, y, mir_pointer_button_primary))); EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsDown(x, y, mir_pointer_button_primary | mir_pointer_button_secondary))); EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsUp(x, y, mir_pointer_button_secondary))); EXPECT_CALL(mock_dispatcher, dispatch(mt::ButtonsUp(x, y, no_buttons))); mouse_sink_1->handle_input(*motion_1); mouse_sink_2->handle_input(*motion_2); mouse_sink_1->handle_input(*motion_3); mouse_sink_1->handle_input(*motion_4); } TEST_F(SingleSeatInputDeviceHubSetup, input_device_changes_sent_to_sink) { EXPECT_CALL(mock_sink, handle_input_device_change(UnorderedElementsAre(DeviceMatches(device.get_device_info())))); hub.add_device(mt::fake_shared(device)); observer_loop.trigger_server_actions(); } TEST_F(SingleSeatInputDeviceHubSetup, input_device_changes_sent_to_sink_multiple_devices) { hub.add_device(mt::fake_shared(device)); observer_loop.trigger_server_actions(); EXPECT_CALL(mock_sink, handle_input_device_change(UnorderedElementsAre(DeviceMatches(device.get_device_info()), DeviceMatches(another_device.get_device_info())))); hub.add_device(mt::fake_shared(another_device)); observer_loop.trigger_server_actions(); } TEST_F(SingleSeatInputDeviceHubSetup, input_device_changes_sent_to_sink_removal) { hub.add_device(mt::fake_shared(device)); hub.add_device(mt::fake_shared(another_device)); observer_loop.trigger_server_actions(); EXPECT_CALL(mock_sink, handle_input_device_change(UnorderedElementsAre(DeviceMatches(another_device.get_device_info())))); hub.remove_device(mt::fake_shared(device)); observer_loop.trigger_server_actions(); } ./tests/integration-tests/input/test_configuring_input_manager.cpp0000644000015600001650000000360612676616125026070 0ustar jenkinsjenkins/* * Copyright © 2012-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr * Daniel d'Andrada */ #include "mir_test_framework/deferred_in_process_server.h" #include "mir_test_framework/testing_server_configuration.h" #include "mir/test/doubles/mock_input_manager.h" #include #include namespace mi = mir::input; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { struct ServerConfig : mtf::TestingServerConfiguration { std::shared_ptr the_input_manager() override { return mock_input_manager; } std::shared_ptr const mock_input_manager = std::make_shared(); }; struct InputManager : mtf::DeferredInProcessServer { mtd::MockInputManager& the_mock_input_manager() { return *server_configuration.mock_input_manager; } mir::DefaultServerConfiguration& server_config() override { return server_configuration; } ServerConfig server_configuration; }; } TEST_F(InputManager, is_started_when_display_server_starts) { EXPECT_CALL(the_mock_input_manager(), start()).Times(1); EXPECT_CALL(the_mock_input_manager(), stop()).Times(1); start_server(); } ./tests/integration-tests/input/test_cursor_listener.cpp0000644000015600001650000000656112676616125024072 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr * Daniel d'Andrada */ #include "mir/events/event_private.h" #include "mir/test/fake_shared.h" #include "mir_test_framework/fake_input_device.h" #include "mir_test_framework/fake_input_server_configuration.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir_test_framework/temporary_environment_value.h" #include "mir/test/doubles/stub_touch_visualizer.h" #include "mir/test/wait_condition.h" #include "mir/test/event_factory.h" #include "mir/input/cursor_listener.h" #include "mir/input/input_dispatcher.h" #include "mir/input/input_manager.h" #include "mir/input/input_device_info.h" #include "mir_test_framework/executable_path.h" #include #include #include #include namespace mi = mir::input; namespace mis = mir::input::synthesis; namespace mt = mir::test; namespace mtf = mir_test_framework; namespace { using namespace ::testing; struct MockCursorListener : public mi::CursorListener { MOCK_METHOD2(cursor_moved_to, void(float, float)); ~MockCursorListener() noexcept {} }; struct CursorListenerIntegrationTest : testing::Test, mtf::FakeInputServerConfiguration { mtf::TemporaryEnvironmentValue input_lib{"MIR_SERVER_PLATFORM_INPUT_LIB", mtf::server_platform("input-stub.so").c_str()}; mtf::TemporaryEnvironmentValue real_input{"MIR_SERVER_TESTS_USE_REAL_INPUT", "1"}; mtf::TemporaryEnvironmentValue no_key_repeat{"MIR_SERVER_ENABLE_KEY_REPEAT", "0"}; std::shared_ptr the_cursor_listener() override { return mt::fake_shared(cursor_listener); } void SetUp() override { input_manager = the_input_manager(); input_manager->start(); input_dispatcher = the_input_dispatcher(); input_dispatcher->start(); } void TearDown() override { input_dispatcher->stop(); input_manager->stop(); } MockCursorListener cursor_listener; std::shared_ptr input_manager; std::shared_ptr input_dispatcher; std::unique_ptr fake_mouse{ mtf::add_fake_input_device(mi::InputDeviceInfo{"mouse", "mouse-uid" , mi::DeviceCapability::pointer}) }; }; } TEST_F(CursorListenerIntegrationTest, cursor_listener_receives_motion) { using namespace ::testing; auto wait_condition = std::make_shared(); static const float x = 100.f; static const float y = 100.f; EXPECT_CALL(cursor_listener, cursor_moved_to(x, y)).Times(1).WillOnce(mt::WakeUp(wait_condition)); fake_mouse->emit_event(mis::a_pointer_event().with_movement(x, y)); wait_condition->wait_for_at_most_seconds(10); } ./tests/integration-tests/input/CMakeLists.txt0000644000015600001650000000044412676616125021637 0ustar jenkinsjenkinslist( APPEND INTEGRATION_TESTS_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_configuring_input_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_cursor_listener.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_single_seat_setup.cpp ) set( INTEGRATION_TESTS_SRCS ${INTEGRATION_TESTS_SRCS} PARENT_SCOPE) ./tests/integration-tests/test_server_shutdown.cpp0000644000015600001650000002466012676616125022752 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/compositor/display_buffer_compositor_factory.h" #include "mir/compositor/display_buffer_compositor.h" #include "mir/run_mir.h" #include "mir/main_loop.h" #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/fake_input_server_configuration.h" #include "mir_test_framework/fake_input_device.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir_test_framework/any_surface.h" #include "mir_test_framework/server_runner.h" #include "mir_test_framework/testing_server_configuration.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir/test/doubles/null_display_buffer_compositor_factory.h" #include "mir/test/doubles/stub_frame_dropping_policy_factory.h" #include "mir/test/doubles/stub_renderer.h" #include "mir/dispatch/action_queue.h" #include "mir/input/input_device.h" #include "mir/input/input_device_info.h" #include "mir/input/input_device_registry.h" #include "mir_toolkit/mir_client_library.h" #include namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace mi = mir::input; namespace { struct ServerShutdown : testing::Test, mtf::ServerRunner { std::unique_ptr server_configuration; mtf::UsingStubClientPlatform using_stub_client_platform; mir::DefaultServerConfiguration& server_config() override { return *server_configuration; } }; void null_buffer_stream_callback(MirBufferStream*, void*) { } void null_lifecycle_callback(MirConnection*, MirLifecycleState, void*) { } struct ClientWithSurface { ClientWithSurface(std::string const& server_socket) : connection{mir_connect_sync( server_socket.c_str(), __PRETTY_FUNCTION__)} { // Default lifecycle handler terminates the process on disconnect, so // override it mir_connection_set_lifecycle_event_callback( connection, null_lifecycle_callback, nullptr); surface = mtf::make_any_surface(connection); } ~ClientWithSurface() { mir_surface_release_sync(surface); mir_connection_release(connection); } void swap_sync() { mir_buffer_stream_swap_buffers_sync( mir_surface_get_buffer_stream(surface)); } void swap_async() { mir_buffer_stream_swap_buffers( mir_surface_get_buffer_stream(surface), null_buffer_stream_callback, nullptr); } MirConnection* const connection; MirSurface* surface; }; } TEST_F(ServerShutdown, server_can_shut_down_when_clients_are_blocked) { struct ServerConfig : mtf::TestingServerConfiguration { // Don't consume frames, so the clients can block waiting for a buffer std::shared_ptr the_display_buffer_compositor_factory() override { return display_buffer_compositor_factory( [] { return std::make_shared(); }); } // Don't drop frames, so the clients can block waiting for a buffer std::shared_ptr the_frame_dropping_policy_factory() override { return frame_dropping_policy_factory( [] { return std::make_shared(); }); } }; server_configuration.reset(new ServerConfig{}); start_server(); ClientWithSurface client1{new_connection()}; ClientWithSurface client2{new_connection()}; ClientWithSurface client3{new_connection()}; client1.swap_sync(); // Ask for the second buffer (should block) client1.swap_async(); client2.swap_sync(); // Ask for the second buffer (should block) client2.swap_async(); client3.swap_sync(); // Ask for the second buffer (should block) client3.swap_async(); // Shutting down the server should not block stop_server(); } TEST_F(ServerShutdown, server_releases_resources_on_shutdown_with_connected_clients) { server_configuration.reset(new mtf::TestingServerConfiguration{}); start_server(); ClientWithSurface client1{new_connection()}; ClientWithSurface client2{new_connection()}; ClientWithSurface client3{new_connection()}; stop_server(); std::weak_ptr display = server_configuration->the_display(); std::weak_ptr compositor = server_configuration->the_compositor(); std::weak_ptr connector = server_configuration->the_connector(); std::weak_ptr input_manager = server_configuration->the_input_manager(); server_configuration.reset(); EXPECT_EQ(0, display.use_count()); EXPECT_EQ(0, compositor.use_count()); EXPECT_EQ(0, connector.use_count()); EXPECT_EQ(0, input_manager.use_count()); } namespace { class ExceptionThrowingDisplayBufferCompositorFactory : public mc::DisplayBufferCompositorFactory { public: std::unique_ptr create_compositor_for(mg::DisplayBuffer&) override { struct ExceptionThrowingDisplayBufferCompositor : mc::DisplayBufferCompositor { void composite(mc::SceneElementSequence&&) override { throw std::runtime_error("ExceptionThrowingDisplayBufferCompositor"); } }; return std::unique_ptr( new ExceptionThrowingDisplayBufferCompositor{}); } }; } TEST(ServerShutdownWithThreadException, server_releases_resources_on_abnormal_compositor_thread_termination) { struct ServerConfig : mtf::TestingServerConfiguration { std::shared_ptr the_display_buffer_compositor_factory() override { return display_buffer_compositor_factory( [this]() { return std::make_shared(); }); } }; auto server_config = std::make_shared(); std::thread server{ [&] { EXPECT_THROW( mir::run_mir(*server_config, [](mir::DisplayServer&){}), std::runtime_error); }}; server.join(); std::weak_ptr display = server_config->the_display(); std::weak_ptr compositor = server_config->the_compositor(); std::weak_ptr connector = server_config->the_connector(); std::weak_ptr input_manager = server_config->the_input_manager(); std::weak_ptr hub = server_config->the_input_device_hub(); server_config.reset(); EXPECT_EQ(0, display.use_count()); EXPECT_EQ(0, compositor.use_count()); EXPECT_EQ(0, connector.use_count()); EXPECT_EQ(0, input_manager.use_count()); EXPECT_EQ(0, hub.use_count()); } TEST(ServerShutdownWithThreadException, server_releases_resources_on_abnormal_input_thread_termination) { auto server_config = std::make_shared(); std::thread server{ [&server_config] { EXPECT_THROW( mir::run_mir(*server_config, [](mir::DisplayServer&){}), std::runtime_error); }}; { auto dev = mtf::add_fake_input_device(mir::input::InputDeviceInfo{"throwing device", "throwing-device-0", mir::input::DeviceCapability::unknown}); dev->emit_runtime_error(); server.join(); dev.reset(); } std::weak_ptr display = server_config->the_display(); std::weak_ptr compositor = server_config->the_compositor(); std::weak_ptr connector = server_config->the_connector(); std::weak_ptr input_manager = server_config->the_input_manager(); std::weak_ptr hub = server_config->the_input_device_hub(); server_config.reset(); EXPECT_EQ(0, display.use_count()); EXPECT_EQ(0, compositor.use_count()); EXPECT_EQ(0, connector.use_count()); EXPECT_EQ(0, input_manager.use_count()); EXPECT_EQ(0, hub.use_count()); } // This also acts as a regression test for LP: #1378740 TEST(ServerShutdownWithThreadException, server_releases_resources_on_abnormal_main_thread_termination) { // Use the FakeInputServerConfiguration to get the production input components auto server_config = std::make_shared(); std::thread server{ [&] { EXPECT_THROW( mir::run_mir(*server_config, [server_config](mir::DisplayServer&) { server_config->the_main_loop()->enqueue( server_config.get(), [] { throw std::runtime_error(""); }); }), std::runtime_error); }}; server.join(); std::weak_ptr display = server_config->the_display(); std::weak_ptr compositor = server_config->the_compositor(); std::weak_ptr connector = server_config->the_connector(); std::weak_ptr input_manager = server_config->the_input_manager(); std::weak_ptr hub = server_config->the_input_device_hub(); server_config.reset(); EXPECT_EQ(0, display.use_count()); EXPECT_EQ(0, compositor.use_count()); EXPECT_EQ(0, connector.use_count()); EXPECT_EQ(0, input_manager.use_count()); EXPECT_EQ(0, hub.use_count()); } ./tests/integration-tests/surface_composition.cpp0000644000015600001650000000733412676616125022524 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "src/server/scene/basic_surface.h" #include "src/server/report/null_report_factory.h" #include "src/server/compositor/buffer_stream_surfaces.h" #include "src/server/compositor/buffer_queue.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/stub_frame_dropping_policy_factory.h" #include "mir/test/doubles/stub_input_sender.h" #include #include namespace geom = mir::geometry; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace mi = mir::input; namespace mr = mir::report; namespace ms = mir::scene; namespace mtd = mir::test::doubles; using namespace testing; namespace { struct SurfaceComposition : Test { auto create_surface() const -> std::shared_ptr { return std::make_shared( std::string("SurfaceComposition"), geom::Rectangle{{},{}}, false, create_buffer_stream(), create_input_channel(), create_input_sender(), create_cursor_image(), mr::null_scene_report()); } int const number_of_buffers = 3; auto create_buffer_stream() const ->std::shared_ptr { return std::make_shared(create_buffer_bundle()); } auto create_buffer_bundle() const -> std::shared_ptr { return std::make_shared( number_of_buffers, allocator, basic_properties, policy_factory); } auto create_input_channel() const -> std::shared_ptr { return {}; } auto create_cursor_image() const -> std::shared_ptr { return {}; } auto create_input_sender() const -> std::shared_ptr { return std::make_shared(); } std::shared_ptr const allocator {std::make_shared()}; mg::BufferProperties const basic_properties { geom::Size{3, 4}, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware }; mtd::StubFrameDroppingPolicyFactory policy_factory; }; } // Presumptive cause of lp:1376324 TEST_F(SurfaceComposition, does_not_send_client_buffers_to_dead_surfaces) { auto surface = create_surface(); mg::Buffer* old_buffer{nullptr}; bool called_back = true; auto const callback = [&] (mg::Buffer* new_buffer) { // If surface is dead then callback is not expected EXPECT_THAT(surface.get(), NotNull()); old_buffer = new_buffer; called_back = true; }; // Exhaust the buffers to ensure we have a pending swap to complete // But also be careful to not pass a formerly released non-null old_buffer // in to swap_buffers... while (called_back) { called_back = false; surface->primary_buffer_stream()->swap_buffers(old_buffer, callback); } auto const renderables = surface->generate_renderables(this); surface.reset(); } ./tests/integration-tests/test_focus_selection.cpp0000644000015600001650000001031412676616125022664 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/shell/input_targeter.h" #include "mir/shell/shell_wrapper.h" #include "mir/test/doubles/mock_input_targeter.h" #include "mir_test_framework/any_surface.h" #include "mir_test_framework/in_process_server.h" #include "mir_test_framework/testing_server_configuration.h" #include #include namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; using namespace testing; namespace { MATCHER(NonNullSession, "") { return arg.operator bool(); } struct MockShell : msh::ShellWrapper { explicit MockShell(std::shared_ptr const& wrapped) : msh::ShellWrapper{wrapped} { ON_CALL(*this, open_session(_, _, _)). WillByDefault(Invoke(this, &MockShell::unmocked_open_session)); ON_CALL(*this, close_session(_)). WillByDefault(Invoke(this, &MockShell::unmocked_close_session)); } MOCK_METHOD3(open_session, std::shared_ptr( pid_t, std::string const&, std::shared_ptr const&)); MOCK_METHOD1(close_session, void (std::shared_ptr const&)); private: std::shared_ptr unmocked_open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) { return msh::ShellWrapper::open_session(client_pid, name, sink); } void unmocked_close_session(std::shared_ptr const& session) { msh::ShellWrapper::close_session(session); } }; struct ServerConfig : mtf::TestingServerConfiguration { std::shared_ptr wrap_shell(std::shared_ptr const& wrapped) override { mock_shell = std::make_shared>(wrapped); return mock_shell; } std::shared_ptr the_input_targeter() override { return mock_input_targeter; } std::shared_ptr> mock_shell; std::shared_ptr> const mock_input_targeter = std::make_shared>(); }; struct FocusSelection : mtf::InProcessServer { mir::DefaultServerConfiguration& server_config() override { return server_configuration; } MockShell& the_mock_shell() { return *server_configuration.mock_shell; } mtd::MockInputTargeter& the_mock_input_targeter() { return *server_configuration.mock_input_targeter; } ServerConfig server_configuration; }; } TEST_F(FocusSelection, when_client_connects_shell_is_notified_of_session) { InSequence seq; EXPECT_CALL(the_mock_shell(), open_session(_, _, _)); EXPECT_CALL(the_mock_shell(), close_session(NonNullSession())); auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_TRUE(mir_connection_is_valid(connection)); mir_connection_release(connection); } TEST_F(FocusSelection, when_surface_gets_valid_contents_input_focus_is_set) { EXPECT_CALL(the_mock_input_targeter(), clear_focus()).Times(AtLeast(0)); EXPECT_CALL(the_mock_input_targeter(), set_focus(_)).Times(1); auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_TRUE(mir_connection_is_valid(connection)); auto const surface = mtf::make_any_surface(connection); mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); mir_surface_release_sync(surface); mir_connection_release(connection); } ./tests/integration-tests/test_server_client_types.cpp0000644000015600001650000000334312676616125023574 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/client_types.h" #include "mir/graphics/display_configuration.h" #include namespace mg = mir::graphics; #define EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(TYPE) \ EXPECT_EQ(static_cast(mir_display_output_type_##TYPE), \ mg::DisplayConfigurationOutputType::TYPE) TEST(ServerClientTypes, display_output_types_match) { EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(unknown); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(vga); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(dvid); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(dvia); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(composite); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(svideo); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(lvds); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(component); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(ninepindin); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(displayport); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(hdmia); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(hdmib); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(tv); EXPECT_DISPLAY_OUTPUT_TYPES_MATCH(edp); } ./tests/integration-tests/test_display_info.cpp0000644000015600001650000000511712676616125022165 0ustar jenkinsjenkins/* * Copyright © 2012-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/test/doubles/null_platform.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir_test_framework/basic_client_server_fixture.h" #include "mir_test_framework/testing_server_configuration.h" #include "mir_toolkit/mir_client_library.h" #include #include namespace mg = mir::graphics; namespace mf = mir::frontend; namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mt = mir::test; namespace { class StubGraphicBufferAllocator : public mtd::StubBufferAllocator { public: std::vector supported_pixel_formats() override { return pixel_formats; } static std::vector const pixel_formats; }; std::vector const StubGraphicBufferAllocator::pixel_formats{ mir_pixel_format_argb_8888, mir_pixel_format_xbgr_8888, mir_pixel_format_bgr_888 }; class StubPlatform : public mtd::NullPlatform { public: mir::UniqueModulePtr create_buffer_allocator() override { return mir::make_module_ptr(); } }; struct ServerConfig : mtf::TestingServerConfiguration { std::shared_ptr the_graphics_platform() override { if (!platform) platform = std::make_shared(); return platform; } std::shared_ptr platform; }; using AvailableSurfaceFormats = mtf::BasicClientServerFixture; } TEST_F(AvailableSurfaceFormats, reach_clients) { using namespace testing; std::vector formats(4); unsigned int returned_format_size = 0; mir_connection_get_available_surface_formats( connection, formats.data(), formats.size(), &returned_format_size); formats.resize(returned_format_size); EXPECT_THAT(formats, ContainerEq(StubGraphicBufferAllocator::pixel_formats)); } ./tests/integration-tests/test_client_screencast.cpp0000644000015600001650000001263012676616125023173 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_toolkit/mir_screencast.h" #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/stubbed_server_configuration.h" #include "mir_test_framework/basic_client_server_fixture.h" #include "mir/test/doubles/null_display_changer.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/mock_screencast.h" #include "mir/test/fake_shared.h" #include #include namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mg = mir::graphics; namespace mf = mir::frontend; namespace mt = mir::test; namespace geom = mir::geometry; namespace { class StubChanger : public mtd::NullDisplayChanger { public: StubChanger() : stub_display_config{{{connected, !used}, {connected, used}}} { } std::shared_ptr base_configuration() override { return mt::fake_shared(stub_display_config); } mtd::StubDisplayConfig stub_display_config; private: static bool const connected; static bool const used; }; bool const StubChanger::connected{true}; bool const StubChanger::used{true}; struct StubServerConfig : mir_test_framework::StubbedServerConfiguration { std::shared_ptr the_frontend_display_changer() override { return mt::fake_shared(stub_changer); } std::shared_ptr the_screencast() override { return mt::fake_shared(mock_screencast); } StubChanger stub_changer; mtd::MockScreencast mock_screencast; }; struct Screencast : mtf::BasicClientServerFixture { mtd::MockScreencast& mock_screencast() { return server_configuration.mock_screencast; } MirScreencastParameters default_screencast_params { {0, 0, 1, 1}, 1, 1, mir_pixel_format_abgr_8888}; }; } TEST_F(Screencast, contacts_server_screencast_for_create_and_release) { using namespace testing; mf::ScreencastSessionId const screencast_session_id{99}; InSequence seq; EXPECT_CALL(mock_screencast(), create_session(_, _, _)) .WillOnce(Return(screencast_session_id)); EXPECT_CALL(mock_screencast(), capture(screencast_session_id)) .WillOnce(Return(std::make_shared())); EXPECT_CALL(mock_screencast(), destroy_session(screencast_session_id)); auto screencast = mir_connection_create_screencast_sync(connection, &default_screencast_params); ASSERT_NE(nullptr, screencast); mir_screencast_release_sync(screencast); } TEST_F(Screencast, contacts_server_screencast_with_provided_params) { using namespace testing; mf::ScreencastSessionId const screencast_session_id{99}; geom::Size const size{default_screencast_params.width, default_screencast_params.height}; geom::Rectangle const region { {default_screencast_params.region.left, default_screencast_params.region.top}, {default_screencast_params.region.width, default_screencast_params.region.height}}; MirPixelFormat pixel_format {default_screencast_params.pixel_format}; InSequence seq; EXPECT_CALL(mock_screencast(), create_session(region, size, pixel_format)) .WillOnce(Return(screencast_session_id)); EXPECT_CALL(mock_screencast(), capture(_)) .WillOnce(Return(std::make_shared())); EXPECT_CALL(mock_screencast(), destroy_session(_)); auto screencast = mir_connection_create_screencast_sync(connection, &default_screencast_params); ASSERT_NE(nullptr, screencast); mir_screencast_release_sync(screencast); } TEST_F(Screencast, gets_valid_egl_native_window) { using namespace testing; mf::ScreencastSessionId const screencast_session_id{99}; InSequence seq; EXPECT_CALL(mock_screencast(), create_session(_, _, _)) .WillOnce(Return(screencast_session_id)); EXPECT_CALL(mock_screencast(), capture(_)) .WillOnce(Return(std::make_shared())); EXPECT_CALL(mock_screencast(), destroy_session(_)); auto screencast = mir_connection_create_screencast_sync(connection, &default_screencast_params); ASSERT_NE(nullptr, screencast); auto egl_native_window = mir_buffer_stream_get_egl_native_window(mir_screencast_get_buffer_stream(screencast)); EXPECT_NE(MirEGLNativeWindowType(), egl_native_window); mir_screencast_release_sync(screencast); } TEST_F(Screencast, fails_on_client_when_server_request_fails) { using namespace testing; EXPECT_CALL(mock_screencast(), create_session(_, _, _)) .WillOnce(Throw(std::runtime_error(""))); auto screencast = mir_connection_create_screencast_sync(connection, &default_screencast_params); ASSERT_EQ(nullptr, screencast); } ./tests/integration-tests/test_swapinterval.cpp0000644000015600001650000001101112676616125022212 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois * Alexandros Frantzis */ #include "mir/scene/buffer_stream_factory.h" #include "mir/test/doubles/stub_buffer_stream.h" #include "mir_test_framework/any_surface.h" #include "mir_test_framework/basic_client_server_fixture.h" #include "mir_test_framework/testing_server_configuration.h" #include "mir_toolkit/mir_client_library.h" #include #include namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace ms = mir::scene; namespace mg = mir::graphics; namespace mc = mir::compositor; namespace mf = mir::frontend; namespace { class StubBufferStream : public mtd::StubBufferStream { public: StubBufferStream(std::atomic& framedropping_enabled) : framedropping_enabled{framedropping_enabled} { } void allow_framedropping(bool allow) override { framedropping_enabled = allow; } private: std::atomic& framedropping_enabled; }; class StubBufferStreamFactory : public ms::BufferStreamFactory { public: StubBufferStreamFactory(std::atomic& framedropping_enabled) : framedropping_enabled{framedropping_enabled} { } std::shared_ptr create_buffer_stream( mf::BufferStreamId id, std::shared_ptr const& sink, int, mg::BufferProperties const& p) override { return create_buffer_stream(id, sink, p); } std::shared_ptr create_buffer_stream( mf::BufferStreamId, std::shared_ptr const&, mg::BufferProperties const&) override { return std::make_shared(framedropping_enabled); } private: std::atomic& framedropping_enabled; }; struct ServerConfig : mtf::TestingServerConfiguration { std::shared_ptr the_buffer_stream_factory() override { if (!stub_buffer_stream_factory) { stub_buffer_stream_factory = std::make_shared(framedropping_enabled); } return stub_buffer_stream_factory; } std::atomic framedropping_enabled{false}; std::shared_ptr stub_buffer_stream_factory; }; struct SwapInterval : mtf::BasicClientServerFixture { void SetUp() { mtf::BasicClientServerFixture::SetUp(); surface = mtf::make_any_surface(connection); } void TearDown() { mir_surface_release_sync(surface); mtf::BasicClientServerFixture::TearDown(); } bool framedropping_enabled() { return server_configuration.framedropping_enabled; } MirSurface* surface; }; } TEST_F(SwapInterval, defaults_to_one) { EXPECT_EQ(1, mir_surface_get_swapinterval(surface)); EXPECT_FALSE(framedropping_enabled()); } TEST_F(SwapInterval, change_to_zero_enables_framedropping) { mir_wait_for(mir_surface_set_swapinterval(surface, 0)); EXPECT_EQ(0, mir_surface_get_swapinterval(surface)); EXPECT_TRUE(framedropping_enabled()); } TEST_F(SwapInterval, change_to_one_disables_framedropping) { mir_wait_for(mir_surface_set_swapinterval(surface, 0)); mir_wait_for(mir_surface_set_swapinterval(surface, 1)); EXPECT_EQ(1, mir_surface_get_swapinterval(surface)); EXPECT_FALSE(framedropping_enabled()); } TEST_F(SwapInterval, is_not_changed_if_requested_value_is_unsupported) { auto const original_swapinterval = mir_surface_get_swapinterval(surface); auto const original_framedropping = framedropping_enabled(); int const unsupported_swap_interval_value{2}; EXPECT_EQ(NULL, mir_surface_set_swapinterval(surface, unsupported_swap_interval_value)); EXPECT_EQ(original_swapinterval, mir_surface_get_swapinterval(surface)); EXPECT_EQ(original_framedropping, framedropping_enabled()); } ./tests/integration-tests/test_error_reporting.cpp0000644000015600001650000001124612676616125022727 0ustar jenkinsjenkins/* * Copyright © 2012-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths * Alexandros Frantzis */ #include "mir_toolkit/mir_client_library.h" #include "mir_test_framework/testing_server_configuration.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/deferred_in_process_server.h" #include "mir/test/doubles/stub_ipc_factory.h" #include "mir/test/doubles/stub_display_server.h" #include "src/server/frontend/display_server.h" #include "src/server/frontend/protobuf_ipc_factory.h" #include "src/server/frontend/resource_cache.h" #include "src/server/scene/surface_allocator.h" #include #include #include "mir/test/gmock_fixes.h" #include "mir/test/validity_matchers.h" namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mf = mir::frontend; namespace { std::string const test_exception_text{"test exception text"}; struct ConnectionErrorServer : mtd::StubDisplayServer { void connect( mir::protobuf::ConnectParameters const*, mir::protobuf::Connection*, google::protobuf::Closure*) { throw std::runtime_error(test_exception_text); } void disconnect( mir::protobuf::Void const*, mir::protobuf::Void*, google::protobuf::Closure*) { throw std::runtime_error(test_exception_text); } }; struct ErrorReporting : mtf::DeferredInProcessServer { std::unique_ptr server_configuration; mtf::UsingStubClientPlatform using_stub_client_platform; void start_server_with_config(std::unique_ptr config) { server_configuration = std::move(config); start_server(); } mir::DefaultServerConfiguration& server_config() override { return *server_configuration; } }; } TEST_F(ErrorReporting, c_api_returns_connection_error) { struct ServerConfig : mtf::TestingServerConfiguration { std::shared_ptr new_ipc_factory( std::shared_ptr const&) override { static auto error_server = std::make_shared(); return std::make_shared(*error_server); } }; start_server_with_config(std::make_unique()); auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_TRUE(connection != NULL); EXPECT_FALSE(mir_connection_is_valid(connection)); EXPECT_THAT(mir_connection_get_error_message(connection), testing::HasSubstr("Failed to connect")); mir_connection_release(connection); } TEST_F(ErrorReporting, c_api_returns_surface_creation_error) { struct ServerConfig : mtf::TestingServerConfiguration { class BoobytrappedSurfaceFactory : public mir::scene::SurfaceFactory { std::shared_ptr create_surface( std::shared_ptr const&, mir::scene::SurfaceCreationParameters const&) override { throw std::runtime_error{test_exception_text}; } }; std::shared_ptr the_surface_factory() override { return std::make_shared(); } } server_config; start_server_with_config(std::make_unique()); auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); ASSERT_THAT(connection, IsValid()); auto const spec = mir_connection_create_spec_for_normal_surface( connection, 640, 480, mir_pixel_format_abgr_8888); auto const surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); ASSERT_TRUE(surface != NULL); ASSERT_THAT(surface, Not(IsValid())); EXPECT_THAT(mir_surface_get_error_message(surface), testing::HasSubstr(test_exception_text)); mir_surface_release_sync(surface); mir_connection_release(connection); } ./tests/integration-tests/test_macros.cpp0000644000015600001650000000205612676616125020770 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "cutils/log.h" // Get the final value of LOG_NDEBUG #include TEST(Macros, android_verbose_logging_is_disabled) { // Ensure verbose logging (ALOGV) is removed. It requires significant // CPU time to constantly format some verbose messages... // This is a regression test for performance bug LP: #1343074. EXPECT_EQ(1, LOG_NDEBUG); } ./tests/integration-tests/test_exchange_buffer.cpp0000644000015600001650000004015512676616125022621 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_test_framework/stubbed_server_configuration.h" #include "mir_test_framework/in_process_server.h" #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/any_surface.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/stub_display.h" #include "mir/test/doubles/null_platform.h" #include "mir/graphics/buffer_id.h" #include "mir/graphics/buffer_ipc_message.h" #include "mir/graphics/platform_operation_message.h" #include "mir/scene/buffer_stream_factory.h" #include "mir/scene/session_coordinator.h" #include "mir/frontend/event_sink.h" #include "mir/compositor/buffer_stream.h" #include "src/server/compositor/buffer_bundle.h" #include "src/server/compositor/buffer_stream_surfaces.h" #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/debug/surface.h" #include "src/client/mir_connection.h" #include "src/client/rpc/mir_display_server.h" #include #include #include #include #include #include #include "mir/fd.h" namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mg = mir::graphics; namespace msc = mir::scene; namespace mc = mir::compositor; namespace geom = mir::geometry; namespace mp = mir::protobuf; namespace mf = mir::frontend; namespace mclr = mir::client::rpc; namespace { MATCHER(DidNotTimeOut, "did not time out") { return arg; } struct StubBundle : public mc::BufferBundle { StubBundle(std::vector const& ids) : buffer_id_seq(ids) { } void client_acquire(std::function complete) { std::shared_ptr stub_buffer; if (buffers_acquired < buffer_id_seq.size()) stub_buffer = std::make_shared(buffer_id_seq.at(buffers_acquired++)); else stub_buffer = std::make_shared(buffer_id_seq.back()); client_buffers.push_back(stub_buffer); complete(stub_buffer.get()); } void client_release(mg::Buffer*) {} std::shared_ptr compositor_acquire(void const*) { return std::make_shared(); } void compositor_release(std::shared_ptr const&) {} std::shared_ptr snapshot_acquire() { return std::make_shared(); } void snapshot_release(std::shared_ptr const&) {} mg::BufferProperties properties() const { return mg::BufferProperties{}; } void allow_framedropping(bool) {} void force_requests_to_complete() {} void resize(const geom::Size&) {} int buffers_ready_for_compositor(void const*) const { return 1; } int buffers_free_for_client() const { return 1; } void drop_old_buffers() {} void drop_client_requests() {} std::vector> client_buffers; std::vector const buffer_id_seq; unsigned int buffers_acquired{0}; }; struct StubBundleFactory : public msc::BufferStreamFactory { StubBundleFactory(std::vector const& ids) : buffer_id_seq(ids) {} std::shared_ptr create_buffer_stream( mf::BufferStreamId i, std::shared_ptr const& s, int, mg::BufferProperties const& p) override { return create_buffer_stream(i, s, p); } std::shared_ptr create_buffer_stream( mf::BufferStreamId, std::shared_ptr const&, mg::BufferProperties const&) override { return std::make_shared(std::make_shared(buffer_id_seq)); } std::vector const buffer_id_seq; }; struct StubBufferPacker : public mg::PlatformIpcOperations { StubBufferPacker(std::shared_ptr const& last_fd) : last_fd{last_fd} { *last_fd = mir::Fd{-1}; } void pack_buffer(mg::BufferIpcMessage&, mg::Buffer const&, mg::BufferIpcMsgType) const override { } void unpack_buffer(mg::BufferIpcMessage& msg, mg::Buffer const&) const override { auto fds = msg.fds(); if (!fds.empty()) { *last_fd = fds[0]; } } std::shared_ptr connection_ipc_package() override { return std::make_shared(); } mg::PlatformOperationMessage platform_operation( unsigned int const, mg::PlatformOperationMessage const&) override { return mg::PlatformOperationMessage(); } private: std::shared_ptr const last_fd; }; struct StubPlatform : public mtd::NullPlatform { StubPlatform(std::shared_ptr const& last_fd) : last_fd(last_fd) { } mir::UniqueModulePtr create_buffer_allocator() override { return mir::make_module_ptr(); } mir::UniqueModulePtr make_ipc_operations() const override { return mir::make_module_ptr(last_fd); } mir::UniqueModulePtr create_display( std::shared_ptr const&, std::shared_ptr const&) override { std::vector rect{geom::Rectangle{{0,0},{1,1}}}; return mir::make_module_ptr(rect); } std::shared_ptr const last_fd; }; class SinkSkimmingCoordinator : public msc::SessionCoordinator { public: SinkSkimmingCoordinator(std::shared_ptr const& wrapped) : wrapped(wrapped) { } void set_focus_to(std::shared_ptr const& focus) override { wrapped->set_focus_to(focus); } void unset_focus() override { wrapped->unset_focus(); } std::shared_ptr open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) override { last_sink = sink; return wrapped->open_session(client_pid, name, sink); } void close_session(std::shared_ptr const& session) override { wrapped->close_session(session); } std::shared_ptr successor_of(std::shared_ptr const& session) const override { return wrapped->successor_of(session); } std::shared_ptr const wrapped; std::weak_ptr last_sink; }; struct ExchangeServerConfiguration : mtf::StubbedServerConfiguration { ExchangeServerConfiguration( std::vector const& id_seq, std::shared_ptr const& last_unpacked_fd) : stream_factory{std::make_shared(id_seq)}, platform{std::make_shared(last_unpacked_fd)} { } std::shared_ptr the_session_coordinator() override { return session_coordinator([this]{ coordinator = std::make_shared( DefaultServerConfiguration::the_session_coordinator()); return coordinator; }); } std::shared_ptr the_graphics_platform() override { return platform; } std::shared_ptr the_buffer_stream_factory() override { return stream_factory; } std::shared_ptr const stream_factory; std::shared_ptr const platform; std::shared_ptr coordinator; }; struct ExchangeBufferTest : mir_test_framework::InProcessServer { std::vector const buffer_id_exchange_seq{ mg::BufferID{4}, mg::BufferID{8}, mg::BufferID{9}, mg::BufferID{3}, mg::BufferID{4}}; std::shared_ptr last_unpacked_fd{std::make_shared()}; ExchangeServerConfiguration server_configuration{buffer_id_exchange_seq, last_unpacked_fd}; mir::DefaultServerConfiguration& server_config() override { return server_configuration; } mtf::UsingStubClientPlatform using_stub_client_platform; void request_completed() { std::unique_lock lk(mutex); arrived = true; cv.notify_all(); } bool exchange_buffer(mclr::DisplayServer& server) { std::unique_lock lk(mutex); mp::Buffer next; server.exchange_buffer(&buffer_request, &next, google::protobuf::NewCallback(this, &ExchangeBufferTest::request_completed)); arrived = false; auto completed = cv.wait_for(lk, std::chrono::seconds(5), [this]() {return arrived;}); for (auto i = 0; i < next.fd().size(); i++) ::close(next.fd(i)); next.set_fds_on_side_channel(0); *buffer_request.mutable_buffer() = next; return completed; } bool submit_buffer(mclr::DisplayServer& server, mp::BufferRequest& request) { std::unique_lock lk(mutex); mp::Void v; server.submit_buffer(&request, &v, google::protobuf::NewCallback(this, &ExchangeBufferTest::request_completed)); arrived = false; return cv.wait_for(lk, std::chrono::seconds(5), [this]() {return arrived;}); } bool allocate_buffers(mclr::DisplayServer& server, mp::BufferAllocation& request) { std::unique_lock lk(mutex); mp::Void v; server.allocate_buffers(&request, &v, google::protobuf::NewCallback(this, &ExchangeBufferTest::request_completed)); arrived = false; return cv.wait_for(lk, std::chrono::seconds(5), [this]() {return arrived;}); } bool release_buffers(mclr::DisplayServer& server, mp::BufferRelease& request) { std::unique_lock lk(mutex); mp::Void v; server.release_buffers(&request, &v, google::protobuf::NewCallback(this, &ExchangeBufferTest::request_completed)); arrived = false; return cv.wait_for(lk, std::chrono::seconds(5), [this]() {return arrived;}); } std::mutex mutex; std::condition_variable cv; bool arrived{false}; mp::BufferRequest buffer_request; }; } //tests for the exchange_buffer rpc call TEST_F(ExchangeBufferTest, exchanges_happen) { auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface = mtf::make_any_surface(connection); auto rpc_channel = connection->rpc_channel(); mclr::DisplayServer server(rpc_channel); buffer_request.mutable_buffer()->set_buffer_id(buffer_id_exchange_seq.begin()->as_value()); for (auto i = 0; i < buffer_request.buffer().fd().size(); i++) ::close(buffer_request.buffer().fd(i)); for (auto const& id : buffer_id_exchange_seq) { EXPECT_THAT(buffer_request.buffer().buffer_id(), testing::Eq(id.as_value())); ASSERT_THAT(exchange_buffer(server), DidNotTimeOut()); } mir_surface_release_sync(surface); mir_connection_release(connection); } namespace { MATCHER(NoErrorOnFileRead, "") { return arg > 0; } } TEST_F(ExchangeBufferTest, fds_can_be_sent_back) { using namespace testing; std::string test_string{"mir was a space station"}; mir::Fd file(fileno(tmpfile())); EXPECT_THAT(write(file, test_string.c_str(), test_string.size()), Gt(0)); auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface = mtf::make_any_surface(connection); auto rpc_channel = connection->rpc_channel(); mclr::DisplayServer server(rpc_channel); for (auto i = 0; i < buffer_request.buffer().fd().size(); i++) ::close(buffer_request.buffer().fd(i)); buffer_request.mutable_buffer()->set_buffer_id(buffer_id_exchange_seq.begin()->as_value()); buffer_request.mutable_buffer()->add_fd(file); ASSERT_THAT(exchange_buffer(server), DidNotTimeOut()); mir_surface_release_sync(surface); mir_connection_release(connection); auto server_received_fd = *last_unpacked_fd; char file_buffer[32]; lseek(file, 0, SEEK_SET); ASSERT_THAT(read(server_received_fd, file_buffer, sizeof(file_buffer)), NoErrorOnFileRead()); EXPECT_THAT(strncmp(test_string.c_str(), file_buffer, test_string.size()), Eq(0)); } //tests for the submit buffer protocol. TEST_F(ExchangeBufferTest, submissions_happen) { auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface = mtf::make_any_surface(connection); auto rpc_channel = connection->rpc_channel(); mclr::DisplayServer server(rpc_channel); mp::BufferRequest request; for (auto const& id : buffer_id_exchange_seq) { buffer_request.mutable_buffer()->set_buffer_id(id.as_value()); ASSERT_THAT(submit_buffer(server, buffer_request), DidNotTimeOut()); } mir_surface_release_sync(surface); mir_connection_release(connection); } namespace { template bool spin_wait_for_id(mg::BufferID id, MirSurface* surface, std::chrono::time_point const& pt) { while(Clock::now() < pt) { if (mir_debug_surface_current_buffer_id(surface) == id.as_value()) return true; std::this_thread::yield(); } return false; } } TEST_F(ExchangeBufferTest, server_can_send_buffer) { using namespace testing; using namespace std::literals::chrono_literals; auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface = mtf::make_any_surface(connection); auto sink = server_configuration.coordinator->last_sink.lock(); //first wait for the last id in the exchange sequence to be seen. Avoids lp: #1487967, where //the stub sink could send before the server sends its sequence. auto timeout = std::chrono::steady_clock::now() + 5s; EXPECT_TRUE(spin_wait_for_id(buffer_id_exchange_seq.back(), surface, timeout)) << "failed to see the last scheduled buffer become the current one"; //spin-wait for the id to become the current one. //The notification doesn't generate a client-facing callback on the stream yet //(although probably should, seems like something a media decoder would need) mtd::StubBuffer stub_buffer; timeout = std::chrono::steady_clock::now() + 5s; sink->send_buffer(mf::BufferStreamId{0}, stub_buffer, mg::BufferIpcMsgType::full_msg); EXPECT_TRUE(spin_wait_for_id(stub_buffer.id(), surface, timeout)) << "failed to see the sent buffer become the current one"; mir_surface_release_sync(surface); mir_connection_release(connection); } //TODO: check that a buffer arrives asynchronously. TEST_F(ExchangeBufferTest, allocate_buffers_doesnt_time_out) { auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface = mtf::make_any_surface(connection); auto rpc_channel = connection->rpc_channel(); mclr::DisplayServer server(rpc_channel); mp::BufferAllocation request; EXPECT_THAT(allocate_buffers(server, request), DidNotTimeOut()); mir_surface_release_sync(surface); mir_connection_release(connection); } TEST_F(ExchangeBufferTest, release_buffers_doesnt_time_out) { auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); auto surface = mtf::make_any_surface(connection); auto rpc_channel = connection->rpc_channel(); mclr::DisplayServer server(rpc_channel); mp::BufferRelease request; EXPECT_THAT(release_buffers(server, request), DidNotTimeOut()); mir_surface_release_sync(surface); mir_connection_release(connection); } ./tests/integration-tests/process/0000755000015600001650000000000012676616126017415 5ustar jenkinsjenkins./tests/integration-tests/process/CMakeLists.txt0000644000015600001650000000023712676616125022156 0ustar jenkinsjenkinslist(APPEND INTEGRATION_TESTS_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_process.cpp ) set( INTEGRATION_TESTS_SRCS ${INTEGRATION_TESTS_SRCS} PARENT_SCOPE) ./tests/integration-tests/process/test_process.cpp0000644000015600001650000000745112676616125022644 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #include "mir_test_framework/process.h" #include #include #include namespace mtf = mir_test_framework; namespace mir { struct MainFunctionFactory { static void an_empty_main_function() { } static void an_infinitely_waiting_main_function() { std::mutex m; std::unique_lock ul(m); std::condition_variable cv; cv.wait(ul); } static void a_value_altering_main_function( int& value, int expected_value_after_increment) { value++; EXPECT_EQ( expected_value_after_increment, value); } }; struct ExitFunctionFactory { static int a_successful_exit_function() { return EXIT_SUCCESS; } static int a_failing_exit_function() { return EXIT_FAILURE; } static int a_gtest_exit_function() { return ::testing::Test::HasFailure() ? EXIT_FAILURE : EXIT_SUCCESS; } }; TEST(ProcessResult, a_default_result_never_succeeds) { mtf::Result r; EXPECT_FALSE(r.succeeded()); } TEST(ProcessResult, a_signalled_result_does_not_succeed) { mtf::Result r; r.reason = mtf::TerminationReason::child_terminated_by_signal; EXPECT_FALSE(r.succeeded()); } TEST(ProcessResult, a_normally_terminated_result_succeeds_only_with_exit_success) { mtf::Result r; r.reason = mtf::TerminationReason::child_terminated_normally; r.exit_code = EXIT_FAILURE; EXPECT_FALSE(r.succeeded()); r.exit_code = EXIT_SUCCESS; EXPECT_TRUE(r.succeeded()); } TEST(Process, a_main_fn_is_executed) { int value = 0; auto p = mtf::fork_and_run_in_a_different_process( std::bind( MainFunctionFactory::a_value_altering_main_function, value, 1), ExitFunctionFactory::a_gtest_exit_function); EXPECT_TRUE(p->wait_for_termination().succeeded()); } TEST(Process, a_successful_exit_function_succeeds) { auto p = mtf::fork_and_run_in_a_different_process( MainFunctionFactory::an_empty_main_function, ExitFunctionFactory::a_successful_exit_function); EXPECT_TRUE(p->wait_for_termination().succeeded()); } TEST(Process, a_failing_exit_function_does_not_succeed) { auto p = mtf::fork_and_run_in_a_different_process( MainFunctionFactory::an_empty_main_function, ExitFunctionFactory::a_failing_exit_function); EXPECT_FALSE(p->wait_for_termination().succeeded()); } TEST(Process, a_terminated_child_is_recognized_as_being_signalled) { auto p = mtf::fork_and_run_in_a_different_process( MainFunctionFactory::an_infinitely_waiting_main_function, ExitFunctionFactory::a_successful_exit_function); p->terminate(); EXPECT_TRUE(p->wait_for_termination().signalled()); } TEST(Process, a_killed_child_is_recognized_as_being_signalled) { auto p = mtf::fork_and_run_in_a_different_process( MainFunctionFactory::an_infinitely_waiting_main_function, ExitFunctionFactory::a_successful_exit_function); p->kill(); EXPECT_TRUE(p->wait_for_termination().signalled()); } } ./tests/integration-tests/test_buffer_scheduling.cpp0000644000015600001650000013253512676616157023175 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/frontend/client_buffers.h" #include "mir/frontend/event_sink.h" #include "mir/frontend/buffer_sink.h" #include "src/client/buffer_vault.h" #include "src/client/client_buffer_depository.h" #include "src/server/compositor/buffer_queue.h" #include "src/server/compositor/stream.h" #include "src/server/compositor/buffer_map.h" #include "src/server/compositor/buffer_stream_surfaces.h" #include "mir/test/doubles/stub_client_buffer_factory.h" #include "mir/test/doubles/mock_client_buffer_factory.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/mock_frame_dropping_policy_factory.h" #include "mir/test/fake_shared.h" #include "mir_protobuf.pb.h" #include namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mcl = mir::client; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace mf = mir::frontend; namespace mp = mir::protobuf; using namespace testing; namespace { enum class TestType { ExchangeSemantics, SubmitSemantics }; enum class Access { blocked, unblocked }; struct BufferEntry { mg::BufferID id; unsigned int age; Access blockage; bool operator==(BufferEntry const& b) const { return ((blockage == b.blockage) && (id == b.id) && (age == b.age)); } }; struct ProducerSystem { virtual bool can_produce() = 0; virtual void produce() = 0; virtual std::vector production_log() = 0; virtual void reset_log() = 0; virtual geom::Size last_size() = 0; virtual mg::BufferID current_id() = 0; virtual ~ProducerSystem() = default; ProducerSystem() = default; ProducerSystem(ProducerSystem const&) = delete; ProducerSystem& operator=(ProducerSystem const&) = delete; }; struct ConsumerSystem { virtual void consume() { consume_resource(); } virtual std::shared_ptr consume_resource() = 0; virtual geom::Size last_size() = 0; virtual void set_framedropping(bool) = 0; virtual std::vector consumption_log() = 0; ConsumerSystem() = default; virtual ~ConsumerSystem() = default; ConsumerSystem(ConsumerSystem const&) = delete; ConsumerSystem& operator=(ConsumerSystem const&) = delete; }; //buffer queue testing struct BufferQueueProducer : ProducerSystem { BufferQueueProducer(mc::BufferStream& stream) : stream(stream) { stream.swap_buffers(buffer, std::bind(&BufferQueueProducer::buffer_ready, this, std::placeholders::_1)); } bool can_produce() { std::unique_lock lk(mutex); return buffer; } mg::BufferID current_id() { if (buffer) return buffer->id(); else return mg::BufferID{INT_MAX}; } void produce() { mg::Buffer* b = nullptr; if (can_produce()) { { std::unique_lock lk(mutex); b = buffer; buffer = nullptr; age++; entries.emplace_back(BufferEntry{b->id(), age, Access::unblocked}); b->write(reinterpret_cast(&age), sizeof(age)); } stream.swap_buffers(b, std::bind(&BufferQueueProducer::buffer_ready, this, std::placeholders::_1)); } else { entries.emplace_back(BufferEntry{mg::BufferID{2}, 0u, Access::blocked}); } } std::vector production_log() { std::unique_lock lk(mutex); return entries; } geom::Size last_size() { if (buffer) return buffer->size(); return geom::Size{}; } void reset_log() { std::unique_lock lk(mutex); return entries.clear(); } private: mc::BufferStream& stream; void buffer_ready(mg::Buffer* b) { std::unique_lock lk(mutex); buffer = b; } std::mutex mutex; unsigned int age {0}; std::vector entries; mg::Buffer* buffer {nullptr}; }; struct BufferQueueConsumer : ConsumerSystem { BufferQueueConsumer(mc::BufferStream& stream) : stream(stream) { } std::shared_ptr consume_resource() override { auto b = stream.lock_compositor_buffer(this); last_size_ = b->size(); b->read([this, b](unsigned char const* p) { entries.emplace_back(BufferEntry{b->id(), *reinterpret_cast(p), Access::unblocked}); }); return b; } std::vector consumption_log() { return entries; } geom::Size last_size() { return last_size_; } void set_framedropping(bool allow) override { stream.allow_framedropping(allow); } mc::BufferStream& stream; std::vector entries; geom::Size last_size_; }; struct StubIpcSystem { void on_server_bound_transfer(std::function fn) { server_bound_fn = fn; } void on_client_bound_transfer(std::function fn) { client_bound_fn = fn; for(auto& b : buffers) client_bound_fn(b); buffers.clear(); } void on_allocate(std::function fn) { allocate_fn = fn; } void on_resize_event(std::function fn) { resize_fn = fn; } void resize_event(geom::Size sz) { if (resize_fn) resize_fn(sz); } void server_bound_transfer(mp::Buffer& buffer) { if (server_bound_fn) server_bound_fn(buffer); last_submit = buffer.buffer_id(); } int last_transferred_to_server() { return last_submit; } void client_bound_transfer(mp::Buffer& buffer) { if (client_bound_fn) client_bound_fn(buffer); else buffers.push_back(buffer); } void allocate(geom::Size sz) { if (allocate_fn) allocate_fn(sz); } std::function allocate_fn; std::function resize_fn; std::function client_bound_fn; std::function server_bound_fn; std::vector buffers; int last_submit{0}; }; struct StubEventSink : public mf::EventSink { StubEventSink(std::shared_ptr const& ipc) : ipc(ipc) { } void send_buffer(mf::BufferStreamId, mg::Buffer& buffer, mg::BufferIpcMsgType) { mp::Buffer protobuffer; protobuffer.set_buffer_id(buffer.id().as_value()); protobuffer.set_width(buffer.size().width.as_int()); protobuffer.set_height(buffer.size().height.as_int()); ipc->client_bound_transfer(protobuffer); } void handle_event(MirEvent const&) {} void handle_lifecycle_event(MirLifecycleState) {} void handle_display_config_change(mg::DisplayConfiguration const&) {} void handle_input_device_change(std::vector> const&) {} void send_ping(int32_t) {} std::shared_ptr ipc; }; //async semantics struct ScheduledConsumer : ConsumerSystem { ScheduledConsumer(std::shared_ptr stream) : stream(stream) { } std::shared_ptr consume_resource() override { auto b = stream->lock_compositor_buffer(this); unsigned int age = 0; last_size_ = b->size(); entries.emplace_back(BufferEntry{b->id(), age, Access::unblocked}); return b; } std::vector consumption_log() override { return entries; } geom::Size last_size() override { return last_size_; } void set_framedropping(bool allow) override { stream->allow_framedropping(allow); } std::shared_ptr stream; std::vector entries; geom::Size last_size_; }; struct ServerRequests : mcl::ServerBufferRequests { ServerRequests(std::shared_ptr const stub_ipc) : ipc(stub_ipc) { } void allocate_buffer(geom::Size sz, MirPixelFormat, int) { ipc->allocate(sz); } void free_buffer(int) { } void submit_buffer(int buffer_id, mcl::ClientBuffer&) { mp::Buffer buffer; buffer.set_buffer_id(buffer_id); ipc->server_bound_transfer(buffer); } std::shared_ptr ipc; }; struct ScheduledProducer : ProducerSystem { ScheduledProducer(std::shared_ptr const& ipc_stub, int nbuffers) : ipc(ipc_stub), vault( std::make_shared(), std::make_shared(ipc), geom::Size(100,100), mir_pixel_format_abgr_8888, 0, nbuffers) { ipc->on_client_bound_transfer([this](mp::Buffer& buffer){ available++; vault.wire_transfer_inbound(buffer); }); ipc->on_resize_event([this](geom::Size sz) { vault.set_size(sz); }); } bool can_produce() { return available > 0; } mg::BufferID current_id() { return current_id_; } void produce() { if (can_produce()) { auto buffer = vault.withdraw().get().buffer; vault.deposit(buffer); vault.wire_transfer_outbound(buffer); last_size_ = buffer->size(); entries.emplace_back(BufferEntry{mg::BufferID{(unsigned int)ipc->last_transferred_to_server()}, age, Access::unblocked}); available--; } else { entries.emplace_back(BufferEntry{mg::BufferID{2}, 0u, Access::blocked}); } } std::vector production_log() { return entries; } geom::Size last_size() { return last_size_; } void reset_log() { entries.clear(); } geom::Size last_size_; std::vector entries; std::shared_ptr ipc; mcl::BufferVault vault; int max, cur; int available{0}; unsigned int age{0}; mg::BufferID current_id_; }; //schedule helpers using tick = std::chrono::duration>; constexpr tick operator ""_t(unsigned long long t) { return tick(t); } struct ScheduleEntry { tick timestamp; std::vector producers; std::vector consumers; }; void run_system(std::vector& schedule) { std::sort(schedule.begin(), schedule.end(), [](ScheduleEntry& a, ScheduleEntry& b) { return a.timestamp < b.timestamp; }); for(auto const& entry : schedule) { for(auto const& p : entry.producers) p->produce(); for(auto const& c : entry.consumers) c->consume(); } } void repeat_system_until( std::vector& schedule, std::function const& predicate) { std::sort(schedule.begin(), schedule.end(), [](ScheduleEntry& a, ScheduleEntry& b) { return a.timestamp < b.timestamp; }); auto entry_it = schedule.begin(); if (entry_it == schedule.end()) return; while(predicate()) { for(auto const& p : entry_it->producers) p->produce(); for(auto const& c : entry_it->consumers) c->consume(); entry_it++; if (entry_it == schedule.end()) entry_it = schedule.begin(); } } size_t unique_ids_in(std::vector log) { std::sort(log.begin(), log.end(), [](BufferEntry const& a, BufferEntry const& b) { return a.id < b.id; }); auto it = std::unique(log.begin(), log.end(), [](BufferEntry const& a, BufferEntry const& b) { return a.id == b.id; } ); return std::distance(log.begin(), it); } //test infrastructure struct BufferScheduling : public Test, ::testing::WithParamInterface> { BufferScheduling() { if (std::get<1>(GetParam()) == TestType::ExchangeSemantics) { auto exchange_stream = std::make_shared(mt::fake_shared(queue)); producer = std::make_unique(*exchange_stream); consumer = std::make_unique(*exchange_stream); second_consumer = std::make_unique(*exchange_stream); third_consumer = std::make_unique(*exchange_stream); stream = exchange_stream; } else { ipc = std::make_shared(); auto submit_stream = std::make_shared( drop_policy, std::make_unique( mf::BufferStreamId{2}, std::make_shared(ipc), std::make_shared()), geom::Size{100,100}, mir_pixel_format_abgr_8888); auto weak_stream = std::weak_ptr(submit_stream); ipc->on_server_bound_transfer( [weak_stream](mp::Buffer& buffer) { auto submit_stream = weak_stream.lock(); if (!submit_stream) return; mtd::StubBuffer b(mg::BufferID{static_cast(buffer.buffer_id())}); submit_stream->swap_buffers(&b, [](mg::Buffer*){}); }); ipc->on_allocate( [weak_stream](geom::Size sz) { auto submit_stream = weak_stream.lock(); if (!submit_stream) return; submit_stream->allocate_buffer( mg::BufferProperties{sz, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware}); }); consumer = std::make_unique(submit_stream); second_consumer = std::make_unique(submit_stream); third_consumer = std::make_unique(submit_stream); producer = std::make_unique(ipc, std::get<0>(GetParam())); stream = submit_stream; } } void resize(geom::Size sz) { if (std::get<1>(GetParam()) == TestType::ExchangeSemantics) { stream->resize(sz); } else { producer->produce(); ipc->resize_event(sz); consumer->consume(); } } void set_scaling_delay(int delay) { if (std::get<1>(GetParam()) == TestType::ExchangeSemantics) queue.set_scaling_delay(delay); } void allow_framedropping() { consumer->set_framedropping(true); } void disallow_framedropping() { consumer->set_framedropping(false); } mtd::MockFrameDroppingPolicyFactory drop_policy; mtd::MockClientBufferFactory client_buffer_factory; mtd::StubBufferAllocator server_buffer_factory; mg::BufferProperties properties{geom::Size{3,3}, mir_pixel_format_abgr_8888, mg::BufferUsage::hardware}; int nbuffers = std::get<0>(GetParam()); mcl::ClientBufferDepository depository{mt::fake_shared(client_buffer_factory), nbuffers}; mc::BufferQueue queue{nbuffers, mt::fake_shared(server_buffer_factory), properties, drop_policy}; std::shared_ptr stream; std::shared_ptr ipc; std::unique_ptr producer; std::unique_ptr consumer; std::unique_ptr second_consumer; std::unique_ptr third_consumer; }; struct WithAnyNumberOfBuffers : BufferScheduling {}; struct WithTwoOrMoreBuffers : BufferScheduling {}; struct WithThreeOrMoreBuffers : BufferScheduling {}; struct WithOneBuffer : BufferScheduling {}; struct WithTwoBuffers : BufferScheduling {}; struct WithThreeBuffers : BufferScheduling {}; } /* Regression test for LP#1270964 */ TEST_P(WithAnyNumberOfBuffers, all_buffers_consumed_in_interleaving_pattern) { std::vector schedule = { {1_t, {producer.get()}, {}}, {60_t, {}, {consumer.get()}}, {61_t, {producer.get()}, {}}, {120_t, {}, {consumer.get()}}, {121_t, {producer.get()}, {}}, {180_t, {}, {consumer.get()}}, }; run_system(schedule); auto production_log = producer->production_log(); auto consumption_log = consumer->consumption_log(); EXPECT_THAT(production_log, Not(IsEmpty())); EXPECT_THAT(consumption_log, Not(IsEmpty())); EXPECT_THAT(consumption_log, ContainerEq(production_log)); } TEST_P(WithTwoOrMoreBuffers, framedropping_producers_dont_block) { allow_framedropping(); std::vector schedule = { {0_t, {producer.get()}, {}}, {61_t, {producer.get()}, {}}, {62_t, {producer.get()}, {}}, {63_t, {producer.get()}, {}}, {64_t, {producer.get()}, {}}, {90_t, {}, {consumer.get()}}, {91_t, {producer.get()}, {}}, {92_t, {producer.get()}, {}}, {93_t, {producer.get()}, {}}, {94_t, {producer.get()}, {}}, {120_t, {}, {consumer.get()}}, }; run_system(schedule); auto production_log = producer->production_log(); auto consumption_log = consumer->consumption_log(); ASSERT_THAT(production_log, SizeIs(9)); EXPECT_THAT(consumption_log, SizeIs(2)); } TEST_P(WithThreeOrMoreBuffers, synchronous_overproducing_producers_has_all_buffers_consumed) { std::vector schedule = { {1_t, {producer.get()}, {}}, {60_t, {}, {consumer.get()}}, {61_t, {producer.get()}, {}}, {62_t, {producer.get()}, {}}, {120_t, {}, {consumer.get()}}, {180_t, {}, {consumer.get()}}, }; run_system(schedule); auto production_log = producer->production_log(); auto consumption_log = consumer->consumption_log(); EXPECT_THAT(production_log, Not(IsEmpty())); EXPECT_THAT(consumption_log, Not(IsEmpty())); EXPECT_THAT(consumption_log, ContainerEq(production_log)); } /* Regression test for LP: #1210042 */ TEST_P(WithThreeOrMoreBuffers, consumers_dont_recycle_startup_buffer ) { std::vector schedule = { {1_t, {producer.get()}, {}}, {2_t, {producer.get()}, {}}, {3_t, {}, {consumer.get()}}, }; run_system(schedule); auto production_log = producer->production_log(); auto consumption_log = consumer->consumption_log(); ASSERT_THAT(production_log, SizeIs(2)); ASSERT_THAT(consumption_log, SizeIs(1)); EXPECT_THAT(consumption_log[0], Eq(production_log[0])); } TEST_P(WithTwoOrMoreBuffers, consumer_cycles_through_all_available_buffers) { auto tick = 0_t; std::vector schedule; for(auto i = 0; i < nbuffers - 1; i++) schedule.emplace_back(ScheduleEntry{tick++, {producer.get()}, {}}); run_system(schedule); for(auto i = 0; i < nbuffers; i++) schedule.emplace_back(ScheduleEntry{tick++, {producer.get()}, {consumer.get()}}); run_system(schedule); auto consumption_log = consumer->consumption_log(); std::sort(consumption_log.begin(), consumption_log.end(), [](BufferEntry const& a, BufferEntry const& b) { return a.id.as_value() > b.id.as_value(); }); auto it = std::unique(consumption_log.begin(), consumption_log.end()); consumption_log.erase(it, consumption_log.end()); EXPECT_THAT(consumption_log, SizeIs(nbuffers)); } TEST_P(WithAnyNumberOfBuffers, compositor_can_always_get_a_buffer) { std::vector schedule = { {0_t, {producer.get()}, {}}, {1_t, {}, {consumer.get()}}, {2_t, {}, {consumer.get()}}, {3_t, {}, {consumer.get()}}, {5_t, {}, {consumer.get()}}, {6_t, {}, {consumer.get()}}, }; run_system(schedule); auto consumption_log = consumer->consumption_log(); ASSERT_THAT(consumption_log, SizeIs(5)); } TEST_P(WithTwoOrMoreBuffers, compositor_doesnt_starve_from_slow_client) { std::vector schedule = { {0_t, {producer.get()}, {}}, {1_t, {}, {consumer.get()}}, {60_t, {}, {consumer.get()}}, {120_t, {}, {consumer.get()}}, {150_t, {producer.get()}, {}}, {180_t, {}, {consumer.get()}}, {240_t, {}, {consumer.get()}}, {270_t, {producer.get()}, {}}, {300_t, {}, {consumer.get()}}, {360_t, {}, {consumer.get()}}, }; run_system(schedule); auto consumption_log = consumer->consumption_log(); ASSERT_THAT(consumption_log, SizeIs(7)); EXPECT_THAT(consumption_log[1], Eq(consumption_log[0])); EXPECT_THAT(consumption_log[2], Eq(consumption_log[0])); EXPECT_THAT(consumption_log[4], Eq(consumption_log[3])); EXPECT_THAT(consumption_log[6], Eq(consumption_log[5])); } TEST_P(WithTwoOrMoreBuffers, multiple_consumers_are_in_sync) { std::vector schedule = { {0_t, {producer.get()}, {}}, {1_t, {}, {consumer.get()}}, {60_t, {}, {consumer.get(), second_consumer.get()}}, {119_t, {}, {consumer.get()}}, {120_t, {}, {second_consumer.get()}}, {130_t, {producer.get()}, {}}, {178_t, {}, {consumer.get()}}, {180_t, {}, {second_consumer.get()}}, {237_t, {}, {consumer.get()}}, {240_t, {}, {second_consumer.get()}}, }; run_system(schedule); auto production_log = producer->production_log(); auto consumption_log_1 = consumer->consumption_log(); auto consumption_log_2 = second_consumer->consumption_log(); ASSERT_THAT(consumption_log_1, SizeIs(5)); ASSERT_THAT(consumption_log_2, SizeIs(4)); ASSERT_THAT(production_log, SizeIs(2)); std::for_each(consumption_log_1.begin(), consumption_log_1.begin() + 3, [&](BufferEntry const& entry){ EXPECT_THAT(entry, Eq(production_log[0])); }); std::for_each(consumption_log_1.begin() + 3, consumption_log_1.end(), [&](BufferEntry const& entry){ EXPECT_THAT(entry, Eq(production_log[1])); }); std::for_each(consumption_log_2.begin(), consumption_log_2.begin() + 2, [&](BufferEntry const& entry){ EXPECT_THAT(entry, Eq(production_log[0])); }); std::for_each(consumption_log_2.begin() + 2, consumption_log_2.end(), [&](BufferEntry const& entry){ EXPECT_THAT(entry, Eq(production_log[1])); }); } TEST_P(WithThreeOrMoreBuffers, multiple_fast_compositors_are_in_sync) { std::vector schedule = { {0_t, {producer.get()}, {}}, {1_t, {producer.get()}, {}}, {60_t, {}, {consumer.get(), second_consumer.get()}}, {61_t, {}, {consumer.get(), second_consumer.get()}}, }; run_system(schedule); auto production_log = producer->production_log(); auto consumption_log_1 = consumer->consumption_log(); auto consumption_log_2 = second_consumer->consumption_log(); EXPECT_THAT(consumption_log_1, Eq(production_log)); EXPECT_THAT(consumption_log_2, Eq(production_log)); } TEST_P(WithTwoOrMoreBuffers, framedropping_clients_dont_block) { allow_framedropping(); std::vector schedule; for (auto i = 0; i < nbuffers * 3; i++) schedule.emplace_back(ScheduleEntry{1_t, {producer.get()}, {}}); run_system(schedule); auto production_log = producer->production_log(); auto block_count = std::count_if(production_log.begin(), production_log.end(), [](BufferEntry const& e) { return e.blockage == Access::blocked; }); EXPECT_THAT(block_count, Eq(0)); } TEST_P(WithTwoOrMoreBuffers, nonframedropping_client_throttles_to_compositor_rate) { unsigned int reps = 50; auto const expected_blocks = reps - nbuffers; std::vector schedule = { {1_t, {producer.get(), producer.get()}, {consumer.get()}}, }; repeat_system_until(schedule, [&reps]{ return --reps != 0; }); auto log = producer->production_log(); auto block_count = std::count_if(log.begin(), log.end(), [](BufferEntry const& e) { return e.blockage == Access::blocked; }); EXPECT_THAT(block_count, Ge(expected_blocks)); } TEST_P(WithAnyNumberOfBuffers, resize_affects_client_acquires_immediately) { unsigned int const sizes_to_test{4}; geom::Size new_size = properties.size; producer->produce(); consumer->consume(); for(auto i = 0u; i < sizes_to_test; i++) { new_size = new_size * 2; resize(new_size); std::vector schedule = {{1_t, {producer.get()}, {consumer.get()}}}; run_system(schedule); EXPECT_THAT(producer->last_size(), Eq(new_size)); } } TEST_P(WithAnyNumberOfBuffers, compositor_acquires_resized_frames) { unsigned int const sizes_to_test{4}; int const attempt_limit{100}; geom::Size new_size = properties.size; producer->produce(); for(auto i = 0u; i < sizes_to_test; i++) { new_size = new_size * 2; consumer->consume(); resize(new_size); std::vector schedule = { {1_t, {producer.get()}, {}}, {2_t, {}, {consumer.get()}}, {3_t, {producer.get()}, {}}, }; run_system(schedule); int attempt_count = 0; schedule = {{2_t, {}, {consumer.get()}}}; repeat_system_until(schedule, [&] { return (consumer->last_size() != new_size) && (attempt_count++ < attempt_limit); }); ASSERT_THAT(attempt_count, Lt(attempt_limit)) << "consumer never got the new size"; } } // Regression test for LP: #1396006 TEST_P(WithTwoOrMoreBuffers, framedropping_policy_never_drops_newest_frame) { for(auto i = 0; i < nbuffers; i++) producer->produce(); drop_policy.trigger_policies(); producer->produce(); auto production_log = producer->production_log(); ASSERT_THAT(production_log, SizeIs(nbuffers + 1)); EXPECT_THAT(production_log[nbuffers], Not(Eq(production_log[nbuffers - 1]))); } //TODO: (kdub) switch this test back to 2 buffers when we have timeout framedropping for NBS and nbuffers == 2 TEST_P(WithThreeOrMoreBuffers, client_is_unblocked_after_policy_is_triggered) { producer->produce(); consumer->consume(); for(auto i = 0; i < nbuffers; i++) producer->produce(); drop_policy.trigger_policies(); producer->produce(); auto production_log = producer->production_log(); ASSERT_THAT(production_log, SizeIs(nbuffers + 2)); EXPECT_THAT(production_log[nbuffers].blockage, Eq(Access::blocked)); EXPECT_THAT(production_log[nbuffers + 1].blockage, Eq(Access::unblocked)); } // Regression test for LP: #1319765 TEST_P(WithTwoBuffers, client_is_not_blocked_prematurely) { producer->produce(); auto a = stream->lock_compositor_buffer(this); producer->produce(); auto b = stream->lock_compositor_buffer(this); ASSERT_NE(a.get(), b.get()); a.reset(); producer->produce(); b.reset(); /* * Update to the original test case; This additional compositor acquire * represents the fixing of LP: #1395581 in the compositor logic. */ if (stream->buffers_ready_for_compositor(this)) stream->lock_compositor_buffer(this); // With the fix, a buffer will be available instantaneously: EXPECT_TRUE(producer->can_produce()); } // Extended regression test for LP: #1319765 TEST_P(WithTwoBuffers, composite_on_demand_never_deadlocks) { for (int i = 0; i < 100; ++i) { producer->produce(); auto a = stream->lock_compositor_buffer(this); producer->produce(); auto b = stream->lock_compositor_buffer(this); ASSERT_NE(a, b); a.reset(); producer->produce(); b.reset(); /* * Update to the original test case; This additional compositor acquire * represents the fixing of LP: #1395581 in the compositor logic. */ if (stream->buffers_ready_for_compositor(this)) stream->lock_compositor_buffer(this); EXPECT_TRUE(producer->can_produce()); consumer->consume(); consumer->consume(); } } // Regression test for LP: #1395581 TEST_P(WithTwoOrMoreBuffers, buffers_ready_is_not_underestimated) { // Produce frame 1 producer->produce(); // Acquire frame 1 auto a = stream->lock_compositor_buffer(this); // Produce frame 2 producer->produce(); // Acquire frame 2 auto b = stream->lock_compositor_buffer(this); // Release frame 1 a.reset(); // Produce frame 3 producer->produce(); // Release frame 2 b.reset(); // Verify frame 3 is ready for the first compositor EXPECT_THAT(stream->buffers_ready_for_compositor(this), Ge(1)); auto c = stream->lock_compositor_buffer(this); // Verify frame 3 is ready for a second compositor int const other_compositor_id = 0; ASSERT_THAT(stream->buffers_ready_for_compositor(&other_compositor_id), Ge(1)); c.reset(); } TEST_P(WithTwoOrMoreBuffers, buffers_ready_eventually_reaches_zero) { const int nmonitors = 3; std::array consumers { { consumer.get(), second_consumer.get(), third_consumer.get() } }; for (auto const& consumer : consumers) EXPECT_EQ(0, stream->buffers_ready_for_compositor(consumer)); producer->produce(); for (auto consumer : consumers) { ASSERT_NE(0, stream->buffers_ready_for_compositor(consumer)); // Multi-consume to account for the extra that // buffers_ready_for_compositor adds to do dynamic performance // detection. int const max_extra_scheduling = 50; for (int c = 0; c < max_extra_scheduling; ++c) consumer->consume(); ASSERT_EQ(0, stream->buffers_ready_for_compositor(consumer)); } } TEST_P(WithTwoOrMoreBuffers, clients_get_new_buffers_on_compositor_release) { // Regression test for LP: #1480164 mtd::MockFrameDroppingPolicyFactory policy_factory; mc::BufferQueue queue{nbuffers, mt::fake_shared(server_buffer_factory), properties, policy_factory}; queue.allow_framedropping(false); mg::Buffer* client_buffer = nullptr; auto callback = [&](mg::Buffer* buffer) { client_buffer = buffer; }; auto client_try_acquire = [&]() -> bool { queue.client_acquire(callback); return client_buffer != nullptr; }; auto client_release = [&]() { EXPECT_TRUE(client_buffer); queue.client_release(client_buffer); client_buffer = nullptr; }; // Skip over the first frame. The early release optimization is too // conservative to allow it to happen right at the start (so as to // maintain correct multimonitor frame rates if required). ASSERT_TRUE(client_try_acquire()); client_release(); queue.compositor_release(queue.compositor_acquire(this)); auto onscreen = queue.compositor_acquire(this); bool blocking; do { blocking = !client_try_acquire(); if (!blocking) client_release(); } while (!blocking); int throttled_count = 0; for (int f = 0; f < 100; ++f) { ASSERT_FALSE(client_buffer); queue.compositor_release(onscreen); if (client_buffer) { // This should always happen if dynamic queue scaling is disabled... client_release(); onscreen = queue.compositor_acquire(this); client_try_acquire(); throttled_count = 0; } else { ASSERT_THAT(queue.scaling_delay(), Ge(0)); ++throttled_count; ASSERT_THAT(throttled_count, Le(nbuffers)); onscreen = queue.compositor_acquire(this); } } } TEST_P(WithTwoOrMoreBuffers, short_buffer_holds_dont_overclock_multimonitor) { // Regression test related to LP: #1480164 mtd::MockFrameDroppingPolicyFactory policy_factory; mc::BufferQueue queue{nbuffers, mt::fake_shared(server_buffer_factory), properties, policy_factory}; queue.allow_framedropping(false); const void* const leftid = "left"; const void* const rightid = "right"; mg::Buffer* client_buffer = nullptr; auto callback = [&](mg::Buffer* buffer) { client_buffer = buffer; }; auto client_try_acquire = [&]() -> bool { queue.client_acquire(callback); return client_buffer != nullptr; }; auto client_release = [&]() { EXPECT_TRUE(client_buffer); queue.client_release(client_buffer); client_buffer = nullptr; }; // Skip over the first frame. The early release optimization is too // conservative to allow it to happen right at the start (so as to // maintain correct multimonitor frame rates if required). ASSERT_TRUE(client_try_acquire()); client_release(); queue.compositor_release(queue.compositor_acquire(leftid)); auto left = queue.compositor_acquire(leftid); auto right = queue.compositor_acquire(rightid); bool blocking; do { blocking = !client_try_acquire(); if (!blocking) client_release(); } while (!blocking); for (int f = 0; f < 100; ++f) { // These two assertions are the important ones ASSERT_FALSE(client_buffer); queue.compositor_release(left); queue.compositor_release(right); ASSERT_FALSE(client_buffer); left = queue.compositor_acquire(leftid); right = queue.compositor_acquire(rightid); // Note: This is only reliably true when queue scaling is disabled: if (client_buffer) { client_release(); client_try_acquire(); } // else dynamic queue scaling is throttling us. } } TEST_P(WithThreeBuffers, gives_compositor_a_valid_buffer_after_dropping_old_buffers_without_clients) { producer->produce(); stream->drop_old_buffers(); consumer->consume(); EXPECT_THAT(consumer->consumption_log(), SizeIs(1)); } TEST_P(WithThreeBuffers, gives_new_compositor_the_newest_buffer_after_dropping_old_buffers) { producer->produce(); consumer->consume(); producer->produce(); stream->drop_old_buffers(); second_consumer->consume(); auto production_log = producer->production_log(); auto consumption_log = consumer->consumption_log(); auto second_consumption_log = second_consumer->consumption_log(); ASSERT_THAT(production_log, SizeIs(2)); ASSERT_THAT(consumption_log, SizeIs(1)); ASSERT_THAT(second_consumption_log, SizeIs(1)); EXPECT_THAT(production_log[0], Eq(consumption_log[0])); EXPECT_THAT(production_log[1], Eq(second_consumption_log[0])); } TEST_P(WithTwoOrMoreBuffers, overlapping_compositors_get_different_frames) { // This test simulates bypass behaviour // overlay/bypass code will need to acquire two buffers at once, as there's a brief period of time where a buffer // is onscreen, and the compositor has to arrange for the next buffer to swap in. auto const num_simultaneous_consumptions = 2u; auto num_iterations = 20u; std::array, num_simultaneous_consumptions> compositor_resources; for (auto i = 0u; i < num_iterations; i++) { // One of the compositors (the oldest one) gets a new buffer... int oldest = i & 1; compositor_resources[oldest].reset(); producer->produce(); compositor_resources[oldest] = consumer->consume_resource(); } // Two compositors acquired, and they're always different... auto log = consumer->consumption_log(); for(auto i = 0u; i < log.size() - 1; i++) EXPECT_THAT(log[i].id, Ne(log[i+1].id)); } // Regression test LP: #1241369 / LP: #1241371 // Test that a triple buffer or higher client can always provide a relatively up-to-date frame // when its producing the buffer around the frame deadline TEST_P(WithThreeOrMoreBuffers, slow_client_framerate_matches_compositor) { // BufferQueue can only satify this for nbuffers >= 3 // since a client can only own up to nbuffers - 1 at any one time auto const iterations = 10u; disallow_framedropping(); //fill up queue at first for(auto i = 0; i < nbuffers - 1; i++) producer->produce(); //a schedule that would block once per iteration for double buffering, but only once for >3 buffers std::vector schedule = { {0_t, {}, {consumer.get()}}, {59_t, {producer.get()}, {}}, {60_t, {}, {consumer.get()}}, {120_t, {}, {consumer.get()}}, {121_t, {producer.get()}, {}}, {179_t, {producer.get()}, {}}, {180_t, {}, {consumer.get()}}, {240_t, {}, {consumer.get()}}, {241_t, {producer.get()}, {}}, {300_t, {}, {consumer.get()}}, }; auto count = 0u; repeat_system_until(schedule, [&]{ return count++ < schedule.size() * iterations; }); auto log = producer->production_log(); auto blockages = std::count_if(log.begin(), log.end(), [](BufferEntry const& e){ return e.blockage == Access::blocked; }); EXPECT_THAT(blockages, Le(1)); } //regression test for LP: #1396006, LP: #1379685 TEST_P(WithTwoOrMoreBuffers, framedropping_surface_never_drops_newest_frame) { allow_framedropping(); for (int f = 0; f < nbuffers; ++f) producer->produce(); for (int n = 0; n < nbuffers - 1; ++n) consumer->consume(); // Ensure it's not the newest frame that gets dropped to satisfy the // client. producer->produce(); consumer->consume(); // The queue could solve this problem a few ways. It might choose to // defer framedropping till it's safe, or even allocate additional // buffers. We don't care which, just verify it's not losing the // latest frame. Because the screen could be indefinitely out of date // if that happens... auto producer_log = producer->production_log(); auto consumer_log = consumer->consumption_log(); EXPECT_TRUE(!producer->can_produce() || (!producer_log.empty() && !consumer_log.empty() && producer_log.back() == consumer_log.back())); } /* Regression test for LP: #1306464 */ TEST_P(WithThreeBuffers, framedropping_client_acquire_does_not_block_when_no_available_buffers) { allow_framedropping(); producer->produce(); /* The client can never own this acquired buffer */ auto comp_buffer = consumer->consume_resource(); /* Let client release all possible buffers so they go into * the ready queue */ for (int i = 0; i < nbuffers; ++i) { producer->produce(); EXPECT_THAT(comp_buffer->id(), Ne(producer->current_id())); } /* Let the compositor acquire all ready buffers */ for (int i = 0; i < nbuffers; ++i) consumer->consume(); /* At this point the queue has 0 free buffers and 0 ready buffers * so the next client request should not be satisfied until * a compositor releases its buffers */ /* ... unless the BufferQueue is overallocating. In that case it will * have succeeding in acquiring immediately. */ EXPECT_TRUE(producer->can_produce()); } TEST_P(WithTwoOrMoreBuffers, client_never_owns_compositor_buffers_and_vice_versa) { producer->produce(); for (int i = 0; i < 100; ++i) { auto buffer = consumer->consume_resource(); producer->produce(); EXPECT_THAT(buffer->id(), Ne(producer->current_id())); } } /* Regression test for an issue brought up at: * http://code.launchpad.net/~albaguirre/mir/ * alternative-switching-bundle-implementation/+merge/216606/comments/517048 */ TEST_P(WithThreeOrMoreBuffers, buffers_are_not_lost) { // This test is technically not valid with dynamic queue scaling on // BufferQueue specific setup set_scaling_delay(-1); const int nmonitors = 2; std::array consumers { { consumer.get(), second_consumer.get(), } }; producer->produce(); /* Hold a reference to current compositor buffer*/ auto comp_buffer1 = consumers[0]->consume_resource(); while (producer->can_produce()) producer->produce(); /* Have a second compositor advance the current compositor buffer at least twice */ for (int acquires = 0; acquires < nbuffers; ++acquires) consumers[1]->consume(); comp_buffer1.reset(); /* An async client should still be able to cycle through all the available * buffers. "async" means any pattern other than "produce,consume,..." */ int const max_ownable_buffers = nbuffers - 1; producer->reset_log(); for (int frame = 0; frame < max_ownable_buffers * 2; frame++) { for (int drain = 0; drain < nbuffers; ++drain) consumers[0]->consume(); while (producer->can_produce()) producer->produce(); } EXPECT_THAT(unique_ids_in(producer->production_log()), Eq(nbuffers)); } // Test that dynamic queue scaling/throttling actually works TEST_P(WithThreeOrMoreBuffers, queue_size_scales_with_client_performance) { //BufferQueue specific for now int const discard = 3; queue.set_scaling_delay(discard); for (int frame = 0; frame < 20; frame++) { producer->produce(); consumer->consume(); } // Expect double-buffers as the steady state for fast clients auto log = producer->production_log(); ASSERT_THAT(log.size(), Gt(discard)); // avoid the below erase crashing log.erase(log.begin(), log.begin() + discard); EXPECT_THAT(unique_ids_in(log), Eq(2)); producer->reset_log(); //put server-side pressure on the buffer count std::shared_ptr a; std::shared_ptr b; producer->produce(); producer->produce(); for (int frame = 0; frame < 20; frame++) { producer->produce(); a = consumer->consume_resource(); b = consumer->consume_resource(); EXPECT_THAT(a, Ne(b)); } a.reset(); b.reset(); log = producer->production_log(); EXPECT_THAT(unique_ids_in(log), Ge(3)); producer->reset_log(); // And what happens if the client becomes fast again?... for (int frame = 0; frame < 20; frame++) { producer->produce(); consumer->consume(); } // Expect double-buffers as the steady state for fast clients log = producer->production_log(); log.erase(log.begin(), log.begin() + discard); EXPECT_THAT(unique_ids_in(log), Eq(2)); } //NOTE: compositors need 2 buffers in overlay/bypass cases, as they //briefly need to arrange the next buffer while the previous one is still held onscreen TEST_P(WithThreeOrMoreBuffers, greedy_compositors_scale_to_triple_buffers) { /* * "Greedy" compositors means those that can hold multiple buffers from * the same client simultaneously or a single buffer for a long time. * This usually means bypass/overlays, but can also mean multi-monitor. */ std::shared_ptr first; std::shared_ptr second; producer->produce(); producer->produce(); for (auto i = 0u; i < 100u; i++) { first = consumer->consume_resource(); second = consumer->consume_resource(); EXPECT_THAT(first, Ne(second)); producer->produce(); } EXPECT_THAT(unique_ids_in(producer->production_log()), Ge(3)); } TEST_P(WithAnyNumberOfBuffers, can_snapshot_repeatedly_without_blocking) { producer->produce(); consumer->consume(); auto const num_snapshots = nbuffers * 2u; std::vector snaps(num_snapshots); for(auto i = 0u; i < num_snapshots; i++) { stream->with_most_recent_buffer_do([i, &snaps](mg::Buffer& buffer) { snaps[i] = buffer.id(); }); } auto production_log = producer->production_log(); ASSERT_THAT(production_log, SizeIs(1)); EXPECT_THAT(snaps, Each(production_log.back().id)); } int const max_buffers_to_test{5}; INSTANTIATE_TEST_CASE_P( BufferScheduling, WithAnyNumberOfBuffers, Combine(Range(2, max_buffers_to_test), Values(TestType::ExchangeSemantics, TestType::SubmitSemantics))); INSTANTIATE_TEST_CASE_P( BufferScheduling, WithTwoOrMoreBuffers, Combine(Range(2, max_buffers_to_test), Values(TestType::ExchangeSemantics, TestType::SubmitSemantics))); INSTANTIATE_TEST_CASE_P( BufferScheduling, WithThreeOrMoreBuffers, Combine(Range(3, max_buffers_to_test), Values(TestType::ExchangeSemantics, TestType::SubmitSemantics))); INSTANTIATE_TEST_CASE_P( BufferScheduling, WithTwoBuffers, Combine(Values(2), Values(TestType::ExchangeSemantics, TestType::SubmitSemantics))); INSTANTIATE_TEST_CASE_P( BufferScheduling, WithThreeBuffers, Combine(Values(3), Values(TestType::ExchangeSemantics, TestType::SubmitSemantics))); ./tests/integration-tests/test_session.cpp0000644000015600001650000001227412676616125021172 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/default_server_configuration.h" #include "src/server/input/null_input_manager.h" #include "mir/compositor/compositor.h" #include "src/server/scene/application_session.h" #include "src/server/scene/pixel_buffer.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/scene/null_session_listener.h" #include "mir/compositor/buffer_stream.h" #include "mir/compositor/renderer.h" #include "mir/compositor/renderer_factory.h" #include "mir/frontend/connector.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/stub_gl_buffer.h" #include "mir/test/doubles/stub_buffer_stream_factory.h" #include "mir/test/doubles/stub_display.h" #include "mir/test/doubles/null_event_sink.h" #include "mir/test/doubles/stub_renderer.h" #include "mir/test/doubles/stub_surface_factory.h" #include "mir/test/doubles/null_pixel_buffer.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir_test_framework/stubbed_server_configuration.h" #include #include #include namespace mc = mir::compositor; namespace mtd = mir::test::doubles; namespace ms = mir::scene; namespace msh = mir::shell; namespace mi = mir::input; namespace mf = mir::frontend; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { struct TestServerConfiguration : public mir_test_framework::StubbedServerConfiguration { std::shared_ptr the_buffer_allocator() override { return buffer_allocator( [] { return std::make_shared(); }); } }; void swap_buffers_blocking(mf::Surface& surf, mg::Buffer*& buffer) { std::mutex mutex; std::condition_variable cv; bool done = false; surf.primary_buffer_stream()->swap_buffers(buffer, [&](mg::Buffer* new_buffer) { std::unique_lock lock(mutex); buffer = new_buffer; done = true; cv.notify_one(); }); std::unique_lock lock(mutex); cv.wait(lock, [&]{ return done; }); } struct StubGLBufferStream : public mtd::StubBufferStream { public: StubGLBufferStream() { stub_compositor_buffer = std::make_shared(); } }; struct StubGLBufferStreamFactory : public mtd::StubBufferStreamFactory { std::shared_ptr create_buffer_stream( mf::BufferStreamId, std::shared_ptr const&, mg::BufferProperties const&) override { return std::make_shared(); } }; } // anonymouse namespace TEST(ApplicationSession, stress_test_take_snapshot) { TestServerConfiguration conf; // Otherwise the input registrar won't function auto dispatcher = conf.the_input_dispatcher(); ms::ApplicationSession session{ conf.the_surface_stack(), conf.the_surface_factory(), std::make_shared(), __LINE__, "stress", conf.the_snapshot_strategy(), std::make_shared(), mtd::StubDisplayConfig{}, std::make_shared() }; session.create_surface(ms::a_surface(), std::make_shared()); auto compositor = conf.the_compositor(); compositor->start(); session.default_surface()->configure(mir_surface_attrib_swapinterval, 0); std::thread client_thread{ [&session] { mg::Buffer* buffer{nullptr}; for (int i = 0; i < 500; ++i) { auto surface = session.default_surface(); swap_buffers_blocking(*surface, buffer); std::this_thread::sleep_for(std::chrono::microseconds{50}); } }}; std::thread snapshot_thread{ [&session] { for (int i = 0; i < 500; ++i) { std::atomic snapshot_taken1{false}; std::atomic snapshot_taken2{false}; session.take_snapshot( [&](ms::Snapshot const&) { snapshot_taken1 = true; }); session.take_snapshot( [&](ms::Snapshot const&) { snapshot_taken2 = true; }); while (!snapshot_taken1 || !snapshot_taken2) std::this_thread::sleep_for(std::chrono::microseconds{50}); } }}; client_thread.join(); snapshot_thread.join(); compositor->stop(); } ./tests/integration-tests/session_management.cpp0000644000015600001650000000715612676616125022332 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/frontend/session.h" #include "mir/frontend/shell.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/scene/session.h" #include "mir/shell/focus_controller.h" #include "mir/scene/surface.h" #include "src/server/scene/surface_stack.h" #include "mir/test/doubles/null_event_sink.h" #include "mir_test_framework/stubbed_server_configuration.h" #include #include #include namespace mf = mir::frontend; namespace mo = mir::options; namespace ms = mir::scene; namespace msh = mir::shell; namespace mtd = mir::test::doubles; using namespace testing; namespace { struct TestSurfaceStack : public ms::SurfaceStack { using ms::SurfaceStack::SurfaceStack; MOCK_METHOD2(add_surface, void( std::shared_ptr const& surface, mir::input::InputReceptionMode input_mode)); MOCK_METHOD1(raise, void( std::weak_ptr const& surface)); void default_add_surface( std::shared_ptr const& surface, mir::input::InputReceptionMode input_mode) { ms::SurfaceStack::add_surface(surface, input_mode); } void default_raise(std::weak_ptr const& surface) { ms::SurfaceStack::raise(surface); } }; struct TestConfiguration : public mir_test_framework::StubbedServerConfiguration { std::shared_ptr the_surface_stack() override { return scene_surface_stack( [this]() { auto const scene_report = the_scene_report(); test_surface_stack = std::make_shared(scene_report); return test_surface_stack; }); } std::shared_ptr test_surface_stack; }; struct SessionManagement : Test { TestConfiguration builder; std::shared_ptr const event_sink = std::make_shared(); std::shared_ptr const session_manager = builder.the_frontend_shell(); std::shared_ptr const& test_surface_stack = builder.test_surface_stack; ms::SurfaceCreationParameters const params = ms::SurfaceCreationParameters().of_size(100,100).of_type(mir_surface_type_normal); void SetUp() { ASSERT_THAT(test_surface_stack, Ne(nullptr)); ON_CALL(*test_surface_stack, add_surface(_,_)) .WillByDefault(Invoke(test_surface_stack.get(), &TestSurfaceStack::default_add_surface)); ON_CALL(*test_surface_stack, raise(_)) .WillByDefault(Invoke(test_surface_stack.get(), &TestSurfaceStack::default_raise)); } }; MATCHER_P(WeakPtrTo, p, "") { return arg.lock() == p; } } TEST_F(SessionManagement, creating_a_surface_adds_it_to_scene) { auto const session = session_manager->open_session(0, __PRETTY_FUNCTION__, event_sink); EXPECT_CALL(*test_surface_stack, add_surface(_,_)).Times(1); session_manager->create_surface(session, params, event_sink); } ./tests/integration-tests/CMakeLists.txt0000644000015600001650000000666412676616157020517 0ustar jenkinsjenkinsinclude(CMakeDependentOption) include_directories( ${CMAKE_SOURCE_DIR} ${PROTOBUF_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/include/cookie ${PROJECT_SOURCE_DIR}/src/include/platform ${PROJECT_SOURCE_DIR}/src/include/cookie ${PROJECT_SOURCE_DIR}/src/include/common ${PROJECT_SOURCE_DIR}/src/include/server ${PROJECT_SOURCE_DIR}/src/include/client ${PROJECT_SOURCE_DIR}/src/include/gl ) protobuf_generate_cpp( GENERATED_PROTOBUF_SRCS GENERATED_PROTOBUF_HDRS test_protobuf.proto ) set( INTEGRATION_TESTS_SRCS test_focus_selection.cpp test_touchspot_visualization.cpp test_surface_stack_with_compositor.cpp test_buffer_scheduling.cpp test_client_screencast.cpp test_large_messages.cpp test_protobuf.cpp test_surfaceloop.cpp test_stale_frames.cpp test_test_framework.cpp test_error_reporting.cpp test_exchange_buffer.cpp test_display_info.cpp test_display_server_main_loop_events.cpp test_macros.cpp test_surface_first_frame_sync.cpp test_swapinterval.cpp test_server_client_types.cpp test_server_shutdown.cpp test_session.cpp session_management.cpp surface_composition.cpp ${GENERATED_PROTOBUF_SRCS} ${GENERATED_PROTOBUF_HDRS} ) add_subdirectory(client/) add_subdirectory(compositor/) add_subdirectory(shell/) add_subdirectory(process/) add_subdirectory(input/) link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) mir_add_wrapped_executable( mir_integration_tests ${INTEGRATION_TESTS_SRCS} ${MIR_SERVER_OBJECTS} ${MIR_PLATFORM_OBJECTS} ${MIR_COMMON_OBJECTS} ) mir_precompiled_header(mir_integration_tests ${CMAKE_CURRENT_SOURCE_DIR}/precompiled.hpp) add_dependencies(mir_integration_tests GMock) uses_android_input(mir_integration_tests) target_link_libraries( mir_integration_tests mir-test-static mir-test-framework-static mir-test-doubles-static mirclient-debug-extension mirclient-static mirdraw mirclientrpc-static mirclientlttng-static ${PROTOBUF_LITE_LIBRARIES} ${Boost_LIBRARIES} ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY} # Mesa platform dependencies ${DRM_LDFLAGS} ${DRM_LIBRARIES} ${GBM_LDFLAGS} ${GBM_LIBRARIES} # Android platform dependencies ${LIBHARDWARE_LIBRARIES} ${ANDROID_PROPERTIES_LIBRARIES} # Shared platform dependencies ${EGL_LDFLAGS} ${EGL_LIBRARIES} ${GLESv2_LDFLAGS} ${GLESv2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} # Link in pthread. ${LIBHARDWARE_LIBRARIES} ${MIR_PLATFORM_REFERENCES} ${MIR_COMMON_REFERENCES} ${MIR_SERVER_REFERENCES} ) if (MIR_TEST_PLATFORM STREQUAL "mesa-kms" OR MIR_TEST_PLATFORM STREQUAL "mesa-x11") target_link_libraries(mir_integration_tests mirsharedmesaservercommon-static ${GBM_LDFLAGS} ${GBM_LIBRARIES} ${DRM_LDFLAGS} ${DRM_LIBRARIES} ) endif() if (MIR_TEST_PLATFORM STREQUAL "android") target_link_libraries(mir_integration_tests mirsharedandroid-static ${ANDROID_PROPERTIES_LDFLAGS} ) endif() CMAKE_DEPENDENT_OPTION( MIR_RUN_INTEGRATION_TESTS "Run integration tests as part of default testing" ON "MIR_BUILD_INTEGRATION_TESTS" OFF) if(MIR_TEST_PLATFORM STREQUAL "mesa-kms" OR MIR_TEST_PLATFORM STREQUAL "mesa-x11") include_directories( ${DRM_INCLUDE_DIRS} ${GBM_INCLUDE_DIRS} ${EGL_INCLUDE_DIRS} ${GLESv2_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ) add_subdirectory(graphics/mesa) endif() if (MIR_RUN_INTEGRATION_TESTS) mir_discover_tests_with_fd_leak_detection(mir_integration_tests) endif (MIR_RUN_INTEGRATION_TESTS) ./tests/integration-tests/graphics/0000755000015600001650000000000012676616160017535 5ustar jenkinsjenkins./tests/integration-tests/graphics/mesa/0000755000015600001650000000000012676616126020464 5ustar jenkinsjenkins./tests/integration-tests/graphics/mesa/test_buffer_integration.cpp0000644000015600001650000001510712676616125026106 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/buffer_basic.h" #include "mir/graphics/buffer_id.h" #include "mir/graphics/buffer_properties.h" #include "mir/options/configuration.h" #include "mir/options/option.h" #include "mir/test/doubles/stub_buffer.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/null_platform.h" #include "mir/test/doubles/stub_gl_config.h" #include "mir/test/doubles/stub_gl_program_factory.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/graphics/default_display_configuration_policy.h" #include "mir/renderer/gl/texture_source.h" #include "src/server/report/null_report_factory.h" #include "mir_test_framework/testing_server_configuration.h" #include #include #include namespace mc = mir::compositor; namespace geom = mir::geometry; namespace mg = mir::graphics; namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mr = mir::report; namespace mir { mir::renderer::gl::TextureSource* as_texture_source( std::shared_ptr const& buffer) { return dynamic_cast( buffer->native_buffer_base()); } class StubBufferThread : public mtd::StubBuffer, public mir::renderer::gl::TextureSource { public: StubBufferThread() : creation_thread_id{std::this_thread::get_id()} {} void gl_bind_to_texture() override { /* * If we are trying to bind the texture from a different thread from * the one used to create the buffer (i.e. a thread in which the * display is not supposed to be configured), force an EGL error to * make the tests happy. */ if (std::this_thread::get_id() != creation_thread_id) { eglInitialize(0, 0, 0); throw std::runtime_error("Binding to texture failed"); } } void bind() override { gl_bind_to_texture(); } void secure_for_render() override {} private: std::thread::id creation_thread_id; }; class StubGraphicBufferAllocator : public mtd::StubBufferAllocator { public: std::shared_ptr alloc_buffer(mg::BufferProperties const&) override { return std::make_shared(); } }; class StubGraphicPlatform : public mtd::NullPlatform { public: mir::UniqueModulePtr create_buffer_allocator() override { return mir::make_module_ptr(); } }; class MesaBufferIntegration : public ::testing::Test { protected: virtual void SetUp() { platform = std::make_shared(); auto conf_policy = std::make_shared(); display = platform->create_display( conf_policy, std::make_shared()); allocator = platform->create_buffer_allocator(); size = geom::Size{100, 100}; pf = mir_pixel_format_abgr_8888; usage = mg::BufferUsage::hardware; buffer_properties = mg::BufferProperties{size, pf, usage}; } std::shared_ptr platform; std::shared_ptr display; std::shared_ptr allocator; geom::Size size; MirPixelFormat pf; mg::BufferUsage usage; mg::BufferProperties buffer_properties; }; struct BufferCreatorThread { BufferCreatorThread(const std::shared_ptr& allocator, mg::BufferProperties const& buffer_properties) : allocator{allocator}, buffer_properties{buffer_properties} { } void operator()() { using namespace testing; buffer = allocator->alloc_buffer(buffer_properties); } std::shared_ptr allocator; std::shared_ptr buffer; mg::BufferProperties buffer_properties; }; struct BufferDestructorThread { BufferDestructorThread(std::shared_ptr buffer) : buffer{std::move(buffer)} { } void operator()() { using namespace testing; buffer.reset(); ASSERT_EQ(EGL_SUCCESS, eglGetError()); } std::shared_ptr buffer; }; struct BufferTextureInstantiatorThread { BufferTextureInstantiatorThread(const std::shared_ptr& buffer) : buffer(buffer), exception_thrown(false) { } void operator()() { using namespace testing; try { as_texture_source(buffer)->gl_bind_to_texture(); } catch(std::runtime_error const&) { exception_thrown = true; } ASSERT_NE(EGL_SUCCESS, eglGetError()); } const std::shared_ptr& buffer; bool exception_thrown; }; TEST_F(MesaBufferIntegration, buffer_creation_from_arbitrary_thread_works) { using namespace testing; EXPECT_NO_THROW({ BufferCreatorThread creator(allocator, buffer_properties); std::thread t{std::ref(creator)}; t.join(); ASSERT_TRUE(creator.buffer.get() != 0); }); } TEST_F(MesaBufferIntegration, buffer_destruction_from_arbitrary_thread_works) { using namespace testing; EXPECT_NO_THROW({ auto buffer = allocator->alloc_buffer(buffer_properties); as_texture_source(buffer)->gl_bind_to_texture(); ASSERT_EQ(EGL_SUCCESS, eglGetError()); BufferDestructorThread destructor{std::move(buffer)}; std::thread t{std::ref(destructor)}; t.join(); }); } TEST_F(MesaBufferIntegration, buffer_lazy_texture_instantiation_from_arbitrary_thread_fails) { using namespace testing; EXPECT_NO_THROW({ auto buffer = allocator->alloc_buffer(buffer_properties); BufferTextureInstantiatorThread texture_instantiator{buffer}; std::thread t{std::ref(texture_instantiator)}; t.join(); ASSERT_TRUE(texture_instantiator.exception_thrown); }); } } ./tests/integration-tests/graphics/mesa/CMakeLists.txt0000644000015600001650000000451312676616125023226 0ustar jenkinsjenkinsif (MIR_BUILD_PLATFORM_MESA_KMS) ########################################################################## # mir_integration_tests_mesa-kms ########################################################################## mir_add_wrapped_executable( mir_integration_tests_mesa-kms ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_integration.cpp ${MIR_SERVER_OBJECTS} ${MIR_PLATFORM_OBJECTS} ${MIR_COMMON_OBJECTS} $ ) add_dependencies(mir_integration_tests_mesa-kms GMock) target_link_libraries( mir_integration_tests_mesa-kms mir-test-static mir-test-framework-static mir-test-doubles-static mirclient-static mirclientrpc-static mirclientlttng-static mirsharedmesaservercommon-static ${PROTOBUF_LITE_LIBRARIES} # Mesa platform dependencies ${DRM_LDFLAGS} ${DRM_LIBRARIES} ${GBM_LDFLAGS} ${GBM_LIBRARIES} ) if (MIR_RUN_INTEGRATION_TESTS) mir_discover_tests_with_fd_leak_detection(mir_integration_tests_mesa-kms) endif (MIR_RUN_INTEGRATION_TESTS) endif() if (MIR_BUILD_PLATFORM_MESA_X11) ########################################################################## # mir_integration_tests_mesa-x11 ########################################################################## mir_add_wrapped_executable( mir_integration_tests_mesa-x11 ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_integration.cpp ${MIR_SERVER_OBJECTS} ${MIR_PLATFORM_OBJECTS} ${MIR_COMMON_OBJECTS} $ $ ) add_dependencies(mir_integration_tests_mesa-x11 GMock) target_link_libraries( mir_integration_tests_mesa-x11 mir-test-static mir-test-framework-static mir-test-doubles-static mirclient-static mirclientrpc-static mirclientlttng-static mirsharedmesaservercommon-static ${PROTOBUF_LITE_LIBRARIES} # Mesa platform dependencies ${DRM_LDFLAGS} ${DRM_LIBRARIES} ${GBM_LDFLAGS} ${GBM_LIBRARIES} X11 ) if (MIR_RUN_INTEGRATION_TESTS) mir_discover_tests_with_fd_leak_detection(mir_integration_tests_mesa-x11) endif (MIR_RUN_INTEGRATION_TESTS) endif() ./tests/integration-tests/client/0000755000015600001650000000000012676616126017215 5ustar jenkinsjenkins./tests/integration-tests/client/test_mirsurface.cpp0000644000015600001650000001775512676616125023136 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #include "mir_test_framework/connected_client_headless_server.h" #include "mir/test/doubles/stub_scene_surface.h" #include "mir/test/fake_shared.h" #include "mir/test/validity_matchers.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/shell/shell_wrapper.h" #include "src/client/mir_surface.h" #include "src/include/common/mir/raii.h" #include namespace ms = mir::scene; namespace mf = mir::frontend; namespace mg = mir::graphics; namespace msh = mir::shell; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; using namespace testing; namespace { struct MockShell : msh::ShellWrapper { using msh::ShellWrapper::ShellWrapper; MOCK_METHOD3(create_surface, mf::SurfaceId( std::shared_ptr const&, ms::SurfaceCreationParameters const&, std::shared_ptr const&)); }; bool parent_field_matches(ms::SurfaceCreationParameters const& params, mir::optional_value const& surf) { if (!surf.is_set()) return !params.parent_id.is_set(); return surf.value()->id() == params.parent_id.value().as_value(); } MATCHER_P(MatchesRequired, spec, "") { return spec->width == arg.size.width.as_int() && spec->height == arg.size.height.as_int() && spec->pixel_format == arg.pixel_format && spec->buffer_usage == static_cast(arg.buffer_usage); } MATCHER_P(MatchesOptional, spec, "") { return spec->surface_name == arg.name && spec->state == arg.state && spec->type == arg.type && spec->pref_orientation == arg.preferred_orientation && spec->output_id == static_cast(arg.output_id.as_value()) && parent_field_matches(arg, spec->parent); } MATCHER(IsAMenu, "") { return arg.type == mir_surface_type_menu; } MATCHER(IsATooltip, "") { return arg.type == mir_surface_type_tip; } MATCHER(IsADialog, "") { return arg.type == mir_surface_type_dialog; } MATCHER_P(HasParent, parent, "") { return arg.parent_id.is_set() && arg.parent_id.value().as_value() == parent->id(); } MATCHER(NoParentSet, "") { return arg.parent_id.is_set() == false; } MATCHER_P(MatchesAuxRect, rect, "") { return arg.aux_rect.is_set() && arg.aux_rect.value().top_left.x.as_int() == rect.left && arg.aux_rect.value().top_left.y.as_int() == rect.top && arg.aux_rect.value().size.width.as_uint32_t() == rect.width && arg.aux_rect.value().size.height.as_uint32_t() == rect.height; } MATCHER_P(MatchesEdge, edge, "") { return arg.edge_attachment.is_set() && arg.edge_attachment.value() == edge; } } struct ClientMirSurface : mtf::ConnectedClientHeadlessServer { void SetUp() override { server.wrap_shell([this] (std::shared_ptr const& wrapped) { wrapped_shell = wrapped; auto const msc = std::make_shared(wrapped); mock_shell = msc; return msc; }); mtf::ConnectedClientHeadlessServer::SetUp(); server.the_surface_stack(); ON_CALL(*mock_shell, create_surface(_, _, _)) .WillByDefault(Invoke(wrapped_shell.get(), &msh::Shell::create_surface)); spec.connection = connection; spec.buffer_usage = mir_buffer_usage_software; spec.surface_name = "test_surface"; spec.output_id = mir_display_output_id_invalid; spec.type = mir_surface_type_dialog; spec.state = mir_surface_state_minimized; spec.pref_orientation = mir_orientation_mode_landscape; } std::unique_ptr> create_surface(MirSurfaceSpec* spec) { return {mir_surface_create_sync(spec), [](MirSurface *surf){mir_surface_release_sync(surf);}}; } MirSurfaceSpec spec{nullptr, 640, 480, mir_pixel_format_abgr_8888}; std::shared_ptr wrapped_shell; std::shared_ptr mock_shell; }; TEST_F(ClientMirSurface, sends_optional_params) { EXPECT_CALL(*mock_shell, create_surface(_,AllOf(MatchesRequired(&spec), MatchesOptional(&spec)),_)); auto surf = create_surface(&spec); // A valid surface is needed to be specified as a parent ASSERT_THAT(surf.get(), IsValid()); spec.parent = surf.get(); EXPECT_CALL(*mock_shell, create_surface(_, MatchesOptional(&spec),_)); create_surface(&spec); } TEST_F(ClientMirSurface, as_menu_sends_correct_params) { EXPECT_CALL(*mock_shell, create_surface(_,_,_)); auto parent = create_surface(&spec); ASSERT_THAT(parent.get(), IsValid()); MirRectangle attachment_rect{100, 200, 300, 400}; MirEdgeAttachment edge{mir_edge_attachment_horizontal}; auto spec_deleter = [](MirSurfaceSpec* spec) {mir_surface_spec_release(spec);}; std::unique_ptr menu_spec{ mir_connection_create_spec_for_menu(connection, 640, 480, mir_pixel_format_abgr_8888, parent.get(), &attachment_rect, edge), spec_deleter }; ASSERT_THAT(menu_spec, NotNull()); EXPECT_CALL(*mock_shell, create_surface(_, AllOf(IsAMenu(), HasParent(parent.get()), MatchesAuxRect(attachment_rect), MatchesEdge(edge)),_)); create_surface(menu_spec.get()); } TEST_F(ClientMirSurface, as_tooltip_sends_correct_params) { EXPECT_CALL(*mock_shell, create_surface(_,_,_)); auto parent = create_surface(&spec); ASSERT_THAT(parent.get(), IsValid()); MirRectangle target_zone_rect{100, 200, 300, 400}; auto spec_deleter = [](MirSurfaceSpec* spec) {mir_surface_spec_release(spec);}; std::unique_ptr tooltip_spec{ mir_connection_create_spec_for_tooltip(connection, 640, 480, mir_pixel_format_abgr_8888, parent.get(), &target_zone_rect), spec_deleter }; ASSERT_THAT(tooltip_spec, NotNull()); EXPECT_CALL(*mock_shell, create_surface(_, AllOf(IsATooltip(), HasParent(parent.get()), MatchesAuxRect(target_zone_rect)),_)); create_surface(tooltip_spec.get()); } TEST_F(ClientMirSurface, as_dialog_sends_correct_params) { auto spec_deleter = [](MirSurfaceSpec* spec) {mir_surface_spec_release(spec);}; std::unique_ptr dialog_spec{ mir_connection_create_spec_for_dialog(connection, 640, 480, mir_pixel_format_abgr_8888), spec_deleter }; ASSERT_THAT(dialog_spec, NotNull()); EXPECT_CALL(*mock_shell, create_surface(_, AllOf(IsADialog(), NoParentSet()),_)); create_surface(dialog_spec.get()); } TEST_F(ClientMirSurface, as_modal_dialog_sends_correct_params) { EXPECT_CALL(*mock_shell, create_surface(_,_,_)); auto parent = create_surface(&spec); ASSERT_THAT(parent.get(), IsValid()); auto spec_deleter = [](MirSurfaceSpec* spec) {mir_surface_spec_release(spec);}; std::unique_ptr dialog_spec{ mir_connection_create_spec_for_modal_dialog(connection, 640, 480, mir_pixel_format_abgr_8888, parent.get()), spec_deleter }; ASSERT_THAT(dialog_spec, NotNull()); EXPECT_CALL(*mock_shell, create_surface(_, AllOf(IsADialog(), HasParent(parent.get())),_)); create_surface(dialog_spec.get()); } ./tests/integration-tests/client/CMakeLists.txt0000644000015600001650000000032712676616125021756 0ustar jenkinsjenkinslist( APPEND INTEGRATION_TESTS_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_screencast.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mirsurface.cpp ) set( INTEGRATION_TESTS_SRCS ${INTEGRATION_TESTS_SRCS} PARENT_SCOPE) ./tests/integration-tests/client/test_screencast.cpp0000644000015600001650000001231512676616125023113 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_protobuf.pb.h" #include "src/client/default_connection_configuration.h" #include "src/client/rpc/mir_display_server.h" #include "src/client/rpc/mir_basic_rpc_channel.h" #include "mir/dispatch/threaded_dispatcher.h" #include "mir/dispatch/dispatchable.h" #include "mir/frontend/connector.h" #include "mir/test/test_protobuf_server.h" #include "mir/test/stub_server_tool.h" #include "mir/test/pipe.h" #include "mir/test/wait_object.h" #include #include namespace mcl = mir::client; namespace mt = mir::test; namespace md = mir::dispatch; namespace { struct StubScreencastServerTool : mt::StubServerTool { void create_screencast( mir::protobuf::ScreencastParameters const*, mir::protobuf::Screencast* response, google::protobuf::Closure* done) override { response->mutable_buffer_stream()->mutable_buffer()->add_fd(pipe.read_fd()); done->Run(); } void screencast_buffer( mir::protobuf::ScreencastId const*, mir::protobuf::Buffer* response, google::protobuf::Closure* done) override { response->add_fd(pipe.read_fd()); done->Run(); } mt::Pipe pipe; }; struct MirScreencastTest : public testing::Test { MirScreencastTest() : server_tool{std::make_shared()} { std::remove(test_socket); test_server = std::make_shared(test_socket, server_tool); test_server->comm->start(); rpc_channel = mcl::DefaultConnectionConfiguration{test_socket}.the_rpc_channel(); protobuf_server = std::make_shared(rpc_channel); eventloop = std::make_shared( "Mir/Client IPC", std::dynamic_pointer_cast(rpc_channel)); } char const* const test_socket = "./test_socket_screencast"; std::shared_ptr const server_tool; std::shared_ptr test_server; std::shared_ptr rpc_channel; std::shared_ptr protobuf_server; std::shared_ptr eventloop; }; } TEST_F(MirScreencastTest, gets_buffer_fd_when_creating_screencast) { std::vector const cookie{'2','3','l','$'}; ASSERT_EQ(static_cast(cookie.size()), write(server_tool->pipe.write_fd(), cookie.data(), cookie.size())); mir::protobuf::ScreencastParameters protobuf_parameters; protobuf_parameters.set_width(0); protobuf_parameters.set_height(0); protobuf_parameters.mutable_region()->set_left(0); protobuf_parameters.mutable_region()->set_top(0); protobuf_parameters.mutable_region()->set_width(0); protobuf_parameters.mutable_region()->set_height(0); protobuf_parameters.set_pixel_format(0); mir::protobuf::Screencast protobuf_screencast; mt::WaitObject wait_rpc; protobuf_server->create_screencast( &protobuf_parameters, &protobuf_screencast, google::protobuf::NewCallback(&wait_rpc, &mt::WaitObject::notify_ready)); wait_rpc.wait_until_ready(); ASSERT_EQ(1, protobuf_screencast.buffer_stream().buffer().fd_size()); auto const read_fd = protobuf_screencast.buffer_stream().buffer().fd(0); std::vector received(cookie.size(), '\0'); EXPECT_EQ(static_cast(cookie.size()), read(read_fd, received.data(), received.size())); EXPECT_EQ(cookie, received); close(read_fd); } TEST_F(MirScreencastTest, gets_buffer_fd_when_getting_screencast_buffer) { std::vector const cookie{'X','%','q','S'}; ASSERT_EQ(static_cast(cookie.size()), write(server_tool->pipe.write_fd(), cookie.data(), cookie.size())); mir::protobuf::ScreencastId protobuf_screencast_id; protobuf_screencast_id.set_value(0); mir::protobuf::Buffer protobuf_buffer; mt::WaitObject wait_rpc; protobuf_server->screencast_buffer( &protobuf_screencast_id, &protobuf_buffer, google::protobuf::NewCallback(&wait_rpc, &mt::WaitObject::notify_ready)); wait_rpc.wait_until_ready(); ASSERT_EQ(1, protobuf_buffer.fd_size()); auto const read_fd = protobuf_buffer.fd(0); std::vector received(cookie.size(), '\0'); EXPECT_EQ(static_cast(cookie.size()), read(read_fd, received.data(), received.size())); EXPECT_EQ(cookie, received); close(read_fd); } ./tests/integration-tests/test_touchspot_visualization.cpp0000644000015600001650000001566212676616125024524 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/input/touch_visualizer.h" #include "mir/input/device_capability.h" #include "mir/input/input_device_info.h" #include "mir/test/barrier.h" #include "mir/test/fake_shared.h" #include "mir_test_framework/deferred_in_process_server.h" #include "mir_test_framework/input_testing_server_configuration.h" #include "mir_test_framework/executable_path.h" #include "mir_test_framework/fake_input_device.h" #include "mir_test_framework/stub_server_platform_factory.h" #include #include #include #include namespace mi = mir::input; namespace mis = mi::synthesis; namespace mt = mir::test; namespace geom = mir::geometry; namespace mtf = mir_test_framework; namespace { //TODO not yet configured at the input simulating device const auto minimum_touch = mtf::FakeInputDevice::minimum_touch_axis_value; const auto maximum_touch = mtf::FakeInputDevice::maximum_touch_axis_value; ACTION_P(UnblockBarrier, barrier) { barrier->ready(); } MATCHER(NoSpots, "") { if (arg.size() > 0) return false; return true; } static bool spots_have_position_and_pressure(std::vector const& spots, std::vector const& positions, float pressure) { if (spots.size() != positions.size()) return false; auto spots_it = spots.begin(); auto positions_it = positions.begin(); while (spots_it != spots.end()) { auto const& spot = *spots_it; if (spot.touch_location != *positions_it) return false; if (spot.pressure != pressure) return false; spots_it++; positions_it++; } return true; } MATCHER_P(TouchedSpotsAt, positions, "") { return spots_have_position_and_pressure(arg, positions, 1.0); } struct MockTouchVisualizer : public mi::TouchVisualizer { MOCK_METHOD1(visualize_touches, void(std::vector const&)); MOCK_METHOD0(enable, void()); MOCK_METHOD0(disable, void()); }; struct ServerConfiguration : mtf::InputTestingServerConfiguration { mt::Barrier& test_complete; MockTouchVisualizer mock_visualizer; static geom::Rectangle const display_bounds; ServerConfiguration(mt::Barrier& test_complete) : InputTestingServerConfiguration({display_bounds}), test_complete(test_complete) { } void inject_input() override{} std::shared_ptr the_touch_visualizer() override { return mt::fake_shared(mock_visualizer); } }; geom::Rectangle const ServerConfiguration::display_bounds = {{0, 0}, {1600, 1600}}; struct TestTouchspotVisualizations : mtf::DeferredInProcessServer { mt::Barrier test_complete_fence{2}; int env_setup = setenv("MIR_SERVER_PLATFORM_INPUT_LIB", mtf::server_platform("input-stub.so").c_str(), 1); ServerConfiguration server_configuration{test_complete_fence}; std::unique_ptr fake_touch_screen{ mtf::add_fake_input_device(mi::InputDeviceInfo{"ts", "ts-uid" , mi::DeviceCapability::touchscreen|mi::DeviceCapability::multitouch}) }; mir::DefaultServerConfiguration& server_config() override { return server_configuration; } void start_server() { DeferredInProcessServer::start_server(); server_configuration.exec(); } void TearDown() { test_complete_fence.ready(); server_configuration.on_exit(); DeferredInProcessServer::TearDown(); } }; geom::Point transform_to_screen_space(geom::Point in_touchpad_space) { auto display_width = ServerConfiguration::display_bounds.size.width.as_uint32_t(); auto display_height = ServerConfiguration::display_bounds.size.height.as_uint32_t(); float scale_x = float(display_width) / (maximum_touch - minimum_touch + 1); float scale_y = float(display_height) / (maximum_touch - minimum_touch + 1); return {scale_x * in_touchpad_space.x.as_uint32_t(), scale_y * in_touchpad_space.y.as_uint32_t()}; } } using namespace ::testing; TEST_F(TestTouchspotVisualizations, touch_is_given_to_touchspot_visualizer) { static geom::Point abs_touch = { minimum_touch, minimum_touch }; static std::vector expected_spots = { transform_to_screen_space(abs_touch) }; InSequence seq; // First we will see the spots cleared, as this is the start of a new gesture. EXPECT_CALL(server_configuration.mock_visualizer, visualize_touches(NoSpots())).Times(AtMost(1)); EXPECT_CALL(server_configuration.mock_visualizer, visualize_touches(TouchedSpotsAt(expected_spots))); EXPECT_CALL(server_configuration.mock_visualizer, visualize_touches(NoSpots())).Times(1). WillOnce(UnblockBarrier(&server_configuration.test_complete)); start_server(); fake_touch_screen->emit_event(mis::a_touch_event().at_position(abs_touch)); fake_touch_screen->emit_event(mis::a_touch_event().at_position(abs_touch) .with_action(mis::TouchParameters::Action::Release)); } TEST_F(TestTouchspotVisualizations, touchspots_follow_gesture) { static geom::Point abs_touch = { minimum_touch, minimum_touch }; static geom::Point abs_touch_2 = { maximum_touch, maximum_touch }; static std::vector expected_spots_1 = { transform_to_screen_space(abs_touch) }; static std::vector expected_spots_2 = { transform_to_screen_space(abs_touch_2) }; InSequence seq; EXPECT_CALL(server_configuration.mock_visualizer, visualize_touches(NoSpots())).Times(AtMost(1)); EXPECT_CALL(server_configuration.mock_visualizer, visualize_touches(TouchedSpotsAt(expected_spots_1))).Times(1); EXPECT_CALL(server_configuration.mock_visualizer, visualize_touches(TouchedSpotsAt(expected_spots_2))).Times(1); EXPECT_CALL(server_configuration.mock_visualizer, visualize_touches(NoSpots())). Times(1).WillOnce(UnblockBarrier(&server_configuration.test_complete)); start_server(); fake_touch_screen->emit_event(mis::a_touch_event().at_position(abs_touch)); fake_touch_screen->emit_event(mis::a_touch_event().at_position(abs_touch_2) .with_action(mis::TouchParameters::Action::Move)); fake_touch_screen->emit_event(mis::a_touch_event().at_position(abs_touch_2) .with_action(mis::TouchParameters::Action::Release)); } ./tests/integration-tests/test_display_server_main_loop_events.cpp0000644000015600001650000004610712676616125026165 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/compositor/compositor.h" #include "mir/frontend/connector.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/display_configuration_policy.h" #include "mir/server_action_queue.h" #include "mir/graphics/event_handler_register.h" #include "mir/server_status_listener.h" #include "mir/events/event_private.h" #include "mir/test/pipe.h" #include "mir/test/wait_condition.h" #include "mir/test/auto_unblock_thread.h" #include "mir_test_framework/testing_server_configuration.h" #include "mir/test/doubles/mock_input_manager.h" #include "mir/test/doubles/mock_input_dispatcher.h" #include "mir/test/doubles/mock_compositor.h" #include "mir/test/doubles/null_display.h" #include "mir/test/doubles/mock_server_status_listener.h" #include "mir/run_mir.h" #include #include #include #include #include #include #include namespace mi = mir::input; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace mf = mir::frontend; namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { class MockConnector : public mf::Connector { public: MOCK_METHOD0(start, void()); MOCK_METHOD0(stop, void()); /* * We don't have expectations for these, so use stubs * to silence gmock warnings. */ int client_socket_fd() const override { return 0; } int client_socket_fd(std::function const&)> const&) const override { return 0; } void remove_endpoint() const {} }; class MockDisplay : public mtd::NullDisplay { public: MockDisplay(std::shared_ptr const& display, int pause_signal, int resume_signal, int fd) : display{display}, pause_signal{pause_signal}, resume_signal{resume_signal}, fd{fd}, pause_handler_invoked_{false}, resume_handler_invoked_{false}, conf_change_handler_invoked_{false} { } void for_each_display_sync_group(std::function const& f) override { display->for_each_display_sync_group(f); } std::unique_ptr configuration() const { return std::unique_ptr( new mtd::NullDisplayConfiguration ); } MOCK_METHOD1(configure, void(mg::DisplayConfiguration const&)); void register_configuration_change_handler( mg::EventHandlerRegister& handlers, mg::DisplayConfigurationChangeHandler const& conf_change_handler) override { handlers.register_fd_handler( {fd}, this, [this,conf_change_handler](int fd) { char c; if (read(fd, &c, 1) == 1) { conf_change_handler(); conf_change_handler_invoked_ = true; } }); } void register_pause_resume_handlers( mg::EventHandlerRegister& handlers, mg::DisplayPauseHandler const& pause_handler, mg::DisplayResumeHandler const& resume_handler) override { handlers.register_signal_handler( {pause_signal}, [this,pause_handler](int) { pause_handler(); pause_handler_invoked_ = true; }); handlers.register_signal_handler( {resume_signal}, [this,resume_handler](int) { resume_handler(); resume_handler_invoked_ = true; }); } MOCK_METHOD0(pause, void()); MOCK_METHOD0(resume, void()); bool pause_handler_invoked() { return pause_handler_invoked_.exchange(false); } bool resume_handler_invoked() { return resume_handler_invoked_.exchange(false); } bool conf_change_handler_invoked() { return conf_change_handler_invoked_.exchange(false); } private: std::shared_ptr const display; int const pause_signal; int const resume_signal; int const fd; std::atomic pause_handler_invoked_; std::atomic resume_handler_invoked_; std::atomic conf_change_handler_invoked_; }; class ServerConfig : public mtf::TestingServerConfiguration { public: std::shared_ptr the_input_manager() override { if (!mock_input_manager) { mock_input_manager = std::make_shared(); EXPECT_CALL(*mock_input_manager, start()).Times(1); EXPECT_CALL(*mock_input_manager, stop()).Times(1); } return mock_input_manager; } std::shared_ptr the_compositor() override { if (!mock_compositor) { mock_compositor = std::make_shared(); EXPECT_CALL(*mock_compositor, start()).Times(1); EXPECT_CALL(*mock_compositor, stop()).Times(1); } return mock_compositor; } private: std::shared_ptr mock_input_manager; std::shared_ptr mock_compositor; }; class TestMainLoopServerConfig : public mtf::TestingServerConfiguration { public: TestMainLoopServerConfig() : pause_signal{SIGUSR1}, resume_signal{SIGUSR2} { } std::shared_ptr the_display() override { if (!mock_display) { auto display = mtf::TestingServerConfiguration::the_display(); mock_display = std::make_shared(display, pause_signal, resume_signal, p.read_fd()); } return mock_display; } std::shared_ptr the_compositor() override { if (!mock_compositor) mock_compositor = std::make_shared(); return mock_compositor; } std::shared_ptr the_connector() override { if (!mock_connector) mock_connector = std::make_shared(); return mock_connector; } std::shared_ptr the_input_manager() override { if (!mock_input_manager) mock_input_manager = std::make_shared(); return mock_input_manager; } std::shared_ptr the_input_dispatcher() override { if (!mock_input_dispatcher) mock_input_dispatcher = std::make_shared(); return mock_input_dispatcher; } std::shared_ptr the_mock_display() { the_display(); return mock_display; } std::shared_ptr the_mock_compositor() { the_compositor(); return mock_compositor; } std::shared_ptr the_mock_connector() { the_connector(); return mock_connector; } std::shared_ptr the_mock_input_manager() { the_input_manager(); return mock_input_manager; } std::shared_ptr the_mock_input_dispatcher() { the_input_dispatcher(); return mock_input_dispatcher; } void emit_pause_event_and_wait_for_handler() { kill(getpid(), pause_signal); while (!mock_display->pause_handler_invoked()) std::this_thread::sleep_for(std::chrono::microseconds{500}); } void emit_resume_event_and_wait_for_handler() { kill(getpid(), resume_signal); while (!mock_display->resume_handler_invoked()) std::this_thread::sleep_for(std::chrono::microseconds{500}); } void emit_configuration_change_event_and_wait_for_handler() { if (write(p.write_fd(), "a", 1)) {} while (!mock_display->conf_change_handler_invoked()) std::this_thread::sleep_for(std::chrono::microseconds{500}); } void wait_for_server_actions_to_finish() { mt::WaitCondition last_action_done; the_server_action_queue()->enqueue(&last_action_done, [&] { last_action_done.wake_up_everyone(); }); last_action_done.wait_for_at_most_seconds(5); } private: std::shared_ptr mock_compositor; std::shared_ptr mock_display; std::shared_ptr mock_connector; std::shared_ptr mock_input_manager; std::shared_ptr mock_input_dispatcher; mt::Pipe p; int const pause_signal; int const resume_signal; }; class TestServerStatusListenerConfig : public TestMainLoopServerConfig { public: std::shared_ptr the_server_status_listener() override { if (!mock_server_status_listener) mock_server_status_listener = std::make_shared(); return mock_server_status_listener; } std::shared_ptr the_mock_server_status_listener() { the_server_status_listener(); return mock_server_status_listener; } private: std::shared_ptr mock_server_status_listener; }; struct DisplayServerMainLoopEvents : testing::Test { void use_config_for_expectations(TestMainLoopServerConfig& server_config) { mock_compositor = server_config.the_mock_compositor(); mock_display = server_config.the_mock_display(); mock_connector = server_config.the_mock_connector(); mock_input_manager = server_config.the_mock_input_manager(); mock_input_dispatcher = server_config.the_mock_input_dispatcher(); } void expect_start() { EXPECT_CALL(*mock_compositor, start()).Times(1); EXPECT_CALL(*mock_input_manager, start()).Times(1); EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); EXPECT_CALL(*mock_connector, start()).Times(1); } void expect_pause() { EXPECT_CALL(*mock_connector, stop()).Times(1); EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); EXPECT_CALL(*mock_input_manager, stop()).Times(1); EXPECT_CALL(*mock_compositor, stop()).Times(1); EXPECT_CALL(*mock_display, pause()).Times(1); } void expect_resume() { EXPECT_CALL(*mock_display, resume()).Times(1); EXPECT_CALL(*mock_compositor, start()).Times(1); EXPECT_CALL(*mock_input_manager, start()).Times(1); EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); EXPECT_CALL(*mock_connector, start()).Times(1); } void expect_stop() { EXPECT_CALL(*mock_connector, stop()).Times(1); EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); EXPECT_CALL(*mock_input_manager, stop()).Times(1); EXPECT_CALL(*mock_compositor, stop()).Times(1); } void expect_change_configuration() { EXPECT_CALL(*mock_compositor, stop()).Times(1); EXPECT_CALL(*mock_display, configure(testing::_)).Times(1); EXPECT_CALL(*mock_compositor, start()).Times(1); } std::shared_ptr mock_display; std::shared_ptr mock_compositor; std::shared_ptr mock_connector; std::shared_ptr mock_input_manager; std::shared_ptr mock_input_dispatcher; }; } TEST_F(DisplayServerMainLoopEvents, display_server_shuts_down_properly_on_sigint) { ServerConfig server_config; mir::run_mir(server_config, [](mir::DisplayServer&) { kill(getpid(), SIGINT); }); } TEST_F(DisplayServerMainLoopEvents, display_server_shuts_down_properly_on_sigterm) { ServerConfig server_config; mir::run_mir(server_config, [](mir::DisplayServer&) { kill(getpid(), SIGTERM); }); } TEST_F(DisplayServerMainLoopEvents, display_server_components_pause_and_resume) { using namespace testing; TestMainLoopServerConfig server_config; use_config_for_expectations(server_config); { InSequence s; expect_start(); expect_pause(); expect_resume(); expect_stop(); } mt::AutoJoinThread t; mir::run_mir(server_config, [&server_config, &t](mir::DisplayServer&) { mt::AutoJoinThread killing_thread{ [&] { server_config.emit_pause_event_and_wait_for_handler(); server_config.emit_resume_event_and_wait_for_handler(); kill(getpid(), SIGTERM); }}; t = std::move(killing_thread); }); } TEST_F(DisplayServerMainLoopEvents, display_server_quits_when_paused) { using namespace testing; TestMainLoopServerConfig server_config; use_config_for_expectations(server_config); { InSequence s; expect_start(); expect_pause(); expect_stop(); } mt::AutoJoinThread t; mir::run_mir(server_config, [&server_config, &t](mir::DisplayServer&) { mt::AutoJoinThread killing_thread{ [&] { server_config.emit_pause_event_and_wait_for_handler(); kill(getpid(), SIGTERM); }}; t = std::move(killing_thread); }); } TEST_F(DisplayServerMainLoopEvents, display_server_attempts_to_continue_on_pause_failure) { using namespace testing; TestMainLoopServerConfig server_config; use_config_for_expectations(server_config); { InSequence s; expect_start(); /* Pause failure */ EXPECT_CALL(*mock_connector, stop()).Times(1); EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); EXPECT_CALL(*mock_input_manager, stop()).Times(1); EXPECT_CALL(*mock_compositor, stop()).Times(1); EXPECT_CALL(*mock_display, pause()) .WillOnce(Throw(std::runtime_error(""))); /* Attempt to continue */ EXPECT_CALL(*mock_compositor, start()).Times(1); EXPECT_CALL(*mock_input_manager, start()).Times(1); EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); EXPECT_CALL(*mock_connector, start()).Times(1); expect_stop(); } mt::AutoJoinThread t; mir::run_mir(server_config, [&server_config, &t](mir::DisplayServer&) { mt::AutoJoinThread killing_thread{ [&] { server_config.emit_pause_event_and_wait_for_handler(); kill(getpid(), SIGTERM); }}; t = std::move(killing_thread); }); } TEST_F(DisplayServerMainLoopEvents, display_server_handles_configuration_change) { using namespace testing; TestMainLoopServerConfig server_config; use_config_for_expectations(server_config); { InSequence s; expect_start(); expect_change_configuration(); expect_stop(); } mt::AutoJoinThread t; mir::run_mir(server_config, [&](mir::DisplayServer&) { mt::AutoJoinThread killing_thread{ [&] { server_config.emit_configuration_change_event_and_wait_for_handler(); server_config.wait_for_server_actions_to_finish(); kill(getpid(), SIGTERM); }}; t = std::move(killing_thread); }); } TEST_F(DisplayServerMainLoopEvents, postpones_configuration_when_paused) { using namespace testing; TestMainLoopServerConfig server_config; use_config_for_expectations(server_config); { InSequence s; expect_start(); expect_pause(); expect_resume(); /* Change configuration (after resuming) */ expect_change_configuration(); expect_stop(); } mt::AutoJoinThread t; mir::run_mir(server_config, [&](mir::DisplayServer&) { mt::AutoJoinThread killing_thread{ [&] { server_config.emit_pause_event_and_wait_for_handler(); server_config.emit_configuration_change_event_and_wait_for_handler(); server_config.emit_resume_event_and_wait_for_handler(); server_config.wait_for_server_actions_to_finish(); kill(getpid(), SIGTERM); }}; t = std::move(killing_thread); }); } TEST_F(DisplayServerMainLoopEvents, server_status_listener) { using namespace testing; TestServerStatusListenerConfig server_config; use_config_for_expectations(server_config); auto mock_server_status_listener = server_config.the_mock_server_status_listener(); { InSequence s; /* "started" is emitted after all components have been started */ expect_start(); EXPECT_CALL(*mock_server_status_listener, started()).Times(1); /* "paused" is emitted after all components have been paused/stopped */ expect_pause(); EXPECT_CALL(*mock_server_status_listener, paused()).Times(1); /* "resumed" is emitted after all components have been resumed/started */ expect_resume(); EXPECT_CALL(*mock_server_status_listener, resumed()).Times(1); expect_stop(); } mt::AutoJoinThread t; mir::run_mir(server_config, [&server_config, &t](mir::DisplayServer&) { mt::AutoJoinThread killing_thread{ [&] { server_config.emit_pause_event_and_wait_for_handler(); server_config.emit_resume_event_and_wait_for_handler(); kill(getpid(), SIGTERM); }}; t = std::move(killing_thread); }); } ./tests/performance-tests/0000755000015600001650000000000012676616160015713 5ustar jenkinsjenkins./tests/performance-tests/test_glmark2-es2-mir.cpp0000644000015600001650000001037612676616157022306 0ustar jenkinsjenkins/* * Copyright © 2014-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mir_test_framework/async_server_runner.h" #include "mir/test/popen.h" #include #include #include #include #include #include namespace mo = mir::options; namespace mtf = mir_test_framework; namespace { struct GLMark2Test : testing::Test, mtf::AsyncServerRunner { void SetUp() override { start_server(); } void TearDown() override { stop_server(); } enum ResultFileType {raw, json}; virtual int run_glmark2(char const* args) { ResultFileType file_type = raw; // Should this still be selectable? auto const cmd = "MIR_SOCKET=" + new_connection() + " glmark2-es2-mir -b build " + args; mir::test::Popen p(cmd); const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); char output_filename[256]; snprintf(output_filename, sizeof(output_filename) - 1, "/tmp/%s_%s.log", test_info->test_case_name(), test_info->name()); printf("Saving GLMark2 detailed results to: %s\n", output_filename); // ^ Although I would vote to just print them to stdout instead std::string line; std::ofstream glmark2_output; int score = -1; glmark2_output.open(output_filename); while (p.get_line(line)) { int match; if (sscanf(line.c_str(), " glmark2 Score: %d", &match) == 1) { score = match; } if (file_type == raw) { glmark2_output << line << std::endl; } } if (file_type == json) { std::string json = "{"; json += "\"benchmark_name\":\"glmark2-es2-mir\""; json += ","; json += "\"score\":\"" + std::to_string(score) + "\""; json += "}"; glmark2_output << json; } return score; } }; TEST_F(GLMark2Test, fullscreen_default) { EXPECT_THAT(run_glmark2("--fullscreen"), ::testing::Ge(56)); } #ifdef ANDROID TEST_F(GLMark2Test, DISABLED_windowed_default) // Android bug LP: #1546912 #else TEST_F(GLMark2Test, windowed_default) #endif { EXPECT_THAT(run_glmark2(""), ::testing::Ge(56)); } #ifdef ANDROID TEST_F(GLMark2Test, DISABLED_fullscreen_interval1) // Android bug LP: #1546912 #else TEST_F(GLMark2Test, fullscreen_interval1) #endif { add_to_environment("MIR_CLIENT_FORCE_SWAP_INTERVAL", "1"); // Our devices seem to range 57-67Hz EXPECT_NEAR(60, run_glmark2("--fullscreen"), 10); } #ifdef ANDROID TEST_F(GLMark2Test, DISABLED_windowed_interval1) // Android bug LP: #1546912 #else TEST_F(GLMark2Test, windowed_interval1) #endif { add_to_environment("MIR_CLIENT_FORCE_SWAP_INTERVAL", "1"); // Our devices seem to range 57-67Hz EXPECT_NEAR(60, run_glmark2(""), 10); } #ifdef ANDROID TEST_F(GLMark2Test, DISABLED_fullscreen_interval0) // Android bug LP: #1369763 // and LP: #1546912 #else TEST_F(GLMark2Test, fullscreen_interval0) #endif { add_to_environment("MIR_CLIENT_FORCE_SWAP_INTERVAL", "0"); EXPECT_THAT(run_glmark2("--fullscreen"), ::testing::Ge(100)); } #ifdef ANDROID TEST_F(GLMark2Test, DISABLED_windowed_interval0) // Android bug LP: #1369763 // and LP: #1546912 #else TEST_F(GLMark2Test, windowed_interval0) #endif { add_to_environment("MIR_CLIENT_FORCE_SWAP_INTERVAL", "0"); EXPECT_THAT(run_glmark2(""), ::testing::Ge(100)); } } ./tests/performance-tests/CMakeLists.txt0000644000015600001650000000055012676616125020454 0ustar jenkinsjenkinsset( PERFORMANCE_TESTS_SOURCES test_glmark2-es2-mir.cpp ) mir_add_wrapped_executable( mir_performance_tests ${PERFORMANCE_TESTS_SOURCES} ) add_dependencies(mir_performance_tests GMock) target_link_libraries( mir_performance_tests mir-test-static mir-test-framework-static mir-test-doubles-static mirserver ${GTEST_BOTH_LIBRARIES} ) ./tests/umock-acceptance-tests/0000755000015600001650000000000012676616160016614 5ustar jenkinsjenkins./tests/umock-acceptance-tests/test_libinput.cpp0000644000015600001650000000632012676616125022207 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/input/platform.h" #include "mir/test/doubles/mock_option.h" #include "mir/shared_library.h" #include "mir_test_framework/udev_environment.h" #include "mir_test_framework/executable_path.h" #include #include namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; namespace mo = mir::options; using namespace ::testing; namespace { auto get_libinput_platform() { auto path = mtf::server_input_platform("input-evdev"); return std::make_shared(path); } char const probe_input_platform_symbol[] = "probe_input_platform"; char const host_socket_opt[] = "host-socket"; } TEST(LibInput, DISABLED_probes_as_unsupported_without_device_access) { NiceMock options; // dumb assumption - nobody runs this test cases as root.. // or allows accessing evdev input devices from non privileged users. auto library = get_libinput_platform(); auto probe_fun = library->load_function(probe_input_platform_symbol); EXPECT_THAT(probe_fun(options), Eq(mir::input::PlatformPriority::unsupported)); } TEST(LibInput, probes_as_supported_with_at_least_one_device_to_deal_with) { mtf::UdevEnvironment env; env.add_standard_device("laptop-keyboard"); NiceMock options; auto library = get_libinput_platform(); auto probe_fun = library->load_function(probe_input_platform_symbol); EXPECT_THAT(probe_fun(options), Ge(mir::input::PlatformPriority::supported)); } TEST(LibInput, probes_as_unsupported_on_nested_configs) { mtf::UdevEnvironment env; env.add_standard_device("laptop-keyboard"); NiceMock options; ON_CALL(options,is_set(StrEq(host_socket_opt))) .WillByDefault(Return(true)); ON_CALL(options,get(StrEq(host_socket_opt), Matcher(_))) .WillByDefault(Return("something")); auto library = get_libinput_platform(); auto probe_fun = library->load_function(probe_input_platform_symbol); EXPECT_THAT(probe_fun(options), Eq(mir::input::PlatformPriority::unsupported)); } TEST(LibInput, probes_as_supported_when_umock_dev_available_or_before_input_devices_are_available) { mtf::UdevEnvironment env; NiceMock options; auto library = get_libinput_platform(); auto probe_fun = library->load_function(probe_input_platform_symbol); EXPECT_THAT(probe_fun(options), Eq(mir::input::PlatformPriority::supported)); } ./tests/umock-acceptance-tests/CMakeLists.txt0000644000015600001650000000157012676616157021365 0ustar jenkinsjenkinsinclude(CMakeDependentOption) include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/tests/include/ ${CMAKE_SOURCE_DIR}/include/cookie ${UMOCKDEV_INCLUDE_DIRS} ) mir_add_wrapped_executable( mir_umock_acceptance_tests test_libinput.cpp $ ) add_dependencies(mir_umock_acceptance_tests GMock) target_link_libraries(mir_umock_acceptance_tests mir-test-assist mir-test-framework-static ${UMOCKDEV_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} # Link in pthread. ) CMAKE_DEPENDENT_OPTION( MIR_RUN_ACCEPTANCE_TESTS "Run acceptance tests as part of default testing" ON "MIR_BUILD_ACCEPTANCE_TESTS" OFF) if (MIR_RUN_ACCEPTANCE_TESTS) mir_discover_tests_with_fd_leak_detection(mir_umock_acceptance_tests LD_PRELOAD=libumockdev-preload.so.0 G_SLICE=always-malloc G_DEBUG=gc-friendly) endif (MIR_RUN_ACCEPTANCE_TESTS) ./tests/loader-tests/0000755000015600001650000000000012676616125014661 5ustar jenkinsjenkins./tests/loader-tests/test_reload.c0000644000015600001650000000265512676616125017342 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis * Daniel van Vugt */ #include #include #include #ifdef __cplusplus #error "This test only works properly as a C program not linked to C++." #endif int main(int argc, char** argv) { int i; char const* libname = argc <= 1 ? DEFAULT_LIB_NAME : argv[1]; for (i = 0; i < 10; i++) { void* dl = NULL; printf("[%d] dlopen `%s' = ", i, libname); dl = dlopen(libname, RTLD_NOW); if (dl) { printf("%p\n", dl); dlclose(dl); } else { printf("NULL (%s)\n", dlerror()); return 1; /* Non-zero means polite test failure */ } } return 0; } ./tests/loader-tests/CMakeLists.txt0000644000015600001650000000223312676616125017421 0ustar jenkinsjenkins # # Note: In order for this test to work (crash in the absence of a fix), it # must be a pure C program. This is because putting it in a non-trivial C++ # program (like mir_*_tests) somehow provides sufficient preloaded C++ symbols # that the bug does not occur. # mir_add_wrapped_executable(mir_test_reload_protobuf test_reload.c) # # XXX I would like to detect the full file name from the mirprotobuf target # too, but it seems only the "LOCATION" property works (and provides too # much info including full directory path). # get_target_property(mirprotobuf_soname mirprotobuf SOVERSION) # # Being a wrapped executable means we already have the correct LD_LIBRARY_PATH # so can just load the library by file name (which also allows for # directory portability). # target_compile_definitions(mir_test_reload_protobuf PRIVATE -DDEFAULT_LIB_NAME="libmirprotobuf.so.${mirprotobuf_soname}" ) # Only a build-time dependency. Don't link to it! add_dependencies(mir_test_reload_protobuf mirprotobuf) target_link_libraries(mir_test_reload_protobuf dl) add_test(NAME Protobuf-can-be-reloaded # LP: #1391976 COMMAND ${CMAKE_BINARY_DIR}/bin/mir_test_reload_protobuf ) ./tests/mir_test_framework/0000755000015600001650000000000012676616160016155 5ustar jenkinsjenkins./tests/mir_test_framework/socket_detect_server.cpp0000644000015600001650000000252712676616125023076 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir_test_framework/detect_server.h" #include #include #include #include #include #include bool mir_test_framework::detect_server( std::string const& socket_file, std::chrono::milliseconds const& timeout) { auto limit = std::chrono::steady_clock::now() + timeout; bool error = false; struct stat file_status; do { if (error) { std::this_thread::sleep_for(std::chrono::milliseconds(0)); } error = stat(socket_file.c_str(), &file_status); } while (error && std::chrono::steady_clock::now() < limit); return !error; } ./tests/mir_test_framework/stub_client_platform_module.cpp0000644000015600001650000000321212676616125024444 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers > */ #include "src/include/client/mir/client_platform_factory.h" #include "src/include/client/mir/client_context.h" #include "mir_test_framework/stub_client_platform_factory.h" #include "mir_test_framework/stub_platform_helpers.h" #include "mir/assert_module_entry_point.h" #include #include namespace mtf = mir_test_framework; namespace mcl = mir::client; mir::UniqueModulePtr create_client_platform(mcl::ClientContext* context) { mir::assert_entry_point_signature(&create_client_platform); return mir::make_module_ptr(context); } bool is_appropriate_module(mcl::ClientContext* context) { using namespace testing; mir::assert_entry_point_signature(&is_appropriate_module); MirPlatformPackage package; context->populate_server_package(package); return Matches(mtf::IsStubPlatformPackage())(package); } ./tests/mir_test_framework/fake_input_device_impl.h0000644000015600001650000000650112676616157023023 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_FRAMEWORK_FAKE_INPUT_DEVICE_IMPL_H_ #define MIR_TEST_FRAMEWORK_FAKE_INPUT_DEVICE_IMPL_H_ #include "mir_test_framework/fake_input_device.h" #include "mir/input/input_device.h" #include "mir/input/pointer_settings.h" #include "mir/input/input_device_info.h" #include "mir/geometry/point.h" namespace mir { namespace dispatch { class ActionQueue; } } namespace mir_test_framework { class FakeInputDeviceImpl : public FakeInputDevice { public: FakeInputDeviceImpl(mir::input::InputDeviceInfo const& info); void emit_device_removal() override; void emit_runtime_error() override; void emit_event(synthesis::KeyParameters const& key_params) override; void emit_event(synthesis::ButtonParameters const& button) override; void emit_event(synthesis::MotionParameters const& motion) override; void emit_event(synthesis::TouchParameters const& touch) override; private: class InputDevice : public mir::input::InputDevice { public: InputDevice(mir::input::InputDeviceInfo const& info, std::shared_ptr const& dispatchable); void start(mir::input::InputSink* destination, mir::input::EventBuilder* builder) override; void stop() override; void synthesize_events(synthesis::KeyParameters const& key_params); void synthesize_events(synthesis::ButtonParameters const& button); void synthesize_events(synthesis::MotionParameters const& motion); void synthesize_events(synthesis::TouchParameters const& touch); mir::input::InputDeviceInfo get_device_info() override { return info; } mir::optional_value get_pointer_settings() const override; void apply_settings(mir::input::PointerSettings const& settings) override; mir::optional_value get_touchpad_settings() const override; void apply_settings(mir::input::TouchpadSettings const& settings) override; private: MirPointerAction update_buttons(synthesis::EventAction action, MirPointerButton button); void update_position(int rel_x, int rel_y); void map_touch_coordinates(float& x, float& y); mir::input::InputSink* sink{nullptr}; mir::input::EventBuilder* builder{nullptr}; mir::input::InputDeviceInfo info; std::shared_ptr const queue; mir::geometry::Point pos, scroll; MirPointerButtons buttons; mir::input::PointerSettings settings; }; std::shared_ptr queue; std::shared_ptr device; }; } #endif ./tests/mir_test_framework/stub_surface.cpp0000644000015600001650000001045612676616125021355 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include namespace mtd = mir::test::doubles; std::string mtd::StubSurface::name() const { return {}; } void mtd::StubSurface::move_to(mir::geometry::Point const& /*top_left*/) { } float mtd::StubSurface::alpha() const { return 0; } mir::geometry::Size mtd::StubSurface::size() const { return {}; } mir::geometry::Size mtd::StubSurface::client_size() const { return {}; } std::shared_ptr mtd::StubSurface::primary_buffer_stream() const { return {}; } void mtd::StubSurface::set_streams(std::list const& /*streams*/) { } bool mtd::StubSurface::supports_input() const { return false; } int mtd::StubSurface::client_input_fd() const { return 0; } std::shared_ptr mtd::StubSurface::input_channel() const { return {}; } mir::input::InputReceptionMode mtd::StubSurface::reception_mode() const { return mir::input::InputReceptionMode::normal; } void mtd::StubSurface::set_reception_mode(mir::input::InputReceptionMode /*mode*/) { } void mtd::StubSurface::set_input_region(std::vector const& /*input_rectangles*/) { } void mtd::StubSurface::resize(mir::geometry::Size const& /*size*/) { } mir::geometry::Point mtd::StubSurface::top_left() const { return {}; } mir::geometry::Rectangle mtd::StubSurface::input_bounds() const { return {}; } bool mtd::StubSurface::input_area_contains(mir::geometry::Point const& /*point*/) const { return false; } void mtd::StubSurface::consume(MirEvent const* /*event*/) { } void mtd::StubSurface::set_alpha(float /*alpha*/) { } void mtd::StubSurface::set_orientation(MirOrientation /*orientation*/) { } void mtd::StubSurface::set_transformation(glm::mat4 const& /*mat4*/) { } bool mtd::StubSurface::visible() const { return false; } mir::graphics::RenderableList mtd::StubSurface::generate_renderables(mir::compositor::CompositorID /*id*/) const { return {}; } int mtd::StubSurface::buffers_ready_for_compositor(void const* /*compositor_id*/) const { return 0; } MirSurfaceType mtd::StubSurface::type() const { return MirSurfaceType::mir_surface_type_normal; } MirSurfaceState mtd::StubSurface::state() const { return MirSurfaceState::mir_surface_state_fullscreen; } int mtd::StubSurface::configure(MirSurfaceAttrib /*attrib*/, int value) { return value; } int mtd::StubSurface::query(MirSurfaceAttrib /*attrib*/) const { return 0; } void mtd::StubSurface::hide() { } void mtd::StubSurface::show() { } void mtd::StubSurface::set_cursor_image(std::shared_ptr const& /*image*/) { } std::shared_ptr mtd::StubSurface::cursor_image() const { return {}; } void mtd::StubSurface::set_cursor_stream( std::shared_ptr const& /*stream*/, mir::geometry::Displacement const& /*hotspot*/) { } void mtd::StubSurface::request_client_surface_close() { } std::shared_ptr mtd::StubSurface::parent() const { return {}; } void mtd::StubSurface::add_observer(std::shared_ptr const& /*observer*/) { } void mtd::StubSurface::remove_observer(std::weak_ptr < mir::scene::SurfaceObserver > const& /*observer*/) { } void mtd::StubSurface::set_keymap(MirInputDeviceId /*id*/, std::string const& /*model*/, std::string const& /*layout*/, std::string const& /*variant*/, std::string const& /*options*/) { } void mtd::StubSurface::rename(std::string const& /*title*/) { } namespace { // Ensure we don't accidentally have an abstract class mtd::StubSurface instantiation_test; } ./tests/mir_test_framework/stub_session.cpp0000644000015600001650000000631212676616125021404 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/test/doubles/stub_session.h" namespace mtd = mir::test::doubles; mtd::StubSession::StubSession(pid_t pid) : pid(pid) {} std::shared_ptr mtd::StubSession::get_surface( mir::frontend::SurfaceId /*surface*/) const { return {}; } std::string mtd::StubSession::name() const { return {}; } void mtd::StubSession::force_requests_to_complete() { } pid_t mtd::StubSession::process_id() const { return pid; } void mtd::StubSession::take_snapshot( mir::scene::SnapshotCallback const& /*snapshot_taken*/) { } std::shared_ptr mtd::StubSession::default_surface() const { return {}; } void mtd::StubSession::set_lifecycle_state(MirLifecycleState /*state*/) { } void mtd::StubSession::send_display_config( mir::graphics::DisplayConfiguration const& /*configuration*/) { } void mtd::StubSession::hide() { } void mtd::StubSession::show() { } void mtd::StubSession::start_prompt_session() { } void mtd::StubSession::stop_prompt_session() { } void mtd::StubSession::suspend_prompt_session() { } void mtd::StubSession::resume_prompt_session() { } mir::frontend::SurfaceId mtd::StubSession::create_surface( mir::scene::SurfaceCreationParameters const& /*params*/, std::shared_ptr const& /*sink*/) { return mir::frontend::SurfaceId{0}; } void mtd::StubSession::destroy_surface(mir::frontend::SurfaceId /*surface*/) { } std::shared_ptr mtd::StubSession::surface( mir::frontend::SurfaceId /*surface*/) const { return {}; } std::shared_ptr mtd::StubSession::surface_after( std::shared_ptr const& /*ptr*/) const { return {}; } std::shared_ptr mtd::StubSession::get_buffer_stream( mir::frontend::BufferStreamId /*stream*/) const { return {}; } mir::frontend::BufferStreamId mtd::StubSession::create_buffer_stream( mir::graphics::BufferProperties const& /*props*/) { return {}; } void mtd::StubSession::destroy_buffer_stream(mir::frontend::BufferStreamId /*stream*/) { } void mtd::StubSession::configure_streams( mir::scene::Surface& /*surface*/, std::vector const& /*config*/) { } void mtd::StubSession::destroy_surface(std::weak_ptr const& /*surface*/) { } void mtd::StubSession::send_input_device_change(std::vector> const& /*devices*/) { } namespace { // Ensure we don't accidentally have an abstract class mtd::StubSession instantiation_test; } ./tests/mir_test_framework/stubbed_graphics_platform.h0000644000015600001650000000306412676616125023546 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_STUBBED_GRAPHICS_PLATFORM_H_ #define MIR_TEST_FRAMEWORK_STUBBED_GRAPHICS_PLATFORM_H_ #include "mir/test/doubles/null_platform.h" namespace mir_test_framework { class StubGraphicPlatform : public mir::test::doubles::NullPlatform { public: StubGraphicPlatform(std::vector const& display_rects); mir::UniqueModulePtr create_buffer_allocator() override; mir::UniqueModulePtr make_ipc_operations() const override; mir::UniqueModulePtr create_display( std::shared_ptr const&, std::shared_ptr const&) override; private: std::vector const display_rects; }; } #endif /* MIR_TEST_FRAMEWORK_STUBBED_GRAPHICS_PLATFORM_H_ */ ./tests/mir_test_framework/udev_environment.cpp0000644000015600001650000001217012676616157022257 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Christopher James Halse Rogers */ #include "mir_test_framework/udev_environment.h" #include "mir_test_framework/executable_path.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace mtf = mir_test_framework; mtf::UdevEnvironment::UdevEnvironment() : recordings_path(mtf::test_data_path() + "/udev-recordings") { testbed = umockdev_testbed_new(); } mtf::UdevEnvironment::~UdevEnvironment() noexcept { g_object_unref(testbed); } std::string mtf::UdevEnvironment::add_device(char const* subsystem, char const* name, char const* parent, std::initializer_list attributes, std::initializer_list properties) { std::vector attrib(attributes); std::vector props(properties); attrib.push_back(nullptr); props.push_back(nullptr); gchar* syspath = umockdev_testbed_add_devicev(testbed, subsystem, name, parent, const_cast(attrib.data()), const_cast(props.data())); if (syspath == nullptr) BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create mock udev device")); std::string retval(syspath); g_free(syspath); return retval; } void mtf::UdevEnvironment::remove_device(std::string const& device_path) { umockdev_testbed_uevent(testbed, device_path.c_str(), "remove"); umockdev_testbed_remove_device(testbed, device_path.c_str()); } void mtf::UdevEnvironment::emit_device_changed(std::string const& device_path) { umockdev_testbed_uevent(testbed, device_path.c_str(), "change"); } void mtf::UdevEnvironment::add_standard_device(std::string const& name) { auto descriptor_filename = recordings_path + "/" + name + ".umockdev"; GError* err = nullptr; if (!umockdev_testbed_add_from_file(testbed, descriptor_filename.c_str(), &err)) { BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to create mock udev device: ") + err->message)); } auto ioctls_filename = recordings_path + "/" + name + ".ioctl"; struct stat sb; if (stat(ioctls_filename.c_str(), &sb) == 0) { if (S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode)) { if (!umockdev_testbed_load_ioctl(testbed, NULL, ioctls_filename.c_str(), &err)) { BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to load ioctl recording: ") + err->message)); } } } auto script_filename = recordings_path + "/" + name + ".script"; if (stat(script_filename.c_str(), &sb) == 0) { if (S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode)) { if (!umockdev_testbed_load_script(testbed, NULL, script_filename.c_str(), &err)) { BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to load device recording: ") + err->message)); } } } } void mtf::UdevEnvironment::load_device_ioctls(std::string const& name) { auto ioctls_filename = recordings_path + "/" + name + ".ioctl"; GError* err = nullptr; if (!umockdev_testbed_load_ioctl(testbed, NULL, ioctls_filename.c_str(), &err)) { BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to load ioctl recording: ") + err->message)); } } void mtf::UdevEnvironment::load_device_evemu(std::string const& name) { auto evemu_filename = recordings_path + "/" + name + ".evemu"; GError* err = nullptr; if (!umockdev_testbed_load_evemu_events(testbed, NULL, evemu_filename.c_str(), &err)) { BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to load evemu recording: ") + err->message)); } } ./tests/mir_test_framework/main.cpp0000644000015600001650000000171212676616125017607 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir_test_framework/main.h" #include #include int main(int argc, char* argv[]) { // Override this standard gtest message std::cout << "Running main() from " << basename(__FILE__) << std::endl; return mir_test_framework::main(argc, argv); } ./tests/mir_test_framework/async_server_runner.cpp0000644000015600001650000000776212676616125022772 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir_test_framework/async_server_runner.h" #include "mir_test_framework/command_line_server_configuration.h" #include "mir/fd.h" #include "mir/main_loop.h" #include "mir/geometry/rectangle.h" #include "mir/options/option.h" #include "mir/test/doubles/null_logger.h" #include #include namespace geom = mir::geometry; namespace ml = mir::logging; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace mt = mir::test; namespace { std::chrono::seconds const timeout{10}; } mtf::AsyncServerRunner::AsyncServerRunner() { configure_from_commandline(server); server.add_configuration_option(mtd::logging_opt, mtd::logging_descr, false); server.override_the_logger([&]() { std::shared_ptr result{}; if (!server.get_options()->get(mtd::logging_opt)) result = std::make_shared(); return result; }); } void mtf::AsyncServerRunner::add_to_environment(char const* key, char const* value) { env.emplace_back(key, value); } void mtf::AsyncServerRunner::start_server() { server.add_init_callback([&] { auto const main_loop = server.the_main_loop(); // By enqueuing the notification code in the main loop, we are // ensuring that the server has really and fully started before // leaving start_mir_server(). main_loop->enqueue( this, [&] { std::lock_guard lock(mutex); server_running = true; started.notify_one(); }); }); server.apply_settings(); mt::AutoJoinThread t([&] { try { server.run(); } catch (std::exception const& e) { FAIL() << e.what(); } std::lock_guard lock(mutex); server_running = false; started.notify_one(); }); std::unique_lock lock(mutex); started.wait_for(lock, timeout, [&] { return server_running; }); if (!server_running) { BOOST_THROW_EXCEPTION(std::runtime_error{"Failed to start server thread"}); } server_thread = std::move(t); } void mtf::AsyncServerRunner::stop_server() { server.stop(); wait_for_server_exit(); } void mtf::AsyncServerRunner::wait_for_server_exit() { std::unique_lock lock(mutex); started.wait_for(lock, timeout, [&] { return !server_running; }); if (server_running) { BOOST_THROW_EXCEPTION(std::logic_error{"stop_server() failed to stop server"}); } server_thread.stop(); } mtf::AsyncServerRunner::~AsyncServerRunner() noexcept = default; auto mtf::AsyncServerRunner::new_connection() -> std::string { return connection(server.open_client_socket()); } auto mtf::AsyncServerRunner::connection(int fd) -> std::string { char connect_string[64] = {0}; // We can't have both the server and the client owning the same fd, since // that will result in a double-close(). We give the client a duplicate which // the client can safely own (and should close when done). sprintf(connect_string, "fd://%d", dup(fd)); return connect_string; } ./tests/mir_test_framework/udev-recordings/0000755000015600001650000000000012676616160021255 5ustar jenkinsjenkins./tests/mir_test_framework/udev-recordings/synaptics-touchpad.umockdev0000644000015600001650000000331012676616125026634 0ustar jenkinsjenkinsP: /devices/platform/i8042/serio2/input/input11/event12 N: input/event12 S: input/by-path/platform-i8042-serio-2-event-mouse E: DEVLINKS=/dev/input/by-path/platform-i8042-serio-2-event-mouse E: DEVNAME=/dev/input/event12 E: ID_INPUT=1 E: ID_INPUT_TOUCHPAD=1 E: ID_PATH=platform-i8042-serio-2 E: ID_PATH_TAG=platform-i8042-serio-2 E: ID_SERIAL=noserial E: MAJOR=13 E: MINOR=76 E: SUBSYSTEM=input A: dev=13:76 L: device=../../input11 P: /devices/platform/i8042/serio2/input/input11 E: ABS=660800011000003 E: EV=b E: ID_FOR_SEAT=input-platform-i8042-serio-2 E: ID_INPUT=1 E: ID_INPUT_TOUCHPAD=1 E: ID_PATH=platform-i8042-serio-2 E: ID_PATH_TAG=platform-i8042-serio-2 E: ID_SERIAL=noserial E: KEY=e520 10000 0 0 0 0 E: MODALIAS=input:b0011v0002p0007e01B1-e0,1,3,k110,145,148,14A,14D,14E,14F,ra0,1,18,1C,2F,35,36,39,3A,mlsfw E: NAME="SynPS/2 Synaptics TouchPad" E: PHYS="isa0060/serio2/input0" E: PRODUCT=11/2/7/1b1 E: PROP=5 E: SUBSYSTEM=input E: TAGS=:seat: L: device=../../../serio2 A: modalias=input:b0011v0002p0007e01B1-e0,1,3,k110,145,148,14A,14D,14E,14F,ra0,1,18,1C,2F,35,36,39,3A,mlsfw A: name=SynPS/2 Synaptics TouchPad A: phys=isa0060/serio2/input0 A: properties=5 A: uniq= P: /devices/platform/i8042/serio2 E: DRIVER=psmouse E: MODALIAS=serio:ty01pr00id00ex00 E: SERIO_EXTRA=00 E: SERIO_ID=00 E: SERIO_PROTO=00 E: SERIO_TYPE=01 E: SUBSYSTEM=serio A: bind_mode=auto A: description=i8042 AUX1 port L: driver=../../../../bus/serio/drivers/psmouse A: modalias=serio:ty01pr00id00ex00 A: protocol=SynPS/2 A: rate=80 A: resetafter=5 A: resolution=200 A: resync_time=0 P: /devices/platform/i8042 E: DRIVER=i8042 E: MODALIAS=platform:i8042 E: SUBSYSTEM=platform L: driver=../../../bus/platform/drivers/i8042 A: modalias=platform:i8042 ./tests/mir_test_framework/udev-recordings/mt-screen-detection.umockdev0000644000015600001650000000174612676616125026676 0ustar jenkinsjenkinsP: /devices/virtual/input/input4/event4 N: input/event4 E: DEVNAME=/dev/input/event4 E: ID_INPUT=1 E: ID_INPUT_KEY=1 E: ID_INPUT_TOUCHSCREEN=1 E: MAJOR=13 E: MINOR=68 E: SUBSYSTEM=input A: dev=13:68 L: device=../../input4 P: /devices/virtual/input/input4 E: ABS=6630000 1000003 E: EV=b E: ID_INPUT=1 E: ID_INPUT_KEY=1 E: ID_INPUT_TOUCHSCREEN=1 E: KEY=400 0 0 0 0 0 0 100000 0 0 0 E: MODALIAS=input:b0000v0000p0000e0000-e0,1,3,k74,14A,ra0,1,18,30,31,35,36,39,3A,mlsfw E: NAME="mtk-tpd" E: PRODUCT=0/0/0/0 E: PROP=2 E: SUBSYSTEM=input E: TAGS=:seat: A: capabilities/abs=6630000 1000003 A: capabilities/ev=b A: capabilities/ff=0 A: capabilities/key=400 0 0 0 0 0 0 100000 0 0 0 A: capabilities/led=0 A: capabilities/msc=0 A: capabilities/rel=0 A: capabilities/snd=0 A: capabilities/sw=0 A: id/bustype=0000 A: id/product=0000 A: id/vendor=0000 A: id/version=0000 A: modalias=input:b0000v0000p0000e0000-e0,1,3,k74,14A,ra0,1,18,30,31,35,36,39,3A,mlsfw A: name=mtk-tpd A: phys= A: properties=2 A: uniq= ./tests/mir_test_framework/udev-recordings/usb-keyboard.umockdev0000644000015600001650000002117212676616125025407 0ustar jenkinsjenkinsP: /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0/input/input24/event14 N: input/event14 S: input/by-id/usb-Fujitsu_Siemens_Computers_GmbH_FSC_KB_USB-event-kbd S: input/by-path/pci-0000:00:14.0-usb-0:2:1.0-event-kbd E: DEVLINKS=/dev/input/by-id/usb-Fujitsu_Siemens_Computers_GmbH_FSC_KB_USB-event-kbd /dev/input/by-path/pci-0000:00:14.0-usb-0:2:1.0-event-kbd E: DEVNAME=/dev/input/event14 E: ID_BUS=usb E: ID_INPUT=1 E: ID_INPUT_KEY=1 E: ID_INPUT_KEYBOARD=1 E: ID_MODEL=FSC_KB_USB E: ID_MODEL_ENC=FSC\x20KB\x20USB E: ID_MODEL_ID=1004 E: ID_PATH=pci-0000:00:14.0-usb-0:2:1.0 E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_2_1_0 E: ID_REVISION=0001 E: ID_SERIAL=Fujitsu_Siemens_Computers_GmbH_FSC_KB_USB E: ID_TYPE=hid E: ID_USB_DRIVER=usbhid E: ID_USB_INTERFACES=:030101:030000: E: ID_USB_INTERFACE_NUM=00 E: ID_VENDOR=Fujitsu_Siemens_Computers_GmbH E: ID_VENDOR_ENC=Fujitsu\x20Siemens\x20Computers\x20GmbH E: ID_VENDOR_ID=0bf8 E: MAJOR=13 E: MINOR=78 E: SUBSYSTEM=input E: XKBLAYOUT=us E: XKBMODEL=pc105 E: XKBOPTIONS=compose:menu,ctrl:nocaps E: XKBVARIANT=dvorak A: dev=13:78 L: device=../../input24 P: /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0/input/input24 E: EV=120013 E: ID_BUS=usb E: ID_FOR_SEAT=input-pci-0000_00_14_0-usb-0_2_1_0 E: ID_INPUT=1 E: ID_INPUT_KEY=1 E: ID_INPUT_KEYBOARD=1 E: ID_MODEL=FSC_KB_USB E: ID_MODEL_ENC=FSC\x20KB\x20USB E: ID_MODEL_ID=1004 E: ID_PATH=pci-0000:00:14.0-usb-0:2:1.0 E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_2_1_0 E: ID_REVISION=0001 E: ID_SERIAL=Fujitsu_Siemens_Computers_GmbH_FSC_KB_USB E: ID_TYPE=hid E: ID_USB_DRIVER=usbhid E: ID_USB_INTERFACES=:030101:030000: E: ID_USB_INTERFACE_NUM=00 E: ID_VENDOR=Fujitsu_Siemens_Computers_GmbH E: ID_VENDOR_ENC=Fujitsu\x20Siemens\x20Computers\x20GmbH E: ID_VENDOR_ID=0bf8 E: KEY=e080ffdf01cfffff fffffffffffffffe E: LED=1f E: MODALIAS=input:b0003v0BF8p1004e0110-e0,1,4,11,14,k77,7D,7E,7F,ram4,l0,1,2,3,4,sfw E: MSC=10 E: NAME="Fujitsu Siemens Computers GmbH FSC KB USB" E: PHYS="usb-0000:00:14.0-2/input0" E: PRODUCT=3/bf8/1004/110 E: PROP=0 E: SUBSYSTEM=input E: TAGS=:seat: E: UNIQ="" L: device=../../../3-2:1.0 A: modalias=input:b0003v0BF8p1004e0110-e0,1,4,11,14,k77,7D,7E,7F,ram4,l0,1,2,3,4,sfw A: name=Fujitsu Siemens Computers GmbH FSC KB USB A: phys=usb-0000:00:14.0-2/input0 A: properties=0 A: uniq= P: /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0 E: DEVTYPE=usb_interface E: DRIVER=usbhid E: ID_VENDOR_FROM_DATABASE=Fujitsu Siemens Computers E: INTERFACE=3/1/1 E: MODALIAS=usb:v0BF8p1004d0001dc00dsc00dp00ic03isc01ip01in00 E: PRODUCT=bf8/1004/1 E: SUBSYSTEM=usb E: TYPE=0/0/0 A: bAlternateSetting= 0 A: bInterfaceClass=03 A: bInterfaceNumber=00 A: bInterfaceProtocol=01 A: bInterfaceSubClass=01 A: bNumEndpoints=01 L: driver=../../../../../../bus/usb/drivers/usbhid A: interface=HID KEYBOARD (v2.0 rf) A: modalias=usb:v0BF8p1004d0001dc00dsc00dp00ic03isc01ip01in00 A: supports_autosuspend=1 P: /devices/pci0000:00/0000:00:14.0/usb3/3-2 N: bus/usb/003/016=1201100100000008F80B041001000102000109023B00020102A032090400000103010103092110010001223F000705810308000A090401000103000000092110010001225100070582030200A0 E: BUSNUM=003 E: DEVNAME=/dev/bus/usb/003/016 E: DEVNUM=016 E: DEVTYPE=usb_device E: DRIVER=usb E: ID_BUS=usb E: ID_MODEL=FSC_KB_USB E: ID_MODEL_ENC=FSC\x20KB\x20USB E: ID_MODEL_ID=1004 E: ID_REVISION=0001 E: ID_SERIAL=Fujitsu_Siemens_Computers_GmbH_FSC_KB_USB E: ID_USB_INTERFACES=:030101:030000: E: ID_VENDOR=Fujitsu_Siemens_Computers_GmbH E: ID_VENDOR_ENC=Fujitsu\x20Siemens\x20Computers\x20GmbH E: ID_VENDOR_FROM_DATABASE=Fujitsu Siemens Computers E: ID_VENDOR_ID=0bf8 E: MAJOR=189 E: MINOR=271 E: PRODUCT=bf8/1004/1 E: SUBSYSTEM=usb E: TYPE=0/0/0 A: authorized=1 A: avoid_reset_quirk=0 A: bConfigurationValue=1 A: bDeviceClass=00 A: bDeviceProtocol=00 A: bDeviceSubClass=00 A: bMaxPacketSize0=8 A: bMaxPower=100mA A: bNumConfigurations=1 A: bNumInterfaces= 2 A: bcdDevice=0001 A: bmAttributes=a0 A: busnum=3 A: configuration=FSC KB USB H: descriptors=1201100100000008F80B041001000102000109023B00020102A032090400000103010103092110010001223F000705810308000A090401000103000000092110010001225100070582030200A0 A: dev=189:271 A: devnum=16 A: devpath=2 L: driver=../../../../../bus/usb/drivers/usb A: idProduct=1004 A: idVendor=0bf8 A: ltm_capable=no A: manufacturer=Fujitsu Siemens Computers GmbH A: maxchild=0 L: port=../3-0:1.0/port2 A: product=FSC KB USB A: quirks=0x0 A: removable=removable A: speed=1.5 A: urbnum=136 A: version= 1.10 P: /devices/pci0000:00/0000:00:14.0/usb3 N: bus/usb/003/001=12010002090001406B1D020013030302010109021900010100E0000904000001090000000705810304000C E: BUSNUM=003 E: DEVNAME=/dev/bus/usb/003/001 E: DEVNUM=001 E: DEVTYPE=usb_device E: DRIVER=usb E: ID_BUS=usb E: ID_FOR_SEAT=usb-pci-0000_00_14_0 E: ID_MODEL=xHCI_Host_Controller E: ID_MODEL_ENC=xHCI\x20Host\x20Controller E: ID_MODEL_FROM_DATABASE=2.0 root hub E: ID_MODEL_ID=0002 E: ID_PATH=pci-0000:00:14.0 E: ID_PATH_TAG=pci-0000_00_14_0 E: ID_REVISION=0313 E: ID_SERIAL=Linux_3.13.0-8-generic_xhci_hcd_xHCI_Host_Controller_0000:00:14.0 E: ID_SERIAL_SHORT=0000:00:14.0 E: ID_USB_INTERFACES=:090000: E: ID_VENDOR=Linux_3.13.0-8-generic_xhci_hcd E: ID_VENDOR_ENC=Linux\x203.13.0-8-generic\x20xhci_hcd E: ID_VENDOR_FROM_DATABASE=Linux Foundation E: ID_VENDOR_ID=1d6b E: MAJOR=189 E: MINOR=256 E: PRODUCT=1d6b/2/313 E: SUBSYSTEM=usb E: TAGS=:seat: E: TYPE=9/0/1 A: authorized=1 A: authorized_default=1 A: avoid_reset_quirk=0 A: bConfigurationValue=1 A: bDeviceClass=09 A: bDeviceProtocol=01 A: bDeviceSubClass=00 A: bMaxPacketSize0=64 A: bMaxPower=0mA A: bNumConfigurations=1 A: bNumInterfaces= 1 A: bcdDevice=0313 A: bmAttributes=e0 A: busnum=3 A: configuration= H: descriptors=12010002090001406B1D020013030302010109021900010100E0000904000001090000000705810304000C A: dev=189:256 A: devnum=1 A: devpath=0 L: driver=../../../../bus/usb/drivers/usb A: idProduct=0002 A: idVendor=1d6b A: ltm_capable=no A: manufacturer=Linux 3.13.0-8-generic xhci_hcd A: maxchild=14 A: product=xHCI Host Controller A: quirks=0x0 A: removable=unknown A: serial=0000:00:14.0 A: speed=480 A: urbnum=236 A: version= 2.00 P: /devices/pci0000:00/0000:00:14.0 E: DRIVER=xhci_hcd E: ID_MODEL_FROM_DATABASE=Lynx Point USB xHCI Host Controller E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller E: ID_VENDOR_FROM_DATABASE=Intel Corporation E: MODALIAS=pci:v00008086d00008C31sv00001558sd00007410bc0Csc03i30 E: PCI_CLASS=C0330 E: PCI_ID=8086:8C31 E: PCI_SLOT_NAME=0000:00:14.0 E: PCI_SUBSYS_ID=1558:7410 E: SUBSYSTEM=pci A: broken_parity_status=0 A: class=0x0c0330 H: config=8680318C060490020430030C000000000400E2F700000000000000000000000000000000000000000000000058151074000000007000000000000000FF010000FD01368089C60F8000000000000000009F6E8807000000000000000000000000302000000000000000000000000000000180C2C108000000000000000000000005008700F802E0FE000000000000000000000000000000000000000000000000400100000000000000000000000000000F000100000000000000000000000000030420C0030C3000030C300000000000FF3F0000FF3F00003F0000003F000000A0000000D03C000000000000D8D8D8080000000000000000B10F050800000000 A: consistent_dma_mask_bits=64 A: d3cold_allowed=1 A: device=0x8c31 A: dma_mask_bits=64 L: driver=../../../bus/pci/drivers/xhci_hcd A: enabled=1 A: irq=42 A: local_cpulist=0-7 A: local_cpus=00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff A: modalias=pci:v00008086d00008C31sv00001558sd00007410bc0Csc03i30 A: msi_bus= A: numa_node=-1 A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 11 11 2112 11\nxHCI ring segments 40 40 4096 40\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 9 32 128 1\nbuffer-32 0 0 32 0 A: resource=0x00000000f7e20000 0x00000000f7e2ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 A: subsystem_device=0x7410 A: subsystem_vendor=0x1558 A: vendor=0x8086 ./tests/mir_test_framework/udev-recordings/laptop-keyboard-hello.evemu0000644000015600001650000001434712676616125026530 0ustar jenkinsjenkins# device /dev/input/event4 # EVEMU 1.2 # Input device name: "AT Translated Set 2 keyboard" # Input device ID: bus 0x11 vendor 0x01 product 0x01 version 0xab83 # Supported events: # Event type 0 (EV_SYN) # Event code 0 (SYN_REPORT) # Event code 1 (SYN_CONFIG) # Event code 4 (FF_STATUS_STOPPED) # Event code 17 ((null)) # Event code 20 ((null)) # Event type 1 (EV_KEY) # Event code 1 (KEY_ESC) # Event code 2 (KEY_1) # Event code 3 (KEY_2) # Event code 4 (KEY_3) # Event code 5 (KEY_4) # Event code 6 (KEY_5) # Event code 7 (KEY_6) # Event code 8 (KEY_7) # Event code 9 (KEY_8) # Event code 10 (KEY_9) # Event code 11 (KEY_0) # Event code 12 (KEY_MINUS) # Event code 13 (KEY_EQUAL) # Event code 14 (KEY_BACKSPACE) # Event code 15 (KEY_TAB) # Event code 16 (KEY_Q) # Event code 17 (KEY_W) # Event code 18 (KEY_E) # Event code 19 (KEY_R) # Event code 20 (KEY_T) # Event code 21 (KEY_Y) # Event code 22 (KEY_U) # Event code 23 (KEY_I) # Event code 24 (KEY_O) # Event code 25 (KEY_P) # Event code 26 (KEY_LEFTBRACE) # Event code 27 (KEY_RIGHTBRACE) # Event code 28 (KEY_ENTER) # Event code 29 (KEY_LEFTCTRL) # Event code 30 (KEY_A) # Event code 31 (KEY_S) # Event code 32 (KEY_D) # Event code 33 (KEY_F) # Event code 34 (KEY_G) # Event code 35 (KEY_H) # Event code 36 (KEY_J) # Event code 37 (KEY_K) # Event code 38 (KEY_L) # Event code 39 (KEY_SEMICOLON) # Event code 40 (KEY_APOSTROPHE) # Event code 41 (KEY_GRAVE) # Event code 42 (KEY_LEFTSHIFT) # Event code 43 (KEY_BACKSLASH) # Event code 44 (KEY_Z) # Event code 45 (KEY_X) # Event code 46 (KEY_C) # Event code 47 (KEY_V) # Event code 48 (KEY_B) # Event code 49 (KEY_N) # Event code 50 (KEY_M) # Event code 51 (KEY_COMMA) # Event code 52 (KEY_DOT) # Event code 53 (KEY_SLASH) # Event code 54 (KEY_RIGHTSHIFT) # Event code 55 (KEY_KPASTERISK) # Event code 56 (KEY_LEFTALT) # Event code 57 (KEY_SPACE) # Event code 58 (KEY_CAPSLOCK) # Event code 59 (KEY_F1) # Event code 60 (KEY_F2) # Event code 61 (KEY_F3) # Event code 62 (KEY_F4) # Event code 63 (KEY_F5) # Event code 64 (KEY_F6) # Event code 65 (KEY_F7) # Event code 66 (KEY_F8) # Event code 67 (KEY_F9) # Event code 68 (KEY_F10) # Event code 69 (KEY_NUMLOCK) # Event code 70 (KEY_SCROLLLOCK) # Event code 71 (KEY_KP7) # Event code 72 (KEY_KP8) # Event code 73 (KEY_KP9) # Event code 74 (KEY_KPMINUS) # Event code 75 (KEY_KP4) # Event code 76 (KEY_KP5) # Event code 77 (KEY_KP6) # Event code 78 (KEY_KPPLUS) # Event code 79 (KEY_KP1) # Event code 80 (KEY_KP2) # Event code 81 (KEY_KP3) # Event code 82 (KEY_KP0) # Event code 83 (KEY_KPDOT) # Event code 85 (KEY_ZENKAKUHANKAKU) # Event code 86 (KEY_102ND) # Event code 87 (KEY_F11) # Event code 88 (KEY_F12) # Event code 89 (KEY_RO) # Event code 90 (KEY_KATAKANA) # Event code 91 (KEY_HIRAGANA) # Event code 92 (KEY_HENKAN) # Event code 93 (KEY_KATAKANAHIRAGANA) # Event code 94 (KEY_MUHENKAN) # Event code 95 (KEY_KPJPCOMMA) # Event code 96 (KEY_KPENTER) # Event code 97 (KEY_RIGHTCTRL) # Event code 98 (KEY_KPSLASH) # Event code 99 (KEY_SYSRQ) # Event code 100 (KEY_RIGHTALT) # Event code 102 (KEY_HOME) # Event code 103 (KEY_UP) # Event code 104 (KEY_PAGEUP) # Event code 105 (KEY_LEFT) # Event code 106 (KEY_RIGHT) # Event code 107 (KEY_END) # Event code 108 (KEY_DOWN) # Event code 109 (KEY_PAGEDOWN) # Event code 110 (KEY_INSERT) # Event code 111 (KEY_DELETE) # Event code 112 (KEY_MACRO) # Event code 113 (KEY_MUTE) # Event code 114 (KEY_VOLUMEDOWN) # Event code 115 (KEY_VOLUMEUP) # Event code 116 (KEY_POWER) # Event code 117 (KEY_KPEQUAL) # Event code 118 (KEY_KPPLUSMINUS) # Event code 119 (KEY_PAUSE) # Event code 121 (KEY_KPCOMMA) # Event code 122 (KEY_HANGEUL) # Event code 123 (KEY_HANJA) # Event code 124 (KEY_YEN) # Event code 125 (KEY_LEFTMETA) # Event code 126 (KEY_RIGHTMETA) # Event code 127 (KEY_COMPOSE) # Event code 128 (KEY_STOP) # Event code 140 (KEY_CALC) # Event code 142 (KEY_SLEEP) # Event code 143 (KEY_WAKEUP) # Event code 155 (KEY_MAIL) # Event code 156 (KEY_BOOKMARKS) # Event code 157 (KEY_COMPUTER) # Event code 158 (KEY_BACK) # Event code 159 (KEY_FORWARD) # Event code 163 (KEY_NEXTSONG) # Event code 164 (KEY_PLAYPAUSE) # Event code 165 (KEY_PREVIOUSSONG) # Event code 166 (KEY_STOPCD) # Event code 172 (KEY_HOMEPAGE) # Event code 173 (KEY_REFRESH) # Event code 183 (KEY_F13) # Event code 184 (KEY_F14) # Event code 185 (KEY_F15) # Event code 217 (KEY_SEARCH) # Event code 226 (KEY_MEDIA) # Event type 4 (EV_MSC) # Event code 4 (MSC_SCAN) # Event type 17 (EV_LED) # Event code 0 (LED_NUML) # Event code 1 (LED_CAPSL) # Event code 2 (LED_SCROLLL) # Event type 20 (EV_REP) # Properties: # N: AT Translated Set 2 keyboard # I: 0011 0001 0001 ab83 # P: 00 00 00 00 00 00 00 00 # B: 00 13 00 12 00 00 00 00 00 # B: 01 fe ff ff ff ff ff ff ff # B: 01 ff ff ef ff df ff ff fe # B: 01 01 d0 00 f8 78 30 80 03 # B: 01 00 00 00 02 04 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 02 00 00 00 00 00 00 00 00 # B: 03 00 00 00 00 00 00 00 00 # B: 04 10 00 00 00 00 00 00 00 # B: 05 00 00 00 00 00 00 00 00 # B: 11 07 00 00 00 00 00 00 00 # B: 12 00 00 00 00 00 00 00 00 # B: 15 00 00 00 00 00 00 00 00 # B: 15 00 00 00 00 00 00 00 00 ################################ # Waiting for events # ################################ E: 0.000000 0004 0004 0028 # EV_MSC / MSC_SCAN 28 E: 0.000000 0001 001c 0000 # EV_KEY / KEY_ENTER 0 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.000000 0004 0004 0036 # EV_MSC / MSC_SCAN 36 E: 0.000000 0001 0024 0001 # EV_KEY / KEY_J 1 ./tests/mir_test_framework/udev-recordings/standard-drm-render-nodes.umockdev0000644000015600001650000002042612676616125027764 0ustar jenkinsjenkinsP: /devices/pci0000:00/0000:00:02.0/drm/renderD128 N: dri/renderD128 E: DEVNAME=/dev/dri/renderD128 E: DEVTYPE=drm_minor E: ID_FOR_SEAT=drm-pci-0000_00_02_0 E: ID_PATH=pci-0000:00:02.0 E: ID_PATH_TAG=pci-0000_00_02_0 E: MAJOR=226 E: MINOR=128 E: SUBSYSTEM=drm E: TAGS=:seat:uaccess: A: dev=226:128 L: device=../../../0000:00:02.0 A: power/async=disabled A: power/control=auto A: power/runtime_active_kids=0 A: power/runtime_active_time=0 A: power/runtime_enabled=disabled A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 A: power/runtime_usage=0 P: /devices/pci0000:00/0000:00:02.0 E: DRIVER=i915 E: ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller E: ID_PCI_CLASS_FROM_DATABASE=Display controller E: ID_PCI_INTERFACE_FROM_DATABASE=VGA controller E: ID_PCI_SUBCLASS_FROM_DATABASE=VGA compatible controller E: ID_VENDOR_FROM_DATABASE=Intel Corporation E: MODALIAS=pci:v00008086d00000166sv0000106Bsd000000F7bc03sc00i00 E: PCI_CLASS=30000 E: PCI_ID=8086:0166 E: PCI_SLOT_NAME=0000:00:02.0 E: PCI_SUBSYS_ID=106B:00F7 E: SUBSYSTEM=pci A: boot_vga=0 A: broken_parity_status=0 A: class=0x030000 H: config=86806601070490000900000300000000040040C1000000000C0000B0000000000130000000000000000000006B10F70000000000900000000000000000010000 A: consistent_dma_mask_bits=40 A: d3cold_allowed=1 A: device=0x0166 A: dma_mask_bits=40 L: driver=../../../bus/pci/drivers/i915 A: driver_override=(null) A: enable=1 A: irq=37 A: local_cpulist=0-7 A: local_cpus=00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff A: modalias=pci:v00008086d00000166sv0000106Bsd000000F7bc03sc00i00 A: msi_bus=1 A: msi_irqs/37=msi A: numa_node=-1 A: power/async=enabled A: power/control=on A: power/runtime_active_kids=0 A: power/runtime_active_time=273632304 A: power/runtime_enabled=forbidden A: power/runtime_status=active A: power/runtime_suspended_time=0 A: power/runtime_usage=2 A: resource=0x00000000c1400000 0x00000000c17fffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x00000000b0000000 0x00000000bfffffff 0x000000000014220c\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000003000 0x000000000000303f 0x0000000000040101\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000020000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 A: subsystem_device=0x00f7 A: subsystem_vendor=0x106b A: vendor=0x8086 P: /devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/renderD129 N: dri/renderD129 E: DEVNAME=/dev/dri/renderD129 E: DEVTYPE=drm_minor E: ID_FOR_SEAT=drm-pci-0000_01_00_0 E: ID_PATH=pci-0000:01:00.0 E: ID_PATH_TAG=pci-0000_01_00_0 E: MAJOR=226 E: MINOR=129 E: SUBSYSTEM=drm E: TAGS=:seat:uaccess: A: dev=226:129 L: device=../../../0000:01:00.0 A: power/async=disabled A: power/control=auto A: power/runtime_active_kids=0 A: power/runtime_active_time=0 A: power/runtime_enabled=disabled A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 A: power/runtime_usage=0 P: /devices/pci0000:00/0000:00:01.0/0000:01:00.0 E: DRIVER=nouveau E: ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 650M Mac Edition] E: ID_PCI_CLASS_FROM_DATABASE=Display controller E: ID_PCI_INTERFACE_FROM_DATABASE=VGA controller E: ID_PCI_SUBCLASS_FROM_DATABASE=VGA compatible controller E: ID_VENDOR_FROM_DATABASE=NVIDIA Corporation E: MODALIAS=pci:v000010DEd00000FD5sv0000106Bsd000000F2bc03sc00i00 E: PCI_CLASS=30000 E: PCI_ID=10DE:0FD5 E: PCI_SLOT_NAME=0000:01:00.0 E: PCI_SUBSYS_ID=106B:00F2 E: SUBSYSTEM=pci A: boot_vga=1 A: broken_parity_status=0 A: class=0x030000 H: config=DE10D50F07041000A100000340008000000000C00C000090000000000C0000A00000000001200000000000006B10F200000000C1600000000000000000010000 A: consistent_dma_mask_bits=40 A: d3cold_allowed=1 A: device=0x0fd5 A: dma_mask_bits=40 L: driver=../../../../bus/pci/drivers/nouveau A: driver_override=(null) A: enable=1 A: irq=39 A: local_cpulist=0-7 A: local_cpus=00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff A: modalias=pci:v000010DEd00000FD5sv0000106Bsd000000F2bc03sc00i00 A: msi_bus=1 A: msi_irqs/39=msi A: numa_node=-1 A: power/async=enabled A: power/autosuspend_delay_ms=5000 A: power/control=auto A: power/runtime_active_kids=0 A: power/runtime_active_time=273632308 A: power/runtime_enabled=enabled A: power/runtime_status=active A: power/runtime_suspended_time=0 A: power/runtime_usage=1 A: power/wakeup=disabled A: power/wakeup_abort_count= A: power/wakeup_active= A: power/wakeup_active_count= A: power/wakeup_count= A: power/wakeup_expire_count= A: power/wakeup_last_time_ms= A: power/wakeup_max_time_ms= A: power/wakeup_total_time_ms= A: resource=0x00000000c0000000 0x00000000c0ffffff 0x0000000000040200\n0x0000000090000000 0x000000009fffffff 0x000000000014220c\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x00000000a0000000 0x00000000a1ffffff 0x000000000014220c\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000002000 0x000000000000207f 0x0000000000040101\n0x00000000c1000000 0x00000000c107ffff 0x000000000004e200\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 A: subsystem_device=0x00f2 A: subsystem_vendor=0x106b A: vendor=0x10de P: /devices/pci0000:00/0000:00:01.0 E: DRIVER=pcieport E: ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port E: ID_PCI_CLASS_FROM_DATABASE=Bridge E: ID_PCI_INTERFACE_FROM_DATABASE=Normal decode E: ID_PCI_SUBCLASS_FROM_DATABASE=PCI bridge E: ID_VENDOR_FROM_DATABASE=Intel Corporation E: MODALIAS=pci:v00008086d00000151sv0000106Bsd000000F7bc06sc04i00 E: PCI_CLASS=60400 E: PCI_ID=8086:0151 E: PCI_SLOT_NAME=0000:00:01.0 E: PCI_SUBSYS_ID=106B:00F7 E: SUBSYSTEM=pci A: broken_parity_status=0 A: class=0x060400 H: config=868051010704100009000406400081000000000000000000000101002020002000C000C10190F1A10000000000000000000000008800000000000000FF010000 A: consistent_dma_mask_bits=32 A: d3cold_allowed=0 A: device=0x0151 A: dma_mask_bits=32 L: driver=../../../bus/pci/drivers/pcieport A: driver_override=(null) A: enable=1 A: irq=24 A: local_cpulist=0-7 A: local_cpus=00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff A: modalias=pci:v00008086d00000151sv0000106Bsd000000F7bc06sc04i00 A: msi_bus=1 A: msi_irqs/24=msi A: numa_node=-1 A: power/async=enabled A: power/clk_ctl=0 A: power/control=on A: power/link_state=7 A: power/runtime_active_kids=2 A: power/runtime_active_time=273632312 A: power/runtime_enabled=forbidden A: power/runtime_status=active A: power/runtime_suspended_time=0 A: power/runtime_usage=2 A: power/wakeup=disabled A: power/wakeup_abort_count= A: power/wakeup_active= A: power/wakeup_active_count= A: power/wakeup_count= A: power/wakeup_expire_count= A: power/wakeup_last_time_ms= A: power/wakeup_max_time_ms= A: power/wakeup_total_time_ms= A: resource=0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000002000 0x0000000000002fff 0x0000000000000100\n0x00000000c0000000 0x00000000c10fffff 0x0000000000000200\n0x0000000090000000 0x00000000a1ffffff 0x0000000000102201\n0x0000000000000000 0x0000000000000000 0x0000000000000000 A: subsystem_device=0x00f7 A: subsystem_vendor=0x106b A: vendor=0x8086 ./tests/mir_test_framework/udev-recordings/standard-drm-render-nodes.ioctl0000644000015600001650000000003112676616125027247 0ustar jenkinsjenkins@DEV /dev/dri/renderD129 ./tests/mir_test_framework/udev-recordings/laptop-mouse-motion.evemu0000644000015600001650000001215512676616125026255 0ustar jenkinsjenkins# device /dev/input/event5 # EVEMU 1.2 # Input device name: "SynPS/2 Synaptics TouchPad" # Input device ID: bus 0x11 vendor 0x02 product 0x07 version 0x1b1 # Supported events: # Event type 0 (EV_SYN) # Event code 0 (SYN_REPORT) # Event code 1 (SYN_CONFIG) # Event code 2 (SYN_MT_REPORT) # Event code 3 (SYN_DROPPED) # Event code 4 ((null)) # Event code 5 ((null)) # Event code 6 ((null)) # Event code 7 ((null)) # Event code 8 ((null)) # Event code 9 ((null)) # Event code 10 ((null)) # Event code 11 ((null)) # Event code 12 ((null)) # Event code 13 ((null)) # Event code 14 ((null)) # Event type 1 (EV_KEY) # Event code 272 (BTN_LEFT) # Event code 273 (BTN_RIGHT) # Event code 325 (BTN_TOOL_FINGER) # Event code 330 (BTN_TOUCH) # Event code 333 (BTN_TOOL_DOUBLETAP) # Event code 334 (BTN_TOOL_TRIPLETAP) # Event type 3 (EV_ABS) # Event code 0 (ABS_X) # Value 4171 # Min 1472 # Max 5470 # Fuzz 0 # Flat 0 # Resolution 60 # Event code 1 (ABS_Y) # Value 3320 # Min 1408 # Max 4498 # Fuzz 0 # Flat 0 # Resolution 85 # Event code 24 (ABS_PRESSURE) # Value 0 # Min 0 # Max 255 # Fuzz 0 # Flat 0 # Resolution 0 # Event code 28 (ABS_TOOL_WIDTH) # Value 0 # Min 0 # Max 15 # Fuzz 0 # Flat 0 # Resolution 0 # Event code 47 (ABS_MT_SLOT) # Value 0 # Min 0 # Max 1 # Fuzz 0 # Flat 0 # Resolution 0 # Event code 53 (ABS_MT_POSITION_X) # Value 0 # Min 1472 # Max 5470 # Fuzz 0 # Flat 0 # Resolution 60 # Event code 54 (ABS_MT_POSITION_Y) # Value 0 # Min 1408 # Max 4498 # Fuzz 0 # Flat 0 # Resolution 85 # Event code 57 (ABS_MT_TRACKING_ID) # Value 0 # Min 0 # Max 65535 # Fuzz 0 # Flat 0 # Resolution 0 # Properties: # Property type 0 (INPUT_PROP_POINTER) # Property type 3 (INPUT_PROP_SEMI_MT) # N: SynPS/2 Synaptics TouchPad # I: 0011 0002 0007 01b1 # P: 09 00 00 00 00 00 00 00 # B: 00 0b 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 03 00 00 00 00 00 # B: 01 20 64 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 02 00 00 00 00 00 00 00 00 # B: 03 03 00 00 11 00 80 60 02 # B: 04 00 00 00 00 00 00 00 00 # B: 05 00 00 00 00 00 00 00 00 # B: 11 00 00 00 00 00 00 00 00 # B: 12 00 00 00 00 00 00 00 00 # B: 14 00 00 00 00 00 00 00 00 # B: 15 00 00 00 00 00 00 00 00 # B: 15 00 00 00 00 00 00 00 00 # A: 00 1472 5470 0 0 60 # A: 01 1408 4498 0 0 85 # A: 18 0 255 0 0 0 # A: 1c 0 15 0 0 0 # A: 2f 0 1 0 0 0 # A: 35 1472 5470 0 0 60 # A: 36 1408 4498 0 0 85 # A: 39 0 65535 0 0 0 ################################ # Waiting for events # ################################ E: 0.000000 0003 0039 0137 # EV_ABS / ABS_MT_TRACKING_ID 137 E: 0.000000 0003 0035 3461 # EV_ABS / ABS_MT_POSITION_X 3461 E: 0.000000 0003 0036 2303 # EV_ABS / ABS_MT_POSITION_Y 2303 E: 0.000000 0003 0000 3461 # EV_ABS / ABS_X 3461 E: 0.000000 0003 0001 2303 # EV_ABS / ABS_Y 2303 E: 0.000000 0003 0018 0015 # EV_ABS / ABS_PRESSURE 15 E: 0.000000 0003 001c 0006 # EV_ABS / ABS_TOOL_WIDTH 6 E: 0.000000 0001 0145 0001 # EV_KEY / BTN_TOOL_FINGER 1 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.000000 0003 0035 3499 # EV_ABS / ABS_MT_POSITION_X 3499 E: 0.000000 0003 0036 2304 # EV_ABS / ABS_MT_POSITION_Y 2304 E: 0.000000 0001 014a 0001 # EV_KEY / BTN_TOUCH 1 E: 0.000000 0003 0000 3499 # EV_ABS / ABS_X 3499 E: 0.000000 0003 0001 2304 # EV_ABS / ABS_Y 2304 E: 0.000000 0003 0018 0047 # EV_ABS / ABS_PRESSURE 47 E: 0.000000 0003 001c 0004 # EV_ABS / ABS_TOOL_WIDTH 4 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.000000 0003 0035 3506 # EV_ABS / ABS_MT_POSITION_X 3506 E: 0.000000 0003 0036 2302 # EV_ABS / ABS_MT_POSITION_Y 2302 E: 0.000000 0003 0000 3506 # EV_ABS / ABS_X 3506 E: 0.000000 0003 0001 2302 # EV_ABS / ABS_Y 2302 E: 0.000000 0003 0018 0054 # EV_ABS / ABS_PRESSURE 54 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.000000 0003 0035 3522 # EV_ABS / ABS_MT_POSITION_X 3522 E: 0.000000 0003 0036 2306 # EV_ABS / ABS_MT_POSITION_Y 2306 E: 0.000000 0003 0000 3522 # EV_ABS / ABS_X 3522 E: 0.000000 0003 0001 2306 # EV_ABS / ABS_Y 2306 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.000000 0003 001c 0005 # EV_ABS / ABS_TOOL_WIDTH 5 ./tests/mir_test_framework/udev-recordings/synaptics-touchpad.ioctl0000644000015600001650000000377312676616125026146 0ustar jenkinsjenkins@DEV /dev/input/event12 EVIOCGNAME(0) 27 53796E50532F322053796E61707469637320546F756368506164000000000000200B0050157F0000C895BE6B157F0000C0080050157F0000C0F4275D157F0000F03FD30100000000F523476D157F00 EVIOCGVERSION 0 01000100 EVIOCGID 0 110002000700B101 EVIOCGPHYS(0) 22 697361303036302F736572696F322F696E707574300068506164000000000000200B0050157F0000C895BE6B157F0000C0080050157F0000C0F4275D157F0000F03FD30100000000F523476D157F00 EVIOCGBIT(1) 96 0000000000000000000000000000000000000000000000000000000000000000000001000000000020E5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGBIT(3) 8 0300001100806006 EVIOCGBIT(2) 2 0000 EVIOCGBIT(5) 2 0000 EVIOCGBIT(17) 2 0000 EVIOCGBIT(21) 16 00000000000000000000000000000000 EVIOCGPROP(0) 4 05000000 EVIOCGABS(53) 0 00000000C00500003016000008000000000000002E000000 EVIOCGABS(54) 0 00000000800500007812000008000000000000003E000000 EVIOCGABS(58) 0 0000000000000000FF000000000000000000000000000000 EVIOCGABS(57) 0 0000000000000000FFFF0000000000000000000000000000 EVIOCGABS(47) 0 000000000000000001000000000000000000000000000000 EVIOCGKEY(0) 96 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGNAME(0) 27 53796E50532F322053796E61707469637320546F756368506164000000000000200B00A8A37F0000C8A59DC6A37F0000C00800A8A37F0000C00407B8A37F0000F0DF950100000000F53326C8A37F00 EVIOCGPHYS(0) 22 697361303036302F736572696F322F696E707574300068506164000000000000200B00A8A37F0000C8A59DC6A37F0000C00800A8A37F0000C00407B8A37F0000F0DF950100000000F53326C8A37F00 EVIOCGNAME(0) 18 48444120496E74656C20504348204D6963007574300068506164000000000000200B00CC1C7F0000C81563E91C7F0000C00800CC1C7F0000C074CCDA1C7F0000307F170200000000F5A3EBEA1C7F00 EVIOCGPHYS(0) 5 414C5341006E74656C20504348204D6963007574300068506164000000000000200B00CC1C7F0000C81563E91C7F0000C00800CC1C7F0000C074CCDA1C7F0000307F170200000000F5A3EBEA1C7F00 ./tests/mir_test_framework/udev-recordings/laptop-mouse.ioctl0000644000015600001650000000377312676616125024751 0ustar jenkinsjenkins@DEV /dev/input/event5 EVIOCGNAME(0) 27 53796E50532F322053796E61707469637320546F756368506164000000000000E0FA01B02A7F00000000000000000000B0A501B02A7F000020DC01B02A7F0000000100000000000004C234C92A7F00 EVIOCGVERSION 0 01000100 EVIOCGID 0 110002000700B101 EVIOCGPHYS(0) 22 697361303036302F736572696F312F696E707574300068506164000000000000E0FA01B02A7F00000000000000000000B0A501B02A7F000020DC01B02A7F0000000100000000000004C234C92A7F00 EVIOCGBIT(1) 96 000000000000000000000000000000000000000000000000000000000000000000000300000000002064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGBIT(3) 8 0300001100806002 EVIOCGBIT(2) 2 0000 EVIOCGBIT(5) 2 0000 EVIOCGBIT(17) 2 0000 EVIOCGBIT(21) 16 00000000000000000000000000000000 EVIOCGPROP(0) 4 09000000 EVIOCGABS(53) 0 00000000C00500005E15000000000000000000003C000000 EVIOCGABS(54) 0 000000008005000092110000000000000000000055000000 EVIOCGABS(57) 0 0000000000000000FFFF0000000000000000000000000000 EVIOCGABS(47) 0 010000000000000001000000000000000000000000000000 EVIOCGKEY(0) 96 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGNAME(0) 27 53796E50532F322053796E61707469637320546F756368506164000000000000E0FA013C997F0000000000000000000090DA013C997F000050EF033C997F0000000100000000000004B2F654997F00 EVIOCGPHYS(0) 22 697361303036302F736572696F312F696E707574300068506164000000000000E0FA013C997F0000000000000000000090DA013C997F000050EF033C997F0000000100000000000004B2F654997F00 EVIOCGABS(47) 0 000000000000000001000000000000000000000000000000 EVIOCGNAME(0) 27 53796E50532F322053796E61707469637320546F75636850616400000000000040C50298B17F0000000000000000000060F40198B17F000070D70298B17F0000000100000000000004128BAEB17F00 EVIOCGPHYS(0) 22 697361303036302F736572696F312F696E70757430006850616400000000000040C50298B17F0000000000000000000060F40198B17F000070D70298B17F0000000100000000000004128BAEB17F00 ./tests/mir_test_framework/udev-recordings/usb-mouse.ioctl0000644000015600001650000000224612676616125024235 0ustar jenkinsjenkins@DEV /dev/input/event13 EVIOCGNAME(0) 27 4C6F67697465636820555342204F70746963616C204D6F757365006C29CED7F73013430200000000C0200014ED7F000080D1FF1BED7F000000000000000000000000000000000000C0F9FF1BED7F00 EVIOCGVERSION 0 01000100 EVIOCGID 0 03006D0418C01101 EVIOCGPHYS(0) 26 7573622D303030303A30303A31342E302D332F696E7075743000006C29CED7F73013430200000000C0200014ED7F000080D1FF1BED7F000000000000000000000000000000000000C0F9FF1BED7F00 EVIOCGUNIQ(0) 1 0073622D303030303A30303A31342E302D332F696E7075743000006C29CED7F73013430200000000C0200014ED7F000080D1FF1BED7F000000000000000000000000000000000000C0F9FF1BED7F00 EVIOCGBIT(1) 96 000000000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGBIT(3) 8 0000000000000000 EVIOCGBIT(2) 2 0301 EVIOCGBIT(5) 2 0000 EVIOCGBIT(17) 2 0000 EVIOCGBIT(21) 16 00000000000000000000000000000000 EVIOCGPROP(0) 4 00000000 EVIOCGKEY(0) 96 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ./tests/mir_test_framework/udev-recordings/usb-keyboard.ioctl0000644000015600001650000000172512676616125024706 0ustar jenkinsjenkins@DEV /dev/input/event14 EVIOCGNAME(0) 42 46756A69747375205369656D656E7320436F6D70757465727320476D624820465343204B42205553420000C0837F000080D1FFC7837F000000000000000000000000000000000000C0F9FFC7837F00 EVIOCGVERSION 0 01000100 EVIOCGID 0 0300F80B04101001 EVIOCGPHYS(0) 26 7573622D303030303A30303A31342E302D322F696E7075743000476D624820465343204B42205553420000C0837F000080D1FFC7837F000000000000000000000000000000000000C0F9FFC7837F00 EVIOCGUNIQ(0) 1 0073622D303030303A30303A31342E302D322F696E7075743000476D624820465343204B42205553420000C0837F000080D1FFC7837F000000000000000000000000000000000000C0F9FFC7837F00 EVIOCGBIT(1) 96 FEFFFFFFFFFFFFFFFFFFCF01DFFF80E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGBIT(3) 8 0000000000000000 EVIOCGBIT(2) 2 0000 EVIOCGBIT(5) 2 0000 EVIOCGBIT(17) 2 1F00 EVIOCGBIT(21) 16 00000000000000000000000000000000 EVIOCGPROP(0) 4 00000000 ./tests/mir_test_framework/udev-recordings/laptop-keyboard.ioctl0000644000015600001650000000144512676616125025413 0ustar jenkinsjenkins@DEV /dev/input/event4 EVIOCGNAME(0) 29 4154205472616E736C61746564205365742032206B6579626F61726400E9B73A300372020000000000CB0170ED7F000080E1C47FED7F000000000000000000000000000000000000C009C57FED7F00 EVIOCGVERSION 0 01000100 EVIOCGID 0 11000100010083AB EVIOCGPHYS(0) 22 697361303036302F736572696F302F696E707574300079626F61726400E9B73A300372020000000000CB0170ED7F000080E1C47FED7F000000000000000000000000000000000000C009C57FED7F00 EVIOCGBIT(1) 96 FEFFFFFFFFFFFFFFFFFFEFFFDFFFFFFE01D000F878308003000000020400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGBIT(3) 8 0000000000000000 EVIOCGBIT(2) 2 0000 EVIOCGBIT(5) 2 0000 EVIOCGBIT(17) 2 0700 EVIOCGBIT(21) 16 00000000000000000000000000000000 EVIOCGPROP(0) 4 00000000 ./tests/mir_test_framework/udev-recordings/bluetooth-magic-trackpad.ioctl0000644000015600001650000000315612676616125027171 0ustar jenkinsjenkins@DEV /dev/input/event13 EVIOCGNAME(0) 24 4170706C6520576972656C65737320547261636B70616400003C6F4488EC284430A3960100000000B08601F81A7F000080018F071B7F000000000000000000000000000000000000C0298F071B7F00 EVIOCGVERSION 0 01000100 EVIOCGID 0 0500AC050E036001 EVIOCGPHYS(0) 18 62383A37363A33663A37383A65623A373600636B70616400003C6F4488EC284430A3960100000000B08601F81A7F000080018F071B7F000000000000000000000000000000000000C0298F071B7F00 EVIOCGUNIQ(0) 18 64383A61323A35653A66383A37303A643600636B70616400003C6F4488EC284430A3960100000000B08601F81A7F000080018F071B7F000000000000000000000000000000000000C0298F071B7F00 EVIOCGBIT(1) 96 0000000000000000000000000000000000000000000000000000000000000000000001000000000020E5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGBIT(3) 8 0300000000807302 EVIOCGBIT(2) 2 0000 EVIOCGBIT(5) 2 0000 EVIOCGBIT(17) 2 0000 EVIOCGBIT(21) 16 00000000000000000000000000000000 EVIOCGPROP(0) 4 05000000 EVIOCGABS(53) 0 00000000A3F4FFFF5F0C000004000000000000002E000000 EVIOCGABS(54) 0 0000000068F6FFFF050A000004000000000000002D000000 EVIOCGABS(48) 0 0000000000000000FC030000040000000000000000000000 EVIOCGABS(49) 0 0000000000000000FC030000040000000000000000000000 EVIOCGABS(52) 0 00000000E1FFFFFF20000000010000000000000000000000 EVIOCGABS(57) 0 0000000000000000FFFF0000000000000000000000000000 EVIOCGABS(47) 0 04000000000000000F000000000000000000000000000000 EVIOCGKEY(0) 96 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ./tests/mir_test_framework/udev-recordings/laptop-mouse.umockdev0000644000015600001650000000654012676616125025447 0ustar jenkinsjenkinsP: /devices/platform/i8042/serio1/input/input5/event5 N: input/event5 S: input/by-path/platform-i8042-serio-1-event-mouse E: DEVLINKS=/dev/input/by-path/platform-i8042-serio-1-event-mouse E: DEVNAME=/dev/input/event5 E: ID_INPUT=1 E: ID_INPUT_HEIGHT_MM=36 E: ID_INPUT_TOUCHPAD=1 E: ID_INPUT_WIDTH_MM=66 E: ID_PATH=platform-i8042-serio-1 E: ID_PATH_TAG=platform-i8042-serio-1 E: ID_SERIAL=noserial E: MAJOR=13 E: MINOR=69 E: SUBSYSTEM=input A: dev=13:69 L: device=../../input5 A: power/async=disabled A: power/control=auto A: power/runtime_active_kids=0 A: power/runtime_active_time=0 A: power/runtime_enabled=disabled A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 A: power/runtime_usage=0 P: /devices/platform/i8042/serio1/input/input5 E: ABS=260800011000003 E: EV=b E: ID_FOR_SEAT=input-platform-i8042-serio-1 E: ID_INPUT=1 E: ID_INPUT_TOUCHPAD=1 E: ID_PATH=platform-i8042-serio-1 E: ID_PATH_TAG=platform-i8042-serio-1 E: ID_SERIAL=noserial E: KEY=6420 30000 0 0 0 0 E: MODALIAS=input:b0011v0002p0007e01B1-e0,1,3,k110,111,145,14A,14D,14E,ra0,1,18,1C,2F,35,36,39,mlsfw E: NAME="SynPS/2 Synaptics TouchPad" E: PHYS="isa0060/serio1/input0" E: PRODUCT=11/2/7/1b1 E: PROP=9 E: SUBSYSTEM=input E: TAGS=:seat: A: capabilities/abs=260800011000003 A: capabilities/ev=b A: capabilities/ff=0 A: capabilities/key=6420 30000 0 0 0 0 A: capabilities/led=0 A: capabilities/msc=0 A: capabilities/rel=0 A: capabilities/snd=0 A: capabilities/sw=0 L: device=../../../serio1 A: id/bustype=0011 A: id/product=0007 A: id/vendor=0002 A: id/version=01b1 A: modalias=input:b0011v0002p0007e01B1-e0,1,3,k110,111,145,14A,14D,14E,ra0,1,18,1C,2F,35,36,39,mlsfw A: name=SynPS/2 Synaptics TouchPad A: phys=isa0060/serio1/input0 A: power/async=disabled A: power/control=auto A: power/runtime_active_kids=0 A: power/runtime_active_time=0 A: power/runtime_enabled=disabled A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 A: power/runtime_usage=0 A: properties=9 A: uniq= P: /devices/platform/i8042/serio1 E: DRIVER=psmouse E: MODALIAS=serio:ty01pr00id00ex00 E: SERIO_EXTRA=00 E: SERIO_FIRMWARE_ID=PNP: LEN0015 PNP0f13 E: SERIO_ID=00 E: SERIO_PROTO=00 E: SERIO_TYPE=01 E: SUBSYSTEM=serio A: bind_mode=auto A: description=i8042 AUX port L: driver=../../../../bus/serio/drivers/psmouse A: firmware_id=PNP: LEN0015 PNP0f13 A: id/extra=00 A: id/id=00 A: id/proto=00 A: id/type=01 A: modalias=serio:ty01pr00id00ex00 A: power/async=disabled A: power/control=auto A: power/runtime_active_kids=0 A: power/runtime_active_time=0 A: power/runtime_enabled=disabled A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 A: power/runtime_usage=0 A: power/wakeup=disabled A: power/wakeup_abort_count= A: power/wakeup_active= A: power/wakeup_active_count= A: power/wakeup_count= A: power/wakeup_expire_count= A: power/wakeup_last_time_ms= A: power/wakeup_max_time_ms= A: power/wakeup_total_time_ms= A: protocol=SynPS/2 A: rate=80 A: resetafter=5 A: resolution=200 A: resync_time=0 P: /devices/platform/i8042 E: DRIVER=i8042 E: MODALIAS=platform:i8042 E: SUBSYSTEM=platform L: driver=../../../bus/platform/drivers/i8042 A: driver_override=(null) A: modalias=platform:i8042 A: power/async=disabled A: power/control=auto A: power/runtime_active_kids=0 A: power/runtime_active_time=0 A: power/runtime_enabled=disabled A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 A: power/runtime_usage=0 ./tests/mir_test_framework/udev-recordings/joystick-detection.ioctl0000644000015600001650000000330512676616125026126 0ustar jenkinsjenkins@DEV /dev/input/event13 EVIOCGBIT(1) 96 000000000000000000000000000000000000000000000000000000000000000000000000FF0F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGBIT(2) 2 0000 EVIOCGBIT(3) 8 6300030000000000 EVIOCGBIT(5) 2 0000 EVIOCGBIT(17) 2 0000 EVIOCGBIT(21) 16 00000000000000000000000000000000 EVIOCGPROP(0) 4 00000000 EVIOCGBIT(0) 8 0B00000000000000 EVIOCGNAME(0) 29 4C6F676974656368204C6F6769746563682045787472656D65203344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGPHYS(0) 26 7573622D303030303A30303A31342E302D322F696E70757430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGUNIQ(0) 1 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGID 0 03006D0415C21001 EVIOCGVERSION 0 01000100 EVIOCGBIT(4) 8 1000000000000000 EVIOCGBIT(18) 8 0000000000000000 EVIOCGKEY(0) 96 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGLED(0) 8 0000000000000000 EVIOCGSW(0) 8 0000000000000000 EVIOCGABS 0 FC01000000000000FF030000030000003F00000000000000 EVIOCGABS(1) 0 FC01000000000000FF030000030000003F00000000000000 EVIOCGABS(5) 0 8000000000000000FF000000000000000F00000000000000 EVIOCGABS(6) 0 0000000000000000FF000000000000000F00000000000000 EVIOCGABS(16) 0 00000000FFFFFFFF01000000000000000000000000000000 EVIOCGABS(17) 0 00000000FFFFFFFF01000000000000000000000000000000 ./tests/mir_test_framework/udev-recordings/joystick-detection.umockdev0000644000015600001650000002741112676616125026635 0ustar jenkinsjenkinsP: /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0/0003:046D:C215.0016/input/input258/event13 N: input/event13 S: input/by-id/usb-Logitech_Logitech_Extreme_3D-event-joystick S: input/by-path/pci-0000:00:14.0-usb-0:2:1.0-event-joystick E: DEVLINKS=/dev/input/by-id/usb-Logitech_Logitech_Extreme_3D-event-joystick /dev/input/by-path/pci-0000:00:14.0-usb-0:2:1.0-event-joystick E: DEVNAME=/dev/input/event13 E: ID_BUS=usb E: ID_FOR_SEAT=input-pci-0000_00_14_0-usb-0_2_1_0 E: ID_INPUT=1 E: ID_INPUT_JOYSTICK=1 E: ID_MODEL=Logitech_Extreme_3D E: ID_MODEL_ENC=Logitech\x20Extreme\x203D E: ID_MODEL_ID=c215 E: ID_PATH=pci-0000:00:14.0-usb-0:2:1.0 E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_2_1_0 E: ID_REVISION=0204 E: ID_SERIAL=Logitech_Logitech_Extreme_3D E: ID_TYPE=hid E: ID_USB_DRIVER=usbhid E: ID_USB_INTERFACES=:030000: E: ID_USB_INTERFACE_NUM=00 E: ID_VENDOR=Logitech E: ID_VENDOR_ENC=Logitech E: ID_VENDOR_ID=046d E: MAJOR=13 E: MINOR=77 E: SUBSYSTEM=input E: TAGS=:seat:uaccess: A: dev=13:77 L: device=../../input258 A: power/async=disabled A: power/control=auto A: power/runtime_active_kids=0 A: power/runtime_active_time=0 A: power/runtime_enabled=disabled A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 A: power/runtime_usage=0 P: /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0/0003:046D:C215.0016/input/input258 E: ABS=30063 E: EV=1b E: ID_BUS=usb E: ID_FOR_SEAT=input-pci-0000_00_14_0-usb-0_2_1_0 E: ID_INPUT=1 E: ID_INPUT_JOYSTICK=1 E: ID_MODEL=Logitech_Extreme_3D E: ID_MODEL_ENC=Logitech\x20Extreme\x203D E: ID_MODEL_ID=c215 E: ID_PATH=pci-0000:00:14.0-usb-0:2:1.0 E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_2_1_0 E: ID_REVISION=0204 E: ID_SERIAL=Logitech_Logitech_Extreme_3D E: ID_TYPE=hid E: ID_USB_DRIVER=usbhid E: ID_USB_INTERFACES=:030000: E: ID_USB_INTERFACE_NUM=00 E: ID_VENDOR=Logitech E: ID_VENDOR_ENC=Logitech E: ID_VENDOR_ID=046d E: KEY=fff00000000 0 0 0 0 E: MODALIAS=input:b0003v046DpC215e0110-e0,1,3,4,k120,121,122,123,124,125,126,127,128,129,12A,12B,ra0,1,5,6,10,11,m4,lsfw E: MSC=10 E: NAME="Logitech Logitech Extreme 3D" E: PHYS="usb-0000:00:14.0-2/input0" E: PRODUCT=3/46d/c215/110 E: PROP=0 E: SUBSYSTEM=input E: TAGS=:seat: E: UNIQ="" A: capabilities/abs=30063 A: capabilities/ev=1b A: capabilities/ff=0 A: capabilities/key=fff00000000 0 0 0 0 A: capabilities/led=0 A: capabilities/msc=10 A: capabilities/rel=0 A: capabilities/snd=0 A: capabilities/sw=0 L: device=../../../0003:046D:C215.0016 A: id/bustype=0003 A: id/product=c215 A: id/vendor=046d A: id/version=0110 A: modalias=input:b0003v046DpC215e0110-e0,1,3,4,k120,121,122,123,124,125,126,127,128,129,12A,12B,ra0,1,5,6,10,11,m4,lsfw A: name=Logitech Logitech Extreme 3D A: phys=usb-0000:00:14.0-2/input0 A: power/async=disabled A: power/control=auto A: power/runtime_active_kids=0 A: power/runtime_active_time=0 A: power/runtime_enabled=disabled A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 A: power/runtime_usage=0 A: properties=0 A: uniq= P: /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0/0003:046D:C215.0016 E: DRIVER=logitech E: HID_ID=0003:0000046D:0000C215 E: HID_NAME=Logitech Logitech Extreme 3D E: HID_PHYS=usb-0000:00:14.0-2/input0 E: MODALIAS=hid:b0003g0000v0000046Dp0000C215 E: SUBSYSTEM=hid E: UPOWER_VENDOR=Logitech, Inc. L: driver=../../../../../../../bus/hid/drivers/logitech A: modalias=hid:b0003g0000v0000046Dp0000C215 A: power/async=disabled A: power/control=auto A: power/runtime_active_kids=0 A: power/runtime_active_time=0 A: power/runtime_enabled=disabled A: power/runtime_status=unsupported A: power/runtime_suspended_time=0 A: power/runtime_usage=0 H: report_descriptor=05010904A101A1029502750A150026FF03350046FF03093009318102750495012507463B01661400093981426500750826FF0046FF0009358102A495087501250145010509190129088102B409368102950475012501450105091909290C810295048101C0A1029504750826FF0046FF000600FF0901B102C0C0 P: /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0 E: DEVTYPE=usb_interface E: DRIVER=usbhid E: ID_MODEL_FROM_DATABASE=Extreme 3D Pro E: ID_VENDOR_FROM_DATABASE=Logitech, Inc. E: INTERFACE=3/0/0 E: MODALIAS=usb:v046DpC215d0204dc00dsc00dp00ic03isc00ip00in00 E: PRODUCT=46d/c215/204 E: SUBSYSTEM=usb E: TYPE=0/0/0 A: bAlternateSetting= 0 A: bInterfaceClass=03 A: bInterfaceNumber=00 A: bInterfaceProtocol=00 A: bInterfaceSubClass=00 A: bNumEndpoints=01 L: driver=../../../../../../bus/usb/drivers/usbhid A: modalias=usb:v046DpC215d0204dc00dsc00dp00ic03isc00ip00in00 A: power/async=enabled A: power/runtime_active_kids=0 A: power/runtime_enabled=enabled A: power/runtime_status=suspended A: power/runtime_usage=0 A: supports_autosuspend=1 P: /devices/pci0000:00/0000:00:14.0/usb3/3-2 N: bus/usb/003/043=12011001000000086D0415C204020102000109022200010100800F090400000103000000092110012101227A000705810307000A E: BUSNUM=003 E: DEVNAME=/dev/bus/usb/003/043 E: DEVNUM=043 E: DEVTYPE=usb_device E: DRIVER=usb E: ID_BUS=usb E: ID_MODEL=Logitech_Extreme_3D E: ID_MODEL_ENC=Logitech\x20Extreme\x203D E: ID_MODEL_FROM_DATABASE=Extreme 3D Pro E: ID_MODEL_ID=c215 E: ID_REVISION=0204 E: ID_SERIAL=Logitech_Logitech_Extreme_3D E: ID_USB_INTERFACES=:030000: E: ID_VENDOR=Logitech E: ID_VENDOR_ENC=Logitech E: ID_VENDOR_FROM_DATABASE=Logitech, Inc. E: ID_VENDOR_ID=046d E: MAJOR=189 E: MINOR=298 E: PRODUCT=46d/c215/204 E: SUBSYSTEM=usb E: TYPE=0/0/0 E: UPOWER_VENDOR=Logitech, Inc. A: authorized=1 A: avoid_reset_quirk=0 A: bConfigurationValue=1 A: bDeviceClass=00 A: bDeviceProtocol=00 A: bDeviceSubClass=00 A: bMaxPacketSize0=8 A: bMaxPower=30mA A: bNumConfigurations=1 A: bNumInterfaces= 1 A: bcdDevice=0204 A: bmAttributes=80 A: busnum=3 A: configuration= H: descriptors=12011001000000086D0415C204020102000109022200010100800F090400000103000000092110012101227A000705810307000A A: dev=189:298 A: devnum=43 A: devpath=2 L: driver=../../../../../bus/usb/drivers/usb A: idProduct=c215 A: idVendor=046d A: ltm_capable=no A: manufacturer=Logitech A: maxchild=0 L: port=../3-0:1.0/usb3-port2 A: power/active_duration=1287064 A: power/async=enabled A: power/autosuspend=2 A: power/autosuspend_delay_ms=2000 A: power/connected_duration=1287064 A: power/control=on A: power/level=on A: power/persist=1 A: power/runtime_active_kids=0 A: power/runtime_active_time=1286764 A: power/runtime_enabled=forbidden A: power/runtime_status=active A: power/runtime_suspended_time=0 A: power/runtime_usage=1 A: product=Logitech Extreme 3D A: quirks=0x0 A: removable=unknown A: speed=1.5 A: urbnum=24 A: version= 1.10 P: /devices/pci0000:00/0000:00:14.0/usb3 N: bus/usb/003/001=12010002090001406B1D020016030302010109021900010100E0000904000001090000000705810304000C E: BUSNUM=003 E: DEVNAME=/dev/bus/usb/003/001 E: DEVNUM=001 E: DEVTYPE=usb_device E: DRIVER=usb E: ID_BUS=usb E: ID_FOR_SEAT=usb-pci-0000_00_14_0 E: ID_MODEL=xHCI_Host_Controller E: ID_MODEL_ENC=xHCI\x20Host\x20Controller E: ID_MODEL_FROM_DATABASE=2.0 root hub E: ID_MODEL_ID=0002 E: ID_PATH=pci-0000:00:14.0 E: ID_PATH_TAG=pci-0000_00_14_0 E: ID_REVISION=0316 E: ID_SERIAL=Linux_3.16.0-24-generic_xhci_hcd_xHCI_Host_Controller_0000:00:14.0 E: ID_SERIAL_SHORT=0000:00:14.0 E: ID_USB_INTERFACES=:090000: E: ID_VENDOR=Linux_3.16.0-24-generic_xhci_hcd E: ID_VENDOR_ENC=Linux\x203.16.0-24-generic\x20xhci_hcd E: ID_VENDOR_FROM_DATABASE=Linux Foundation E: ID_VENDOR_ID=1d6b E: MAJOR=189 E: MINOR=256 E: PRODUCT=1d6b/2/316 E: SUBSYSTEM=usb E: TAGS=:seat: E: TYPE=9/0/1 A: authorized=1 A: authorized_default=1 A: avoid_reset_quirk=0 A: bConfigurationValue=1 A: bDeviceClass=09 A: bDeviceProtocol=01 A: bDeviceSubClass=00 A: bMaxPacketSize0=64 A: bMaxPower=0mA A: bNumConfigurations=1 A: bNumInterfaces= 1 A: bcdDevice=0316 A: bmAttributes=e0 A: busnum=3 A: configuration= H: descriptors=12010002090001406B1D020016030302010109021900010100E0000904000001090000000705810304000C A: dev=189:256 A: devnum=1 A: devpath=0 L: driver=../../../../bus/usb/drivers/usb A: idProduct=0002 A: idVendor=1d6b A: ltm_capable=no A: manufacturer=Linux 3.16.0-24-generic xhci_hcd A: maxchild=14 A: power/active_duration=471394472 A: power/async=enabled A: power/autosuspend=0 A: power/autosuspend_delay_ms=0 A: power/connected_duration=493735372 A: power/control=auto A: power/level=auto A: power/runtime_active_kids=3 A: power/runtime_active_time=466119148 A: power/runtime_enabled=enabled A: power/runtime_status=active A: power/runtime_suspended_time=22335316 A: power/runtime_usage=0 A: power/wakeup=disabled A: power/wakeup_abort_count= A: power/wakeup_active= A: power/wakeup_active_count= A: power/wakeup_count= A: power/wakeup_expire_count= A: power/wakeup_last_time_ms= A: power/wakeup_max_time_ms= A: power/wakeup_total_time_ms= A: product=xHCI Host Controller A: quirks=0x0 A: removable=unknown A: serial=0000:00:14.0 A: speed=480 A: urbnum=1514 A: version= 2.00 P: /devices/pci0000:00/0000:00:14.0 E: DRIVER=xhci_hcd E: ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB xHCI E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller E: ID_VENDOR_FROM_DATABASE=Intel Corporation E: MODALIAS=pci:v00008086d00008C31sv00001558sd00007410bc0Csc03i30 E: PCI_CLASS=C0330 E: PCI_ID=8086:8C31 E: PCI_SLOT_NAME=0000:00:14.0 E: PCI_SUBSYS_ID=1558:7410 E: SUBSYSTEM=pci A: broken_parity_status=0 A: class=0x0c0330 H: config=8680318C060490020530030C000000000400E2F7000000000000000000000000000000000000000000000000581510740000000070000000000000000B010000FD01368089C60F8000000000000000009F6E8807000000000000000000000000302000000000000000000000000000000180C2C108000000000000000000000005008700F802E0FE000000000000000000000000000000000000000000000000400100000000000000000000000000000F000100000000000000000000000000030420C0030C3000030C300000000000FF3F0000FF3F00003F0000003F000000A0000000D03C000000000000D8D8D8080000000000000000B10F060800000000 A: consistent_dma_mask_bits=64 A: d3cold_allowed=1 A: device=0x8c31 A: dma_mask_bits=64 L: driver=../../../bus/pci/drivers/xhci_hcd A: driver_override=(null) A: enabled=1 A: irq=42 A: local_cpulist=0-7 A: local_cpus=00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff A: modalias=pci:v00008086d00008C31sv00001558sd00007410bc0Csc03i30 A: msi_bus= A: msi_irqs/42=msi A: numa_node=-1 A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 7 11 2112 11\nxHCI ring segments 82 152 1024 38\nbuffer-2048 0 38 2048 19\nbuffer-512 0 16 512 2\nbuffer-128 3 32 128 1\nbuffer-32 0 0 32 0 A: power/async=enabled A: power/control=on A: power/runtime_active_kids=1 A: power/runtime_active_time=493735800 A: power/runtime_enabled=forbidden A: power/runtime_status=active A: power/runtime_suspended_time=0 A: power/runtime_usage=1 A: power/wakeup=enabled A: power/wakeup_abort_count=0 A: power/wakeup_active=0 A: power/wakeup_active_count=0 A: power/wakeup_count=0 A: power/wakeup_expire_count=0 A: power/wakeup_last_time_ms=1534 A: power/wakeup_max_time_ms=0 A: power/wakeup_total_time_ms=0 A: resource=0x00000000f7e20000 0x00000000f7e2ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 A: subsystem_device=0x7410 A: subsystem_vendor=0x1558 A: vendor=0x8086 ./tests/mir_test_framework/udev-recordings/mt-screen-detection.ioctl0000644000015600001650000000357112676616125026171 0ustar jenkinsjenkins@DEV /dev/input/event4 EVIOCGBIT(1) 96 000000000000000000000000000010000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGBIT(2) 2 0000 EVIOCGBIT(3) 8 0300000100006306 EVIOCGBIT(5) 2 0000 EVIOCGBIT(17) 2 0000 EVIOCGBIT(21) 16 00000000000000000000000000000000 EVIOCGPROP(0) 8 02000000 EVIOCGBIT(0) 8 0B00000000000000 EVIOCGNAME(0) 8 6D746B2D747064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGID 0 0000000000000000 EVIOCGPHYS(0) 0 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGUNIQ(0) 0 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGVERSION 0 01000100 EVIOCGBIT(4) 8 0000000000000000 EVIOCGBIT(18) 8 0000000000000000 EVIOCGKEY(0) 96 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 EVIOCGLED(0) 4 00000000 EVIOCGSW(0) 4 00000000 EVIOCGABS 0 00000000000000001C02000000000000000000001C020000 EVIOCGABS(1) 0 0000000000000000C00300000000000000000000C0030000 EVIOCGABS(24) 0 0000000000000000FF000000000000000000000000000000 EVIOCGABS(48) 0 000000000000000064000000000000000000000000000000 EVIOCGABS(49) 0 000000000000000064000000000000000000000000000000 EVIOCGABS(53) 0 00000000000000001C020000000000000000000000000000 EVIOCGABS(54) 0 0000000000000000C0030000000000000000000000000000 EVIOCGABS(57) 0 000000000000000000000000000000000000000000000000 EVIOCGABS(58) 0 0000000000000000FF000000000000000000000000000000 ./tests/mir_test_framework/udev-recordings/usb-mouse.umockdev0000644000015600001650000002025312676616125024736 0ustar jenkinsjenkinsP: /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/input/input23/event13 N: input/event13 S: input/by-id/usb-Logitech_USB_Optical_Mouse-event-mouse S: input/by-path/pci-0000:00:14.0-usb-0:3:1.0-event-mouse E: DEVLINKS=/dev/input/by-id/usb-Logitech_USB_Optical_Mouse-event-mouse /dev/input/by-path/pci-0000:00:14.0-usb-0:3:1.0-event-mouse E: DEVNAME=/dev/input/event13 E: ID_BUS=usb E: ID_INPUT=1 E: ID_INPUT_MOUSE=1 E: ID_MODEL=USB_Optical_Mouse E: ID_MODEL_ENC=USB\x20Optical\x20Mouse E: ID_MODEL_ID=c018 E: ID_PATH=pci-0000:00:14.0-usb-0:3:1.0 E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_3_1_0 E: ID_REVISION=4301 E: ID_SERIAL=Logitech_USB_Optical_Mouse E: ID_TYPE=hid E: ID_USB_DRIVER=usbhid E: ID_USB_INTERFACES=:030102: E: ID_USB_INTERFACE_NUM=00 E: ID_VENDOR=Logitech E: ID_VENDOR_ENC=Logitech E: ID_VENDOR_ID=046d E: MAJOR=13 E: MINOR=77 E: SUBSYSTEM=input A: dev=13:77 L: device=../../input23 P: /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/input/input23 E: EV=17 E: ID_BUS=usb E: ID_FOR_SEAT=input-pci-0000_00_14_0-usb-0_3_1_0 E: ID_INPUT=1 E: ID_INPUT_MOUSE=1 E: ID_MODEL=USB_Optical_Mouse E: ID_MODEL_ENC=USB\x20Optical\x20Mouse E: ID_MODEL_ID=c018 E: ID_PATH=pci-0000:00:14.0-usb-0:3:1.0 E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_3_1_0 E: ID_REVISION=4301 E: ID_SERIAL=Logitech_USB_Optical_Mouse E: ID_TYPE=hid E: ID_USB_DRIVER=usbhid E: ID_USB_INTERFACES=:030102: E: ID_USB_INTERFACE_NUM=00 E: ID_VENDOR=Logitech E: ID_VENDOR_ENC=Logitech E: ID_VENDOR_ID=046d E: KEY=70000 0 0 0 0 E: MODALIAS=input:b0003v046DpC018e0111-e0,1,2,4,k110,111,112,r0,1,8,am4,lsfw E: MSC=10 E: NAME="Logitech USB Optical Mouse" E: PHYS="usb-0000:00:14.0-3/input0" E: PRODUCT=3/46d/c018/111 E: PROP=0 E: REL=103 E: SUBSYSTEM=input E: TAGS=:seat: E: UNIQ="" L: device=../../../3-3:1.0 A: modalias=input:b0003v046DpC018e0111-e0,1,2,4,k110,111,112,r0,1,8,am4,lsfw A: name=Logitech USB Optical Mouse A: phys=usb-0000:00:14.0-3/input0 A: properties=0 A: uniq= P: /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0 E: DEVTYPE=usb_interface E: DRIVER=usbhid E: ID_MODEL_FROM_DATABASE=Optical Wheel Mouse E: ID_VENDOR_FROM_DATABASE=Logitech, Inc. E: INTERFACE=3/1/2 E: MODALIAS=usb:v046DpC018d4301dc00dsc00dp00ic03isc01ip02in00 E: PRODUCT=46d/c018/4301 E: SUBSYSTEM=usb E: TYPE=0/0/0 A: bAlternateSetting= 0 A: bInterfaceClass=03 A: bInterfaceNumber=00 A: bInterfaceProtocol=02 A: bInterfaceSubClass=01 A: bNumEndpoints=01 L: driver=../../../../../../bus/usb/drivers/usbhid A: modalias=usb:v046DpC018d4301dc00dsc00dp00ic03isc01ip02in00 A: supports_autosuspend=1 P: /devices/pci0000:00/0000:00:14.0/usb3/3-3 N: bus/usb/003/015=12010002000000086D0418C001430102000109022200010100A0320904000001030102000921110100012234000705810305000A E: BUSNUM=003 E: DEVNAME=/dev/bus/usb/003/015 E: DEVNUM=015 E: DEVTYPE=usb_device E: DRIVER=usb E: ID_BUS=usb E: ID_MODEL=USB_Optical_Mouse E: ID_MODEL_ENC=USB\x20Optical\x20Mouse E: ID_MODEL_FROM_DATABASE=Optical Wheel Mouse E: ID_MODEL_ID=c018 E: ID_REVISION=4301 E: ID_SERIAL=Logitech_USB_Optical_Mouse E: ID_USB_INTERFACES=:030102: E: ID_VENDOR=Logitech E: ID_VENDOR_ENC=Logitech E: ID_VENDOR_FROM_DATABASE=Logitech, Inc. E: ID_VENDOR_ID=046d E: MAJOR=189 E: MINOR=270 E: PRODUCT=46d/c018/4301 E: SUBSYSTEM=usb E: TYPE=0/0/0 E: UPOWER_VENDOR=Logitech, Inc. A: authorized=1 A: avoid_reset_quirk=0 A: bConfigurationValue=1 A: bDeviceClass=00 A: bDeviceProtocol=00 A: bDeviceSubClass=00 A: bMaxPacketSize0=8 A: bMaxPower=100mA A: bNumConfigurations=1 A: bNumInterfaces= 1 A: bcdDevice=4301 A: bmAttributes=a0 A: busnum=3 A: configuration= H: descriptors=12010002000000086D0418C001430102000109022200010100A0320904000001030102000921110100012234000705810305000A A: dev=189:270 A: devnum=15 A: devpath=3 L: driver=../../../../../bus/usb/drivers/usb A: idProduct=c018 A: idVendor=046d A: ltm_capable=no A: manufacturer=Logitech A: maxchild=0 L: port=../3-0:1.0/port3 A: product=USB Optical Mouse A: quirks=0x0 A: removable=removable A: speed=1.5 A: urbnum=167 A: version= 2.00 P: /devices/pci0000:00/0000:00:14.0/usb3 N: bus/usb/003/001=12010002090001406B1D020013030302010109021900010100E0000904000001090000000705810304000C E: BUSNUM=003 E: DEVNAME=/dev/bus/usb/003/001 E: DEVNUM=001 E: DEVTYPE=usb_device E: DRIVER=usb E: ID_BUS=usb E: ID_FOR_SEAT=usb-pci-0000_00_14_0 E: ID_MODEL=xHCI_Host_Controller E: ID_MODEL_ENC=xHCI\x20Host\x20Controller E: ID_MODEL_FROM_DATABASE=2.0 root hub E: ID_MODEL_ID=0002 E: ID_PATH=pci-0000:00:14.0 E: ID_PATH_TAG=pci-0000_00_14_0 E: ID_REVISION=0313 E: ID_SERIAL=Linux_3.13.0-8-generic_xhci_hcd_xHCI_Host_Controller_0000:00:14.0 E: ID_SERIAL_SHORT=0000:00:14.0 E: ID_USB_INTERFACES=:090000: E: ID_VENDOR=Linux_3.13.0-8-generic_xhci_hcd E: ID_VENDOR_ENC=Linux\x203.13.0-8-generic\x20xhci_hcd E: ID_VENDOR_FROM_DATABASE=Linux Foundation E: ID_VENDOR_ID=1d6b E: MAJOR=189 E: MINOR=256 E: PRODUCT=1d6b/2/313 E: SUBSYSTEM=usb E: TAGS=:seat: E: TYPE=9/0/1 A: authorized=1 A: authorized_default=1 A: avoid_reset_quirk=0 A: bConfigurationValue=1 A: bDeviceClass=09 A: bDeviceProtocol=01 A: bDeviceSubClass=00 A: bMaxPacketSize0=64 A: bMaxPower=0mA A: bNumConfigurations=1 A: bNumInterfaces= 1 A: bcdDevice=0313 A: bmAttributes=e0 A: busnum=3 A: configuration= H: descriptors=12010002090001406B1D020013030302010109021900010100E0000904000001090000000705810304000C A: dev=189:256 A: devnum=1 A: devpath=0 L: driver=../../../../bus/usb/drivers/usb A: idProduct=0002 A: idVendor=1d6b A: ltm_capable=no A: manufacturer=Linux 3.13.0-8-generic xhci_hcd A: maxchild=14 A: product=xHCI Host Controller A: quirks=0x0 A: removable=unknown A: serial=0000:00:14.0 A: speed=480 A: urbnum=225 A: version= 2.00 P: /devices/pci0000:00/0000:00:14.0 E: DRIVER=xhci_hcd E: ID_MODEL_FROM_DATABASE=Lynx Point USB xHCI Host Controller E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller E: ID_VENDOR_FROM_DATABASE=Intel Corporation E: MODALIAS=pci:v00008086d00008C31sv00001558sd00007410bc0Csc03i30 E: PCI_CLASS=C0330 E: PCI_ID=8086:8C31 E: PCI_SLOT_NAME=0000:00:14.0 E: PCI_SUBSYS_ID=1558:7410 E: SUBSYSTEM=pci A: broken_parity_status=0 A: class=0x0c0330 H: config=8680318C060490020430030C000000000400E2F700000000000000000000000000000000000000000000000058151074000000007000000000000000FF010000FD01368089C60F8000000000000000009F6E8807000000000000000000000000302000000000000000000000000000000180C2C108000000000000000000000005008700F802E0FE000000000000000000000000000000000000000000000000400100000000000000000000000000000F000100000000000000000000000000030420C0030C3000030C300000000000FF3F0000FF3F00003F0000003F000000A0000000D03C000000000000D8D8D8080000000000000000B10F050800000000 A: consistent_dma_mask_bits=64 A: d3cold_allowed=1 A: device=0x8c31 A: dma_mask_bits=64 L: driver=../../../bus/pci/drivers/xhci_hcd A: enabled=1 A: irq=42 A: local_cpulist=0-7 A: local_cpus=00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff A: modalias=pci:v00008086d00008C31sv00001558sd00007410bc0Csc03i30 A: msi_bus= A: numa_node=-1 A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 9 9 2112 9\nxHCI ring segments 34 34 4096 34\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 3 32 128 1\nbuffer-32 0 0 32 0 A: resource=0x00000000f7e20000 0x00000000f7e2ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 A: subsystem_device=0x7410 A: subsystem_vendor=0x1558 A: vendor=0x8086 ./tests/mir_test_framework/udev-recordings/bluetooth-magic-trackpad.umockdev0000644000015600001650000002030212676616125027664 0ustar jenkinsjenkinsP: /devices/pci0000:00/0000:00:14.0/usb3/3-4/3-4:1.0/bluetooth/hci0/hci0:21/input20/event13 N: input/event13 E: DEVNAME=/dev/input/event13 E: ID_INPUT=1 E: ID_INPUT_TOUCHPAD=1 E: MAJOR=13 E: MINOR=77 E: SUBSYSTEM=input A: dev=13:77 L: device=../../input20 P: /devices/pci0000:00/0000:00:14.0/usb3/3-4/3-4:1.0/bluetooth/hci0/hci0:21/input20 E: ABS=273800000000003 E: EV=10001b E: ID_FOR_SEAT=input-pci-0000_00_14_0-usb-0_4_1_0 E: ID_INPUT=1 E: ID_INPUT_TOUCHPAD=1 E: ID_PATH=pci-0000:00:14.0-usb-0:4:1.0 E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_4_1_0 E: KEY=e520 10000 0 0 0 0 E: MODALIAS=input:b0005v05ACp030Ee0160-e0,1,3,4,14,k110,145,148,14A,14D,14E,14F,ra0,1,2F,30,31,34,35,36,39,m4,lsfw E: MSC=10 E: NAME="Apple Wireless Trackpad" E: PHYS="b8:76:3f:78:eb:76" E: PRODUCT=5/5ac/30e/160 E: PROP=5 E: SUBSYSTEM=input E: TAGS=:seat: E: UNIQ="d8:a2:5e:f8:70:d6" L: device=../../hci0:21 A: modalias=input:b0005v05ACp030Ee0160-e0,1,3,4,14,k110,145,148,14A,14D,14E,14F,ra0,1,2F,30,31,34,35,36,39,m4,lsfw A: name=Apple Wireless Trackpad A: phys=b8:76:3f:78:eb:76 A: properties=5 A: uniq=d8:a2:5e:f8:70:d6 P: /devices/pci0000:00/0000:00:14.0/usb3/3-4/3-4:1.0/bluetooth/hci0/hci0:21 E: DEVTYPE=link E: SUBSYSTEM=bluetooth A: address=d8:a2:5e:f8:70:d6 L: device=../../hci0 A: type=ACL P: /devices/pci0000:00/0000:00:14.0/usb3/3-4/3-4:1.0/bluetooth/hci0 E: DEVTYPE=host E: SUBSYSTEM=bluetooth A: address=b8:76:3f:78:eb:76 L: device=../../../3-4:1.0 A: name=ubuntu-0 A: type=BR/EDR P: /devices/pci0000:00/0000:00:14.0/usb3/3-4/3-4:1.0 E: DEVTYPE=usb_interface E: DRIVER=btusb E: ID_USB_CLASS_FROM_DATABASE=Wireless E: ID_USB_PROTOCOL_FROM_DATABASE=Bluetooth E: ID_USB_SUBCLASS_FROM_DATABASE=Radio Frequency E: ID_VENDOR_FROM_DATABASE=Atheros Communications, Inc. E: INTERFACE=224/1/1 E: MODALIAS=usb:v0CF3p3004d0002dcE0dsc01dp01icE0isc01ip01in00 E: PRODUCT=cf3/3004/2 E: SUBSYSTEM=usb E: TYPE=224/1/1 A: bAlternateSetting= 0 A: bInterfaceClass=e0 A: bInterfaceNumber=00 A: bInterfaceProtocol=01 A: bInterfaceSubClass=01 A: bNumEndpoints=03 L: driver=../../../../../../bus/usb/drivers/btusb A: modalias=usb:v0CF3p3004d0002dcE0dsc01dp01icE0isc01ip01in00 A: supports_autosuspend=1 P: /devices/pci0000:00/0000:00:14.0/usb3/3-4 N: bus/usb/003/003=12011001E0010140F30C04300200010203010902B100020104E0320904000003E00101000705810310000107058202400001070502024000010904010002E001010007058301000001070503010000010904010102E001010007058301090001070503010900010904010202E001010007058301110001070503011100010904010302E001010007058301190001070503011900010904010402E001010007058301210001070503012100010904010502E00101000705830131000107050301310001 E: BUSNUM=003 E: DEVNAME=/dev/bus/usb/003/003 E: DEVNUM=003 E: DEVTYPE=usb_device E: DRIVER=usb E: ID_BUS=usb E: ID_MODEL=3004 E: ID_MODEL_ENC=3004 E: ID_MODEL_ID=3004 E: ID_REVISION=0002 E: ID_SERIAL=0cf3_3004 E: ID_USB_INTERFACES=:e00101: E: ID_VENDOR=0cf3 E: ID_VENDOR_ENC=0cf3 E: ID_VENDOR_FROM_DATABASE=Atheros Communications, Inc. E: ID_VENDOR_ID=0cf3 E: MAJOR=189 E: MINOR=258 E: PRODUCT=cf3/3004/2 E: SUBSYSTEM=usb E: TYPE=224/1/1 A: authorized=1 A: avoid_reset_quirk=0 A: bConfigurationValue=1 A: bDeviceClass=e0 A: bDeviceProtocol=01 A: bDeviceSubClass=01 A: bMaxPacketSize0=64 A: bMaxPower=100mA A: bNumConfigurations=1 A: bNumInterfaces= 2 A: bcdDevice=0002 A: bmAttributes=e0 A: busnum=3 A: configuration= H: descriptors=12011001E0010140F30C04300200010203010902B100020104E0320904000003E00101000705810310000107058202400001070502024000010904010002E001010007058301000001070503010000010904010102E001010007058301090001070503010900010904010202E001010007058301110001070503011100010904010302E001010007058301190001070503011900010904010402E001010007058301210001070503012100010904010502E00101000705830131000107050301310001 A: dev=189:258 A: devnum=3 A: devpath=4 L: driver=../../../../../bus/usb/drivers/usb A: idProduct=3004 A: idVendor=0cf3 A: ltm_capable=no A: maxchild=0 L: port=../3-0:1.0/port4 A: quirks=0x0 A: removable=removable A: speed=12 A: urbnum=2662 A: version= 1.10 P: /devices/pci0000:00/0000:00:14.0/usb3 N: bus/usb/003/001=12010002090001406B1D020013030302010109021900010100E0000904000001090000000705810304000C E: BUSNUM=003 E: DEVNAME=/dev/bus/usb/003/001 E: DEVNUM=001 E: DEVTYPE=usb_device E: DRIVER=usb E: ID_BUS=usb E: ID_FOR_SEAT=usb-pci-0000_00_14_0 E: ID_MODEL=xHCI_Host_Controller E: ID_MODEL_ENC=xHCI\x20Host\x20Controller E: ID_MODEL_FROM_DATABASE=2.0 root hub E: ID_MODEL_ID=0002 E: ID_PATH=pci-0000:00:14.0 E: ID_PATH_TAG=pci-0000_00_14_0 E: ID_REVISION=0313 E: ID_SERIAL=Linux_3.13.0-8-generic_xhci_hcd_xHCI_Host_Controller_0000:00:14.0 E: ID_SERIAL_SHORT=0000:00:14.0 E: ID_USB_INTERFACES=:090000: E: ID_VENDOR=Linux_3.13.0-8-generic_xhci_hcd E: ID_VENDOR_ENC=Linux\x203.13.0-8-generic\x20xhci_hcd E: ID_VENDOR_FROM_DATABASE=Linux Foundation E: ID_VENDOR_ID=1d6b E: MAJOR=189 E: MINOR=256 E: PRODUCT=1d6b/2/313 E: SUBSYSTEM=usb E: TAGS=:seat: E: TYPE=9/0/1 A: authorized=1 A: authorized_default=1 A: avoid_reset_quirk=0 A: bConfigurationValue=1 A: bDeviceClass=09 A: bDeviceProtocol=01 A: bDeviceSubClass=00 A: bMaxPacketSize0=64 A: bMaxPower=0mA A: bNumConfigurations=1 A: bNumInterfaces= 1 A: bcdDevice=0313 A: bmAttributes=e0 A: busnum=3 A: configuration= H: descriptors=12010002090001406B1D020013030302010109021900010100E0000904000001090000000705810304000C A: dev=189:256 A: devnum=1 A: devpath=0 L: driver=../../../../bus/usb/drivers/usb A: idProduct=0002 A: idVendor=1d6b A: ltm_capable=no A: manufacturer=Linux 3.13.0-8-generic xhci_hcd A: maxchild=14 A: product=xHCI Host Controller A: quirks=0x0 A: removable=unknown A: serial=0000:00:14.0 A: speed=480 A: urbnum=178 A: version= 2.00 P: /devices/pci0000:00/0000:00:14.0 E: DRIVER=xhci_hcd E: ID_MODEL_FROM_DATABASE=Lynx Point USB xHCI Host Controller E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller E: ID_VENDOR_FROM_DATABASE=Intel Corporation E: MODALIAS=pci:v00008086d00008C31sv00001558sd00007410bc0Csc03i30 E: PCI_CLASS=C0330 E: PCI_ID=8086:8C31 E: PCI_SLOT_NAME=0000:00:14.0 E: PCI_SUBSYS_ID=1558:7410 E: SUBSYSTEM=pci A: broken_parity_status=0 A: class=0x0c0330 H: config=8680318C060490020430030C000000000400E2F700000000000000000000000000000000000000000000000058151074000000007000000000000000FF010000FD01368089C60F8000000000000000009F6E8807000000000000000000000000302000000000000000000000000000000180C2C108000000000000000000000005008700F802E0FE000000000000000000000000000000000000000000000000400100000000000000000000000000000F000100000000000000000000000000030420C0030C3000030C300000000000FF3F0000FF3F00003F0000003F000000A0000000D03C000000000000D8D8D8080000000000000000B10F050800000000 A: consistent_dma_mask_bits=64 A: d3cold_allowed=1 A: device=0x8c31 A: dma_mask_bits=64 L: driver=../../../bus/pci/drivers/xhci_hcd A: enabled=1 A: irq=42 A: local_cpulist=0-7 A: local_cpus=00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff A: modalias=pci:v00008086d00008C31sv00001558sd00007410bc0Csc03i30 A: msi_bus= A: numa_node=-1 A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 7 7 2112 7\nxHCI ring segments 40 40 4096 40\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0 A: resource=0x00000000f7e20000 0x00000000f7e2ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 A: subsystem_device=0x7410 A: subsystem_vendor=0x1558 A: vendor=0x8086 ./tests/mir_test_framework/udev-recordings/CMakeLists.txt0000644000015600001650000000022712676616157024024 0ustar jenkinsjenkinsfile(GLOB UDEV_FILES *.umockdev *.ioctl *.evemu) install(FILES ${UDEV_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/mir-test-data/udev-recordings) ./tests/mir_test_framework/udev-recordings/standard-drm-devices.umockdev0000644000015600001650000001650012676616125027017 0ustar jenkinsjenkinsP: /devices/pci0000:00/0000:00:02.0/drm/card1/card1-HDMI-A-1 E: SUBSYSTEM=drm A: dpms=On H: edid=00FFFFFFFFFFFF0010AC16F04C574B300415010380342078EA1EC5AE4F34B1260E5054A54B008180A940D100714F0101010101010101283C80A070B023403020360006442100001A000000FF00483339304D31314A304B574C0A000000FC0044454C4C2055323431300A2020000000FD00384C1E5111000A202020202020013E020329F15090050403020716011F121314201511062309070767030C001000382D83010000E3050301023A801871382D40582C450006442100001E011D8018711C1620582C250006442100009E011D007251D01E206E28550006442100001E8C0AD08A20E02D10103E960006442100001800000000000000000000000000003E A: enabled=enabled A: modes=1920x1200\n1920x1080\n1920x1080\n1920x1080\n1920x1080i\n1920x1080i\n1600x1200\n1280x1024\n1280x1024\n1152x864\n1280x720\n1280x720\n1440x576i\n1024x768\n1024x768\n1440x480i\n800x600\n800x600\n720x576\n720x480\n640x480\n640x480\n640x480\n720x400 A: status=connected P: /devices/pci0000:00/0000:00:02.0/drm/card1 N: dri/card1 E: DEVNAME=/dev/dri/card1 E: DEVTYPE=drm_minor E: ID_FOR_SEAT=drm-pci-0000_00_02_0 E: ID_PATH=pci-0000:00:02.0 E: ID_PATH_TAG=pci-0000_00_02_0 E: MAJOR=226 E: MINOR=1 E: SUBSYSTEM=drm E: TAGS=:seat:uaccess: A: dev=226:1 A: gt_RP0_freq_mhz=1200 A: gt_RP1_freq_mhz=650 A: gt_RPn_freq_mhz=650 A: gt_cur_freq_mhz=1200 A: gt_max_freq_mhz=1200 A: gt_min_freq_mhz=650 P: /devices/pci0000:00/0000:00:02.0 E: DRIVER=i915 E: ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller E: ID_PCI_CLASS_FROM_DATABASE=Display controller E: ID_PCI_INTERFACE_FROM_DATABASE=VGA controller E: ID_PCI_SUBCLASS_FROM_DATABASE=VGA compatible controller E: ID_VENDOR_FROM_DATABASE=Intel Corporation E: MODALIAS=pci:v00008086d00000116sv00001028sd000004C1bc03sc00i00 E: PCI_CLASS=30000 E: PCI_ID=8086:0116 E: PCI_SLOT_NAME=0000:00:02.0 E: PCI_SUBSYS_ID=1028:04C1 E: SUBSYSTEM=pci A: boot_vga=1 A: broken_parity_status=0 A: class=0x030000 H: config=86801601070490000900000300000000040000E0000000000C0000D0000000000150000000000000000000002810C1040000000090000000000000000B010000 A: consistent_dma_mask_bits=40 A: d3cold_allowed=1 A: device=0x0116 A: dma_mask_bits=40 A: irq=50 A: local_cpulist=0-3 A: local_cpus=00000000,00000000,00000000,00000000,00000000,00000000,00000000,0000000f A: modalias=pci:v00008086d00000116sv00001028sd000004C1bc03sc00i00 A: msi_bus= A: numa_node=-1 A: resource=0x00000000e0000000 0x00000000e03fffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x00000000d0000000 0x00000000dfffffff 0x000000000014220c\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000005000 0x000000000000503f 0x0000000000040101\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000002\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 A: subsystem_device=0x04c1 A: subsystem_vendor=0x1028 A: vendor=0x8086 P: /devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card0 N: dri/card0 E: DEVNAME=/dev/dri/card0 E: DEVTYPE=drm_minor E: ID_FOR_SEAT=drm-pci-0000_01_00_0 E: ID_PATH=pci-0000:01:00.0 E: ID_PATH_TAG=pci-0000_01_00_0 E: MAJOR=226 E: MINOR=0 E: PRIMARY_DEVICE_FOR_DISPLAY=1 E: SUBSYSTEM=drm E: TAGS=:seat:uaccess: A: dev=226:0 P: /devices/pci0000:00/0000:00:01.0/0000:01:00.0 E: DRIVER=radeon E: ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6600M/6700M/7600M Series] E: ID_PCI_CLASS_FROM_DATABASE=Display controller E: ID_PCI_INTERFACE_FROM_DATABASE=VGA controller E: ID_PCI_SUBCLASS_FROM_DATABASE=VGA compatible controller E: ID_VENDOR_FROM_DATABASE=Advanced Micro Devices, Inc. [AMD/ATI] E: MODALIAS=pci:v00001002d00006741sv00001028sd000004C1bc03sc00i00 E: PCI_CLASS=30000 E: PCI_ID=1002:6741 E: PCI_SLOT_NAME=0000:01:00.0 E: PCI_SUBSYS_ID=1028:04C1 E: SUBSYSTEM=pci A: boot_vga=0 A: broken_parity_status=0 A: class=0x030000 H: config=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF A: consistent_dma_mask_bits=40 A: d3cold_allowed=1 A: device=0x6741 A: dma_mask_bits=40 A: irq=49 A: local_cpulist=0-3 A: local_cpus=00000000,00000000,00000000,00000000,00000000,00000000,00000000,0000000f A: modalias=pci:v00001002d00006741sv00001028sd000004C1bc03sc00i00 A: msi_bus= A: numa_node=-1 A: power_method=profile A: power_profile=default A: resource=0x00000000c0000000 0x00000000cfffffff 0x000000000014220c\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x00000000e1700000 0x00000000e171ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000004000 0x00000000000040ff 0x0000000000040101\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x00000000e1720000 0x00000000e173ffff 0x000000000004e200\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 A: subsystem_device=0x04c1 A: subsystem_vendor=0x1028 A: vendor=0x1002 P: /devices/pci0000:00/0000:00:01.0 E: DRIVER=pcieport E: ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family PCI Express Root Port E: ID_PCI_CLASS_FROM_DATABASE=Bridge E: ID_PCI_INTERFACE_FROM_DATABASE=Normal decode E: ID_PCI_SUBCLASS_FROM_DATABASE=PCI bridge E: ID_VENDOR_FROM_DATABASE=Intel Corporation E: MODALIAS=pci:v00008086d00000101sv00008086sd00002010bc06sc04i00 E: PCI_CLASS=60400 E: PCI_ID=8086:0101 E: PCI_SLOT_NAME=0000:00:01.0 E: PCI_SUBSYS_ID=8086:2010 E: SUBSYSTEM=pci A: broken_parity_status=0 A: class=0x060400 H: config=868001010704100009000406100081000000000000000000000101004040000070E170E101C0F1CF00000000000000000000000088000000000000000B010000 A: consistent_dma_mask_bits=32 A: d3cold_allowed=0 A: device=0x0101 A: dma_mask_bits=32 A: irq=40 A: local_cpulist=0-3 A: local_cpus=00000000,00000000,00000000,00000000,00000000,00000000,00000000,0000000f A: modalias=pci:v00008086d00000101sv00008086sd00002010bc06sc04i00 A: msi_bus=1 A: numa_node=-1 A: resource=0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000004000 0x0000000000004fff 0x0000000000000100\n0x00000000e1700000 0x00000000e17fffff 0x0000000000000200\n0x00000000c0000000 0x00000000cfffffff 0x0000000000102201\n0x0000000000000000 0x0000000000000000 0x0000000000000000 A: subsystem_device=0x2010 A: subsystem_vendor=0x8086 A: vendor=0x8086 ./tests/mir_test_framework/udev-recordings/laptop-keyboard.umockdev0000644000015600001650000000377312676616125026124 0ustar jenkinsjenkinsP: /devices/platform/i8042/serio0/input/input4/event4 N: input/event4 S: input/by-path/platform-i8042-serio-0-event-kbd E: DEVLINKS=/dev/input/by-path/platform-i8042-serio-0-event-kbd E: DEVNAME=/dev/input/event4 E: ID_INPUT=1 E: ID_INPUT_KEY=1 E: ID_INPUT_KEYBOARD=1 E: ID_PATH=platform-i8042-serio-0 E: ID_PATH_TAG=platform-i8042-serio-0 E: ID_SERIAL=noserial E: MAJOR=13 E: MINOR=68 E: SUBSYSTEM=input E: XKBLAYOUT=us E: XKBMODEL=pc105 E: XKBOPTIONS=compose:menu,ctrl:nocaps E: XKBVARIANT=dvorak A: dev=13:68 L: device=../../input4 P: /devices/platform/i8042/serio0/input/input4 E: EV=120013 E: ID_FOR_SEAT=input-platform-i8042-serio-0 E: ID_INPUT=1 E: ID_INPUT_KEY=1 E: ID_INPUT_KEYBOARD=1 E: ID_PATH=platform-i8042-serio-0 E: ID_PATH_TAG=platform-i8042-serio-0 E: ID_SERIAL=noserial E: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe E: LED=7 E: MODALIAS=input:b0011v0001p0001eAB83-e0,1,4,11,14,k71,72,73,74,75,76,77,79,7A,7B,7C,7D,7E,7F,80,8C,8E,8F,9B,9C,9D,9E,9F,A3,A4,A5,A6,AC,AD,B7,B8,B9,D9,E2,ram4,l0,1,2,sfw E: MSC=10 E: NAME="AT Translated Set 2 keyboard" E: PHYS="isa0060/serio0/input0" E: PRODUCT=11/1/1/ab83 E: PROP=0 E: SUBSYSTEM=input E: TAGS=:seat: L: device=../../../serio0 A: modalias=input:b0011v0001p0001eAB83-e0,1,4,11,14,k71,72,73,74,75,76,77,79,7A,7B,7C,7D,7E,7F,80,8C,8E,8F,9B,9C,9D,9E,9F,A3,A4,A5,A6,AC,AD,B7,B8,B9,D9,E2,ram4,l0,1,2,sfw A: name=AT Translated Set 2 keyboard A: phys=isa0060/serio0/input0 A: properties=0 A: uniq= P: /devices/platform/i8042/serio0 E: DRIVER=atkbd E: MODALIAS=serio:ty06pr00id00ex00 E: SERIO_EXTRA=00 E: SERIO_ID=00 E: SERIO_PROTO=00 E: SERIO_TYPE=06 E: SUBSYSTEM=serio A: bind_mode=auto A: description=i8042 KBD port L: driver=../../../../bus/serio/drivers/atkbd A: err_count=0 A: extra=0 A: force_release=369-370 A: modalias=serio:ty06pr00id00ex00 A: scroll=0 A: set=2 A: softraw=1 A: softrepeat=0 P: /devices/platform/i8042 E: DRIVER=i8042 E: MODALIAS=platform:i8042 E: SUBSYSTEM=platform L: driver=../../../bus/platform/drivers/i8042 A: modalias=platform:i8042 ./tests/mir_test_framework/udev-recordings/laptop-mouse-click.evemu0000644000015600001650000001007112676616125026030 0ustar jenkinsjenkins# device /dev/input/event5 # EVEMU 1.2 # Input device name: "SynPS/2 Synaptics TouchPad" # Input device ID: bus 0x11 vendor 0x02 product 0x07 version 0x1b1 # Supported events: # Event type 0 (EV_SYN) # Event code 0 (SYN_REPORT) # Event code 1 (SYN_CONFIG) # Event code 2 (SYN_MT_REPORT) # Event code 3 (SYN_DROPPED) # Event code 4 ((null)) # Event code 5 ((null)) # Event code 6 ((null)) # Event code 7 ((null)) # Event code 8 ((null)) # Event code 9 ((null)) # Event code 10 ((null)) # Event code 11 ((null)) # Event code 12 ((null)) # Event code 13 ((null)) # Event code 14 ((null)) # Event type 1 (EV_KEY) # Event code 272 (BTN_LEFT) # Event code 273 (BTN_RIGHT) # Event code 325 (BTN_TOOL_FINGER) # Event code 330 (BTN_TOUCH) # Event code 333 (BTN_TOOL_DOUBLETAP) # Event code 334 (BTN_TOOL_TRIPLETAP) # Event type 3 (EV_ABS) # Event code 0 (ABS_X) # Value 2840 # Min 1472 # Max 5470 # Fuzz 0 # Flat 0 # Resolution 60 # Event code 1 (ABS_Y) # Value 2457 # Min 1408 # Max 4498 # Fuzz 0 # Flat 0 # Resolution 85 # Event code 24 (ABS_PRESSURE) # Value 0 # Min 0 # Max 255 # Fuzz 0 # Flat 0 # Resolution 0 # Event code 28 (ABS_TOOL_WIDTH) # Value 0 # Min 0 # Max 15 # Fuzz 0 # Flat 0 # Resolution 0 # Event code 47 (ABS_MT_SLOT) # Value 0 # Min 0 # Max 1 # Fuzz 0 # Flat 0 # Resolution 0 # Event code 53 (ABS_MT_POSITION_X) # Value 0 # Min 1472 # Max 5470 # Fuzz 0 # Flat 0 # Resolution 60 # Event code 54 (ABS_MT_POSITION_Y) # Value 0 # Min 1408 # Max 4498 # Fuzz 0 # Flat 0 # Resolution 85 # Event code 57 (ABS_MT_TRACKING_ID) # Value 0 # Min 0 # Max 65535 # Fuzz 0 # Flat 0 # Resolution 0 # Properties: # Property type 0 (INPUT_PROP_POINTER) # Property type 3 (INPUT_PROP_SEMI_MT) # N: SynPS/2 Synaptics TouchPad # I: 0011 0002 0007 01b1 # P: 09 00 00 00 00 00 00 00 # B: 00 0b 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 03 00 00 00 00 00 # B: 01 20 64 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 01 00 00 00 00 00 00 00 00 # B: 02 00 00 00 00 00 00 00 00 # B: 03 03 00 00 11 00 80 60 02 # B: 04 00 00 00 00 00 00 00 00 # B: 05 00 00 00 00 00 00 00 00 # B: 11 00 00 00 00 00 00 00 00 # B: 12 00 00 00 00 00 00 00 00 # B: 14 00 00 00 00 00 00 00 00 # B: 15 00 00 00 00 00 00 00 00 # B: 15 00 00 00 00 00 00 00 00 # A: 00 1472 5470 0 0 60 # A: 01 1408 4498 0 0 85 # A: 18 0 255 0 0 0 # A: 1c 0 15 0 0 0 # A: 2f 0 1 0 0 0 # A: 35 1472 5470 0 0 60 # A: 36 1408 4498 0 0 85 # A: 39 0 65535 0 0 0 ################################ # Waiting for events # ################################ E: 0.000000 0001 0110 0001 # EV_KEY / BTN_LEFT 1 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.000000 0001 0110 0000 # EV_KEY / BTN_LEFT 0 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.000000 0001 0110 0001 # EV_KEY / BTN_LEFT 1 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.000000 0001 0110 0000 # EV_KEY / BTN_LEFT 0 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.000000 0001 0110 0001 # EV_KEY / BTN_LEFT 1 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 0.000000 0001 0110 0000 # EV_KEY / BTN_LEFT 0 E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- ./tests/mir_test_framework/input_testing_server_options.cpp0000644000015600001650000000574412676616125024731 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir_test_framework/input_testing_server_configuration.h" #include "mir/input/input_channel.h" #include "mir/input/surface.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/frontend/session.h" #include "mir/input/composite_event_filter.h" #include "mir/test/wait_condition.h" #include #include #include namespace mtf = mir_test_framework; namespace mt = mir::test; namespace mf = mir::frontend; namespace mg = mir::graphics; namespace mi = mir::input; namespace ms = mir::shell; namespace geom = mir::geometry; mtf::InputTestingServerConfiguration::InputTestingServerConfiguration() { } mtf::InputTestingServerConfiguration::InputTestingServerConfiguration( std::vector const& display_rects) : TestingServerConfiguration(display_rects) { } void mtf::InputTestingServerConfiguration::on_start() { auto const start_input_injection = std::make_shared(); input_injection_thread = std::thread{ [this, start_input_injection] { // We need to wait for the 'input_injection_thread' variable to be // assigned to before starting. Otherwise we may end up calling // on_exit() before assignment and try to join an unjoinable thread. start_input_injection->wait_for_at_most_seconds(3); if (!start_input_injection->woken()) BOOST_THROW_EXCEPTION(std::runtime_error("Input injection thread start signal timed out")); inject_input(); }}; start_input_injection->wake_up_everyone(); } void mtf::InputTestingServerConfiguration::on_exit() { input_injection_thread.join(); } std::shared_ptr mtf::InputTestingServerConfiguration::the_input_targeter() { return DefaultServerConfiguration::the_input_targeter(); } std::shared_ptr mtf::InputTestingServerConfiguration::the_input_manager() { return DefaultServerConfiguration::the_input_manager(); } std::shared_ptr mtf::InputTestingServerConfiguration::the_input_dispatcher() { return DefaultServerConfiguration::the_input_dispatcher(); } std::shared_ptr mtf::InputTestingServerConfiguration::the_input_sender() { return DefaultServerConfiguration::the_input_sender(); } ./tests/mir_test_framework/stubbed_server_configuration.cpp0000644000015600001650000001064212676616157024637 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir_test_framework/stubbed_server_configuration.h" #include "mir_test_framework/command_line_server_configuration.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir/options/default_configuration.h" #include "mir/graphics/cursor.h" #include "mir/test/doubles/stub_display_buffer.h" #include "mir/test/doubles/stub_renderer.h" #include "mir/test/doubles/stub_input_sender.h" #include "mir/compositor/renderer_factory.h" #include "src/server/input/null_input_manager.h" #include "src/server/input/null_input_dispatcher.h" #include "src/server/input/null_input_targeter.h" #include "mir/test/doubles/null_logger.h" #include "mir/test/doubles/stub_cursor.h" namespace geom = mir::geometry; namespace mc = mir::compositor; namespace msh = mir::shell; namespace mg = mir::graphics; namespace mi = mir::input; namespace ml = mir::logging; namespace mo = mir::options; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { class StubRendererFactory : public mc::RendererFactory { public: std::unique_ptr create_renderer_for(mg::DisplayBuffer&) { return std::unique_ptr(new mtd::StubRenderer()); } }; } mtf::StubbedServerConfiguration::StubbedServerConfiguration() : StubbedServerConfiguration({geom::Rectangle{{0,0},{1600,1600}}}) { } mtf::StubbedServerConfiguration::StubbedServerConfiguration( std::vector const& display_rects) : DefaultServerConfiguration([] { auto result = mtf::configuration_from_commandline(); namespace po = boost::program_options; result->add_options() (mtd::logging_opt, po::value()->default_value(false), mtd::logging_descr) ("tests-use-real-input", po::value()->default_value(false), "Use real input in tests."); return result; }()), display_rects{display_rects} { } mtf::StubbedServerConfiguration::~StubbedServerConfiguration() = default; std::shared_ptr mtf::StubbedServerConfiguration::the_graphics_platform() { if (!graphics_platform) { graphics_platform = mtf::make_stubbed_server_graphics_platform(display_rects); } return graphics_platform; } std::shared_ptr mtf::StubbedServerConfiguration::the_renderer_factory() { auto options = the_options(); return renderer_factory( [&]() { return std::make_shared(); }); } std::shared_ptr mtf::StubbedServerConfiguration::the_input_manager() { auto options = the_options(); if (options->get("tests-use-real-input")) return DefaultServerConfiguration::the_input_manager(); else return std::make_shared(); } std::shared_ptr mtf::StubbedServerConfiguration::the_input_targeter() { auto options = the_options(); if (options->get("tests-use-real-input")) return DefaultServerConfiguration::the_input_targeter(); else return std::make_shared(); } std::shared_ptr mtf::StubbedServerConfiguration::the_input_sender() { auto options = the_options(); if (options->get("tests-use-real-input")) return DefaultServerConfiguration::the_input_sender(); else return std::make_shared(); } std::shared_ptr mtf::StubbedServerConfiguration::the_cursor() { return std::make_shared(); } std::shared_ptr mtf::StubbedServerConfiguration::the_logger() { if (the_options()->get(mtd::logging_opt)) return DefaultServerConfiguration::the_logger(); return std::make_shared(); } ./tests/mir_test_framework/symbols-client.map0000644000015600001650000000015512676616157021627 0ustar jenkinsjenkinsMIR_CLIENT_PLATFORM_5 { global: create_client_platform; is_appropriate_module; local: *; }; ./tests/mir_test_framework/libinput_environment.cpp0000644000015600001650000001160112676616157023140 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir_test_framework/libinput_environment.h" #include #include namespace mtf = mir_test_framework; using namespace testing; using DeviceEntry = std::pair; std::string const mtf::LibInputEnvironment::synaptics_touchpad("synaptics-touchpad"); std::string const mtf::LibInputEnvironment::bluetooth_magic_trackpad("bluetooth-magic-trackpad"); std::string const mtf::LibInputEnvironment::usb_keyboard("usb-keyboard"); std::string const mtf::LibInputEnvironment::usb_mouse("usb-mouse"); std::string const mtf::LibInputEnvironment::laptop_keyboard("laptop-keyboard"); std::string const mtf::LibInputEnvironment::mtk_tpd("mtk-tpd"); std::string const mtf::LibInputEnvironment::usb_joystick("usb_joystick"); namespace { std::string const model("ID_MODEL"); std::string const input("ID_INPUT"); std::string const touchpad("ID_INPUT_TOUCHPAD"); std::string const touchscreen("ID_INPUT_TOUCHSCREEN"); std::string const mouse("ID_INPUT_MOUSE"); std::string const key("ID_INPUT_KEY"); std::string const keyboard("ID_INPUT_KEYBOARD"); std::string const joystick("ID_INPUT_JOYSTICK"); } mtf::LibInputEnvironment::LibInputEnvironment() : standard_devices{DeviceEntry{synaptics_touchpad, DeviceSetup{"/dev/input/event2", }}, DeviceEntry{usb_keyboard, DeviceSetup{"/dev/input/event11"}}, DeviceEntry{usb_mouse, DeviceSetup{"/dev/input/event12"}}, DeviceEntry{laptop_keyboard, DeviceSetup{"/dev/input/event4"}}, DeviceEntry{bluetooth_magic_trackpad, DeviceSetup{"/dev/input/event13"}}, DeviceEntry{usb_joystick, DeviceSetup{"/dev/input/event15"}}, DeviceEntry{mtk_tpd, DeviceSetup{"/dev/input/event1"}}} { standard_devices[synaptics_touchpad].properties[touchpad] = "1"; standard_devices[bluetooth_magic_trackpad].properties[touchpad]="1"; standard_devices[usb_keyboard].properties[key]="1"; standard_devices[usb_keyboard].properties[keyboard]="1"; standard_devices[usb_mouse].properties[mouse]="1"; standard_devices[laptop_keyboard].properties[key]="1"; standard_devices[laptop_keyboard].properties[keyboard]="1"; standard_devices[mtk_tpd].properties[touchscreen]="1"; standard_devices[mtk_tpd].properties[key]="1"; standard_devices[usb_joystick].properties[joystick]="1"; standard_devices[usb_joystick].properties[key]="1"; for (auto device : standard_devices) { device.second.properties[input] = "1"; device.second.properties[model] = device.first; } ON_CALL(mock_libinput, libinput_udev_create_context(_,_,_)) .WillByDefault(Return(li_context)); ON_CALL(mock_libinput, libinput_device_get_device_group(_)) .WillByDefault(Return(nullptr)); } void mtf::LibInputEnvironment::add_standard_device(std::string const& device_name) { auto dev = setup_device(device_name); mock_libinput.setup_device_add_event(dev); } void mtf::LibInputEnvironment::remove_standard_device(std::string const& device_name) { mock_libinput.setup_device_remove_event(available_devs[device_name]); available_devs.erase(device_name); } libinput_device* mtf::LibInputEnvironment::setup_device(std::string const& device_name) { auto entry = standard_devices.find(device_name); if (entry == standard_devices.end()) BOOST_THROW_EXCEPTION(std::runtime_error("device not known")); auto dev = mock_libinput.get_next_fake_ptr(); auto group = mock_libinput.get_next_fake_ptr(); auto u_dev = mock_libinput.get_next_fake_ptr(); mock_libinput.setup_device(dev, group, u_dev, device_name.c_str(), 123, 456); ON_CALL(mock_udev, udev_device_get_devnode(u_dev)) .WillByDefault(Return(entry->second.path.c_str())); ON_CALL(mock_udev, udev_device_get_property_value(u_dev, _)) .WillByDefault(Invoke( [this, device_name](udev_device*, char const* property) { return standard_devices[device_name].properties[property].c_str(); } )); available_devs[device_name] = dev; return dev; } ./tests/mir_test_framework/testing-cursor-theme/0000755000015600001650000000000012676616160022245 5ustar jenkinsjenkins./tests/mir_test_framework/testing-cursor-theme/default/0000755000015600001650000000000012676616124023671 5ustar jenkinsjenkins./tests/mir_test_framework/testing-cursor-theme/default/cursors/0000755000015600001650000000000012676616125025372 5ustar jenkinsjenkins./tests/mir_test_framework/testing-cursor-theme/default/cursors/green.in0000644000015600001650000000002012676616125027012 0ustar jenkinsjenkins24 0 0 green.png./tests/mir_test_framework/testing-cursor-theme/default/cursors/red0000644000015600001650000000450012676616125026066 0ustar jenkinsjenkinsXcur$2./tests/mir_test_framework/testing-cursor-theme/default/cursors/generate-theme.sh0000755000015600001650000000015412676616125030623 0ustar jenkinsjenkins#!/bin/sh xcursorgen red.in red xcursorgen blue.in blue xcursorgen green.in green xcursorgen arrow.in arrow ./tests/mir_test_framework/testing-cursor-theme/default/cursors/green0000644000015600001650000000450012676616125026414 0ustar jenkinsjenkinsXcur$2./tests/mir_test_framework/testing-cursor-theme/default/cursors/arrow0000644000015600001650000000450012676616125026446 0ustar jenkinsjenkinsXcur$2./tests/mir_test_framework/testing-cursor-theme/default/cursors/arrow.in0000644000015600001650000000002012676616125027044 0ustar jenkinsjenkins24 0 0 arrow.png./tests/mir_test_framework/testing-cursor-theme/default/cursors/arrow.png0000644000015600001650000000047012676616125027233 0ustar jenkinsjenkinsPNG  IHDR UgAMA asRGB cHRMz&u0`:pQ<bKGD݊ pHYs B(x IDATc`-`7x%tEXtdate:create2014-06-18T16:11:39-07:00 %tEXtdate:modify2014-06-18T16:11:36-07:00HDtEXtSoftwarewww.inkscape.org<IENDB`./tests/mir_test_framework/testing-cursor-theme/default/cursors/blue.in0000644000015600001650000000001712676616125026647 0ustar jenkinsjenkins24 0 0 blue.png./tests/mir_test_framework/testing-cursor-theme/default/cursors/red.png0000644000015600001650000000051112676616125026647 0ustar jenkinsjenkinsPNG  IHDRڹgAMA asRGB cHRMz&u0`:pQ<PLTEA4bKGD- pHYs B(x IDATc`-`7x%tEXtdate:create2014-06-18T16:09:35-07:00wuQ%tEXtdate:modify2014-06-18T16:09:14-07:00x$tEXtSoftwarewww.inkscape.org<IENDB`./tests/mir_test_framework/testing-cursor-theme/default/cursors/green.png0000644000015600001650000000051112676616125027175 0ustar jenkinsjenkinsPNG  IHDRڹgAMA asRGB cHRMz&u0`:pQ<PLTEoXQbKGD- pHYs B(x IDATc`-`7x%tEXtdate:create2014-06-18T16:09:26-07:005oR%tEXtdate:modify2014-06-18T16:09:22-07:00a'tEXtSoftwarewww.inkscape.org<IENDB`./tests/mir_test_framework/testing-cursor-theme/default/cursors/blue0000644000015600001650000000450012676616125026243 0ustar jenkinsjenkinsXcur$2./tests/mir_test_framework/testing-cursor-theme/default/cursors/blue.png0000644000015600001650000000051112676616125027024 0ustar jenkinsjenkinsPNG  IHDRڹgAMA asRGB cHRMz&u0`:pQ<PLTE{ܙ,bKGD- pHYs B(x IDATc`-`7x%tEXtdate:create2014-06-18T16:09:29-07:00}%tEXtdate:modify2014-06-18T16:09:08-07:00rtEXtSoftwarewww.inkscape.org<IENDB`./tests/mir_test_framework/testing-cursor-theme/default/cursors/red.in0000644000015600001650000000001612676616125026471 0ustar jenkinsjenkins24 0 0 red.png./tests/mir_test_framework/testing-cursor-theme/CMakeLists.txt0000644000015600001650000000015112676616157025010 0ustar jenkinsjenkinsinstall(DIRECTORY default DESTINATION ${CMAKE_INSTALL_PREFIX}/share/mir-test-data/testing-cursor-theme) ./tests/mir_test_framework/headless_test.cpp0000644000015600001650000000334612676616125021517 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir_test_framework/headless_test.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir/shared_library.h" #include "mir/geometry/rectangle.h" #include "mir_test_framework/executable_path.h" #include namespace geom = mir::geometry; namespace mtf = mir_test_framework; mtf::HeadlessTest::HeadlessTest() { add_to_environment("MIR_SERVER_PLATFORM_GRAPHICS_LIB", mtf::server_platform("graphics-dummy.so").c_str()); add_to_environment("MIR_SERVER_PLATFORM_INPUT_LIB", mtf::server_platform("input-stub.so").c_str()); add_to_environment("MIR_SERVER_ENABLE_KEY_REPEAT", "false"); } mtf::HeadlessTest::~HeadlessTest() noexcept = default; void mtf::HeadlessTest::preset_display(std::shared_ptr const& display) { mtf::set_next_preset_display(display); } void mtf::HeadlessTest::initial_display_layout(std::vector const& display_rects) { mtf::set_next_display_rects(std::unique_ptr>(new std::vector(display_rects))); } ./tests/mir_test_framework/stubbed_graphics_platform.cpp0000644000015600001650000003121612676616125024101 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "stubbed_graphics_platform.h" #include "mir_test_framework/stub_graphics_platform_operation.h" #include "mir/graphics/buffer_ipc_message.h" #include "mir_test_framework/stub_platform_helpers.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/stub_display.h" #include "mir/fd.h" #include "mir/assert_module_entry_point.h" #include "mir/test/pipe.h" #include "mir/libname.h" #ifdef ANDROID #include "mir/test/doubles/stub_android_native_buffer.h" #endif #include #include #include #include #include #include #include namespace geom = mir::geometry; namespace mg = mir::graphics; namespace mo = mir::options; namespace mtd = mir::test::doubles; namespace mtf = mir_test_framework; namespace { namespace { mir::ModuleProperties const module_properties = { "mir:stub-graphics", MIR_VERSION_MAJOR, MIR_VERSION_MINOR, MIR_VERSION_MICRO, mir::libname() }; } class StubFDBuffer : public mtd::StubBuffer { public: StubFDBuffer(mg::BufferProperties const& properties) : StubBuffer(properties), properties{properties} { fd = open("/dev/zero", O_RDONLY); if (fd < 0) BOOST_THROW_EXCEPTION( boost::enable_error_info( std::system_error(errno, std::system_category(), "Failed to open dummy fd"))); } std::shared_ptr native_buffer_handle() const override { #if defined(MESA_KMS) || defined(MESA_X11) auto native_buffer = std::make_shared(); native_buffer->data_items = 1; native_buffer->data[0] = 0xDEADBEEF; native_buffer->fd_items = 1; native_buffer->fd[0] = fd; native_buffer->width = properties.size.width.as_int(); native_buffer->height = properties.size.height.as_int(); native_buffer->flags = 0; if (properties.size.width.as_int() >= 800 && properties.size.height.as_int() >= 600 && properties.usage == mg::BufferUsage::hardware) { native_buffer->flags |= mir_buffer_flag_can_scanout; } #else auto native_buffer = std::make_shared(); auto anwb = native_buffer->anwb(); anwb->width = properties.size.width.as_int(); anwb->height = properties.size.width.as_int(); #endif return native_buffer; } ~StubFDBuffer() noexcept { close(fd); } private: int fd; const mg::BufferProperties properties; }; struct WrappingDisplay : mg::Display { WrappingDisplay(std::shared_ptr const& display) : display{display} {} void for_each_display_sync_group(std::function const& f) override { display->for_each_display_sync_group(f); } std::unique_ptr configuration() const override { return display->configuration(); } void configure(mg::DisplayConfiguration const& conf) override { display->configure(conf); } void register_configuration_change_handler( mg::EventHandlerRegister& handlers, mg::DisplayConfigurationChangeHandler const& conf_change_handler) override { display->register_configuration_change_handler(handlers, conf_change_handler); } void register_pause_resume_handlers( mg::EventHandlerRegister& handlers, mg::DisplayPauseHandler const& pause_handler, mg::DisplayResumeHandler const& resume_handler) override { display->register_pause_resume_handlers(handlers, pause_handler, resume_handler); } void pause() override { display->pause(); } void resume( )override { display->resume(); } std::shared_ptr create_hardware_cursor(std::shared_ptr const& initial_image) override { return display->create_hardware_cursor(initial_image); } std::unique_ptr create_gl_context() override { return display->create_gl_context(); } std::unique_ptr create_virtual_output(int width, int height) override { return display->create_virtual_output(width, height); } std::shared_ptr const display; }; class StubGraphicBufferAllocator : public mtd::StubBufferAllocator { public: std::shared_ptr alloc_buffer(mg::BufferProperties const& properties) override { if (properties.size.width == geom::Width{0} || properties.size.height == geom::Height{0}) { BOOST_THROW_EXCEPTION( std::runtime_error("Request for allocation of buffer with invalid size")); } return std::make_shared(properties); } }; class StubIpcOps : public mg::PlatformIpcOperations { void pack_buffer( mg::BufferIpcMessage& message, mg::Buffer const& buffer, mg::BufferIpcMsgType msg_type) const override { if (msg_type == mg::BufferIpcMsgType::full_msg) { #if defined(MESA_KMS) || defined(MESA_X11) auto native_handle = buffer.native_buffer_handle(); for(auto i=0; idata_items; i++) { message.pack_data(native_handle->data[i]); } for(auto i=0; ifd_items; i++) { using namespace mir; message.pack_fd(Fd(IntOwnedFd{native_handle->fd[i]})); } message.pack_flags(native_handle->flags); #endif message.pack_stride(buffer.stride()); message.pack_size(buffer.size()); } } void unpack_buffer( mg::BufferIpcMessage&, mg::Buffer const&) const override { } std::shared_ptr connection_ipc_package() override { auto package = std::make_shared(&module_properties); mtf::pack_stub_ipc_package(*package); return package; } mg::PlatformOperationMessage platform_operation( unsigned int const opcode, mg::PlatformOperationMessage const& message) override { mg::PlatformOperationMessage reply; if (opcode == static_cast(mtf::StubGraphicsPlatformOperation::add)) { if (message.data.size() != 2 * sizeof(int)) { BOOST_THROW_EXCEPTION( std::runtime_error("Invalid parameters for 'add' platform operation")); } auto const int_data = reinterpret_cast(message.data.data()); reply.data.resize(sizeof(int)); *(reinterpret_cast(reply.data.data())) = int_data[0] + int_data[1]; } else if (opcode == static_cast(mtf::StubGraphicsPlatformOperation::echo_fd)) { if (message.fds.size() != 1) { BOOST_THROW_EXCEPTION( std::runtime_error("Invalid parameters for 'echo_fd' platform operation")); } mir::Fd const request_fd{message.fds[0]}; char request_char{0}; if (read(request_fd, &request_char, 1) != 1) { BOOST_THROW_EXCEPTION( std::runtime_error("Failed to read character from request fd in 'echo_fd' operation")); } mir::test::Pipe pipe; if (write(pipe.write_fd(), &request_char, 1) != 1) { BOOST_THROW_EXCEPTION( std::runtime_error("Failed to write to pipe in 'echo_fd' operation")); } reply.fds.push_back(dup(pipe.read_fd())); } else { BOOST_THROW_EXCEPTION( std::runtime_error("Invalid platform operation")); } return reply; } }; } mtf::StubGraphicPlatform::StubGraphicPlatform(std::vector const& display_rects) : display_rects{display_rects} { } mir::UniqueModulePtr mtf::StubGraphicPlatform::create_buffer_allocator() { return mir::make_module_ptr(); } mir::UniqueModulePtr mtf::StubGraphicPlatform::make_ipc_operations() const { return mir::make_module_ptr(); } namespace { std::shared_ptr display_preset; } mir::UniqueModulePtr mtf::StubGraphicPlatform::create_display( std::shared_ptr const&, std::shared_ptr const&) { if (display_preset) return mir::make_module_ptr(std::move(display_preset)); return mir::make_module_ptr(display_rects); } namespace { struct GuestPlatformAdapter : mg::Platform { GuestPlatformAdapter( std::shared_ptr const& context, std::shared_ptr const& adaptee) : context(context), adaptee(adaptee) { } mir::UniqueModulePtr create_buffer_allocator() override { return adaptee->create_buffer_allocator(); } mir::UniqueModulePtr make_ipc_operations() const override { return adaptee->make_ipc_operations(); } mir::UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, std::shared_ptr const& gl_config) override { return adaptee->create_display(initial_conf_policy, gl_config); } EGLNativeDisplayType egl_native_display() const override { return adaptee->egl_native_display(); } std::shared_ptr const context; std::shared_ptr const adaptee; }; std::weak_ptr the_graphics_platform{}; std::unique_ptr> chosen_display_rects; } extern "C" std::shared_ptr create_stub_platform(std::vector const& display_rects) { return std::make_shared(display_rects); } mir::UniqueModulePtr create_host_platform( std::shared_ptr const& /*options*/, std::shared_ptr const& /*emergency_cleanup_registry*/, std::shared_ptr const& /*report*/) { mir::assert_entry_point_signature(&create_host_platform); std::shared_ptr result{}; if (auto const display_rects = std::move(chosen_display_rects)) { result = create_stub_platform(*display_rects); } else { static std::vector const default_display_rects{geom::Rectangle{{0,0},{1600,1600}}}; result = create_stub_platform(default_display_rects); } the_graphics_platform = result; return mir::make_module_ptr(nullptr, result); } mir::UniqueModulePtr create_guest_platform( std::shared_ptr const&, std::shared_ptr const& context) { mir::assert_entry_point_signature(&create_guest_platform); auto graphics_platform = the_graphics_platform.lock(); if (!graphics_platform) { static std::vector const default_display_rects{geom::Rectangle{{0,0},{1600,1600}}}; the_graphics_platform = graphics_platform = create_stub_platform(default_display_rects); } return mir::make_module_ptr(context, graphics_platform); } void add_graphics_platform_options( boost::program_options::options_description& /*config*/) { mir::assert_entry_point_signature(&add_graphics_platform_options); } extern "C" void set_next_display_rects( std::unique_ptr>&& display_rects) { chosen_display_rects = std::move(display_rects); } extern "C" void set_next_preset_display(std::shared_ptr const& display) { display_preset = display; } ./tests/mir_test_framework/stub_client_platform_factory.cpp0000644000015600001650000000521112676616157024634 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir_test_framework/stub_client_platform_factory.h" #include "mir/test/doubles/stub_client_buffer_factory.h" #include "mir/client_buffer_factory.h" #include "mir/client_buffer.h" #include "mir/client_context.h" #include #include namespace mcl = mir::client; namespace geom = mir::geometry; namespace mtf = mir_test_framework; namespace mtd = mir::test::doubles; mtf::StubClientPlatform::StubClientPlatform(mir::client::ClientContext* context) : context{context} { } MirPlatformType mtf::StubClientPlatform::platform_type() const { return mir_platform_type_gbm; } void mtf::StubClientPlatform::populate(MirPlatformPackage& package) const { context->populate_server_package(package); } MirPlatformMessage* mtf::StubClientPlatform::platform_operation(MirPlatformMessage const*) { return nullptr; } std::shared_ptr mtf::StubClientPlatform::create_buffer_factory() { return std::make_shared(); } std::shared_ptr mtf::StubClientPlatform::create_egl_native_window(mir::client::EGLNativeSurface* surface) { return std::shared_ptr{surface, [](void*){}}; } std::shared_ptr mtf::StubClientPlatform::create_egl_native_display() { auto fake_display = reinterpret_cast(0x12345678lu); return std::make_shared(fake_display); } MirNativeBuffer* mtf::StubClientPlatform::convert_native_buffer(mir::graphics::NativeBuffer* buf) const { static_cast(buf); #if defined(MESA_KMS) || defined(MESA_X11) return buf; #else return nullptr; #endif } MirPixelFormat mtf::StubClientPlatform::get_egl_pixel_format(EGLDisplay, EGLConfig) const { return mir_pixel_format_argb_8888; } std::shared_ptr mtf::StubClientPlatformFactory::create_client_platform(mcl::ClientContext* context) { return std::make_shared(context); } ./tests/mir_test_framework/headless_in_process_server.cpp0000644000015600001650000000215612676616125024270 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir_test_framework/headless_in_process_server.h" #include "mir_test_framework/stub_server_platform_factory.h" namespace mtf = mir_test_framework; mtf::HeadlessInProcessServer::HeadlessInProcessServer() { add_to_environment("MIR_SERVER_NO_FILE", ""); } void mtf::HeadlessInProcessServer::SetUp() { start_server(); } void mtf::HeadlessInProcessServer::TearDown() { mtf::set_next_preset_display(nullptr); stop_server(); } ./tests/mir_test_framework/stub_server_platform_factory.cpp0000644000015600001650000000560312676616125024664 0ustar jenkinsjenkins /* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/shared_library.h" #include "mir/geometry/rectangle.h" #include "mir_test_framework/executable_path.h" #include "mir_test_framework/stub_server_platform_factory.h" #include "mir_test_framework/fake_input_device.h" #include namespace geom = mir::geometry; namespace mg = mir::graphics; namespace mtf = mir_test_framework; namespace { // NOTE: Raw pointer, deliberately leaked to bypass all the fun // issues around global destructor ordering. mir::SharedLibrary* platform_graphics_lib{nullptr}; mir::SharedLibrary* platform_input_lib{nullptr}; void ensure_platform_library() { if (!platform_graphics_lib) { platform_graphics_lib = new mir::SharedLibrary{mtf::server_platform("graphics-dummy.so")}; } if (!platform_input_lib) { platform_input_lib = new mir::SharedLibrary{mtf::server_platform("input-stub.so")}; } } } std::shared_ptr mtf::make_stubbed_server_graphics_platform(std::vector const& display_rects) { ensure_platform_library(); auto factory = platform_graphics_lib->load_function(*)(std::vector const&)>("create_stub_platform"); return factory(display_rects); } void mtf::set_next_display_rects(std::unique_ptr>&& display_rects) { ensure_platform_library(); auto rect_setter = platform_graphics_lib->load_function>&&)>("set_next_display_rects"); rect_setter(std::move(display_rects)); } void mtf::set_next_preset_display(std::shared_ptr const& display) { ensure_platform_library(); auto display_setter = platform_graphics_lib->load_function const&)>("set_next_preset_display"); display_setter(display); } mir::UniqueModulePtr mtf::add_fake_input_device(mir::input::InputDeviceInfo const& info) { ensure_platform_library(); auto add_device = platform_input_lib->load_function< mir::UniqueModulePtr(*)(mir::input::InputDeviceInfo const&) >("add_fake_input_device"); return add_device(info); } ./tests/mir_test_framework/testing_server_options.cpp0000644000015600001650000001035212676616157023506 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir_test_framework/testing_server_configuration.h" #include "mir/server_status_listener.h" #include #include #include #include namespace mt = mir::test; namespace mtf = mir_test_framework; namespace geom = mir::geometry; namespace { bool socket_exists(std::string const& socket_name) { std::string socket_path{socket_name}; socket_path.insert(std::begin(socket_path), ' '); std::ifstream socket_names_file("/proc/net/unix"); std::string line; while (std::getline(socket_names_file, line)) { if (line.find(socket_path) != std::string::npos) return true; } return false; } std::string create_random_socket_name() { std::random_device random_device("/dev/urandom"); std::mt19937 generator(random_device()); int max_concurrent_test_instances = 99999; std::uniform_int_distribution<> dist(1, max_concurrent_test_instances); std::string const suffix{"-mir_socket_test"}; /* check for name collisions against other test instances * running concurrently */ for (int i = 0; i < max_concurrent_test_instances; i++) { std::string name{std::to_string(dist(generator))}; name.append(suffix); if (!socket_exists(name)) return name; } throw std::runtime_error("Too many test socket instances exist!"); } } mtf::TestingServerConfiguration::TestingServerConfiguration() : using_server_started_sync(false) { } mtf::TestingServerConfiguration::TestingServerConfiguration(std::vector const& display_rects) : StubbedServerConfiguration(display_rects), using_server_started_sync(false) { } mtf::TestingServerConfiguration::~TestingServerConfiguration() = default; void mtf::TestingServerConfiguration::exec() { } void mtf::TestingServerConfiguration::on_start() { } void mtf::TestingServerConfiguration::on_exit() { } std::string mtf::TestingServerConfiguration::the_socket_file() const { return test_socket_file(); } std::shared_ptr mtf::TestingServerConfiguration::the_server_status_listener() { struct TestingServerStatusListener : public mir::ServerStatusListener { TestingServerStatusListener(mt::CrossProcessSync const& sync, std::function const& on_start) : server_started_sync{sync}, on_start{on_start} { } void paused() {} void resumed() {} void started() { server_started_sync.try_signal_ready_for(); on_start(); } mt::CrossProcessSync server_started_sync; std::function const on_start; }; return server_status_listener( [this] { using_server_started_sync = true; return std::make_shared( server_started_sync, [this] { on_start(); }); }); } void mtf::TestingServerConfiguration::wait_for_server_start() { auto listener = the_server_status_listener(); if (!using_server_started_sync) { BOOST_THROW_EXCEPTION( std::runtime_error( "Not using cross process sync mechanism for server startup detection." "Did you override the_server_status_listener() in the test?")); } server_started_sync.wait_for_signal_ready_for(); } std::string const& mtf::test_socket_file() { static const std::string socket_file{create_random_socket_name()}; return socket_file; } ./tests/mir_test_framework/any_surface.cpp0000644000015600001650000000221712676616125021163 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #include "mir_test_framework/any_surface.h" namespace mtf = mir_test_framework; namespace { int width = 783; int height = 691; MirPixelFormat format = mir_pixel_format_abgr_8888; } MirSurface* mtf::make_any_surface(MirConnection *connection) { auto spec = mir_connection_create_spec_for_normal_surface(connection, width, height, format); auto surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); return surface; } ./tests/mir_test_framework/temporary_environment_value.cpp0000644000015600001650000000241312676616125024524 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir_test_framework/temporary_environment_value.h" #include namespace mtf = mir_test_framework; mtf::TemporaryEnvironmentValue::TemporaryEnvironmentValue(char const* name, char const* value) : name{name}, has_old_value{getenv(name) != nullptr}, old_value{has_old_value ? getenv(name) : ""} { if (value) setenv(name, value, overwrite); else unsetenv(name); } mtf::TemporaryEnvironmentValue::~TemporaryEnvironmentValue() { if (has_old_value) setenv(name.c_str(), old_value.c_str(), overwrite); else unsetenv(name.c_str()); } ./tests/mir_test_framework/server_runner.cpp0000644000015600001650000000711512676616125021565 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir_test_framework/server_runner.h" #include "mir/default_server_configuration.h" #include "mir/display_server.h" #include "mir/frontend/connector.h" #include "mir/run_mir.h" #include "mir/main_loop.h" #include "mir/log.h" #include #include #include namespace mtf = mir_test_framework; namespace { char const* const env_no_file = "MIR_SERVER_NO_FILE"; } mtf::ServerRunner::ServerRunner() : old_env(getenv(env_no_file)) { if (!old_env) setenv(env_no_file, "", true); } void mtf::ServerRunner::start_server() { auto const ml = start_mir_server(); if (ml == nullptr) { BOOST_THROW_EXCEPTION(std::runtime_error{"Failed to start server thread"}); } std::lock_guard main_loop_lock{main_loop_mutex}; main_loop = ml; } std::string mtf::ServerRunner::new_connection() { char connect_string[64] = {0}; sprintf(connect_string, "fd://%d", server_config().the_connector()->client_socket_fd()); return connect_string; } std::string mtf::ServerRunner::new_prompt_connection() { char connect_string[64] = {0}; sprintf(connect_string, "fd://%d", server_config().the_prompt_connector()->client_socket_fd()); return connect_string; } void mtf::ServerRunner::stop_server() { decltype(main_loop) ml; { std::lock_guard main_loop_lock{main_loop_mutex}; ml = main_loop; } if (ml == nullptr) { BOOST_THROW_EXCEPTION(std::logic_error{"stop_server() called without calling start_server()?"}); } ml->stop(); if (server_thread.joinable()) server_thread.join(); } mtf::ServerRunner::~ServerRunner() { if (server_thread.joinable()) server_thread.join(); if (!old_env) unsetenv(env_no_file); } std::shared_ptr mtf::ServerRunner::start_mir_server() { std::mutex mutex; std::condition_variable started_cv; bool started{false}; auto const ml = server_config().the_main_loop(); mir::logging::set_logger(server_config().the_logger()); server_thread = std::thread([&] { try { mir::run_mir(server_config(), [&](mir::DisplayServer&) { // By enqueuing the notification code in the main loop, we are // ensuring that the server has really and fully started before // leaving start_mir_server(). ml->enqueue( this, [&] { std::lock_guard lock(mutex); started = true; started_cv.notify_one(); }); }); } catch (std::exception const& e) { FAIL() << e.what(); } }); std::unique_lock lock(mutex); started_cv.wait_for(lock, std::chrono::seconds{10}, [&]{ return started; }); return ml; } ./tests/mir_test_framework/headless_nested_server_runner.cpp0000644000015600001650000000214012676616125024770 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #include "mir_test_framework/headless_nested_server_runner.h" #include "mir_test_framework/executable_path.h" namespace mtf = mir_test_framework; mtf::HeadlessNestedServerRunner::HeadlessNestedServerRunner(std::string const& connect_string) { add_to_environment("MIR_SERVER_PLATFORM_GRAPHICS_LIB", mtf::server_platform("graphics-dummy.so").c_str()); add_to_environment("MIR_SERVER_HOST_SOCKET", connect_string.c_str()); } ./tests/mir_test_framework/process.cpp0000644000015600001650000001376112676616125020350 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Thomas Guest */ #include "mir_test_framework/process.h" #include #include #include #include #include #include #include #include #include #include namespace mtf = mir_test_framework; namespace { struct SignalNumberErrorInfoTag {}; typedef boost::error_info errinfo_signum; struct ProcessIdErrorInfoTag {}; typedef boost::error_info errinfo_pid; void signal_process(pid_t pid, int signum) { if (::kill(pid, signum) != 0) { BOOST_THROW_EXCEPTION( ::boost::enable_error_info(std::system_error(errno, std::system_category(), "Failed to kill process.")) << errinfo_pid(pid) << errinfo_signum(signum)); } } } mtf::Result::Result() : reason(TerminationReason::unknown) , exit_code(EXIT_FAILURE) , signal(_NSIG+1) { } bool mtf::Result::succeeded() const { return reason == TerminationReason::child_terminated_normally && exit_code == EXIT_SUCCESS; } bool mtf::Result::signalled() const { return reason == TerminationReason::child_terminated_by_signal; } mtf::Process::Process(pid_t pid) : pid(pid) , terminated(false) , detached(false) { assert(pid > 0); } mtf::Process::~Process() { if (!detached && !terminated) { try { terminate(); wait_for_termination(); } catch (std::exception const &) { // Ignore a failure to signal the process. } } } mtf::Result mtf::Process::wait_for_termination(const std::chrono::milliseconds& timeout) { Result result; int status; if (!detached) { auto tp = std::chrono::steady_clock::now() + timeout; int rc = -1; while (true) { if ((rc = ::waitpid(pid, &status, WNOHANG)) == pid) { if (WIFEXITED(status)) { terminated = true; result.reason = TerminationReason::child_terminated_normally; result.exit_code = WEXITSTATUS(status); break; } else if (WIFSIGNALED(status)) { terminated = true; result.reason = TerminationReason::child_terminated_by_signal; result.signal = WTERMSIG(status); break; } else { terminated = true; result.reason = TerminationReason::unknown; break; } } else if (rc == 0) { if (std::chrono::steady_clock::now() < tp) { std::this_thread::yield(); continue; } else { BOOST_THROW_EXCEPTION( ::boost::enable_error_info(std::runtime_error("Timeout while waiting for child to change state")) << errinfo_pid(pid)); } } else break; } } return result; } void mtf::Process::kill() { if (!detached) signal_process(pid, SIGKILL); } void mtf::Process::terminate() { if (!detached) signal_process(pid, SIGTERM); } void mtf::Process::stop() { if (!detached) signal_process(pid, SIGSTOP); } void mtf::Process::cont() { if (!detached) signal_process(pid, SIGCONT); } void mtf::Process::detach() { detached = true; } namespace { std::ostream& print_reason(std::ostream & out, mtf::TerminationReason reason) { switch (reason) { case mtf::TerminationReason::unknown: out << "unknown"; break; case mtf::TerminationReason::child_terminated_normally: out << "child_terminated_normally"; break; case mtf::TerminationReason::child_terminated_by_signal: out << "child_terminated_by_signal"; break; case mtf::TerminationReason::child_terminated_with_core_dump: out << "child_terminated_with_core_dump"; break; case mtf::TerminationReason::child_stopped_by_signal: out << "child_stopped_by_signal"; break; case mtf::TerminationReason::child_resumed_by_signal: out << "child_resumed_by_signal"; break; } return out; } std::ostream& print_signal(std::ostream& out, int signal) { out << "signal(" << signal << ")"; return out; } std::ostream& print_exit_code(std::ostream& out, int exit_code) { if (exit_code == EXIT_SUCCESS) { out << "success"; } else { out << "failure(" << exit_code << ')'; } return out; } } std::ostream& mtf::operator<<(std::ostream& out, const mtf::Result& result) { out << "process::Result("; print_reason(out, result.reason); out << ", "; if (result.signalled()) { print_signal(out, result.signal); out << ", "; } if (result.reason == TerminationReason::child_terminated_normally) { print_exit_code(out, result.exit_code); } return out << ')'; } ./tests/mir_test_framework/command_line_server_configuration.cpp0000644000015600001650000000306112676616125025624 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "mir_test_framework/command_line_server_configuration.h" #include "mir_test_framework/main.h" #include "mir/options/default_configuration.h" #include "mir/server.h" #include #include #include namespace mo=mir::options; namespace { char const* args[] = {nullptr}; int argc; char const** argv = args; } auto mir_test_framework::configuration_from_commandline() -> std::shared_ptr { return std::make_shared(::argc, ::argv); } void mir_test_framework::configure_from_commandline(mir::Server& server) { server.set_command_line(::argc, ::argv); } void mir_test_framework::set_commandline(int argc, char* argv[]) { ::argv = const_cast(argv); ::argc = argc; } int mir_test_framework::main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); set_commandline(argc, argv); return RUN_ALL_TESTS(); } ./tests/mir_test_framework/fake_input_device_impl.cpp0000644000015600001650000002302512676616157023356 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "fake_input_device_impl.h" #include "mir_test_framework/stub_input_platform.h" #include "mir/input/input_device.h" #include "mir/input/input_device_info.h" #include "mir/input/input_sink.h" #include "mir/input/pointer_settings.h" #include "mir/input/touchpad_settings.h" #include "mir/input/event_builder.h" #include "mir/dispatch/action_queue.h" #include "mir/geometry/displacement.h" #include "src/platforms/evdev/button_utils.h" #include "boost/throw_exception.hpp" #include "mir/events/event_builders.h" #include namespace mi = mir::input; namespace mie = mi::evdev; namespace md = mir::dispatch; namespace mtf = mir_test_framework; mtf::FakeInputDeviceImpl::FakeInputDeviceImpl(mi::InputDeviceInfo const& info) : queue{std::make_shared()}, device{std::make_shared(info, queue)} { mtf::StubInputPlatform::add(device); } void mtf::FakeInputDeviceImpl::emit_device_removal() { mtf::StubInputPlatform::remove(device); } void mtf::FakeInputDeviceImpl::emit_runtime_error() { queue->enqueue([]() { throw std::runtime_error("runtime error in input device"); }); } void mtf::FakeInputDeviceImpl::emit_event(synthesis::KeyParameters const& key) { queue->enqueue([this, key]() { device->synthesize_events(key); }); } void mtf::FakeInputDeviceImpl::emit_event(synthesis::ButtonParameters const& button) { queue->enqueue([this, button]() { device->synthesize_events(button); }); } void mtf::FakeInputDeviceImpl::emit_event(synthesis::MotionParameters const& motion) { queue->enqueue([this, motion]() { device->synthesize_events(motion); }); } void mtf::FakeInputDeviceImpl::emit_event(synthesis::TouchParameters const& touch) { queue->enqueue([this, touch]() { device->synthesize_events(touch); }); } mtf::FakeInputDeviceImpl::InputDevice::InputDevice(mi::InputDeviceInfo const& info, std::shared_ptr const& dispatchable) : info(info), queue{dispatchable}, buttons{0} { // the default setup results in a direct mapping of input velocity to output velocity. settings.acceleration = mir_pointer_acceleration_none; settings.cursor_acceleration_bias = 0.0; } void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::KeyParameters const& key_params) { xkb_keysym_t key_code = 0; auto event_time = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()); auto input_action = (key_params.action == synthesis::EventAction::Down) ? mir_keyboard_action_down : mir_keyboard_action_up; auto key_event = builder->key_event(event_time, input_action, key_code, key_params.scancode); if (!sink) BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started.")); sink->handle_input(*key_event); } void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::ButtonParameters const& button) { auto event_time = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()); auto action = update_buttons(button.action, mie::to_pointer_button(button.button, settings.handedness)); auto button_event = builder->pointer_event(event_time, action, buttons, scroll.x.as_float(), scroll.y.as_float(), 0.0f, 0.0f); if (!sink) BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started.")); sink->handle_input(*button_event); } MirPointerAction mtf::FakeInputDeviceImpl::InputDevice::update_buttons(synthesis::EventAction action, MirPointerButton button) { if (action == synthesis::EventAction::Down) { buttons |= button; return mir_pointer_action_button_down; } else { buttons &= ~button; return mir_pointer_action_button_up; } } void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::MotionParameters const& pointer) { if (!sink) BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started.")); auto event_time = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()); // constant scaling is used here to simplify checking for the // expected results. Default settings of the device lead to no // scaling at all. auto const acceleration = settings.cursor_acceleration_bias + 1.0; auto const rel_x = pointer.rel_x * acceleration; auto const rel_y = pointer.rel_y * acceleration; auto pointer_event = builder->pointer_event(event_time, mir_pointer_action_motion, buttons, scroll.x.as_float(), scroll.y.as_float(), rel_x, rel_y); sink->handle_input(*pointer_event); } void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::TouchParameters const& touch) { if (!sink) BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started.")); auto event_time = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()); auto touch_event = builder->touch_event(event_time); auto touch_action = mir_touch_action_up; if (touch.action == synthesis::TouchParameters::Action::Tap) touch_action = mir_touch_action_down; else if (touch.action == synthesis::TouchParameters::Action::Move) touch_action = mir_touch_action_change; MirTouchId touch_id = 1; float pressure = 1.0f; float abs_x = touch.abs_x; float abs_y = touch.abs_y; map_touch_coordinates(abs_x, abs_y); // those values would need scaling too as soon as they can be controlled by the caller float touch_major = 5.0f; float touch_minor = 8.0f; float size_value = 8.0f; builder->add_touch(*touch_event, touch_id, touch_action, mir_touch_tooltype_finger, abs_x, abs_y, pressure, touch_major, touch_minor, size_value); sink->handle_input(*touch_event); } mir::optional_value mtf::FakeInputDeviceImpl::InputDevice::get_pointer_settings() const { mir::optional_value ret; if (!contains(info.capabilities, mi::DeviceCapability::pointer)) return ret; ret = settings; return ret; } void mtf::FakeInputDeviceImpl::InputDevice::apply_settings(mi::PointerSettings const& settings) { if (!contains(info.capabilities, mi::DeviceCapability::pointer)) return; this->settings = settings; } mir::optional_value mtf::FakeInputDeviceImpl::InputDevice::get_touchpad_settings() const { mir::optional_value ret; if (contains(info.capabilities, mi::DeviceCapability::pointer)) ret = mi::TouchpadSettings(); return ret; } void mtf::FakeInputDeviceImpl::InputDevice::apply_settings(mi::TouchpadSettings const&) { // Not applicable for configuration since FakeInputDevice just // forwards already interpreted events. } void mtf::FakeInputDeviceImpl::InputDevice::map_touch_coordinates(float& x, float& y) { // TODO take orientation of input sink into account? auto area = sink->bounding_rectangle(); auto touch_range = FakeInputDevice::maximum_touch_axis_value - FakeInputDevice::minimum_touch_axis_value + 1; auto x_scale = area.size.width.as_float() / float(touch_range); auto y_scale = area.size.height.as_float() / float(touch_range); x = (x - float(FakeInputDevice::minimum_touch_axis_value))*x_scale + area.top_left.x.as_float(); y = (y - float(FakeInputDevice::minimum_touch_axis_value))*y_scale + area.top_left.y.as_float(); } void mtf::FakeInputDeviceImpl::InputDevice::start(mi::InputSink* destination, mi::EventBuilder* event_builder) { sink = destination; builder = event_builder; mtf::StubInputPlatform::register_dispatchable(queue); } void mtf::FakeInputDeviceImpl::InputDevice::stop() { sink = nullptr; builder = nullptr; mtf::StubInputPlatform::unregister_dispatchable(queue); } ./tests/mir_test_framework/platform_graphics_throw.cpp0000644000015600001650000000510212676616125023607 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/graphics/platform.h" #include "mir/test/doubles/null_platform.h" #include "mir/assert_module_entry_point.h" #include "mir/libname.h" #include namespace mg = mir::graphics; namespace mo = mir::options; namespace mir { namespace options { class ProgramOption; } } namespace { class ExceptionThrowingPlatform : public mir::test::doubles::NullPlatform { public: ExceptionThrowingPlatform() { BOOST_THROW_EXCEPTION(std::runtime_error("Exception during construction")); } }; } mg::PlatformPriority probe_graphics_platform(mo::ProgramOption const& /*options*/) { mir::assert_entry_point_signature(&probe_graphics_platform); return mg::PlatformPriority::unsupported; } mir::ModuleProperties const description { "throw-on-creation", MIR_VERSION_MAJOR, MIR_VERSION_MINOR, MIR_VERSION_MICRO, mir::libname() }; mir::ModuleProperties const* describe_graphics_module() { mir::assert_entry_point_signature(&describe_graphics_module); return &description; } void add_graphics_platform_options(boost::program_options::options_description&) { mir::assert_entry_point_signature(&add_graphics_platform_options); } mir::UniqueModulePtr create_host_platform( std::shared_ptr const&, std::shared_ptr const&, std::shared_ptr const&) { mir::assert_entry_point_signature(&create_host_platform); return mir::make_module_ptr(); } mir::UniqueModulePtr create_guest_platform( std::shared_ptr const&, std::shared_ptr const&) { mir::assert_entry_point_signature(&create_guest_platform); return mir::make_module_ptr(); } ./tests/mir_test_framework/stub_input_platform.cpp0000644000015600001650000001041412676616157022767 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir_test_framework/stub_input_platform.h" #include "mir/input/input_device_registry.h" #include "mir/dispatch/action_queue.h" #include "mir/dispatch/multiplexing_dispatchable.h" #include "mir/module_deleter.h" #include namespace mtf = mir_test_framework; namespace mi = mir::input; mtf::StubInputPlatform::StubInputPlatform( std::shared_ptr const& input_device_registry) : platform_dispatchable{std::make_shared()}, platform_queue{std::make_shared()}, registry(input_device_registry) { stub_input_platform = this; platform_dispatchable->add_watch(platform_queue); } mtf::StubInputPlatform::~StubInputPlatform() { std::lock_guard lk{device_store_guard}; device_store.clear(); stub_input_platform = nullptr; } void mtf::StubInputPlatform::start() { std::lock_guard lk{device_store_guard}; for (auto const& dev : device_store) { auto device = dev.lock(); if (device) registry->add_device(device); } } std::shared_ptr mtf::StubInputPlatform::dispatchable() { return platform_dispatchable; } void mtf::StubInputPlatform::stop() { for (auto const& dev : device_store) { auto device = dev.lock(); if (device) registry->remove_device(device); } } void mtf::StubInputPlatform::add(std::shared_ptr const& dev) { auto input_platform = stub_input_platform.load(); if (!input_platform) { std::lock_guard lk{device_store_guard}; device_store.push_back(dev); return; } input_platform->platform_queue->enqueue( [registry=input_platform->registry,dev] { registry->add_device(dev); }); } void mtf::StubInputPlatform::remove(std::shared_ptr const& dev) { auto input_platform = stub_input_platform.load(); if (!input_platform) BOOST_THROW_EXCEPTION(std::runtime_error("No stub input platform available")); std::lock_guard lk{device_store_guard}; device_store.erase( std::remove_if(begin(device_store), end(device_store), [dev](auto weak_dev) { return (weak_dev.lock() == dev); }), end(device_store)); input_platform->platform_queue->enqueue( [ registry = input_platform->registry, dev ] { registry->remove_device(dev); }); } void mtf::StubInputPlatform::register_dispatchable(std::shared_ptr const& queue) { auto input_platform = stub_input_platform.load(); if (!input_platform) BOOST_THROW_EXCEPTION(std::runtime_error("No stub input platform available")); input_platform->platform_dispatchable->add_watch(queue); } void mtf::StubInputPlatform::unregister_dispatchable(std::shared_ptr const& queue) { auto input_platform = stub_input_platform.load(); if (!input_platform) BOOST_THROW_EXCEPTION(std::runtime_error("No stub input platform available")); input_platform->platform_dispatchable->remove_watch(queue); } std::atomic mtf::StubInputPlatform::stub_input_platform{nullptr}; std::vector> mtf::StubInputPlatform::device_store; std::mutex mtf::StubInputPlatform::device_store_guard; ./tests/mir_test_framework/interprocess_client_server_test.cpp0000644000015600001650000001200612676616125025364 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir_test_framework/interprocess_client_server_test.h" #include "mir_test_framework/process.h" #include #include namespace mt = mir::test; namespace mtf = mir_test_framework; using namespace ::testing; mtf::InterprocessClientServerTest::~InterprocessClientServerTest() { if (getpid() != test_process_id) { shutdown_sync.reset(); auto const status = ::testing::Test::HasFailure() ? EXIT_FAILURE : EXIT_SUCCESS; exit(status); } } void mtf::InterprocessClientServerTest::init_server(std::function const& init_code) { if (auto const& existing = server_setup) { server_setup = [=] { existing(); init_code(); }; } else { server_setup = init_code; } } void mtf::InterprocessClientServerTest::run_in_server(std::function const& exec_code) { if (test_process_id != getpid()) return; mt::CrossProcessSync started_sync; pid_t pid = fork(); if (pid < 0) { throw std::runtime_error("Failed to fork process"); } if (pid == 0) { server_process_id = getpid(); process_tag = "server"; add_to_environment("MIR_SERVER_FILE", mir_test_socket); server_setup(); start_server(); started_sync.signal_ready(); exec_code(); } else { server_process = std::make_shared(pid); started_sync.wait_for_signal_ready_for(); } } void mtf::InterprocessClientServerTest::run_in_client(std::function const& client_code) { auto const client_process = new_client_process(client_code); if (test_process_id != getpid()) return; Result result = client_process->wait_for_termination(); EXPECT_THAT(result.exit_code, Eq(EXIT_SUCCESS)); } auto mtf::InterprocessClientServerTest::new_client_process(std::function const& client_code) -> std::shared_ptr { if (test_process_id != getpid()) return std::shared_ptr{}; pid_t pid = fork(); if (pid < 0) { throw std::runtime_error("Failed to fork process"); } if (pid == 0) { process_tag = "client"; add_to_environment("MIR_SOCKET", mir_test_socket); client_code(); return std::shared_ptr{}; } else { client_process_id = pid; return std::make_shared(pid); } } bool mtf::InterprocessClientServerTest::is_test_process() const { return test_process_id == getpid(); } void mtf::InterprocessClientServerTest::TearDown() { if (server_process_id == getpid()) { shutdown_sync->wait_for_signal_ready_for(); } stop_server(); } void mtf::InterprocessClientServerTest::stop_server() { if (server_process_id == getpid()) { HeadlessTest::stop_server(); } if (test_process_id != getpid()) return; shutdown_sync->signal_ready(); if (server_process) { Result result = server_process->wait_for_termination(); server_process.reset(); if (!server_signal_expected) { EXPECT_THAT(result.exit_code, Eq(EXIT_SUCCESS)); } else { EXPECT_THAT(result.signalled(), Eq(true)); // FIXME Under valgrind we always see SIGKILL (so treat that as a pass) EXPECT_THAT(result.signal, AnyOf(Eq(expected_server_failure_signal), Eq(SIGKILL))); } } } void mtf::InterprocessClientServerTest::expect_server_signalled(int signal) { server_signal_expected = true; expected_server_failure_signal = signal; } bool mtf::InterprocessClientServerTest::sigkill_server_process() { if (test_process_id != getpid()) throw std::logic_error("Only the test process may kill the server"); if (!server_process) throw std::logic_error("No server process to kill"); server_process->kill(); auto result = wait_for_shutdown_server_process(); return result.reason == TerminationReason::child_terminated_by_signal && result.signal == SIGKILL; } mtf::Result mtf::InterprocessClientServerTest::wait_for_shutdown_server_process() { if (!server_process) throw std::logic_error("No server process to monitor"); Result result = server_process->wait_for_termination(); server_process.reset(); return result; } ./tests/mir_test_framework/placement_applying_shell.cpp0000644000015600001650000000510512676616157023732 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir_test_framework/placement_applying_shell.h" namespace mtf = mir_test_framework; mtf::PlacementApplyingShell::PlacementApplyingShell( std::shared_ptr wrapped_coordinator, ClientInputRegions const& client_input_regions, ClientPositions const& client_positions) : mir::shell::ShellWrapper(wrapped_coordinator), client_input_regions(client_input_regions), client_positions(client_positions) { } mtf::PlacementApplyingShell::~PlacementApplyingShell() = default; mir::frontend::SurfaceId mtf::PlacementApplyingShell::create_surface( std::shared_ptr const& session, mir::scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) { auto creation_parameters = params; auto const id = wrapped->create_surface(session, creation_parameters, sink); auto const surface = session->surface(id); auto position= client_positions.find(params.name); if (position != client_positions.end()) { surface->move_to(position->second.top_left); surface->resize(position->second.size); } auto regions = client_input_regions.find(params.name); if (regions != client_input_regions.end()) surface->set_input_region(regions->second); return id; } void mtf::PlacementApplyingShell::modify_surface( std::shared_ptr const& session, std::shared_ptr const& surface, mir::shell::SurfaceSpecification const& modifications) { mir::shell::ShellWrapper::modify_surface(session, surface, modifications); std::unique_lock lk(mutex); modified = true; cv.notify_all(); } bool mtf::PlacementApplyingShell::wait_for_modify_surface(std::chrono::seconds timeout) { std::unique_lock lk(mutex); return cv.wait_for(lk, timeout, [this] { return modified; }); } ./tests/mir_test_framework/visible_surface.cpp0000644000015600001650000000510412676616125022027 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Kevin DuBois */ #include "mir_test_framework/visible_surface.h" namespace mtf = mir_test_framework; mtf::VisibleSurface::VisibleSurface(MirSurfaceSpec* spec) : visible{false} { mir_surface_spec_set_event_handler(spec, VisibleSurface::event_callback, this); surface = mir_surface_create_sync(spec); // Swap buffers to ensure surface is visible for event based tests if (mir_surface_is_valid(surface)) { mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface)); std::unique_lock lk(mutex); if (!cv.wait_for(lk, std::chrono::seconds(5), [this] { return visible; })) throw std::runtime_error("timeout waiting for visibility of surface"); } } mtf::VisibleSurface::~VisibleSurface() { if (surface) mir_surface_release_sync(surface); } void mtf::VisibleSurface::event_callback(MirSurface* surf, MirEvent const* ev, void* context) { if (mir_event_get_type(ev) == mir_event_type_surface) { if (mir_surface_event_get_attribute(mir_event_get_surface_event(ev)) == mir_surface_attrib_visibility) { auto ctx = reinterpret_cast(context); ctx->set_visibility(surf, mir_surface_event_get_attribute_value(mir_event_get_surface_event(ev))); } } } void mtf::VisibleSurface::set_visibility(MirSurface* surf, bool vis) { std::lock_guard lk(mutex); if (surf != surface) return; visible = vis; cv.notify_all(); } mtf::VisibleSurface::operator MirSurface*() const { return surface; } mtf::VisibleSurface::VisibleSurface(VisibleSurface&& that) : surface{that.surface} { that.surface = nullptr; } mtf::VisibleSurface& mtf::VisibleSurface::operator=(VisibleSurface&& that) { std::swap(that.surface, surface); return *this; } std::ostream& mtf::operator<<(std::ostream& os, VisibleSurface const& s) { return os << static_cast(s); } ./tests/mir_test_framework/symbols-server.map.in0000644000015600001650000000031112676616125022251 0ustar jenkinsjenkins@MIR_SERVER_GRAPHICS_PLATFORM_VERSION@ { global: add_graphics_platform_options; create_host_platform; create_guest_platform; probe_graphics_platform; describe_graphics_module; }; ./tests/mir_test_framework/CMakeLists.txt0000644000015600001650000001314312676616157020725 0ustar jenkinsjenkinsinclude_directories( ${CMAKE_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/src/include/platform ${PROJECT_SOURCE_DIR}/src/include/common ${PROJECT_SOURCE_DIR}/src/include/server ${PROJECT_SOURCE_DIR}/src/include/client ${Boost_INCLUDE_DIRS} ${GLESv2_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${UMOCKDEV_INCLUDE_DIRS} ${ANDROID_HEADERS_INCLUDE_DIRS} ) add_definitions( -DMIR_CLIENT_PLATFORM_PATH="${MIR_CLIENT_PLATFORM_PATH}" -DMIR_SERVER_PLATFORM_PATH="${MIR_SERVER_PLATFORM_PATH}" -DMIR_CLIENT_PLATFORM_ABI_STRING="${MIR_CLIENT_PLATFORM_ABI}" -DMIR_SERVER_GRAPHICS_PLATFORM_ABI_STRING="${MIR_SERVER_GRAPHICS_PLATFORM_ABI}" -DMIR_SERVER_INPUT_PLATFORM_ABI_STRING="${MIR_SERVER_INPUT_PLATFORM_ABI}" -DMIR_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}" -DMIR_BUILD_PREFIX="${CMAKE_BINARY_DIR}" ) add_library(mir-public-test-framework OBJECT any_surface.cpp async_server_runner.cpp command_line_server_configuration.cpp connected_client_headless_server.cpp connected_client_with_a_surface.cpp declarative_placement_window_manage_policy.cpp executable_path.cpp headless_in_process_server.cpp headless_nested_server_runner.cpp headless_test.cpp interprocess_client_server_test.cpp main.cpp libinput_environment.cpp placement_applying_shell.cpp process.cpp server_runner.cpp socket_detect_server.cpp stub_input_platform.cpp stub_client_platform_factory.cpp stub_server_platform_factory.cpp stub_session.cpp stub_surface.cpp stubbed_server_configuration.cpp testing_client_options.cpp testing_server_options.cpp temporary_environment_value.cpp using_stub_client_platform.cpp visible_surface.cpp ) # Umockdev uses glib, which uses the deprecated "register" storage qualifier set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dregister=") add_library(mir-protected-test-framework OBJECT fake_input_server_configuration.cpp input_testing_server_options.cpp ) add_library(mir-libinput-test-framework OBJECT libinput_environment.cpp) add_library(mir-umock-test-framework OBJECT udev_environment.cpp) add_library(mir-test-framework-static STATIC $ $ $ ) add_dependencies(mir-test-framework-static GMock) uses_android_input(mir-protected-test-framework) target_link_libraries(mir-test-framework-static ${Boost_LIBRARIES} ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY} ${UMOCKDEV_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} # Link in pthread. ) set(symbol_map ${CMAKE_CURRENT_SOURCE_DIR}/symbols-client.map) add_library( mirclientplatformstub MODULE stub_client_platform_module.cpp ) target_link_libraries( mirclientplatformstub mir-test-framework-static mircommon ${UMOCKDEV_LDFLAGS} ${UMOCKDEV_LIBRARIES} ) set_target_properties( mirclientplatformstub PROPERTIES; LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/client-modules OUTPUT_NAME dummy PREFIX "" LINK_FLAGS "-Wl,--version-script,${symbol_map}" ) add_library( mirplatforminputstub MODULE stub_input.cpp fake_input_device_impl.cpp stub_input_platform.cpp $ ) target_link_libraries(mirplatforminputstub mircommon) set_target_properties( mirplatforminputstub PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/server-modules OUTPUT_NAME input-stub PREFIX "" LINK_FLAGS "-Wl,--version-script,${MIR_INPUT_PLATFORM_VERSION_SCRIPT}" ) install(TARGETS mirplatforminputstub LIBRARY DESTINATION ${MIR_SERVER_PLATFORM_PATH}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/symbols-server.map.in ${CMAKE_CURRENT_BINARY_DIR}/symbols-server.map) set(server_symbol_map ${CMAKE_CURRENT_BINARY_DIR}/symbols-server.map) add_library( mirplatformgraphicsstub MODULE platform_graphics_dummy.cpp stubbed_graphics_platform.cpp ) target_link_libraries( mirplatformgraphicsstub PRIVATE mir-test-static mir-test-framework-static mir-test-doubles-static PUBLIC ${UMOCKDEV_LDFLAGS} ${UMOCKDEV_LIBRARIES} ) set_target_properties( mirplatformgraphicsstub PROPERTIES; LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/server-modules OUTPUT_NAME graphics-dummy PREFIX "" LINK_FLAGS "-Wl,--version-script,${server_symbol_map}" ) add_library( mirplatformgraphicsthrow MODULE platform_graphics_throw.cpp ) target_link_libraries( mirplatformgraphicsthrow mirplatform ) set_target_properties( mirplatformgraphicsthrow PROPERTIES; LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/server-modules OUTPUT_NAME graphics-throw PREFIX "" LINK_FLAGS "-Wl,--version-script,${server_symbol_map}" ) add_custom_command(TARGET mir-test-framework-static POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/udev-recordings ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-data/udev-recordings COMMENT "Copying umockdev recordings to build dir..." ) add_custom_command(TARGET mir-test-framework-static POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testing-cursor-theme ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-data/testing-cursor-theme COMMENT "Copying testing cursor themes to build dir..." ) string (REPLACE " -Wl,--no-undefined" " " CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}) install(TARGETS mirplatformgraphicsstub LIBRARY DESTINATION ${MIR_SERVER_PLATFORM_PATH}) install(TARGETS mirplatformgraphicsthrow LIBRARY DESTINATION ${MIR_SERVER_PLATFORM_PATH}) install(TARGETS mirclientplatformstub LIBRARY DESTINATION ${MIR_CLIENT_PLATFORM_PATH}) add_subdirectory(udev-recordings/) add_subdirectory(testing-cursor-theme/) ./tests/mir_test_framework/using_stub_client_platform.cpp0000644000015600001650000000330712676616125024311 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_test_framework/using_stub_client_platform.h" #include "mir_test_framework/using_client_platform.h" #include "mir_toolkit/mir_client_library.h" namespace mtf = mir_test_framework; namespace mcl = mir::client; MirWaitHandle* mtf::StubMirConnectionAPI::connect( mcl::ConfigurationFactory /*configuration*/, char const* socket_file, char const* name, mir_connected_callback callback, void* context) { return prev_api->connect(configuration_factory(), socket_file, name, callback, context); } void mtf::StubMirConnectionAPI::release(MirConnection* connection) { return prev_api->release(connection); } mcl::ConfigurationFactory mtf::StubMirConnectionAPI::configuration_factory() { return factory; } class mtf::UsingStubClientPlatform::Impl : public UsingClientPlatform {}; mtf::UsingStubClientPlatform::UsingStubClientPlatform() : impl{std::make_unique()} { } mtf::UsingStubClientPlatform::~UsingStubClientPlatform() { }./tests/mir_test_framework/testing_client_options.cpp0000644000015600001650000000235712676616125023457 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir_test_framework/stub_client_connection_configuration.h" #include "src/client/default_connection_configuration.h" #include "mir_test_framework/stub_client_platform_factory.h" namespace mcl = mir::client; namespace mtf = mir_test_framework; mtf::StubConnectionConfiguration::StubConnectionConfiguration(std::string const& socket_file) : DefaultConnectionConfiguration(socket_file) { } std::shared_ptr mtf::StubConnectionConfiguration::the_client_platform_factory() { return std::make_shared(); } ./tests/mir_test_framework/connected_client_headless_server.cpp0000644000015600001650000000255512676616125025427 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir_test_framework/connected_client_headless_server.h" #include "boost/throw_exception.hpp" namespace mtf = mir_test_framework; void mtf::ConnectedClientHeadlessServer::SetUp() { HeadlessInProcessServer::SetUp(); connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); if (!mir_connection_is_valid(connection)) { BOOST_THROW_EXCEPTION( std::runtime_error{std::string{"Failed to connect to test server: "} + mir_connection_get_error_message(connection)}); } } void mtf::ConnectedClientHeadlessServer::TearDown() { mir_connection_release(connection); HeadlessInProcessServer::TearDown(); } ./tests/mir_test_framework/connected_client_with_a_surface.cpp0000644000015600001650000000271512676616125025232 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir_test_framework/connected_client_with_a_surface.h" namespace mtf = mir_test_framework; void mtf::ConnectedClientWithASurface::SetUp() { ConnectedClientHeadlessServer::SetUp(); auto const spec = mir_connection_create_spec_for_normal_surface( connection, surface_size.width.as_int(), surface_size.height.as_int(), mir_pixel_format_abgr_8888); mir_surface_spec_set_name(spec, "ConnectedClientWithASurfaceFixtureSurface"); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_hardware); surface = mir_surface_create_sync(spec); mir_surface_spec_release(spec); ASSERT_TRUE(mir_surface_is_valid(surface)); } void mtf::ConnectedClientWithASurface::TearDown() { mir_surface_release_sync(surface); ConnectedClientHeadlessServer::TearDown(); } ./tests/mir_test_framework/executable_path.cpp0000644000015600001650000001055512676616157022032 0ustar jenkinsjenkins/* * Copyright © 2013,2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Christopher James Halse Rogers * Kevin DuBois */ #include "mir_test_framework/executable_path.h" #include #include #include #include #include std::string mir_test_framework::executable_path() { char buf[1024]; auto tmp = readlink("/proc/self/exe", buf, sizeof buf); if (tmp < 0) BOOST_THROW_EXCEPTION(boost::enable_error_info( std::runtime_error("Failed to find our executable path")) << boost::errinfo_errno(errno)); if (tmp > static_cast(sizeof(buf) - 1)) BOOST_THROW_EXCEPTION(std::runtime_error("Path to executable is too long!")); buf[tmp] = '\0'; return dirname(buf); } std::string mir_test_framework::library_path() { return executable_path() + "/../lib"; } std::string mir_test_framework::server_platform_path() { for (auto const& option : {library_path() + "/server-modules/", library_path() + "/server-platform/", std::string(MIR_SERVER_PLATFORM_PATH) + '/'}) if (boost::filesystem::exists(option)) return option; BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find server platform in standard search locations")); } std::string mir_test_framework::test_data_path() { std::string run_path = executable_path() + "/test-data"; std::string install_path = MIR_INSTALL_PREFIX"/share/mir-test-data"; if (boost::filesystem::exists(run_path)) return run_path; else if (boost::filesystem::exists(install_path)) return install_path; BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find test data in standard search locations")); } std::string mir_test_framework::server_platform(std::string const& name) { std::string libname{name}; if (libname.find(".so") == std::string::npos) libname += ".so." MIR_SERVER_GRAPHICS_PLATFORM_ABI_STRING; for (auto const& option : {library_path() + "/server-modules/", library_path() + "/server-platform/", std::string(MIR_SERVER_PLATFORM_PATH) + '/'}) { auto path_to_test = option + libname; if (boost::filesystem::exists(path_to_test)) return path_to_test; } BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find server platform in standard search locations")); } std::string mir_test_framework::server_input_platform(std::string const& name) { std::string libname{name}; if (libname.find(".so") == std::string::npos) libname += ".so." MIR_SERVER_INPUT_PLATFORM_ABI_STRING; for (auto const& option : {library_path() + "/server-modules/", library_path() + "/server-platform/", std::string(MIR_SERVER_PLATFORM_PATH) + '/'}) { auto path_to_test = option + libname; if (boost::filesystem::exists(path_to_test)) return path_to_test; } BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find server input platform in standard search locations")); } std::string mir_test_framework::client_platform(std::string const& name) { std::string libname{name}; if (libname.find(".so") == std::string::npos) libname += ".so." MIR_CLIENT_PLATFORM_ABI_STRING; for (auto const& option : {library_path() + "/client-modules/", library_path() + "/client-platform/", std::string(MIR_CLIENT_PLATFORM_PATH) + '/'}) { auto path_to_test = option + libname; if (boost::filesystem::exists(path_to_test)) return path_to_test; } BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find client platform in standard search locations")); } ./tests/mir_test_framework/declarative_placement_window_manage_policy.cpp0000644000015600001650000000350512676616157027463 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir_test_framework/declarative_placement_window_manage_policy.h" #include "mir/scene/surface_creation_parameters.h" namespace ms = mir::scene; namespace mtf = mir_test_framework; mtf::DeclarativePlacementWindowManagerPolicy::DeclarativePlacementWindowManagerPolicy( mir::shell::WindowManagerTools* const tools, SurfaceGeometries const& positions_by_name, std::shared_ptr const& display_layout) : mir::shell::CanonicalWindowManagerPolicy{tools, display_layout}, surface_geometries_by_name{positions_by_name} { } ms::SurfaceCreationParameters mtf::DeclarativePlacementWindowManagerPolicy::handle_place_new_surface( std::shared_ptr const& /*session*/, ms::SurfaceCreationParameters const& request_parameters) { auto placed = request_parameters; auto const& name = request_parameters.name; if (surface_geometries_by_name.find(name) != surface_geometries_by_name.end()) { auto const& geometry = surface_geometries_by_name.at(name); placed.top_left = geometry.top_left; placed.size = geometry.size; } return placed; } ./tests/mir_test_framework/fake_input_server_configuration.cpp0000644000015600001650000000356512676616157025342 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr * Alexandros Frantzis */ #include "mir_test_framework/fake_input_server_configuration.h" namespace mtf = mir_test_framework; namespace mi = mir::input; namespace ms = mir::scene; namespace msh = mir::shell; mtf::FakeInputServerConfiguration::FakeInputServerConfiguration() = default; mtf::FakeInputServerConfiguration::FakeInputServerConfiguration(std::vector const& display_rects) : TestingServerConfiguration(display_rects) { } mtf::FakeInputServerConfiguration::~FakeInputServerConfiguration() = default; std::shared_ptr mtf::FakeInputServerConfiguration::the_input_manager() { return DefaultServerConfiguration::the_input_manager(); } std::shared_ptr mtf::FakeInputServerConfiguration::the_input_targeter() { return DefaultServerConfiguration::the_input_targeter(); } std::shared_ptr mtf::FakeInputServerConfiguration::the_input_dispatcher() { return DefaultServerConfiguration::the_input_dispatcher(); } std::shared_ptr mtf::FakeInputServerConfiguration::the_input_sender() { return DefaultServerConfiguration::the_input_sender(); } ./tests/mir_test_framework/stub_input.cpp0000644000015600001650000000454412676616125021065 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir_test_framework/stub_input_platform.h" #include "fake_input_device_impl.h" #include "mir/module_properties.h" #include "mir/assert_module_entry_point.h" #include "mir/libname.h" namespace mtf = mir_test_framework; namespace mo = mir::options; namespace mi = mir::input; mir::UniqueModulePtr create_input_platform( mo::Option const& /*options*/, std::shared_ptr const& /*emergency_cleanup_registry*/, std::shared_ptr const& input_device_registry, std::shared_ptr const& /*report*/) { mir::assert_entry_point_signature(&create_input_platform); return mir::make_module_ptr(input_device_registry); } void add_input_platform_options( boost::program_options::options_description& /*config*/) { mir::assert_entry_point_signature(&add_input_platform_options); // no options to add yet } mi::PlatformPriority probe_input_platform( mo::Option const& /*options*/) { mir::assert_entry_point_signature(&probe_input_platform); return mi::PlatformPriority::dummy; } namespace { mir::ModuleProperties const description = { "mir:stub-input", MIR_VERSION_MAJOR, MIR_VERSION_MINOR, MIR_VERSION_MICRO, mir::libname() }; } mir::ModuleProperties const* describe_input_module() { mir::assert_entry_point_signature(&describe_input_module); return &description; } extern "C" mir::UniqueModulePtr add_fake_input_device(mi::InputDeviceInfo const& info) { return mir::make_module_ptr(info); } ./tests/mir_test_framework/platform_graphics_dummy.cpp0000644000015600001650000000263712676616125023611 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers > */ #include "mir/graphics/platform.h" #include "mir/assert_module_entry_point.h" namespace mir { namespace options { class ProgramOption; } } mir::graphics::PlatformPriority probe_graphics_platform(mir::options::ProgramOption const& /*options*/) { mir::assert_entry_point_signature(&probe_graphics_platform); return mir::graphics::PlatformPriority::dummy; } mir::ModuleProperties const description { "dummy", MIR_VERSION_MAJOR, MIR_VERSION_MINOR, MIR_VERSION_MICRO, "dummy" }; mir::ModuleProperties const* describe_graphics_module() { mir::assert_entry_point_signature(&describe_graphics_module); return &description; } ./tests/mirtest.pc.in0000644000015600001650000000034712676616125014677 0ustar jenkinsjenkinsprefix=@PREFIX@ libdir=@LIBDIR@ includedir=@INCLUDEDIR@ Name: mirtest Description: Mir test assist library Version: @MIR_VERSION@ Requires: mirserver mirplatform mirclient Libs: -L@LIBDIR@ -lmir-test-assist Cflags: -I@INCLUDEDIR@ ./tests/mir-stress/0000755000015600001650000000000012676616125014363 5ustar jenkinsjenkins./tests/mir-stress/src/0000755000015600001650000000000012676616126015153 5ustar jenkinsjenkins./tests/mir-stress/src/threading.cpp0000644000015600001650000000333012676616125017622 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomi Richards */ #include "threading.h" #include "client.h" #include "results.h" #include #include #include std::string get_application_unique_name() { std::stringstream strstr; strstr << std::this_thread::get_id(); return strstr.str(); } ThreadResults run_mir_test(std::chrono::seconds for_seconds) { std::string thread_name(get_application_unique_name()); ThreadResults results(thread_name); auto start_time = std::chrono::steady_clock::now(); while (start_time + for_seconds > std::chrono::steady_clock::now()) { ClientStateMachine::Ptr p = ClientStateMachine::Create(); if (!p->connect(thread_name)) { results.addTestResult(false); } else { if (!p->create_surface()) { results.addTestResult(false); } else { p->release_surface(); } p->disconnect(); results.addTestResult(true); } } return results; } ./tests/mir-stress/src/threading.h0000644000015600001650000000166012676616125017273 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomi Richards */ #ifndef MIR_STRESS_TEST_THREADING_H_ #define MIR_STRESS_TEST_THREADING_H_ #include class ThreadResults; // Run the mir test suite for some time, and return the results. ThreadResults run_mir_test(std::chrono::seconds for_seconds); #endif ./tests/mir-stress/src/client.h0000644000015600001650000000314712676616125016606 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomi Richards */ #ifndef MIR_STRESS_TEST_CLIENT_H_ #define MIR_STRESS_TEST_CLIENT_H_ #include /// A simple state machine that knows how to connect to a mir server, /// issue some devices, and exit cleanly. class ClientStateMachine { public: typedef std::shared_ptr Ptr; virtual ~ClientStateMachine() {}; static Ptr Create(); virtual bool connect(std::string unique_name, const char* socket_file=0) =0; virtual bool create_surface() =0; virtual void release_surface() =0; virtual void disconnect() =0; }; struct MirConnection; struct MirSurface; class UnacceleratedClient: public ClientStateMachine { public: UnacceleratedClient(); bool connect(std::string unique_name, const char* socket_file=0) override; bool create_surface() override; void release_surface() override; void disconnect() override; private: MirConnection* connection_; MirSurface* surface_; }; #endif ./tests/mir-stress/src/mir-stress.cpp0000644000015600001650000000466212676616125017776 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomi Richards */ #include "results.h" #include "threading.h" #include #include #include #include #include #include #include const std::string option_str("hn:t:p"); const std::string usage("Usage:\n" " -h: This help text.\n" " -n seconds Number of seconds to run. Default is 600 (10 minutes).\n" " -t threads Number of threads to create. Default is the number of cores\n" " on this machine.\n" ); int main(int argc, char **argv) { std::chrono::seconds duration_to_run(60 * 10); unsigned int num_threads = std::thread::hardware_concurrency(); int arg; opterr = 0; while ((arg = getopt(argc, argv, option_str.c_str())) != -1) { switch (arg) { case 'n': duration_to_run = std::chrono::seconds(std::atoi(optarg)); break; case 't': // TODO: limit to sensible values. num_threads = std::atoi(optarg); break; case '?': case 'h': default: std::cout << usage << std::endl; return -1; } } std::vector> futures; for (unsigned int i = 0; i < num_threads; i++) { std::cout << "Creating thread..." << std::endl; futures.push_back( std::async( std::launch::async, run_mir_test, duration_to_run ) ); } std::vector results; for (auto &t: futures) { results.push_back(t.get()); } bool success = true; for (auto &r: results) { std::cout << r.summary(); success &= r.success(); } return success ? 0 : 1; } ./tests/mir-stress/src/results.cpp0000644000015600001650000000252712676616125017365 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomi Richards */ #include "results.h" #include ThreadResults::ThreadResults(std::string thread_id) : thread_id_(thread_id) , tests_passed_(0) , tests_failed_(0) {} void ThreadResults::addTestResult(bool passed) { if (passed) ++tests_passed_; else ++tests_failed_; } std::string ThreadResults::summary() const { std::stringstream strstr; strstr << "Thread '" << thread_id_ << "' ran " << tests_passed_ + tests_failed_ << " tests, of which " << tests_passed_ << " passed and " << tests_failed_ << " failed." << std::endl; return strstr.str(); } bool ThreadResults::success() const { return tests_failed_ == 0; } ./tests/mir-stress/src/results.h0000644000015600001650000000202612676616125017024 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomi Richards */ #ifndef MIR_STRESS_TEST_RESULTS_H_ #define MIR_STRESS_TEST_RESULTS_H_ #include class ThreadResults { public: ThreadResults(std::string thread_id); void addTestResult(bool passed); std::string summary() const; bool success() const; private: std::string thread_id_; int tests_passed_; int tests_failed_; }; #endif ./tests/mir-stress/src/client.cpp0000644000015600001650000000561612676616125017144 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomi Richards */ #include "client.h" #include "mir_toolkit/mir_client_library.h" #include #include #include #include ClientStateMachine::Ptr ClientStateMachine::Create() { // in the future we can extend this to return different // types of clients. Right now we only return an unaccelerated client. return std::make_shared(); } UnacceleratedClient::UnacceleratedClient() : connection_(nullptr) , surface_(nullptr) { } bool UnacceleratedClient::connect(std::string unique_name, const char* socket_file) { assert(connection_ == nullptr); connection_ = mir_connect_sync(socket_file, unique_name.c_str()); if (! connection_ || ! mir_connection_is_valid(connection_)) { const char* error_message = mir_connection_get_error_message(connection_); if (std::strcmp(error_message, "") != 0) { std::cout << "Could not connect to server, error is: " << error_message << std::endl; } return false; } return true; } bool UnacceleratedClient::create_surface() { auto display_configuration = mir_connection_create_display_config(connection_); if (display_configuration->num_outputs < 1) return false; MirDisplayOutput display_state = display_configuration->outputs[0]; if (display_state.num_output_formats < 1) return false; // TODO: instead of picking the first pixel format, pick a random one! MirPixelFormat const pixel_format = display_state.output_formats[0]; mir_display_config_destroy(display_configuration); auto const spec = mir_connection_create_spec_for_normal_surface( connection_, 640, 480, pixel_format); mir_surface_spec_set_name(spec, __PRETTY_FUNCTION__); mir_surface_spec_set_buffer_usage(spec, mir_buffer_usage_software); surface_ = mir_surface_create_sync(spec); mir_surface_spec_release(spec); return true; } void UnacceleratedClient::release_surface() { if (surface_ != nullptr) { mir_surface_release_sync(surface_); surface_ = nullptr; } } void UnacceleratedClient::disconnect() { if (connection_ != nullptr) { mir_connection_release(connection_); connection_ = nullptr; } } ./tests/mir-stress/CMakeLists.txt0000644000015600001650000000027212676616125017124 0ustar jenkinsjenkinsproject(mir_stress) mir_add_wrapped_executable(mir_stress src/client.cpp src/mir-stress.cpp src/results.cpp src/threading.cpp ) target_link_libraries(mir_stress mirclient ) ./tests/client-language/0000755000015600001650000000000012676616125015312 5ustar jenkinsjenkins./tests/client-language/c99.c0000644000015600001650000000171312676616125016064 0ustar jenkinsjenkins/* * Stub client to be compiled with -std=c99, just to check we have correct * language compatibility in the client header(s). * * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt * */ #include "mir_toolkit/mir_client_library.h" #include "mir_toolkit/debug/surface.h" #include "mir_toolkit/event.h" int main() { return 0; } ./tests/client-language/CMakeLists.txt0000644000015600001650000000017112676616125020051 0ustar jenkinsjenkinsset (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") mir_add_wrapped_executable(client-language-test-c99 NOINSTALL c99.c ) ./tests/CMakeLists.txt0000644000015600001650000001031212676616125015010 0ustar jenkinsjenkinspkg_check_modules(UMOCKDEV REQUIRED umockdev-1.0>=0.6) if (NOT UMOCKDEV_FOUND) message(FATAL_ERROR "Umockdev not found, cannot build without disabling tests (via MIR_ENABLE_TESTS).") endif() include_directories(${MIR_3RD_PARTY_INCLUDE_DIRECTORIES}) include_directories(${MIR_ANDROID_INCLUDE_DIRECTORIES}) include(CheckCXXCompilerFlag) check_cxx_compiler_flag(-Wnull-dereference HAS_W_NULL_DEREFERENCE) check_cxx_compiler_flag(-Woverloaded-virtual HAS_W_OVERLOADED_VIRTUAL) check_cxx_compiler_flag(-Winconsistent-missing-override HAS_W_INCONSISTENT_MISSING_OVERRIDE) if (HAS_W_NULL_DEREFERENCE) # Avoid clang complaints about poor quality gmock/gtest headers set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=null-dereference") endif() if (HAS_W_OVERLOADED_VIRTUAL) # Avoid clang complaints about poor quality gmock/gtest headers set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=overloaded-virtual") endif() if (HAS_W_INCONSISTENT_MISSING_OVERRIDE) # MOCK_METHOD()s cannot be marked as override; we cannot consistently mark overrides # in the tests. set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override") endif() if (MIR_TEST_PLATFORM STREQUAL "android") add_definitions(-DANDROID) endif() if (MIR_TEST_PLATFORM STREQUAL "mesa-kms") add_definitions(-DMESA_KMS) endif() if (MIR_TEST_PLATFORM STREQUAL "mesa-x11") add_definitions(-DMESA_X11) endif() if (MIR_BUILD_PLATFORM_ANDROID) #avoid complaints about poor quality android headers include_directories(SYSTEM ${LIBHARDWARE_INCLUDE_DIRS}) add_definitions(-DMIR_BUILD_PLATFORM_ANDROID) endif() if (MIR_BUILD_PLATFORM_MESA_KMS) add_definitions(-DMIR_BUILD_PLATFORM_MESA_KMS) endif() # public headers (only public headers should be accessed by acceptance tests) include_directories( ${PROJECT_SOURCE_DIR}/include/platform ${PROJECT_SOURCE_DIR}/include/server ${PROJECT_SOURCE_DIR}/include/client ${PROJECT_SOURCE_DIR}/include/common ${PROJECT_SOURCE_DIR}/include/test ${PROJECT_SOURCE_DIR}/include/renderers/gl ) option(MIR_BUILD_ACCEPTANCE_TESTS "Build acceptance tests" ON) option(MIR_BUILD_INTEGRATION_TESTS "Build integration tests" ON) option(MIR_BUILD_PERFORMANCE_TESTS "Build performance tests" ON) option(MIR_BUILD_PRIVILEGED_TESTS "Build privileged tests" ON) option(MIR_BUILD_UNIT_TESTS "Build unit tests" ON) if (MIR_BUILD_ACCEPTANCE_TESTS) add_subdirectory(acceptance-tests/) add_subdirectory(umock-acceptance-tests/) endif (MIR_BUILD_ACCEPTANCE_TESTS) if (MIR_BUILD_PERFORMANCE_TESTS) add_subdirectory(performance-tests/) endif(MIR_BUILD_PERFORMANCE_TESTS) if (MIR_BUILD_PRIVILEGED_TESTS) add_subdirectory(privileged-tests/) endif(MIR_BUILD_PRIVILEGED_TESTS) # Private test headers used by integration and unit tests include_directories( include ) if (MIR_BUILD_INTEGRATION_TESTS) add_subdirectory(integration-tests/) endif (MIR_BUILD_INTEGRATION_TESTS) if (MIR_BUILD_UNIT_TESTS) add_subdirectory(unit-tests/) endif (MIR_BUILD_UNIT_TESTS) add_subdirectory(mir_test/) add_subdirectory(mir_test_framework/) add_subdirectory(mir_test_doubles/) add_subdirectory(client-language/) add_subdirectory(mir-stress/) add_subdirectory(loader-tests/) add_library(mir-test-assist STATIC $ $ $ $ ) target_link_libraries(mir-test-assist mirclient mirserver ${Boost_LIBRARIES} ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} # Link in pthread. ) set(PREFIX "${CMAKE_INSTALL_PREFIX}") set(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}") set(INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include/mirtest") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/mirtest.pc.in ${CMAKE_CURRENT_BINARY_DIR}/mirtest.pc ) install(TARGETS mir-test-assist ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/test/mir DESTINATION "include/mirtest" ) install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/test/mir_test_framework DESTINATION "include/mirtest" ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mirtest.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) mir_add_memcheck_test() mir_add_detect_fd_leaks_test() ./tests/mir_test_doubles/0000755000015600001650000000000012676616160015615 5ustar jenkinsjenkins./tests/mir_test_doubles/null_event_sink_factory.cpp0000644000015600001650000000203712676616125023252 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/test/doubles/null_event_sink_factory.h" #include "mir/test/doubles/null_event_sink.h" namespace mf = mir::frontend; namespace mtd = mir::test::doubles; std::unique_ptr mtd::NullEventSinkFactory::create_sink(std::shared_ptr const&) { return std::make_unique(); } ./tests/mir_test_doubles/test_protobuf_client.cpp0000644000015600001650000001565112676616157022574 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/test/test_protobuf_client.h" #include "mir/test/doubles/mock_rpc_report.h" #include "mir/test/doubles/null_client_event_sink.h" #include "src/client/connection_surface_map.h" #include "src/client/display_configuration.h" #include "src/client/lifecycle_control.h" #include "src/client/buffer_factory.h" #include "src/client/rpc/make_rpc_channel.h" #include "src/client/rpc/mir_basic_rpc_channel.h" #include "mir/input/input_devices.h" #include "mir/dispatch/dispatchable.h" #include "mir/dispatch/threaded_dispatcher.h" #include "mir/events/event_private.h" #include #include #include namespace mtd = mir::test::doubles; namespace mclr = mir::client::rpc; namespace md = mir::dispatch; mir::test::TestProtobufClient::TestProtobufClient(std::string socket_file, int timeout_ms) : rpc_report(std::make_shared>()), channel(mclr::make_rpc_channel( socket_file, std::make_shared(), std::make_shared(), std::make_shared(), std::make_shared(), rpc_report, std::make_shared(), std::make_shared>(), std::make_shared())), eventloop{std::make_shared("Mir/TestIPC", std::dynamic_pointer_cast(channel))}, display_server(channel), maxwait(timeout_ms), connect_done_called(false), create_surface_called(false), next_buffer_called(false), exchange_buffer_called(false), disconnect_done_called(false), configure_display_done_called(false), create_surface_done_count(0) { surface_parameters.set_width(640); surface_parameters.set_height(480); surface_parameters.set_pixel_format(0); surface_parameters.set_buffer_usage(0); surface_parameters.set_output_id(mir_display_output_id_invalid); prompt_session_parameters.set_application_pid(__LINE__); ON_CALL(*this, connect_done()) .WillByDefault(testing::Invoke(this, &TestProtobufClient::on_connect_done)); ON_CALL(*this, create_surface_done()) .WillByDefault(testing::Invoke(this, &TestProtobufClient::on_create_surface_done)); ON_CALL(*this, next_buffer_done()) .WillByDefault(testing::Invoke(this, &TestProtobufClient::on_next_buffer_done)); ON_CALL(*this, disconnect_done()) .WillByDefault(testing::Invoke(this, &TestProtobufClient::on_disconnect_done)); ON_CALL(*this, display_configure_done()) .WillByDefault(testing::Invoke(this, &TestProtobufClient::on_configure_display_done)); } void mir::test::TestProtobufClient::signal_condition(bool& condition) { std::lock_guard lk{guard}; condition = true; cv.notify_all(); } void mir::test::TestProtobufClient::reset_condition(bool& condition) { std::lock_guard lk{guard}; condition = false; } void mir::test::TestProtobufClient::on_connect_done() { signal_condition(connect_done_called); } void mir::test::TestProtobufClient::on_create_surface_done() { std::lock_guard lk{guard}; create_surface_called = true; create_surface_done_count++; cv.notify_all(); } void mir::test::TestProtobufClient::on_next_buffer_done() { signal_condition(next_buffer_called); } void mir::test::TestProtobufClient::on_disconnect_done() { signal_condition(disconnect_done_called); } void mir::test::TestProtobufClient::on_configure_display_done() { signal_condition(configure_display_done_called); } void mir::test::TestProtobufClient::connect() { reset_condition(connect_done_called); display_server.connect( &connect_parameters, &connection, google::protobuf::NewCallback(this, &TestProtobufClient::connect_done)); } void mir::test::TestProtobufClient::disconnect() { reset_condition(disconnect_done_called); display_server.disconnect( &ignored, &ignored, google::protobuf::NewCallback(this, &TestProtobufClient::disconnect_done)); } void mir::test::TestProtobufClient::create_surface() { reset_condition(create_surface_called); display_server.create_surface( &surface_parameters, &surface, google::protobuf::NewCallback(this, &TestProtobufClient::create_surface_done)); } void mir::test::TestProtobufClient::next_buffer() { reset_condition(next_buffer_called); display_server.next_buffer( &surface.id(), surface.mutable_buffer(), google::protobuf::NewCallback(this, &TestProtobufClient::next_buffer_done)); } void mir::test::TestProtobufClient::configure_display() { reset_condition(configure_display_done_called); display_server.configure_display( &disp_config, &disp_config_response, google::protobuf::NewCallback(this, &TestProtobufClient::display_configure_done)); } void mir::test::TestProtobufClient::wait_for_configure_display_done() { wait_for([this]{ return configure_display_done_called; }, "Timed out waiting to configure display"); } void mir::test::TestProtobufClient::wait_for_connect_done() { wait_for([this]{ return connect_done_called; }, "Timed out waiting to connect"); } void mir::test::TestProtobufClient::wait_for_create_surface() { wait_for([this]{ return create_surface_called; }, "Timed out waiting create surface"); } void mir::test::TestProtobufClient::wait_for_next_buffer() { wait_for([this] { return next_buffer_called; }, "Timed out waiting for next buffer"); } void mir::test::TestProtobufClient::wait_for_disconnect_done() { wait_for([this] { return disconnect_done_called; }, "Timed out waiting to disconnect"); } void mir::test::TestProtobufClient::wait_for_surface_count(int count) { wait_for([this, count] { return create_surface_done_count == count; }, "Timed out waiting for surface count"); } void mir::test::TestProtobufClient::wait_for(std::function const& predicate, std::string const& error_message) { std::unique_lock lk{guard}; if (!cv.wait_for(lk, std::chrono::milliseconds(maxwait), predicate)) { BOOST_THROW_EXCEPTION(std::runtime_error(error_message)); } } ./tests/mir_test_doubles/mock_event_sink_factory.cpp0000644000015600001650000000632612676616125023236 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/test/doubles/mock_event_sink_factory.h" #include "mir/frontend/event_sink.h" #include "mir/test/doubles/mock_event_sink.h" #include "mir/graphics/buffer.h" namespace mf = mir::frontend; namespace mg = mir::graphics; namespace mtd = mir::test::doubles; namespace { class GloballyUniqueMockEventSink : public mf::EventSink { public: GloballyUniqueMockEventSink(std::shared_ptr const& underlying_sink); ~GloballyUniqueMockEventSink() override; void handle_event(MirEvent const& ev) override; void handle_lifecycle_event(MirLifecycleState state) override; void handle_display_config_change(mg::DisplayConfiguration const& conf) override; void send_ping(int32_t serial) override; void send_buffer(mf::BufferStreamId id, mg::Buffer& buf, mg::BufferIpcMsgType type) override; void handle_input_device_change(std::vector> const& devices) override; private: std::shared_ptr underlying_sink; }; } GloballyUniqueMockEventSink::GloballyUniqueMockEventSink(std::shared_ptr const& underlying_sink) : underlying_sink{underlying_sink} { } GloballyUniqueMockEventSink::~GloballyUniqueMockEventSink() { } void GloballyUniqueMockEventSink::handle_event(MirEvent const& ev) { underlying_sink->handle_event(ev); } void GloballyUniqueMockEventSink::handle_lifecycle_event(MirLifecycleState state) { underlying_sink->handle_lifecycle_event(state); } void GloballyUniqueMockEventSink::handle_display_config_change( mg::DisplayConfiguration const& conf) { underlying_sink->handle_display_config_change(conf); } void GloballyUniqueMockEventSink::send_ping(int32_t serial) { underlying_sink->send_ping(serial); } void GloballyUniqueMockEventSink::handle_input_device_change( std::vector> const& devices) { underlying_sink->handle_input_device_change(devices); } void GloballyUniqueMockEventSink::send_buffer( mf::BufferStreamId id, mg::Buffer& buf, mg::BufferIpcMsgType type) { underlying_sink->send_buffer(id, buf, type); } mtd::MockEventSinkFactory::MockEventSinkFactory() : underlying_sink{std::make_shared>()} { } std::unique_ptr mtd::MockEventSinkFactory::create_sink(std::shared_ptr const&) { return std::make_unique(underlying_sink); } std::shared_ptr mtd::MockEventSinkFactory::the_mock_sink() { return underlying_sink; } ./tests/mir_test_doubles/fake_alarm_factory.cpp0000644000015600001650000001163712676616157022150 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "mir/test/doubles/fake_alarm_factory.h" #include #include namespace mtd = mir::test::doubles; namespace mt = mir::time; class mtd::FakeAlarmFactory::FakeAlarm : public mt::Alarm { public: FakeAlarm( std::function const& callback, std::shared_ptr const& clock, std::function const& on_destruction); ~FakeAlarm() override; void time_updated(); int wakeup_count() const; bool cancel() override; State state() const override; bool reschedule_in(std::chrono::milliseconds delay) override; bool reschedule_for(mir::time::Timestamp timeout) override; private: int triggered_count; std::function const callback; State alarm_state; mir::time::Timestamp triggers_at; std::shared_ptr clock; std::function const on_destruction; }; mtd::FakeAlarmFactory::FakeAlarm::FakeAlarm( std::function const& callback, std::shared_ptr const& clock, std::function const& on_destruction) : triggered_count{0}, callback{callback}, alarm_state{State::cancelled}, triggers_at{mir::time::Timestamp::max()}, clock{clock}, on_destruction{on_destruction} { } mtd::FakeAlarmFactory::FakeAlarm::~FakeAlarm() { on_destruction(this); } void mtd::FakeAlarmFactory::FakeAlarm::time_updated() { if (clock->now() > triggers_at) { triggers_at = mir::time::Timestamp::max(); alarm_state = State::triggered; callback(); ++triggered_count; } } int mtd::FakeAlarmFactory::FakeAlarm::wakeup_count() const { return triggered_count; } bool mtd::FakeAlarmFactory::FakeAlarm::cancel() { if (alarm_state == State::pending) { triggers_at = mir::time::Timestamp::max(); alarm_state = State::cancelled; return true; } return false; } mt::Alarm::State mtd::FakeAlarmFactory::FakeAlarm::state() const { return alarm_state; } bool mtd::FakeAlarmFactory::FakeAlarm::reschedule_in(std::chrono::milliseconds delay) { bool rescheduled = alarm_state == State::pending; alarm_state = State::pending; triggers_at = clock->now() + std::chrono::duration_cast(delay); return rescheduled; } bool mtd::FakeAlarmFactory::FakeAlarm::reschedule_for(mir::time::Timestamp timeout) { bool rescheduled = alarm_state == State::pending; if (timeout > clock->now()) { alarm_state = State::pending; triggers_at = timeout; } else { callback(); triggers_at = mir::time::Timestamp::max(); alarm_state = State::triggered; } return rescheduled; } mtd::FakeAlarmFactory::FakeAlarmFactory() : clock{std::make_shared()} { } std::unique_ptr mtd::FakeAlarmFactory::create_alarm( std::function const& callback) { std::unique_ptr alarm = std::make_unique( callback, clock, [this](FakeAlarm* destroying) { alarms.erase(std::remove(alarms.begin(), alarms.end(), destroying), alarms.end()); }); alarms.push_back(static_cast(alarm.get())); return alarm; } std::unique_ptr mtd::FakeAlarmFactory::create_alarm( std::shared_ptr const& /*callback*/) { throw std::logic_error{"Lockable alarm creation not implemented for fake alarms"}; } void mtd::FakeAlarmFactory::advance_by(mt::Duration step) { clock->advance_by(step); // Guard against alarms deleting themselves from their callback... auto temp_alarms = alarms; for (auto& alarm : temp_alarms) { alarm->time_updated(); } } void mtd::FakeAlarmFactory::advance_smoothly_by(mt::Duration step) { using namespace std::literals::chrono_literals; auto const step_by = 1ms; while (step.count() > 0) { advance_by(step_by); step -= step_by; } } int mtd::FakeAlarmFactory::wakeup_count() const { return std::accumulate( alarms.begin(), alarms.end(), 0, [](int count, FakeAlarm const* alarm) { return count + alarm->wakeup_count(); }); } ./tests/mir_test_doubles/test_protobuf_socket_server.cpp0000644000015600001650000000461212676616125024162 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/test/test_protobuf_server.h" #include "mir/test/doubles/stub_ipc_factory.h" #include "mir/test/doubles/stub_session_authorizer.h" #include "mir/frontend/connector_report.h" #include "mir/frontend/protobuf_connection_creator.h" #include "src/server/frontend/published_socket_connector.h" #include "src/server/report/null_report_factory.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/test/doubles/null_platform_ipc_operations.h" namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace mf = mir::frontend; namespace mr = mir::report; namespace { std::shared_ptr make_connector( std::string const& socket_name, std::shared_ptr const& factory, std::shared_ptr const& report) { mtd::NullEmergencyCleanup null_emergency_cleanup; return std::make_shared( socket_name, std::make_shared( factory, std::make_shared(), std::make_shared(), mr::null_message_processor_report()), null_emergency_cleanup, report); } } mt::TestProtobufServer::TestProtobufServer( std::string const& socket_name, const std::shared_ptr& tool) : TestProtobufServer(socket_name, tool, mr::null_connector_report()) { } mt::TestProtobufServer::TestProtobufServer( std::string const& socket_name, const std::shared_ptr& tool, std::shared_ptr const& report) : comm(make_connector(socket_name, std::make_shared(*tool), report)) { } ./tests/mir_test_doubles/mock_gbm.cpp0000644000015600001650000001174312676616125020106 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Alexandros Frantzis */ #include "mir/test/doubles/mock_gbm.h" #include namespace mtd=mir::test::doubles; namespace { mtd::MockGBM* global_mock = NULL; } mtd::FakeGBMResources::FakeGBMResources() : device(reinterpret_cast(0x12345678)), surface(reinterpret_cast(0x1234abcd)), bo(reinterpret_cast(0xabcdef12)) { bo_handle.u32 = 0x0987; } mtd::MockGBM::MockGBM() { using namespace testing; assert(global_mock == NULL && "Only one mock object per process is allowed"); global_mock = this; ON_CALL(*this, gbm_create_device(_)) .WillByDefault(Return(fake_gbm.device)); ON_CALL(*this, gbm_device_is_format_supported(_,_,_)) .WillByDefault(Return(1)); ON_CALL(*this, gbm_surface_create(fake_gbm.device,_,_,_,_)) .WillByDefault(Return(fake_gbm.surface)); ON_CALL(*this, gbm_surface_lock_front_buffer(fake_gbm.surface)) .WillByDefault(Return(fake_gbm.bo)); ON_CALL(*this, gbm_bo_create(fake_gbm.device,_,_,_,_)) .WillByDefault(Return(fake_gbm.bo)); ON_CALL(*this, gbm_bo_get_device(_)) .WillByDefault(Return(fake_gbm.device)); ON_CALL(*this, gbm_bo_get_handle(fake_gbm.bo)) .WillByDefault(Return(fake_gbm.bo_handle)); ON_CALL(*this, gbm_bo_set_user_data(_,_,_)) .WillByDefault(Invoke(this, &MockGBM::on_gbm_bo_set_user_data)); ON_CALL(*this, gbm_bo_write(_,_,_)) .WillByDefault(Return(0)); } mtd::MockGBM::~MockGBM() { // this is probably later than optimal, but at least ensures memory freed for (auto i = destroyers.begin(); i != destroyers.end(); ++i) (*i)(); global_mock = NULL; } struct gbm_device* gbm_create_device(int fd) { return global_mock->gbm_create_device(fd); } void gbm_device_destroy(struct gbm_device *gbm) { return global_mock->gbm_device_destroy(gbm); } int gbm_device_get_fd(struct gbm_device *gbm) { return global_mock->gbm_device_get_fd(gbm); } int gbm_device_is_format_supported(struct gbm_device *gbm, uint32_t format, uint32_t usage) { return global_mock->gbm_device_is_format_supported(gbm, format, usage); } struct gbm_surface *gbm_surface_create(struct gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags) { return global_mock->gbm_surface_create(gbm, width, height, format, flags); } void gbm_surface_destroy(struct gbm_surface *surface) { return global_mock->gbm_surface_destroy(surface); } struct gbm_bo *gbm_surface_lock_front_buffer(struct gbm_surface *surface) { return global_mock->gbm_surface_lock_front_buffer(surface); } void gbm_surface_release_buffer(struct gbm_surface *surface, struct gbm_bo *bo) { global_mock->gbm_surface_release_buffer(surface, bo); } struct gbm_bo *gbm_bo_create(struct gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags) { return global_mock->gbm_bo_create(gbm, width, height, format,flags); } struct gbm_device *gbm_bo_get_device(struct gbm_bo *bo) { return global_mock->gbm_bo_get_device(bo); } uint32_t gbm_bo_get_width(struct gbm_bo *bo) { return global_mock->gbm_bo_get_width(bo); } uint32_t gbm_bo_get_height(struct gbm_bo *bo) { return global_mock->gbm_bo_get_height(bo); } uint32_t gbm_bo_get_stride(struct gbm_bo *bo) { return global_mock->gbm_bo_get_stride(bo); } uint32_t gbm_bo_get_format(struct gbm_bo *bo) { return global_mock->gbm_bo_get_format(bo); } union gbm_bo_handle gbm_bo_get_handle(struct gbm_bo *bo) { return global_mock->gbm_bo_get_handle(bo); } void gbm_bo_set_user_data(struct gbm_bo *bo, void *data, void (*destroy_user_data)(struct gbm_bo *, void *)) { global_mock->gbm_bo_set_user_data(bo, data, destroy_user_data); } void *gbm_bo_get_user_data(struct gbm_bo *bo) { return global_mock->gbm_bo_get_user_data(bo); } int gbm_bo_write(struct gbm_bo *bo, const void *buf, size_t count) { return global_mock->gbm_bo_write(bo, buf, count); } void gbm_bo_destroy(struct gbm_bo *bo) { return global_mock->gbm_bo_destroy(bo); } struct gbm_bo* gbm_bo_import(struct gbm_device* device, uint32_t type, void* data, uint32_t flags) { return global_mock->gbm_bo_import(device, type, data, flags); } ./tests/mir_test_doubles/mock_egl.cpp0000644000015600001650000003340512676616125020107 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Thomas Voss * Kevin DuBois */ #include "mir/egl_native_surface.h" #include "mir/test/doubles/mock_egl.h" #include namespace mtd = mir::test::doubles; namespace { mtd::MockEGL* global_mock_egl = NULL; } EGLConfig configs[] = { (void*)3, (void*)4, (void*)8, (void*)14 }; EGLint config_size = 4; /* We prefix GL/EGL extensions with "extension_" so code under test has to get their function ptrs with eglGetProcAddress */ EGLImageKHR extension_eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); EGLBoolean extension_eglDestroyImageKHR (EGLDisplay dpy, EGLImageKHR image); void extension_glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image); EGLSyncKHR extension_eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list); EGLBoolean extension_eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync); EGLint extension_eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout); /* EGL{Surface,Display,Config,Context} are all opaque types, so we can put whatever we want in them for testing */ mtd::MockEGL::MockEGL() : fake_egl_display((EGLDisplay) 0x0530), fake_configs(configs), fake_configs_num(config_size), fake_egl_surface((EGLSurface) 0xa034), fake_egl_context((EGLContext) 0xbeef), fake_egl_image((EGLImageKHR) 0x1234), fake_visual_id(1) //HAL_PIXEL_FORMAT_RGBA on android { using namespace testing; assert(global_mock_egl == NULL && "Only one mock object per process is allowed"); global_mock_egl = this; ON_CALL(*this, eglGetDisplay(_)) .WillByDefault(Return(fake_egl_display)); ON_CALL(*this, eglInitialize(_,_,_)) .WillByDefault(DoAll( SetArgPointee<1>(1), SetArgPointee<2>(4), Return(EGL_TRUE))); ON_CALL(*this, eglBindApi(EGL_OPENGL_ES_API)) .WillByDefault(Return(EGL_TRUE)); ON_CALL(*this, eglGetConfigs(_,NULL, 0, _)) .WillByDefault(DoAll( SetArgPointee<3>(config_size), Return(EGL_TRUE))); ON_CALL(*this, eglGetConfigAttrib(_, _, EGL_NATIVE_VISUAL_ID, _)) .WillByDefault(DoAll( SetArgPointee<3>(fake_visual_id), Return(EGL_TRUE))); ON_CALL(*this, eglChooseConfig(_,_,_,_,_)) .WillByDefault(Invoke( [&] (EGLDisplay, const EGLint *, EGLConfig *configs, EGLint config_size, EGLint *num_config) -> EGLBoolean { EGLint const min_size = std::min(config_size, fake_configs_num); std::copy(fake_configs, fake_configs + min_size, configs); *num_config = min_size; return EGL_TRUE; })); ON_CALL(*this, eglCreateWindowSurface(_,_,_,_)) .WillByDefault(Return(fake_egl_surface)); ON_CALL(*this, eglCreatePbufferSurface(_,_,_)) .WillByDefault(Return(fake_egl_surface)); ON_CALL(*this, eglCreateContext(_,_,_,_)) .WillByDefault(Return(fake_egl_context)); ON_CALL(*this, eglMakeCurrent(_,_,_,_)) .WillByDefault(Invoke( [this] (EGLDisplay, EGLSurface, EGLSurface, EGLContext context) { std::lock_guard lock{current_contexts_mutex}; current_contexts[std::this_thread::get_id()] = context; return EGL_TRUE; })); ON_CALL(*this, eglGetCurrentContext()) .WillByDefault(Invoke([this] { std::lock_guard lock{current_contexts_mutex}; return current_contexts[std::this_thread::get_id()]; })); ON_CALL(*this, eglSwapBuffers(_,_)) .WillByDefault(Return(EGL_TRUE)); ON_CALL(*this, eglGetCurrentDisplay()) .WillByDefault(Return(fake_egl_display)); ON_CALL(*this, eglCreateImageKHR(_,_,_,_,_)) .WillByDefault(Return(fake_egl_image)); typedef mtd::MockEGL::generic_function_pointer_t func_ptr_t; ON_CALL(*this, eglGetProcAddress(StrEq("eglCreateImageKHR"))) .WillByDefault(Return(reinterpret_cast(extension_eglCreateImageKHR))); ON_CALL(*this, eglGetProcAddress(StrEq("eglDestroyImageKHR"))) .WillByDefault(Return(reinterpret_cast(extension_eglDestroyImageKHR))); ON_CALL(*this, eglGetProcAddress(StrEq("glEGLImageTargetTexture2DOES"))) .WillByDefault(Return(reinterpret_cast(extension_glEGLImageTargetTexture2DOES))); ON_CALL(*this, eglGetProcAddress(StrEq("eglCreateSyncKHR"))) .WillByDefault(Return(reinterpret_cast(extension_eglCreateSyncKHR))); ON_CALL(*this, eglGetProcAddress(StrEq("eglDestroySyncKHR"))) .WillByDefault(Return(reinterpret_cast(extension_eglDestroySyncKHR))); ON_CALL(*this, eglGetProcAddress(StrEq("eglClientWaitSyncKHR"))) .WillByDefault(Return(reinterpret_cast(extension_eglClientWaitSyncKHR))); } void mtd::MockEGL::provide_egl_extensions() { using namespace testing; const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap"; ON_CALL(*this, eglQueryString(_,EGL_EXTENSIONS)) .WillByDefault(Return(egl_exts)); } void mtd::MockEGL::provide_stub_platform_buffer_swapping() { using namespace ::testing; // TODO: Comment ON_CALL(*this, eglCreateWindowSurface(_,_,_,_)) .WillByDefault(Invoke( [&] (EGLDisplay,EGLConfig,NativeWindowType nw, EGLint const*) -> EGLSurface { return reinterpret_cast(nw); })); ON_CALL(*this, eglSwapBuffers(_,_)) .WillByDefault(Invoke( [&](EGLDisplay,EGLSurface surface) -> EGLBoolean { auto mir_surf = reinterpret_cast(surface); mir_surf->request_and_wait_for_next_buffer(); return true; })); } mtd::MockEGL::~MockEGL() { global_mock_egl = NULL; } #define CHECK_GLOBAL_MOCK(rettype) \ if (!global_mock_egl) \ { \ using namespace ::testing; \ ADD_FAILURE_AT(__FILE__,__LINE__); \ rettype type = (rettype) 0; \ return type; \ } #define CHECK_GLOBAL_VOID_MOCK() \ if (!global_mock_egl) \ { \ using namespace ::testing; \ ADD_FAILURE_AT(__FILE__,__LINE__); \ return; \ } EGLint eglGetError (void) { CHECK_GLOBAL_MOCK(EGLint) return global_mock_egl->eglGetError(); } EGLDisplay eglGetDisplay (NativeDisplayType display) { CHECK_GLOBAL_MOCK(EGLDisplay); return global_mock_egl->eglGetDisplay(display); } EGLBoolean eglInitialize (EGLDisplay dpy, EGLint *major, EGLint *minor) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglInitialize(dpy, major, minor); } EGLBoolean eglTerminate (EGLDisplay dpy) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglTerminate(dpy); } const char * eglQueryString (EGLDisplay dpy, EGLint name) { CHECK_GLOBAL_MOCK(const char *) return global_mock_egl->eglQueryString(dpy, name); } EGLBoolean eglBindAPI (EGLenum api) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglBindApi(api); } mtd::MockEGL::generic_function_pointer_t eglGetProcAddress (const char *name) { CHECK_GLOBAL_MOCK(mtd::MockEGL::generic_function_pointer_t) return global_mock_egl->eglGetProcAddress(name); } EGLBoolean eglGetConfigs (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglGetConfigs(dpy, configs, config_size, num_config); } EGLBoolean eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglChooseConfig(dpy, attrib_list, configs, config_size, num_config); } EGLBoolean eglGetConfigAttrib (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglGetConfigAttrib(dpy, config, attribute, value); } EGLSurface eglCreateWindowSurface (EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list) { CHECK_GLOBAL_MOCK(EGLSurface) return global_mock_egl->eglCreateWindowSurface(dpy, config, window, attrib_list); } EGLSurface eglCreatePixmapSurface (EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap, const EGLint *attrib_list) { CHECK_GLOBAL_MOCK(EGLSurface) return global_mock_egl->eglCreatePixmapSurface(dpy, config, pixmap, attrib_list); } EGLSurface eglCreatePbufferSurface (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list) { CHECK_GLOBAL_MOCK(EGLSurface) return global_mock_egl->eglCreatePbufferSurface(dpy, config, attrib_list); } EGLBoolean eglDestroySurface (EGLDisplay dpy, EGLSurface surface) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglDestroySurface(dpy, surface); } EGLBoolean eglQuerySurface (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglQuerySurface(dpy, surface, attribute, value); } /* EGL 1.1 render-to-texture APIs */ EGLBoolean eglSurfaceAttrib (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglSurfaceAttrib(dpy, surface, attribute, value); } EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglBindTexImage(dpy, surface, buffer); } EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglReleaseTexImage(dpy, surface, buffer); } /* EGL 1.1 swap control API */ EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglSwapInterval(dpy, interval); } EGLContext eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list) { CHECK_GLOBAL_MOCK(EGLContext) return global_mock_egl->eglCreateContext(dpy, config, share_list, attrib_list); } EGLBoolean eglDestroyContext (EGLDisplay dpy, EGLContext ctx) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglDestroyContext(dpy, ctx); } EGLBoolean eglMakeCurrent (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglMakeCurrent(dpy, draw, read, ctx); } EGLContext eglGetCurrentContext (void) { CHECK_GLOBAL_MOCK(EGLContext) return global_mock_egl->eglGetCurrentContext(); } EGLSurface eglGetCurrentSurface (EGLint readdraw) { CHECK_GLOBAL_MOCK(EGLSurface) return global_mock_egl->eglGetCurrentSurface(readdraw); } EGLDisplay eglGetCurrentDisplay (void) { CHECK_GLOBAL_MOCK(EGLDisplay) return global_mock_egl->eglGetCurrentDisplay(); } EGLBoolean eglQueryContext (EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglQueryContext(dpy, ctx, attribute, value); } EGLBoolean eglWaitGL (void) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglWaitGL(); } EGLBoolean eglWaitNative (EGLint engine) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglWaitNative(engine); } EGLBoolean eglSwapBuffers (EGLDisplay dpy, EGLSurface draw) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglSwapBuffers(dpy, draw); } EGLBoolean eglCopyBuffers (EGLDisplay dpy, EGLSurface surface, NativePixmapType target) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglCopyBuffers(dpy, surface, target); } /* extensions */ EGLImageKHR extension_eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) { CHECK_GLOBAL_MOCK(EGLImageKHR) return global_mock_egl->eglCreateImageKHR(dpy, ctx, target, buffer, attrib_list); } EGLBoolean extension_eglDestroyImageKHR (EGLDisplay dpy, EGLImageKHR image) { CHECK_GLOBAL_MOCK(EGLBoolean) return global_mock_egl->eglDestroyImageKHR(dpy, image); } void extension_glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) { CHECK_GLOBAL_VOID_MOCK(); global_mock_egl->glEGLImageTargetTexture2DOES(target, image); } EGLSyncKHR extension_eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) { CHECK_GLOBAL_MOCK(EGLSyncKHR); return global_mock_egl->eglCreateSyncKHR(dpy, type, attrib_list); } EGLBoolean extension_eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { CHECK_GLOBAL_MOCK(EGLBoolean); return global_mock_egl->eglDestroySyncKHR(dpy, sync); } EGLint extension_eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { CHECK_GLOBAL_MOCK(EGLint); return global_mock_egl->eglClientWaitSyncKHR(dpy, sync, flags, timeout); } ./tests/mir_test_doubles/triggered_main_loop.cpp0000644000015600001650000000526712676616157022352 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/test/doubles/triggered_main_loop.h" #include "mir/test/doubles/stub_alarm.h" #include #include namespace mtd = mir::test::doubles; using base = ::testing::NiceMock; void mtd::TriggeredMainLoop::register_fd_handler(std::initializer_list fds, void const* owner, fd_callback const& handler) { base::register_fd_handler(fds, owner, handler); for (int fd : fds) { fd_callbacks.emplace_back(Item{fd, owner, handler}); } } void mtd::TriggeredMainLoop::unregister_fd_handler(void const* owner) { base::unregister_fd_handler(owner); fd_callbacks.erase( remove_if( begin(fd_callbacks), end(fd_callbacks), [owner](Item const& item) { return item.owner == owner; }), end(fd_callbacks) ); } void mtd::TriggeredMainLoop::trigger_pending_fds() { fd_set read_fds; FD_ZERO(&read_fds); int max_fd = 0; for (auto const & item : fd_callbacks) { FD_SET(item.fd, &read_fds); max_fd = std::max(item.fd, max_fd); } struct timeval do_not_wait{0, 0}; if (select(max_fd+1, &read_fds, nullptr, nullptr, &do_not_wait)) { for (auto const& item : fd_callbacks) { FD_ISSET(item.fd, &read_fds); item.callback(item.fd); } } } void mtd::TriggeredMainLoop::enqueue(void const* owner, ServerAction const& action) { base::enqueue(owner, action); actions.push_back(action); } void mtd::TriggeredMainLoop::trigger_server_actions() { for (auto const& action : actions) action(); actions.clear(); } std::unique_ptr mtd::TriggeredMainLoop::create_alarm(callback const& call) { base::create_alarm(call); timeout_callbacks.push_back(call); return std::unique_ptr{new mtd::StubAlarm}; } void mtd::TriggeredMainLoop::fire_all_alarms() { for(auto const& callback : timeout_callbacks) callback(); } ./tests/mir_test_doubles/stub_buffer.cpp0000644000015600001650000000231012676616125020624 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/test/doubles/stub_buffer.h" #ifdef ANDROID #include "mir/test/doubles/stub_android_native_buffer.h" #elif defined(MESA_KMS) || defined(MESA_X11) #include "mir/test/doubles/stub_gbm_native_buffer.h" #endif namespace mtd=mir::test::doubles; auto mtd::StubBuffer::create_native_buffer() -> std::shared_ptr { #if defined(MESA_KMS) || defined(MESA_X11) return std::make_shared(geometry::Size{0,0}); #elif ANDROID return std::make_shared(); #endif } ./tests/mir_test_doubles/mock_gl.cpp0000644000015600001650000002746112676616125017747 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #include "mir/test/doubles/mock_gl.h" #include #include namespace mtd = mir::test::doubles; namespace { mtd::MockGL* global_mock_gl = NULL; } mtd::MockGL::MockGL() { using namespace testing; assert(global_mock_gl == NULL && "Only one mock object per process is allowed"); global_mock_gl = this; ON_CALL(*this, glCheckFramebufferStatus(_)) .WillByDefault(Return(GL_FRAMEBUFFER_COMPLETE)); ON_CALL(*this, glGetShaderiv(_,_,_)) .WillByDefault(SetArgPointee<2>(GL_TRUE)); ON_CALL(*this, glGetProgramiv(_,_,_)) .WillByDefault(SetArgPointee<2>(GL_TRUE)); ON_CALL(*this, glGenTextures(_, _)) .WillByDefault(Invoke( [] (GLsizei n, GLuint *textures) { std::memset(textures, 0, n * sizeof(*textures)); })); } void mtd::MockGL::provide_gles_extensions() { using namespace testing; const char* gl_exts = "GL_OES_EGL_image"; ON_CALL(*this, glGetString(GL_EXTENSIONS)) .WillByDefault(Return(reinterpret_cast(gl_exts))); } mtd::MockGL::~MockGL() { global_mock_gl = NULL; } #define CHECK_GLOBAL_VOID_MOCK() \ if (!global_mock_gl) \ { \ using namespace ::testing; \ ADD_FAILURE_AT(__FILE__,__LINE__); \ return; \ } #define CHECK_GLOBAL_MOCK(rettype) \ if (!global_mock_gl) \ { \ using namespace ::testing; \ ADD_FAILURE_AT(__FILE__,__LINE__); \ rettype type = (rettype) 0; \ return type; \ } GLenum glGetError() { CHECK_GLOBAL_MOCK(GLenum); return global_mock_gl->glGetError(); } const GLubyte* glGetString(GLenum name) { CHECK_GLOBAL_MOCK(const GLubyte*); return global_mock_gl->glGetString(name); } void glUseProgram(GLuint program) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glUseProgram (program); } void glClear (GLbitfield mask) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glClear(mask); } void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glClearColor(red, green, blue, alpha); } void glColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glColorMask(r, g, b, a); } void glEnable(GLenum func) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glEnable (func); } void glDisable(GLenum func) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glDisable(func); } void glBlendFunc(GLenum src, GLenum dst) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glBlendFunc (src, dst); } void glBlendFuncSeparate(GLenum src_rgb, GLenum dst_rgb, GLenum src_alpha, GLenum dst_alpha) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha); } void glActiveTexture(GLenum unit) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glActiveTexture (unit); } void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glUniformMatrix4fv(location, count, transpose, value); } void glUniform1f(GLint location, GLfloat x) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glUniform1f(location, x); } void glUniform2f(GLint location, GLfloat x, GLfloat y) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glUniform2f(location, x, y); } void glBindBuffer(GLenum buffer, GLuint name) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glBindBuffer(buffer, name); } void glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glVertexAttribPointer(indx, size, type, normalized, stride, ptr); } void glBindTexture(GLenum target, GLuint texture) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glBindTexture(target, texture); } void glEnableVertexAttribArray(GLuint index) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glEnableVertexAttribArray(index); } void glDisableVertexAttribArray(GLuint index) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glDisableVertexAttribArray(index); } void glDrawArrays(GLenum mode, GLint first, GLsizei count) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glDrawArrays(mode, first, count); } GLuint glCreateShader(GLenum type) { CHECK_GLOBAL_MOCK(GLuint); return global_mock_gl->glCreateShader(type); } void glDeleteShader(GLuint shader) { CHECK_GLOBAL_VOID_MOCK(); return global_mock_gl->glDeleteShader(shader); } /* This is the version of glShaderSource in Mesa < 9.0.1 */ void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glShaderSource (shader, count, string, length); } /* This is the version of glShaderSource in Mesa >= 9.0.1 */ void glShaderSource(GLuint shader, GLsizei count, const GLchar* const* string, const GLint *length) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glShaderSource (shader, count, string, length); } void glCompileShader(GLuint shader) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glCompileShader(shader); } void glGetShaderiv(GLuint shader, GLenum pname, GLint *params) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glGetShaderiv(shader, pname, params); } void glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei *length, GLchar *infolog) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glGetShaderInfoLog(shader, bufsize, length, infolog); } GLuint glCreateProgram() { CHECK_GLOBAL_MOCK(GLuint); return global_mock_gl->glCreateProgram(); } void glDeleteProgram(GLuint program) { CHECK_GLOBAL_VOID_MOCK(); return global_mock_gl->glDeleteProgram(program); } void glAttachShader(GLuint program, GLuint shader) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glAttachShader(program, shader); } void glLinkProgram(GLuint program) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glLinkProgram(program); } GLint glGetUniformLocation(GLuint program, const GLchar *name) { CHECK_GLOBAL_MOCK(GLint); return global_mock_gl->glGetUniformLocation(program, name); } GLint glGetAttribLocation(GLuint program, const GLchar *name) { CHECK_GLOBAL_MOCK(GLint); return global_mock_gl->glGetAttribLocation(program, name); } void glTexParameteri(GLenum target, GLenum pname, GLint param) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glTexParameteri(target, pname, param); } void glGenTextures(GLsizei n, GLuint *textures) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glGenTextures(n, textures); } void glDeleteTextures(GLsizei n, const GLuint *textures) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glDeleteTextures(n, textures); } void glUniform1i(GLint location, GLint x) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glUniform1i(location, x); } void glGenBuffers(GLsizei n, GLuint *buffers) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glGenBuffers(n, buffers); } void glDeleteBuffers(GLsizei n, const GLuint *buffers) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glDeleteBuffers(n, buffers); } void glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glBufferData(target, size, data, usage); } void glGetProgramiv(GLuint program, GLenum pname, GLint *params) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glGetProgramiv(program, pname, params); } void glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei *length, GLchar *infolog) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glGetProgramInfoLog(program, bufsize, length, infolog); } void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); } void glGenFramebuffers(GLsizei n, GLuint *framebuffers) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glGenFramebuffers(n, framebuffers); } void glDeleteFramebuffers(GLsizei n, const GLuint * framebuffers) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glDeleteFramebuffers(n, framebuffers); } void glBindFramebuffer(GLenum target, GLuint framebuffer) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glBindFramebuffer(target, framebuffer); } void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glFramebufferTexture2D(target, attachment, textarget, texture, level); } GLenum glCheckFramebufferStatus(GLenum target) { CHECK_GLOBAL_MOCK(GLenum); return global_mock_gl->glCheckFramebufferStatus(target); } void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glReadPixels(x, y, width, height, format, type, pixels); } void glGetIntegerv(GLenum target, GLint* params) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glGetIntegerv(target, params); } void glBindRenderbuffer(GLenum target, GLuint renderbuffer) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glBindRenderbuffer(target, renderbuffer); } void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); } void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glGenRenderbuffers(n, renderbuffers); } void glDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glDeleteRenderbuffers(n, renderbuffers); } void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glRenderbufferStorage(target, internalformat, width, height); } void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glViewport(x, y, width, height); } void glFinish() { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glFinish(); } void glGenerateMipmap(GLenum target) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glGenerateMipmap(target); } void glPixelStorei(GLenum pname, GLint param) { CHECK_GLOBAL_VOID_MOCK(); global_mock_gl->glPixelStorei(pname, param); } ./tests/mir_test_doubles/mock_x11.cpp0000644000015600001650000001371512676616157017760 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Cemil Azizoglu */ #include "mir/test/doubles/mock_x11.h" #include #include namespace mtd=mir::test::doubles; namespace { mtd::MockX11* global_mock = nullptr; } mtd::FakeX11Resources::FakeX11Resources() : display{reinterpret_cast(0x12345678)}, window{reinterpret_cast((long unsigned int)9876543210)} { std::memset(&keypress_event_return, 0, sizeof(XEvent)); std::memset(&button_release_event_return, 0, sizeof(XEvent)); std::memset(&expose_event_return, 0, sizeof(XEvent)); std::memset(&focus_in_event_return, 0, sizeof(XEvent)); std::memset(&focus_out_event_return, 0, sizeof(XEvent)); std::memset(&vscroll_event_return, 0, sizeof(XEvent)); std::memset(&motion_event_return, 0, sizeof(XEvent)); visual_info.depth = 24; keypress_event_return.type = KeyPress; button_release_event_return.type = ButtonRelease; button_release_event_return.xbutton.button = 0; expose_event_return.type = Expose; focus_in_event_return.type = FocusIn; focus_out_event_return.type = FocusOut; vscroll_event_return.type = ButtonPress; XButtonEvent& xbev = (XButtonEvent&)vscroll_event_return; xbev.button = Button4; motion_event_return.type = MotionNotify; } mtd::MockX11::MockX11() { using namespace testing; assert(global_mock == nullptr && "Only one mock object per process is allowed"); global_mock = this; ON_CALL(*this, XOpenDisplay(_)) .WillByDefault(Return(fake_x11.display)); ON_CALL(*this, XGetVisualInfo(fake_x11.display,_,_,_)) .WillByDefault(Return(&fake_x11.visual_info)); ON_CALL(*this, XCreateWindow_wrapper(fake_x11.display,_,_,_,_,_,_,_,_,_)) .WillByDefault(Return(fake_x11.window)); ON_CALL(*this, XInitThreads()) .WillByDefault(Return(1)); ON_CALL(*this, XPending(_)) .WillByDefault(InvokeWithoutArgs([this]() { return fake_x11.pending_events; })); } mtd::MockX11::~MockX11() { global_mock = nullptr; } Display* XOpenDisplay(const char* display_name) { return global_mock->XOpenDisplay(display_name); } int XCloseDisplay(Display* display) { return global_mock->XCloseDisplay(display); } XVisualInfo* XGetVisualInfo(Display* display, long vinfo_mask, XVisualInfo* vinfo_template, int* nitems_return) { return global_mock->XGetVisualInfo(display, vinfo_mask, vinfo_template, nitems_return); } Colormap XCreateColormap(Display* display, Window w, Visual* visual, int alloc) { return global_mock->XCreateColormap(display, w, visual, alloc); } Window XCreateWindow(Display* display, Window parent, int /*x*/, int /*y*/, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int clss, Visual* visual, unsigned long valuemask, XSetWindowAttributes* attributes) { return global_mock->XCreateWindow_wrapper(display, parent, width, height, border_width, depth, clss, visual, valuemask, attributes); } int XSetNormalHints(Display* display, Window w, XSizeHints* hints) { return global_mock->XSetNormalHints(display, w, hints); } int XSetStandardProperties(Display* display, Window w, const char *window_name, const char *icon_name, Pixmap icon_pixmap, char** argv, int argc, XSizeHints* hints) { return global_mock->XSetStandardProperties(display, w, window_name, icon_name, icon_pixmap, argv, argc, hints); } int XFree(void* data) { return global_mock->XFree(data); } int XMapWindow(Display* display, Window w) { return global_mock->XMapWindow(display, w); } int XDestroyWindow(Display* display, Window w) { return global_mock->XDestroyWindow(display, w); } int XConnectionNumber(Display* display) { return global_mock->XConnectionNumber(display); } int XNextEvent(Display* display, XEvent* event_return) { auto const result = global_mock->XNextEvent(display, event_return); if (result) --global_mock->fake_x11.pending_events; return result; } int XLookupString(XKeyEvent* event_struct, char* buffer_return, int bytes_buffer, KeySym* keysym_return, XComposeStatus* status_in_out) { return global_mock->XLookupString(event_struct, buffer_return, bytes_buffer, keysym_return, status_in_out); } int XRefreshKeyboardMapping(XMappingEvent* event_map) { return global_mock->XRefreshKeyboardMapping(event_map); } Window XDefaultRootWindow(Display* display) { return global_mock->XDefaultRootWindow(display); } int XGrabKeyboard(Display* display, Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode, Time time) { return global_mock->XGrabKeyboard(display, grab_window, owner_events, pointer_mode, keyboard_mode, time); } int XUngrabKeyboard(Display* display, Time time) { return global_mock->XUngrabKeyboard(display, time); } int XGetErrorText(Display* display, int code, char* buffer_return, int length) { return global_mock->XGetErrorText(display, code, buffer_return, length); } XErrorHandler XSetErrorHandler(XErrorHandler handler) { return global_mock->XSetErrorHandler(handler); } Status XInitThreads() { return global_mock->XInitThreads(); } int XSetWMHints(Display* display, Window window, XWMHints* wmhints) { return global_mock->XSetWMHints(display, window, wmhints); } int XPending(Display* display) { return global_mock->XPending(display); } ./tests/mir_test_doubles/mock_drm.cpp0000644000015600001650000003315212676616125020121 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Alexandros Frantzis */ #include "mir/test/doubles/mock_drm.h" #include "mir/geometry/size.h" #include #include #include #include namespace mtd=mir::test::doubles; namespace geom = mir::geometry; namespace { mtd::MockDRM* global_mock = nullptr; } mtd::FakeDRMResources::FakeDRMResources() : pipe_fds{-1, -1} { /* Use the read end of a pipe as the fake DRM fd */ if (pipe(pipe_fds) < 0 || pipe_fds[0] < 0) throw std::runtime_error("Failed to create fake DRM fd"); /* Add some default resources */ uint32_t const invalid_id{0}; uint32_t const crtc0_id{10}; uint32_t const crtc1_id{11}; uint32_t const encoder0_id{20}; uint32_t const encoder1_id{21}; uint32_t const connector0_id{30}; uint32_t const connector1_id{31}; uint32_t const all_crtcs_mask{0x3}; modes.push_back(create_mode(1920, 1080, 138500, 2080, 1111, PreferredMode)); modes.push_back(create_mode(832, 624, 57284, 1152, 667, NormalMode)); connector_encoder_ids.push_back(encoder0_id); connector_encoder_ids.push_back(encoder1_id); add_crtc(crtc0_id, drmModeModeInfo()); add_crtc(crtc1_id, modes[1]); add_encoder(encoder0_id, invalid_id, all_crtcs_mask); add_encoder(encoder1_id, crtc1_id, all_crtcs_mask); add_connector(connector0_id, DRM_MODE_CONNECTOR_VGA, DRM_MODE_DISCONNECTED, invalid_id, modes_empty, connector_encoder_ids, geom::Size()); add_connector(connector1_id, DRM_MODE_CONNECTOR_DVID, DRM_MODE_CONNECTED, encoder1_id, modes, connector_encoder_ids, geom::Size{121, 144}); prepare(); } mtd::FakeDRMResources::~FakeDRMResources() { if (pipe_fds[0] >= 0) close(pipe_fds[0]); if (pipe_fds[1] >= 0) close(pipe_fds[1]); } int mtd::FakeDRMResources::fd() const { return pipe_fds[0]; } int mtd::FakeDRMResources::write_fd() const { return pipe_fds[1]; } drmModeRes* mtd::FakeDRMResources::resources_ptr() { return &resources; } void mtd::FakeDRMResources::prepare() { resources.count_crtcs = crtcs.size(); for (auto const& crtc: crtcs) crtc_ids.push_back(crtc.crtc_id); resources.crtcs = crtc_ids.data(); resources.count_encoders = encoders.size(); for (auto const& encoder: encoders) encoder_ids.push_back(encoder.encoder_id); resources.encoders = encoder_ids.data(); resources.count_connectors = connectors.size(); for (auto const& connector: connectors) connector_ids.push_back(connector.connector_id); resources.connectors = connector_ids.data(); } void mtd::FakeDRMResources::reset() { resources = drmModeRes(); crtcs.clear(); encoders.clear(); connectors.clear(); crtc_ids.clear(); encoder_ids.clear(); connector_ids.clear(); } void mtd::FakeDRMResources::add_crtc(uint32_t id, drmModeModeInfo mode) { drmModeCrtc crtc = drmModeCrtc(); crtc.crtc_id = id; crtc.mode = mode; crtcs.push_back(crtc); } void mtd::FakeDRMResources::add_encoder(uint32_t encoder_id, uint32_t crtc_id, uint32_t possible_crtcs_mask) { drmModeEncoder encoder = drmModeEncoder(); encoder.encoder_id = encoder_id; encoder.crtc_id = crtc_id; encoder.possible_crtcs = possible_crtcs_mask; encoders.push_back(encoder); } void mtd::FakeDRMResources::add_connector(uint32_t connector_id, uint32_t type, drmModeConnection connection, uint32_t encoder_id, std::vector& modes, std::vector& possible_encoder_ids, geom::Size const& physical_size) { drmModeConnector connector = drmModeConnector(); connector.connector_id = connector_id; connector.connector_type = type; connector.connection = connection; connector.encoder_id = encoder_id; connector.modes = modes.data(); connector.count_modes = modes.size(); connector.encoders = possible_encoder_ids.data(); connector.count_encoders = possible_encoder_ids.size(); connector.mmWidth = physical_size.width.as_uint32_t(); connector.mmHeight = physical_size.height.as_uint32_t(); connectors.push_back(connector); } drmModeCrtc* mtd::FakeDRMResources::find_crtc(uint32_t id) { for (auto& crtc : crtcs) { if (crtc.crtc_id == id) return &crtc; } return nullptr; } drmModeEncoder* mtd::FakeDRMResources::find_encoder(uint32_t id) { for (auto& encoder : encoders) { if (encoder.encoder_id == id) return &encoder; } return nullptr; } drmModeConnector* mtd::FakeDRMResources::find_connector(uint32_t id) { for (auto& connector : connectors) { if (connector.connector_id == id) return &connector; } return nullptr; } drmModeModeInfo mtd::FakeDRMResources::create_mode(uint16_t hdisplay, uint16_t vdisplay, uint32_t clock, uint16_t htotal, uint16_t vtotal, ModePreference preferred) { drmModeModeInfo mode = drmModeModeInfo(); mode.hdisplay = hdisplay; mode.vdisplay = vdisplay; mode.clock = clock; mode.htotal = htotal; mode.vtotal = vtotal; uint32_t total = htotal; total *= vtotal; // extend to 32 bits mode.vrefresh = clock * 1000UL / total; if (preferred) mode.type |= DRM_MODE_TYPE_PREFERRED; return mode; } mtd::MockDRM::MockDRM() { using namespace testing; assert(global_mock == NULL && "Only one mock object per process is allowed"); global_mock = this; ON_CALL(*this, open(_,_,_)) .WillByDefault(Return(fake_drm.fd())); ON_CALL(*this, drmOpen(_,_)) .WillByDefault(Return(fake_drm.fd())); ON_CALL(*this, drmClose(_)) .WillByDefault(WithArg<0>(Invoke([&](int fd){ return close(fd); }))); ON_CALL(*this, drmModeGetResources(_)) .WillByDefault(Return(fake_drm.resources_ptr())); ON_CALL(*this, drmModeGetCrtc(_, _)) .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_crtc))); ON_CALL(*this, drmModeGetEncoder(_, _)) .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_encoder))); ON_CALL(*this, drmModeGetConnector(_, _)) .WillByDefault(WithArgs<1>(Invoke(&fake_drm, &FakeDRMResources::find_connector))); ON_CALL(*this, drmSetInterfaceVersion(_, _)) .WillByDefault(Return(0)); ON_CALL(*this, drmGetBusid(_)) .WillByDefault(WithoutArgs(Invoke([]{ return static_cast(malloc(10)); }))); ON_CALL(*this, drmFreeBusid(_)) .WillByDefault(WithArg<0>(Invoke([&](const char* busid){ free(const_cast(busid)); }))); } mtd::MockDRM::~MockDRM() noexcept { global_mock = nullptr; } int drmOpen(const char *name, const char *busid) { return global_mock->drmOpen(name, busid); } int drmClose(int fd) { return global_mock->drmClose(fd); } int drmIoctl(int fd, unsigned long request, void *arg) { return global_mock->drmIoctl(fd, request, arg); } drmModeResPtr drmModeGetResources(int fd) { return global_mock->drmModeGetResources(fd); } drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connectorId) { return global_mock->drmModeGetConnector(fd, connectorId); } drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id) { return global_mock->drmModeGetEncoder(fd, encoder_id); } drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId) { return global_mock->drmModeGetCrtc(fd, crtcId); } int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, uint32_t x, uint32_t y, uint32_t *connectors, int count, drmModeModeInfoPtr mode) { return global_mock->drmModeSetCrtc(fd, crtcId, bufferId, x, y, connectors, count, mode); } void drmModeFreeResources(drmModeResPtr ptr) { global_mock->drmModeFreeResources(ptr); } void drmModeFreeProperty(drmModePropertyPtr ptr) { global_mock->drmModeFreeProperty(ptr); } int drmGetCap(int fd, uint64_t capability, uint64_t *value) { return global_mock->drmGetCap(fd, capability, value); } drmModePropertyPtr drmModeGetProperty(int fd, uint32_t propertyId) { return global_mock->drmModeGetProperty(fd, propertyId); } int drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id, uint64_t value) { return global_mock->drmModeConnectorSetProperty(fd, connector_id, property_id, value); } void drmModeFreeConnector(drmModeConnectorPtr ptr) { global_mock->drmModeFreeConnector(ptr); } void drmModeFreeEncoder(drmModeEncoderPtr ptr) { global_mock->drmModeFreeEncoder(ptr); } void drmModeFreeCrtc(drmModeCrtcPtr ptr) { global_mock->drmModeFreeCrtc(ptr); } int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth, uint8_t bpp, uint32_t pitch, uint32_t bo_handle, uint32_t *buf_id) { return global_mock->drmModeAddFB(fd, width, height, depth, bpp, pitch, bo_handle, buf_id); } int drmModeAddFB2(int fd, uint32_t width, uint32_t height, uint32_t pixel_format, uint32_t bo_handles[4], uint32_t pitches[4], uint32_t offsets[4], uint32_t *buf_id, uint32_t flags) { return global_mock->drmModeAddFB2(fd, width, height, pixel_format, bo_handles, pitches, offsets, buf_id, flags); } int drmModeRmFB(int fd, uint32_t bufferId) { return global_mock->drmModeRmFB(fd, bufferId); } int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id, uint32_t flags, void *user_data) { return global_mock->drmModePageFlip(fd, crtc_id, fb_id, flags, user_data); } int drmHandleEvent(int fd, drmEventContextPtr evctx) { return global_mock->drmHandleEvent(fd, evctx); } int drmGetMagic(int fd, drm_magic_t *magic) { return global_mock->drmGetMagic(fd, magic); } int drmAuthMagic(int fd, drm_magic_t magic) { return global_mock->drmAuthMagic(fd, magic); } int drmPrimeHandleToFD(int fd, uint32_t handle, uint32_t flags, int *prime_fd) { return global_mock->drmPrimeHandleToFD(fd, handle, flags, prime_fd); } int drmPrimeFDToHandle(int fd, int prime_fd, uint32_t *handle) { return global_mock->drmPrimeFDToHandle(fd, prime_fd, handle); } int drmSetMaster(int fd) { return global_mock->drmSetMaster(fd); } int drmDropMaster(int fd) { return global_mock->drmDropMaster(fd); } int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height) { return global_mock->drmModeSetCursor(fd, crtcId, bo_handle, width, height); } int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y) { return global_mock->drmModeMoveCursor(fd, crtcId, x, y); } int drmSetInterfaceVersion(int fd, drmSetVersion* sv) { return global_mock->drmSetInterfaceVersion(fd, sv); } char* drmGetBusid(int fd) { return global_mock->drmGetBusid(fd); } void drmFreeBusid(const char* busid) { return global_mock->drmFreeBusid(busid); } // We need to wrap open as we sometimes open() the DRM device // We need to explicitly mark this as C because we don't match the // libc header; we only care about the three-parameter version extern "C" { int open(char const* path, int flags, mode_t mode) { char const* drm_prefix = "/dev/dri/"; if (!strncmp(path, drm_prefix, strlen(drm_prefix))) return global_mock->open(path, flags, mode); int (*real_open)(char const *path, int flags, mode_t mode); *(void **)(&real_open) = dlsym(RTLD_NEXT, "open"); return (*real_open)(path, flags, mode); } int open64(char const* path, int flags, mode_t mode) { char const* drm_prefix = "/dev/dri/"; if (!strncmp(path, drm_prefix, strlen(drm_prefix))) return global_mock->open(path, flags, mode); int (*real_open64)(char const *path, int flags, mode_t mode); *(void **)(&real_open64) = dlsym(RTLD_NEXT, "open64"); return (*real_open64)(path, flags, mode); } int __open(char const* path, int flags, mode_t mode) { char const* drm_prefix = "/dev/dri/"; if (!strncmp(path, drm_prefix, strlen(drm_prefix))) return global_mock->open(path, flags, mode); int (*real_open)(char const *path, int flags, mode_t mode); *(void **)(&real_open) = dlsym(RTLD_NEXT, "__open"); return (*real_open)(path, flags, mode); } int __open64(char const* path, int flags, mode_t mode) { char const* drm_prefix = "/dev/dri/"; if (!strncmp(path, drm_prefix, strlen(drm_prefix))) return global_mock->open(path, flags, mode); int (*real_open64)(char const *path, int flags, mode_t mode); *(void **)(&real_open64) = dlsym(RTLD_NEXT, "__open64"); return (*real_open64)(path, flags, mode); } } ./tests/mir_test_doubles/mock_timer.cpp0000644000015600001650000001047312676616125020460 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Alberto Aguirre */ #include "mir/test/doubles/mock_timer.h" #include "mir/lockable_callback.h" #include "mir/basic_callback.h" #include namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace { class FakeAlarm : public mir::time::Alarm { public: FakeAlarm(std::shared_ptr const& callback, std::shared_ptr const& clock); ~FakeAlarm() override; bool cancel() override; State state() const override; bool reschedule_in(std::chrono::milliseconds delay) override; bool reschedule_for(mir::time::Timestamp timeout) override; private: struct InternalState { explicit InternalState(std::shared_ptr const& callback); State state; std::shared_ptr callback; mt::FakeClock::time_point threshold; }; bool handle_time_change(InternalState& state, mt::FakeClock::time_point now); std::shared_ptr const internal_state; std::shared_ptr const clock; }; FakeAlarm::InternalState::InternalState( std::shared_ptr const& callback) : state{mir::time::Alarm::pending}, callback{callback} { } FakeAlarm::FakeAlarm( std::shared_ptr const& callback, std::shared_ptr const& clock) : internal_state{std::make_shared(callback)}, clock{clock} { } FakeAlarm::~FakeAlarm() { cancel(); } bool FakeAlarm::cancel() { if (internal_state->state == triggered) return false; internal_state->state = cancelled; return true; } FakeAlarm::State FakeAlarm::state() const { return internal_state->state; } bool FakeAlarm::handle_time_change(InternalState& state, mir::test::FakeClock::time_point now) { if (state.state == pending) { if (now > state.threshold) { state.state = triggered; auto& handler = *state.callback; std::lock_guard guard{handler}; handler(); return false; } return true; } else { return false; } } bool FakeAlarm::reschedule_in(std::chrono::milliseconds delay) { bool rescheduled = internal_state->state == pending; internal_state->state = pending; internal_state->threshold = clock->now() + delay; std::shared_ptr state_copy{internal_state}; clock->register_time_change_callback([this, state_copy](mt::FakeClock::time_point now) { return handle_time_change(*state_copy, now); }); return rescheduled; } bool FakeAlarm::reschedule_for(mir::time::Timestamp timeout) { // time::Timestamp is on a different clock, so not directly comparable auto delay = std::chrono::duration_cast(timeout.time_since_epoch() - clock->now().time_since_epoch()); return reschedule_in(delay); } } mtd::FakeTimer::FakeTimer(std::shared_ptr const& clock) : clock{clock} { } std::unique_ptr mir::test::doubles::FakeTimer::create_alarm(std::function const& callback) { auto const handler = std::make_shared(callback); return std::unique_ptr{new FakeAlarm{handler, clock}}; } std::unique_ptr mir::test::doubles::FakeTimer::create_alarm(std::shared_ptr const& callback) { return std::unique_ptr{new FakeAlarm{callback, clock}}; } ./tests/mir_test_doubles/mock_libinput.cpp0000644000015600001650000007773312676616157021207 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/test/doubles/mock_libinput.h" namespace mtd = mir::test::doubles; using namespace testing; namespace { mtd::MockLibInput* global_libinput = nullptr; } mtd::MockLibInput::MockLibInput() { assert(global_libinput == NULL && "Only one mock object per process is allowed"); global_libinput = this; ON_CALL(*this, libinput_device_ref(_)).WillByDefault(ReturnArg<0>()); ON_CALL(*this, libinput_get_fd(_)).WillByDefault(Return(int(libinput_simulation_queue.watch_fd()))); ON_CALL(*this, libinput_get_event(_)) .WillByDefault(Invoke([this](libinput*) -> libinput_event* { if (events.empty()) return nullptr; auto ret = events.front(); events.erase(events.begin()); return ret; } )); ON_CALL(*this, libinput_device_config_left_handed_set(_, _)) .WillByDefault(Return(LIBINPUT_CONFIG_STATUS_SUCCESS)); ON_CALL(*this, libinput_device_config_accel_set_speed(_, _)) .WillByDefault(Return(LIBINPUT_CONFIG_STATUS_SUCCESS)); ON_CALL(*this, libinput_device_config_dwt_set_enabled(_, _)) .WillByDefault(Return(LIBINPUT_CONFIG_STATUS_SUCCESS)); ON_CALL(*this, libinput_device_config_scroll_set_button(_, _)) .WillByDefault(Return(LIBINPUT_CONFIG_STATUS_SUCCESS)); ON_CALL(*this, libinput_device_config_scroll_set_method(_, _)) .WillByDefault(Return(LIBINPUT_CONFIG_STATUS_SUCCESS)); ON_CALL(*this, libinput_device_config_click_set_method(_, _)) .WillByDefault(Return(LIBINPUT_CONFIG_STATUS_SUCCESS)); ON_CALL(*this, libinput_device_config_tap_set_enabled(_, _)) .WillByDefault(Return(LIBINPUT_CONFIG_STATUS_SUCCESS)); ON_CALL(*this, libinput_device_config_send_events_set_mode(_, _)) .WillByDefault(Return(LIBINPUT_CONFIG_STATUS_SUCCESS)); ON_CALL(*this, libinput_device_config_middle_emulation_set_enabled(_, _)) .WillByDefault(Return(LIBINPUT_CONFIG_STATUS_SUCCESS)); #if MIR_LIBINPUT_HAS_ACCEL_PROFILE ON_CALL(*this, libinput_device_config_accel_set_profile(_, _)) .WillByDefault(Return(LIBINPUT_CONFIG_STATUS_SUCCESS)); #endif } void mtd::MockLibInput::wake() { libinput_simulation_queue.enqueue([]{}); } void mtd::MockLibInput::push_back(libinput_event* event) { events.push_back(event); wake(); } void mtd::MockLibInput::setup_device(libinput_device* dev, libinput_device_group* group, udev_device* u_dev, char const* name, unsigned int vendor, unsigned int product) { ON_CALL(*this, libinput_device_get_name(dev)) .WillByDefault(Return(name)); ON_CALL(*this, libinput_device_get_sysname(dev)) .WillByDefault(Return(name)); ON_CALL(*this, libinput_device_get_device_group(dev)) .WillByDefault(Return(group)); ON_CALL(*this, libinput_device_get_id_product(dev)) .WillByDefault(Return(product)); ON_CALL(*this, libinput_device_ref(dev)) .WillByDefault(Return(dev)); ON_CALL(*this, libinput_device_get_id_vendor(dev)) .WillByDefault(Return(vendor)); ON_CALL(*this, libinput_device_unref(dev)) .WillByDefault(Return(nullptr)); ON_CALL(*this, libinput_device_get_udev_device(dev)) .WillByDefault(Return(u_dev)); } mtd::MockLibInput::~MockLibInput() noexcept { global_libinput = nullptr; } void libinput_event_destroy(libinput_event* event) { global_libinput->libinput_event_destroy(event); } libinput_event_type libinput_event_get_type(libinput_event* event) { return global_libinput->libinput_event_get_type(event); } libinput_device* libinput_event_get_device(libinput_event* event) { return global_libinput->libinput_event_get_device(event); } libinput_event_pointer* libinput_event_get_pointer_event(libinput_event* event) { return global_libinput->libinput_event_get_pointer_event(event); } libinput_event_keyboard* libinput_event_get_keyboard_event(libinput_event* event) { return global_libinput->libinput_event_get_keyboard_event(event); } libinput_event_touch* libinput_event_get_touch_event(libinput_event* event) { return global_libinput->libinput_event_get_touch_event(event); } uint32_t libinput_event_keyboard_get_time(libinput_event_keyboard* event) { return global_libinput->libinput_event_keyboard_get_time(event); } uint64_t libinput_event_keyboard_get_time_usec(libinput_event_keyboard* event) { return global_libinput->libinput_event_keyboard_get_time_usec(event); } uint32_t libinput_event_keyboard_get_key(libinput_event_keyboard* event) { return global_libinput->libinput_event_keyboard_get_key(event); } libinput_key_state libinput_event_keyboard_get_key_state(libinput_event_keyboard* event) { return global_libinput->libinput_event_keyboard_get_key_state(event); } uint32_t libinput_event_keyboard_get_seat_key_count(libinput_event_keyboard* event) { return global_libinput->libinput_event_keyboard_get_seat_key_count(event); } uint32_t libinput_event_pointer_get_time(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_time(event); } uint64_t libinput_event_pointer_get_time_usec(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_time_usec(event); } double libinput_event_pointer_get_dx(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_dx(event); } double libinput_event_pointer_get_dy(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_dy(event); } double libinput_event_pointer_get_absolute_x(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_absolute_x(event); } double libinput_event_pointer_get_absolute_y(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_absolute_y(event); } double libinput_event_pointer_get_absolute_x_transformed(libinput_event_pointer* event, uint32_t width) { return global_libinput->libinput_event_pointer_get_absolute_x_transformed(event, width); } double libinput_event_pointer_get_absolute_y_transformed(libinput_event_pointer* event, uint32_t height) { return global_libinput->libinput_event_pointer_get_absolute_y_transformed(event, height); } uint32_t libinput_event_pointer_get_button(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_button(event); } libinput_button_state libinput_event_pointer_get_button_state(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_button_state(event); } uint32_t libinput_event_pointer_get_seat_button_count(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_seat_button_count(event); } libinput_pointer_axis libinput_event_pointer_get_axis(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_axis(event); } libinput_pointer_axis_source libinput_event_pointer_get_axis_source(libinput_event_pointer* event) { return global_libinput->libinput_event_pointer_get_axis_source(event); } double libinput_event_pointer_get_axis_value(libinput_event_pointer* event, libinput_pointer_axis axis) { return global_libinput->libinput_event_pointer_get_axis_value(event, axis); } double libinput_event_pointer_get_axis_value_discrete(libinput_event_pointer* event, libinput_pointer_axis axis) { return global_libinput->libinput_event_pointer_get_axis_value_discrete(event, axis); } int libinput_event_pointer_has_axis(libinput_event_pointer* event, libinput_pointer_axis axis) { return global_libinput->libinput_event_pointer_has_axis(event, axis); } uint32_t libinput_event_touch_get_time(libinput_event_touch* event) { return global_libinput->libinput_event_touch_get_time(event); } uint64_t libinput_event_touch_get_time_usec(libinput_event_touch* event) { return global_libinput->libinput_event_touch_get_time_usec(event); } int32_t libinput_event_touch_get_slot(libinput_event_touch* event) { return global_libinput->libinput_event_touch_get_slot(event); } int32_t libinput_event_touch_get_seat_slot(libinput_event_touch* event) { return global_libinput->libinput_event_touch_get_seat_slot(event); } double libinput_event_touch_get_x(libinput_event_touch* event) { return global_libinput->libinput_event_touch_get_x(event); } double libinput_event_touch_get_y(libinput_event_touch* event) { return global_libinput->libinput_event_touch_get_y(event); } double libinput_event_touch_get_x_transformed(libinput_event_touch* event, uint32_t width) { return global_libinput->libinput_event_touch_get_x_transformed(event, width); } double libinput_event_touch_get_y_transformed(libinput_event_touch* event, uint32_t height) { return global_libinput->libinput_event_touch_get_y_transformed(event, height); } double libinput_event_touch_get_major(libinput_event_touch* event) { return global_libinput->libinput_event_touch_get_major(event); } double libinput_event_touch_get_minor(libinput_event_touch* event) { return global_libinput->libinput_event_touch_get_minor(event); } double libinput_event_touch_get_major_transformed(libinput_event_touch* event, uint32_t width, uint32_t height) { return global_libinput->libinput_event_touch_get_major_transformed(event, width, height); } double libinput_event_touch_get_minor_transformed(libinput_event_touch* event, uint32_t width, uint32_t height) { return global_libinput->libinput_event_touch_get_minor_transformed(event, width, height); } double libinput_event_touch_get_pressure(libinput_event_touch* event) { return global_libinput->libinput_event_touch_get_pressure(event); } double libinput_event_touch_get_orientation(libinput_event_touch* event) { return global_libinput->libinput_event_touch_get_orientation(event); } libinput* libinput_udev_create_context(const libinput_interface* interface, void* user_data, struct udev* udev) { return global_libinput->libinput_udev_create_context(interface, user_data, udev); } int libinput_udev_assign_seat(libinput* ctx, char const* seat) { return global_libinput->libinput_udev_assign_seat(ctx, seat); } libinput* libinput_path_create_context(const libinput_interface* interface, void* user_data) { return global_libinput->libinput_path_create_context(interface, user_data); } libinput_device* libinput_path_add_device(libinput* libinput, const char* path) { return global_libinput->libinput_path_add_device(libinput, path); } void libinput_path_remove_device(libinput_device* device) { return global_libinput->libinput_path_remove_device(device); } int libinput_get_fd(libinput* libinput) { return global_libinput->libinput_get_fd(libinput); } int libinput_dispatch(libinput* libinput) { return global_libinput->libinput_dispatch(libinput); } libinput_event* libinput_get_event(libinput* libinput) { return global_libinput->libinput_get_event(libinput); } libinput* libinput_ref(libinput* libinput) { return global_libinput->libinput_ref(libinput); } libinput* libinput_unref(libinput* libinput) { return global_libinput->libinput_unref(libinput); } libinput_device* libinput_device_ref(libinput_device* device) { return global_libinput->libinput_device_ref(device); } libinput_device* libinput_device_unref(libinput_device* device) { return global_libinput->libinput_device_unref(device); } char const* libinput_device_get_name(libinput_device* device) { return global_libinput->libinput_device_get_name(device); } udev_device* libinput_device_get_udev_device(libinput_device* device) { return global_libinput->libinput_device_get_udev_device(device); } unsigned int libinput_device_get_id_vendor(libinput_device* device) { return global_libinput->libinput_device_get_id_vendor(device); } unsigned int libinput_device_get_id_product(libinput_device* device) { return global_libinput->libinput_device_get_id_product(device); } char const* libinput_device_get_sysname(libinput_device* device) { return global_libinput->libinput_device_get_sysname(device); } libinput_device_group* libinput_device_get_device_group(libinput_device* device) { return global_libinput->libinput_device_get_device_group(device); } int libinput_device_config_tap_get_finger_count(libinput_device *device) { return global_libinput->libinput_device_config_tap_get_finger_count(device); } libinput_config_status libinput_device_config_tap_set_enabled(libinput_device *device, libinput_config_tap_state enable) { return global_libinput->libinput_device_config_tap_set_enabled(device, enable); } libinput_config_tap_state libinput_device_config_tap_get_enabled(libinput_device *device) { return global_libinput->libinput_device_config_tap_get_enabled(device); } libinput_config_tap_state libinput_device_config_tap_get_default_enabled(libinput_device *device) { return global_libinput->libinput_device_config_tap_get_default_enabled(device); } libinput_config_status libinput_device_config_tap_set_drag_lock_enabled(libinput_device *device, libinput_config_drag_lock_state enable) { return global_libinput->libinput_device_config_tap_set_drag_lock_enabled(device, enable); } libinput_config_drag_lock_state libinput_device_config_tap_get_drag_lock_enabled(libinput_device *device) { return global_libinput->libinput_device_config_tap_get_drag_lock_enabled(device); } libinput_config_drag_lock_state libinput_device_config_tap_get_default_drag_lock_enabled(libinput_device *device) { return global_libinput->libinput_device_config_tap_get_default_drag_lock_enabled(device); } int libinput_device_config_calibration_has_matrix(libinput_device *device) { return global_libinput->libinput_device_config_calibration_has_matrix(device); } libinput_config_status libinput_device_config_calibration_set_matrix(libinput_device *device, const float matrix[6]) { return global_libinput->libinput_device_config_calibration_set_matrix(device, matrix); } int libinput_device_config_calibration_get_matrix(libinput_device *device, float matrix[6]) { return global_libinput->libinput_device_config_calibration_get_matrix(device, matrix); } int libinput_device_config_calibration_get_default_matrix(libinput_device *device, float matrix[6]) { return global_libinput->libinput_device_config_calibration_get_default_matrix(device, matrix); } uint32_t libinput_device_config_send_events_get_modes(libinput_device *device) { return global_libinput->libinput_device_config_send_events_get_modes(device); } libinput_config_status libinput_device_config_send_events_set_mode(libinput_device *device, uint32_t mode) { return global_libinput->libinput_device_config_send_events_set_mode(device, mode); } uint32_t libinput_device_config_send_events_get_mode(libinput_device *device) { return global_libinput->libinput_device_config_send_events_get_mode(device); } uint32_t libinput_device_config_send_events_get_default_mode(libinput_device *device) { return global_libinput->libinput_device_config_send_events_get_default_mode(device); } int libinput_device_config_accel_is_available(libinput_device *device) { return global_libinput->libinput_device_config_accel_is_available(device); } libinput_config_status libinput_device_config_accel_set_speed(libinput_device *device, double speed) { return global_libinput->libinput_device_config_accel_set_speed(device, speed); } double libinput_device_config_accel_get_speed(libinput_device *device) { return global_libinput->libinput_device_config_accel_get_speed(device); } double libinput_device_config_accel_get_default_speed(libinput_device *device) { return global_libinput->libinput_device_config_accel_get_default_speed(device); } #if MIR_LIBINPUT_HAS_ACCEL_PROFILE libinput_config_status libinput_device_config_accel_set_profile(libinput_device* dev, libinput_config_accel_profile profile) { return global_libinput->libinput_device_config_accel_set_profile(dev, profile); } libinput_config_accel_profile libinput_device_config_accel_get_profile(libinput_device* dev) { return global_libinput->libinput_device_config_accel_get_profile(dev); } #endif int libinput_device_config_scroll_has_natural_scroll(libinput_device *device) { return global_libinput->libinput_device_config_scroll_has_natural_scroll(device); } libinput_config_status libinput_device_config_scroll_set_natural_scroll_enabled(libinput_device *device, int enable) { return global_libinput->libinput_device_config_scroll_set_natural_scroll_enabled(device, enable); } int libinput_device_config_scroll_get_natural_scroll_enabled(libinput_device *device) { return global_libinput->libinput_device_config_scroll_get_natural_scroll_enabled(device); } int libinput_device_config_scroll_get_default_natural_scroll_enabled(libinput_device *device) { return global_libinput->libinput_device_config_scroll_get_default_natural_scroll_enabled(device); } int libinput_device_config_left_handed_is_available(libinput_device *device) { return global_libinput->libinput_device_config_left_handed_is_available(device); } libinput_config_status libinput_device_config_left_handed_set(libinput_device *device, int left_handed) { return global_libinput->libinput_device_config_left_handed_set(device, left_handed); } int libinput_device_config_left_handed_get(libinput_device *device) { return global_libinput->libinput_device_config_left_handed_get(device); } int libinput_device_config_left_handed_get_default(libinput_device *device) { return global_libinput->libinput_device_config_left_handed_get_default(device); } uint32_t libinput_device_config_click_get_methods(libinput_device *device) { return global_libinput->libinput_device_config_click_get_methods(device); } libinput_config_status libinput_device_config_click_set_method(libinput_device *device, libinput_config_click_method method) { return global_libinput->libinput_device_config_click_set_method(device, method); } libinput_config_click_method libinput_device_config_click_get_method(libinput_device *device) { return global_libinput->libinput_device_config_click_get_method(device); } libinput_config_click_method libinput_device_config_click_get_default_method(libinput_device *device) { return global_libinput->libinput_device_config_click_get_default_method(device); } int libinput_device_config_middle_emulation_is_available(libinput_device *device) { return global_libinput->libinput_device_config_middle_emulation_is_available(device); } libinput_config_status libinput_device_config_middle_emulation_set_enabled(libinput_device *device, libinput_config_middle_emulation_state enable) { return global_libinput->libinput_device_config_middle_emulation_set_enabled(device, enable); } libinput_config_middle_emulation_state libinput_device_config_middle_emulation_get_enabled(libinput_device *device) { return global_libinput->libinput_device_config_middle_emulation_get_enabled(device); } libinput_config_middle_emulation_state libinput_device_config_middle_emulation_get_default_enabled(libinput_device *device) { return global_libinput->libinput_device_config_middle_emulation_get_default_enabled(device); } uint32_t libinput_device_config_scroll_get_methods(libinput_device *device) { return global_libinput->libinput_device_config_scroll_get_methods(device); } libinput_config_status libinput_device_config_scroll_set_method(libinput_device *device, libinput_config_scroll_method method) { return global_libinput->libinput_device_config_scroll_set_method(device, method); } libinput_config_scroll_method libinput_device_config_scroll_get_method(libinput_device *device) { return global_libinput->libinput_device_config_scroll_get_method(device); } libinput_config_scroll_method libinput_device_config_scroll_get_default_method(libinput_device *device) { return global_libinput->libinput_device_config_scroll_get_default_method(device); } libinput_config_status libinput_device_config_scroll_set_button(libinput_device *device, uint32_t button) { return global_libinput->libinput_device_config_scroll_set_button(device, button); } uint32_t libinput_device_config_scroll_get_button(libinput_device *device) { return global_libinput->libinput_device_config_scroll_get_button(device); } uint32_t libinput_device_config_scroll_get_default_button(libinput_device *device) { return global_libinput->libinput_device_config_scroll_get_default_button(device); } int libinput_device_config_dwt_is_available(libinput_device *device) { return global_libinput->libinput_device_config_dwt_is_available(device); } libinput_config_status libinput_device_config_dwt_set_enabled(libinput_device *device, libinput_config_dwt_state enable) { return global_libinput->libinput_device_config_dwt_set_enabled(device, enable); } libinput_config_dwt_state libinput_device_config_dwt_get_enabled(libinput_device *device) { return global_libinput->libinput_device_config_dwt_get_enabled(device); } libinput_config_dwt_state libinput_device_config_dwt_get_default_enabled(libinput_device *device) { return global_libinput->libinput_device_config_dwt_get_default_enabled(device); } libinput_event* mtd::MockLibInput::setup_touch_event(libinput_device* dev, libinput_event_type type, uint64_t event_time, int slot, float x, float y, float major, float minor, float pressure) { auto event = get_next_fake_ptr(); auto touch_event = reinterpret_cast(event); push_back(event); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(type)); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); ON_CALL(*this, libinput_event_get_touch_event(event)) .WillByDefault(Return(touch_event)); ON_CALL(*this, libinput_event_touch_get_slot(touch_event)) .WillByDefault(Return(slot)); ON_CALL(*this, libinput_event_touch_get_x_transformed(touch_event, _)) .WillByDefault(Return(x)); ON_CALL(*this, libinput_event_touch_get_y_transformed(touch_event, _)) .WillByDefault(Return(y)); ON_CALL(*this, libinput_event_touch_get_time_usec(touch_event)) .WillByDefault(Return(event_time)); ON_CALL(*this, libinput_event_touch_get_major_transformed(touch_event, _, _)) .WillByDefault(Return(major)); ON_CALL(*this, libinput_event_touch_get_minor_transformed(touch_event, _, _)) .WillByDefault(Return(minor)); ON_CALL(*this, libinput_event_touch_get_pressure(touch_event)) .WillByDefault(Return(pressure)); return event; } libinput_event* mtd::MockLibInput::setup_touch_frame(libinput_device *dev, uint64_t event_time) { auto event = get_next_fake_ptr(); auto touch_event = reinterpret_cast(event); push_back(event); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(LIBINPUT_EVENT_TOUCH_FRAME)); ON_CALL(*this, libinput_event_get_touch_event(event)) .WillByDefault(Return(touch_event)); ON_CALL(*this, libinput_event_touch_get_time_usec(touch_event)) .WillByDefault(Return(event_time)); return event; } libinput_event* mtd::MockLibInput::setup_touch_up_event(libinput_device* dev, uint64_t event_time, int slot) { auto event = get_next_fake_ptr(); auto touch_event = reinterpret_cast(event); push_back(event); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(LIBINPUT_EVENT_TOUCH_UP)); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); ON_CALL(*this, libinput_event_get_touch_event(event)) .WillByDefault(Return(touch_event)); ON_CALL(*this, libinput_event_touch_get_slot(touch_event)) .WillByDefault(Return(slot)); ON_CALL(*this, libinput_event_touch_get_time_usec(touch_event)) .WillByDefault(Return(event_time)); return event; } libinput_event* mtd::MockLibInput::setup_key_event(libinput_device* dev, uint64_t event_time, uint32_t key, libinput_key_state state) { auto event = get_next_fake_ptr(); auto key_event = reinterpret_cast(event); push_back(event); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(LIBINPUT_EVENT_KEYBOARD_KEY)); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); ON_CALL(*this, libinput_event_get_keyboard_event(event)) .WillByDefault(Return(key_event)); ON_CALL(*this, libinput_event_keyboard_get_time_usec(key_event)) .WillByDefault(Return(event_time)); ON_CALL(*this, libinput_event_keyboard_get_key(key_event)) .WillByDefault(Return(key)); ON_CALL(*this, libinput_event_keyboard_get_key_state(key_event)) .WillByDefault(Return(state)); return event; } libinput_event* mtd::MockLibInput::setup_pointer_event(libinput_device* dev, uint64_t event_time, float relatve_x, float relatve_y) { auto event = get_next_fake_ptr(); auto pointer_event = reinterpret_cast(event); push_back(event); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(LIBINPUT_EVENT_POINTER_MOTION)); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); ON_CALL(*this, libinput_event_get_pointer_event(event)) .WillByDefault(Return(pointer_event)); ON_CALL(*this, libinput_event_pointer_get_time_usec(pointer_event)) .WillByDefault(Return(event_time)); ON_CALL(*this, libinput_event_pointer_get_dx(pointer_event)) .WillByDefault(Return(relatve_x)); ON_CALL(*this, libinput_event_pointer_get_dy(pointer_event)) .WillByDefault(Return(relatve_y)); return event; } libinput_event* mtd::MockLibInput::setup_absolute_pointer_event(libinput_device* dev, uint64_t event_time, float x, float y) { auto event = get_next_fake_ptr(); auto pointer_event = reinterpret_cast(event); push_back(event); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE)); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); ON_CALL(*this, libinput_event_get_pointer_event(event)) .WillByDefault(Return(pointer_event)); ON_CALL(*this, libinput_event_pointer_get_time_usec(pointer_event)) .WillByDefault(Return(event_time)); ON_CALL(*this, libinput_event_pointer_get_absolute_x_transformed(pointer_event, _)) .WillByDefault(Return(x)); ON_CALL(*this, libinput_event_pointer_get_absolute_y_transformed(pointer_event, _)) .WillByDefault(Return(y)); return event; } libinput_event* mtd::MockLibInput::setup_button_event(libinput_device* dev, uint64_t event_time, int button, libinput_button_state state) { auto event = get_next_fake_ptr(); auto pointer_event = reinterpret_cast(event); push_back(event); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(LIBINPUT_EVENT_POINTER_BUTTON)); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); ON_CALL(*this, libinput_event_get_pointer_event(event)) .WillByDefault(Return(pointer_event)); ON_CALL(*this, libinput_event_pointer_get_time_usec(pointer_event)) .WillByDefault(Return(event_time)); ON_CALL(*this, libinput_event_pointer_get_button(pointer_event)) .WillByDefault(Return(button)); ON_CALL(*this, libinput_event_pointer_get_button_state(pointer_event)) .WillByDefault(Return(state)); return event; } libinput_event* mtd::MockLibInput::setup_axis_event(libinput_device* dev, uint64_t event_time, double horizontal, double vertical) { auto event = get_next_fake_ptr(); auto pointer_event = reinterpret_cast(event); push_back(event); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(LIBINPUT_EVENT_POINTER_AXIS)); ON_CALL(*this, libinput_event_get_pointer_event(event)) .WillByDefault(Return(pointer_event)); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); ON_CALL(*this, libinput_event_pointer_get_time_usec(pointer_event)) .WillByDefault(Return(event_time)); ON_CALL(*this, libinput_event_pointer_get_axis_source(pointer_event)) .WillByDefault(Return(LIBINPUT_POINTER_AXIS_SOURCE_WHEEL)); ON_CALL(*this, libinput_event_pointer_has_axis(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) .WillByDefault(Return(horizontal!=0.0)); ON_CALL(*this, libinput_event_pointer_has_axis(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) .WillByDefault(Return(vertical!=0.0)); ON_CALL(*this, libinput_event_pointer_get_axis_value_discrete(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) .WillByDefault(Return(vertical)); ON_CALL(*this, libinput_event_pointer_get_axis_value_discrete(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) .WillByDefault(Return(horizontal)); return event; } libinput_event* mtd::MockLibInput::setup_finger_axis_event(libinput_device* dev, uint64_t event_time, double horizontal, double vertical) { auto event = get_next_fake_ptr(); auto pointer_event = reinterpret_cast(event); push_back(event); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(LIBINPUT_EVENT_POINTER_AXIS)); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); ON_CALL(*this, libinput_event_get_pointer_event(event)) .WillByDefault(Return(pointer_event)); ON_CALL(*this, libinput_event_pointer_get_time_usec(pointer_event)) .WillByDefault(Return(event_time)); ON_CALL(*this, libinput_event_pointer_get_axis_source(pointer_event)) .WillByDefault(Return(LIBINPUT_POINTER_AXIS_SOURCE_FINGER)); ON_CALL(*this, libinput_event_pointer_has_axis(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) .WillByDefault(Return(horizontal!=0.0)); ON_CALL(*this, libinput_event_pointer_has_axis(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) .WillByDefault(Return(vertical!=0.0)); ON_CALL(*this, libinput_event_pointer_get_axis_value(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) .WillByDefault(Return(vertical)); ON_CALL(*this, libinput_event_pointer_get_axis_value(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) .WillByDefault(Return(horizontal)); return event; } libinput_event* mtd::MockLibInput::setup_device_add_event(libinput_device* dev) { auto event = get_next_fake_ptr(); push_back(event); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(LIBINPUT_EVENT_DEVICE_ADDED)); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); return event; } libinput_event* mtd::MockLibInput::setup_device_remove_event(libinput_device* dev) { auto event = get_next_fake_ptr(); push_back(event); ON_CALL(*this, libinput_event_get_type(event)) .WillByDefault(Return(LIBINPUT_EVENT_DEVICE_REMOVED)); ON_CALL(*this, libinput_event_get_device(event)) .WillByDefault(Return(dev)); return event; } ./tests/mir_test_doubles/mock_frame_dropping_policy_factory.cpp0000644000015600001650000000435112676616125025440 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Alberto Aguirre */ #include "mir/test/doubles/mock_frame_dropping_policy_factory.h" #include "mir/lockable_callback.h" #include namespace mc = mir::compositor; namespace mtd = mir::test::doubles; mtd::MockFrameDroppingPolicy::MockFrameDroppingPolicy( std::shared_ptr const& callback, MockFrameDroppingPolicyFactory const* parent) : callback{callback}, parent{parent} { } mtd::MockFrameDroppingPolicy::~MockFrameDroppingPolicy() { if (parent) parent->policy_destroyed(this); } void mtd::MockFrameDroppingPolicy::trigger() { auto& handler = *callback; std::lock_guard lock{handler}; handler(); } void mtd::MockFrameDroppingPolicy::parent_destroyed() { parent = nullptr; } std::unique_ptr mtd::MockFrameDroppingPolicyFactory::create_policy(std::shared_ptr const& callback) const { auto policy = new ::testing::NiceMock{callback, this}; policies.insert(policy); return std::unique_ptr{policy}; } mtd::MockFrameDroppingPolicyFactory::~MockFrameDroppingPolicyFactory() { for (auto policy : policies) policy->parent_destroyed(); } void mtd::MockFrameDroppingPolicyFactory::trigger_policies() const { for (auto policy : policies) policy->trigger(); } void mtd::MockFrameDroppingPolicyFactory::policy_destroyed(MockFrameDroppingPolicy* policy) const { policies.erase(policy); } ./tests/mir_test_doubles/nested_mock_egl.cpp0000644000015600001650000000467112676616125021454 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/test/doubles/nested_mock_egl.h" namespace mtd = mir::test::doubles; using namespace ::testing; mtd::NestedMockEGL::NestedMockEGL() { { InSequence init_before_terminate; EXPECT_CALL(*this, eglGetDisplay(_)).Times(1); EXPECT_CALL(*this, eglTerminate(_)).Times(1); } EXPECT_CALL(*this, eglCreateWindowSurface(_, _, _, _)).Times(AnyNumber()); EXPECT_CALL(*this, eglMakeCurrent(_, _, _, _)).Times(AnyNumber()); EXPECT_CALL(*this, eglDestroySurface(_, _)).Times(AnyNumber()); EXPECT_CALL(*this, eglQueryString(_, _)).Times(AnyNumber()); provide_egl_extensions(); provide_stub_platform_buffer_swapping(); EXPECT_CALL(*this, eglChooseConfig(_, _, _, _, _)).Times(AnyNumber()).WillRepeatedly( DoAll(WithArgs<2, 4>(Invoke(this, &NestedMockEGL::egl_choose_config)), Return(EGL_TRUE))); EXPECT_CALL(*this, eglGetCurrentContext()).Times(AnyNumber()); EXPECT_CALL(*this, eglCreatePbufferSurface(_, _, _)).Times(AnyNumber()); EXPECT_CALL(*this, eglGetProcAddress(StrEq("eglCreateImageKHR"))).Times(AnyNumber()); EXPECT_CALL(*this, eglGetProcAddress(StrEq("eglDestroyImageKHR"))).Times(AnyNumber()); EXPECT_CALL(*this, eglGetProcAddress(StrEq("glEGLImageTargetTexture2DOES"))).Times(AnyNumber()); { InSequence context_lifecycle; EXPECT_CALL(*this, eglCreateContext(_, _, _, _)).Times(AnyNumber()).WillRepeatedly(Return((EGLContext)this)); EXPECT_CALL(*this, eglDestroyContext(_, _)).Times(AnyNumber()).WillRepeatedly(Return(EGL_TRUE)); } } void mtd::NestedMockEGL::egl_initialize(EGLint* major, EGLint* minor) { *major = 1; *minor = 4; } void mtd::NestedMockEGL::egl_choose_config(EGLConfig* config, EGLint* num_config) { *config = this; *num_config = 1; } ./tests/mir_test_doubles/CMakeLists.txt0000644000015600001650000000612312676616157020365 0ustar jenkinsjenkinsinclude_directories( ${CMAKE_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/src/include/platform ${PROJECT_SOURCE_DIR}/src/include/common ${PROJECT_SOURCE_DIR}/src/include/server ${PROJECT_SOURCE_DIR}/src/include/client ${PROJECT_SOURCE_DIR}/src/include/cookie ${Boost_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ) set( TEST_UTILS_SRCS mock_input_device.cpp mock_frame_dropping_policy_factory.cpp mock_timer.cpp stub_buffer.cpp test_protobuf_client.cpp test_protobuf_socket_server.cpp triggered_main_loop.cpp fake_alarm_factory.cpp ${PROJECT_SOURCE_DIR}/tests/include/mir/test/doubles/null_message_sender.h ${PROJECT_SOURCE_DIR}/tests/include/mir/test/doubles/mock_message_sender.h ${PROJECT_SOURCE_DIR}/tests/include/mir/test/doubles/null_event_sink_factory.h null_event_sink_factory.cpp mock_event_sink_factory.cpp ${PROJECT_SOURCE_DIR}/tests/include/mir/test/doubles/mock_event_sink_factory.h ) set(MIR_TEST_DOUBLES_UDEV_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/mock_udev.cpp) set( MIR_TEST_DOUBLES_PLATFORM_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/mock_libinput.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mock_egl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mock_gl.cpp ) if (MIR_BUILD_PLATFORM_MESA_X11) include_directories( ${PROJECT_SOURCE_DIR}/src/platforms/mesa/server/common ${DRM_INCLUDE_DIRS} ) list(APPEND MIR_TEST_DOUBLES_PLATFORM_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/mock_drm.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mock_gbm.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mock_x11.cpp ) endif() if (MIR_BUILD_PLATFORM_MESA_KMS) include_directories( ${PROJECT_SOURCE_DIR}/src/platforms/mesa/server/common ${DRM_INCLUDE_DIRS} ${GBM_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ) list(APPEND MIR_TEST_DOUBLES_PLATFORM_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/mock_drm.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mock_gbm.cpp ) endif() if (MIR_BUILD_PLATFORM_ANDROID) include_directories(SYSTEM ${ANDROID_HEADERS_INCLUDE_DIRS}) list(APPEND MIR_TEST_DOUBLES_PLATFORM_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/mock_android_hw.cpp ) endif() add_library(mir-public-test-doubles OBJECT nested_mock_egl.cpp null_logger.cpp stub_display_configuration.cpp ${CMAKE_SOURCE_DIR}/include/test/mir/test/doubles/stub_display_configuration.h fake_display.cpp ${CMAKE_SOURCE_DIR}/include/test/mir/test/doubles/fake_display.h ) add_library(mir-test-doubles-static STATIC $ ${TEST_UTILS_SRCS} ) add_dependencies(mir-test-doubles-static GMock) uses_android_input(mir-public-test-doubles) target_link_libraries(mir-test-doubles-static mirserver ${Boost_LIBRARIES} ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} # Link in pthread. ) add_library(mir-public-test-doubles-platform OBJECT ${MIR_TEST_DOUBLES_PLATFORM_SRCS} ) add_library( mir-test-doubles-platform-static STATIC $ ) target_link_libraries( mir-test-doubles-platform-static -ldl ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} # Link in pthread. ) add_library(mir-test-doubles-udev OBJECT ${MIR_TEST_DOUBLES_UDEV_SRCS} ) ./tests/mir_test_doubles/fake_display.cpp0000644000015600001650000000601012676616125020752 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/test/doubles/fake_display.h" #include "mir/test/doubles/stub_display_configuration.h" #include "mir/graphics/event_handler_register.h" #include namespace mtd = mir::test::doubles; mtd::FakeDisplay::FakeDisplay() : config{std::make_shared()}, handler_called{false} { } mtd::FakeDisplay::FakeDisplay(std::vector const& output_rects) : config{std::make_shared(output_rects)}, handler_called{false} { for (auto const& rect : output_rects) groups.emplace_back(new StubDisplaySyncGroup({rect})); } void mtd::FakeDisplay::for_each_display_sync_group(std::function const& f) { for (auto& group : groups) f(*group); } std::unique_ptr mtd::FakeDisplay::configuration() const { return std::unique_ptr(new StubDisplayConfig(*config)); } void mtd::FakeDisplay::register_configuration_change_handler( mir::graphics::EventHandlerRegister& handlers, mir::graphics::DisplayConfigurationChangeHandler const& handler) { handlers.register_fd_handler( {p.read_fd()}, this, [this, handler](int fd) { char c; if (read(fd, &c, 1) == 1) { handler(); handler_called = true; } }); } void mtd::FakeDisplay::configure(mir::graphics::DisplayConfiguration const& new_config) { decltype(config) new_configuration = std::make_shared(new_config); decltype(groups) new_groups; new_configuration->for_each_output([&](mir::graphics::DisplayConfigurationOutput const& output) { new_groups.emplace_back(new StubDisplaySyncGroup({output.extents()})); }); swap(config, new_configuration); swap(groups, new_groups); } void mtd::FakeDisplay::emit_configuration_change_event( std::shared_ptr const& new_config) { handler_called = false; config = std::make_shared(*new_config); if (write(p.write_fd(), "a", 1)) {} } void mtd::FakeDisplay::wait_for_configuration_change_handler() { while (!handler_called) std::this_thread::sleep_for(std::chrono::milliseconds{1}); } ./tests/mir_test_doubles/mock_udev.cpp0000644000015600001650000000264712676616157020314 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/test/doubles/mock_udev.h" namespace mtd = mir::test::doubles; namespace { mtd::MockUdev* global_udev = nullptr; } mtd::MockUdev::MockUdev() { using namespace testing; assert(global_udev == nullptr && "Only one udev mock object per process is allowed"); global_udev = this; } mtd::MockUdev::~MockUdev() noexcept { global_udev = nullptr; } char const* udev_device_get_devnode(udev_device* dev) { return global_udev->udev_device_get_devnode(dev); } char const* udev_device_get_property_value(udev_device* dev, char const* property) { return global_udev->udev_device_get_property_value(dev, property); } udev_device* udev_device_unref(udev_device* device) { return global_udev->udev_device_unref(device); } ./tests/mir_test_doubles/mock_android_hw.cpp0000644000015600001650000000632712676616125021461 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/test/doubles/mock_android_hw.h" #include "mir/test/doubles/mock_hwc_composer_device_1.h" #include #include #include #include namespace mt = mir::test; namespace mtd = mir::test::doubles; namespace { mtd::HardwareAccessMock* global_mock_android_hw = NULL; std::atomic open_count; } mtd::HardwareModuleStub::HardwareModuleStub(hw_device_t& device) : mock_hw_device(device) { gr_methods.open = hw_open; methods = &gr_methods; } int mtd::HardwareModuleStub::hw_open(const struct hw_module_t* module, const char*, struct hw_device_t** device) { auto self = static_cast(module); self->mock_hw_device.close = hw_close; *device = static_cast(&self->mock_hw_device); open_count++; return 0; } int mtd::HardwareModuleStub::hw_close(struct hw_device_t*) { open_count--; return 0; } mtd::FailingHardwareModuleStub::FailingHardwareModuleStub() { gr_methods.open = hw_open; methods = &gr_methods; } int mtd::FailingHardwareModuleStub::hw_open(const struct hw_module_t*, const char*, struct hw_device_t**) { return -1; } int mtd::FailingHardwareModuleStub::hw_close(struct hw_device_t*) { return 0; } mtd::HardwareAccessMock::HardwareAccessMock() { using namespace testing; assert(global_mock_android_hw == NULL && "Only one mock object per process is allowed"); global_mock_android_hw = this; mock_alloc_device = std::make_shared>(); mock_gralloc_module = std::make_shared(mock_alloc_device->common); mock_hwc_device = std::make_shared>(); mock_hwc_module = std::make_shared(mock_hwc_device->common); ON_CALL(*this, hw_get_module(StrEq(GRALLOC_HARDWARE_MODULE_ID),_)) .WillByDefault(DoAll(SetArgPointee<1>(mock_gralloc_module.get()), Return(0))); ON_CALL(*this, hw_get_module(StrEq(HWC_HARDWARE_MODULE_ID),_)) .WillByDefault(DoAll(SetArgPointee<1>(mock_hwc_module.get()), Return(0))); open_count.store(0); } bool mtd::HardwareAccessMock::open_count_matches_close() { return (open_count == 0); } mtd::HardwareAccessMock::~HardwareAccessMock() { global_mock_android_hw = NULL; } int hw_get_module(const char *id, const struct hw_module_t **module) { if (!global_mock_android_hw) { ADD_FAILURE_AT(__FILE__,__LINE__); \ return -1; } int rc = global_mock_android_hw->hw_get_module(id, module); return rc; } ./tests/mir_test_doubles/stub_display_configuration.cpp0000644000015600001650000002055112676616157023763 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/test/doubles/stub_display_configuration.h" #include #include namespace mtd = mir::test::doubles; mtd::StubDisplayConfigurationOutput::StubDisplayConfigurationOutput( geometry::Size px_size, geometry::Size mm_size, MirPixelFormat format, double vrefresh, bool connected) : StubDisplayConfigurationOutput(graphics::DisplayConfigurationOutputId{1}, px_size, mm_size, format, vrefresh, connected) { } mtd::StubDisplayConfigurationOutput::StubDisplayConfigurationOutput( graphics::DisplayConfigurationOutputId id, geometry::Size px_size, geometry::Size mm_size, MirPixelFormat format, double vrefresh, bool connected) : DisplayConfigurationOutput{ id, graphics::DisplayConfigurationCardId{0}, graphics::DisplayConfigurationOutputType::lvds, {format}, {{px_size, vrefresh}}, 0, mm_size, connected, connected, {0,0}, 0, format, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor } { } mtd::StubDisplayConfigurationOutput::StubDisplayConfigurationOutput( graphics::DisplayConfigurationOutputId id, std::vector modes, std::vector formats) : DisplayConfigurationOutput{ id, graphics::DisplayConfigurationCardId{0}, graphics::DisplayConfigurationOutputType::edp, formats, modes, static_cast(modes.size() - 1), {200, 200}, true, true, {0, 0}, 0, formats[0], mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor } { if (modes.empty()) { BOOST_THROW_EXCEPTION(std::logic_error{"Attempted to create a stub output with no modes"}); } } mtd::StubDisplayConfig::StubDisplayConfig() : StubDisplayConfig(3) { } mtd::StubDisplayConfig::StubDisplayConfig(StubDisplayConfig const& other) : graphics::DisplayConfiguration(), cards(other.cards), outputs(other.outputs) { } mtd::StubDisplayConfig::StubDisplayConfig(graphics::DisplayConfiguration const& other) : graphics::DisplayConfiguration() { other.for_each_card([this](auto card) { cards.push_back(card); }); other.for_each_output( [this](graphics::DisplayConfigurationOutput const& output) { outputs.push_back(output); }); } mtd::StubDisplayConfig::StubDisplayConfig(unsigned int num_displays) : StubDisplayConfig(num_displays, { mir_pixel_format_bgr_888, mir_pixel_format_abgr_8888, mir_pixel_format_xbgr_8888 }) { } mtd::StubDisplayConfig::StubDisplayConfig(std::vector> const& connected_used) : StubDisplayConfig(connected_used.size()) { for (auto i = 0u; i < outputs.size(); ++i) { outputs[i].connected = connected_used[i].first; outputs[i].used = connected_used[i].second; outputs[i].current_format = mir_pixel_format_abgr_8888; outputs[i].id = graphics::DisplayConfigurationOutputId{static_cast(i+1)}; } } mtd::StubDisplayConfig::StubDisplayConfig(unsigned int num_displays, std::vector const& pfs) { /* construct a non-trivial dummy display config to send */ int mode_index = 1; for (auto i = 0u; i < num_displays; i++) { std::vector modes; // Every second output, starting with the first, is connected... // (Android tests assume the first output in a configuration is connected) auto const connected = [](int index) { return (index % 2) == 0; }; // ..and every second connected output is used... auto const used = [](int index) { return (index % 4) == 0; }; for (auto j = 0u; j <= i; j++) { geometry::Size sz{mode_index*4, mode_index*3}; graphics::DisplayConfigurationMode mode{sz, 10.0f * mode_index }; mode_index++; modes.push_back(mode); } uint32_t current_mode_index; uint32_t preferred_mode_index; if (connected(i)) { current_mode_index = modes.size() - 1; preferred_mode_index = i; } else { current_mode_index = std::numeric_limits::max(); preferred_mode_index = std::numeric_limits::max(); } geometry::Size physical_size{}; geometry::Point top_left{}; graphics::DisplayConfigurationOutput output{ graphics::DisplayConfigurationOutputId{static_cast(i + 1)}, graphics::DisplayConfigurationCardId{1}, graphics::DisplayConfigurationOutputType::vga, pfs, connected(i) ? modes : std::vector{}, preferred_mode_index, physical_size, connected(i), used(i), top_left, current_mode_index, pfs[0], used(i) ? mir_power_mode_on : mir_power_mode_off, mir_orientation_normal, 1.0f, mir_form_factor_monitor }; outputs.push_back(output); } graphics::DisplayConfigurationCard card{ graphics::DisplayConfigurationCardId{1}, 5 }; cards.push_back(card); } mtd::StubDisplayConfig::StubDisplayConfig(std::vector const& rects) { int id = 1; for (auto const& rect : rects) { graphics::DisplayConfigurationOutput output { graphics::DisplayConfigurationOutputId{id}, graphics::DisplayConfigurationCardId{1}, graphics::DisplayConfigurationOutputType::vga, std::vector{mir_pixel_format_abgr_8888}, {{rect.size, 60.0}}, 0, geometry::Size{}, true, true, rect.top_left, 0, mir_pixel_format_abgr_8888, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor }; outputs.push_back(output); ++id; } graphics::DisplayConfigurationCard card{ graphics::DisplayConfigurationCardId{static_cast(1)}, rects.size() }; cards.push_back(card); } mtd::StubDisplayConfig::StubDisplayConfig(std::vector const& outputs) { graphics::DisplayConfigurationCard card{ graphics::DisplayConfigurationCardId{static_cast(1)}, outputs.size() }; cards.push_back(card); this->outputs = outputs; for (auto& output : this->outputs) { output.card_id = cards[0].id; } } mtd::StubDisplayConfig::StubDisplayConfig( std::vector const& cards, std::vector const& outputs) : cards(cards), outputs(outputs) { } void mtd::StubDisplayConfig::for_each_card(std::function f) const { for (auto const& card : cards) f(card); } void mtd::StubDisplayConfig::for_each_output(std::function f) const { for (auto& disp : outputs) { f(disp); } } void mtd::StubDisplayConfig::for_each_output(std::function f) { for (auto& disp : outputs) { graphics::UserDisplayConfigurationOutput user(disp); f(user); } } std::unique_ptr mtd::StubDisplayConfig::clone() const { return std::make_unique(*this); } ./tests/mir_test_doubles/null_logger.cpp0000644000015600001650000000171612676616125020640 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/test/doubles/null_logger.h" namespace mtd = mir::test::doubles; char const* const mtd::logging_opt = "logging"; char const* const mtd::logging_descr = "output log during tests"; void mtd::NullLogger::log(mir::logging::Severity, const std::string&, const std::string&) { } ./tests/mir_test_doubles/mock_input_device.cpp0000644000015600001650000000335112676616125022013 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/test/doubles/mock_input_device.h" #include "mir/input/device_capability.h" #include "mir/input/pointer_settings.h" #include "mir/input/touchpad_settings.h" namespace mtd = mir::test::doubles; mtd::MockInputDevice::MockInputDevice(char const* name, char const* uid, input::DeviceCapabilities caps) { using namespace testing; ON_CALL(*this, get_device_info()) .WillByDefault(Return(input::InputDeviceInfo{name, uid, caps})); if (contains(caps, input::DeviceCapability::pointer)) ON_CALL(*this, get_pointer_settings()) .WillByDefault(Return(input::PointerSettings())); else ON_CALL(*this, get_pointer_settings()) .WillByDefault(Return(mir::optional_value())); if (contains(caps, input::DeviceCapability::touchpad)) ON_CALL(*this, get_touchpad_settings()) .WillByDefault(Return(input::TouchpadSettings())); else ON_CALL(*this, get_touchpad_settings()) .WillByDefault(Return(mir::optional_value())); } ./include/0000755000015600001650000000000012676616124012533 5ustar jenkinsjenkins./include/server/0000755000015600001650000000000012676616124014041 5ustar jenkinsjenkins./include/server/mir/0000755000015600001650000000000012676616126014632 5ustar jenkinsjenkins./include/server/mir/server.h0000644000015600001650000004402012676616125016310 0ustar jenkinsjenkins/* * Copyright © 2014-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SERVER_H_ #define MIR_SERVER_H_ #include "mir/shell/window_manager_builder.h" #include "mir_toolkit/common.h" #include #include #include namespace mir { namespace compositor { class Compositor; class DisplayBufferCompositorFactory; class CompositorReport; } namespace frontend { class SessionAuthorizer; class Session; class SessionMediatorReport; } namespace graphics { class Cursor; class Platform; class Display; class GLConfig; class DisplayConfigurationPolicy; class DisplayConfigurationReport; } namespace input { class CompositeEventFilter; class InputDispatcher; class CursorListener; class CursorImages; class TouchVisualizer; class InputDeviceHub;} namespace logging { class Logger; } namespace options { class Option; } namespace cookie { using Secret = std::vector; class Authority; } namespace shell { class DisplayLayout; class DisplayConfigurationController; class FocusController; class HostLifecycleEventListener; class InputTargeter; class Shell; class SurfaceStack; } namespace scene { class ApplicationNotRespondingDetector; class BufferStreamFactory; class PromptSessionListener; class PromptSessionManager; class SessionListener; class SessionCoordinator; class SurfaceFactory; class CoordinateTranslator; } class Fd; class MainLoop; class ServerStatusListener; enum class OptionType { null, integer, string, boolean }; /// Customise and run a Mir server. class Server { public: Server(); /** @name Essential operations * These are the commands used to run and stop. * @{ */ /// set the command line. /// This must remain valid while apply_settings() and run() are called. void set_command_line(int argc, char const* argv[]); /// Sets an override functor for creating the cookie authority. /// A secret can be saved and any process this secret is shared /// with can verify Mir-generated cookies, or produce their own. void override_the_cookie_authority( std::function()> const& cookie_authority_builder); /// Applies any configuration options, hooks, or custom implementations. /// Must be called before calling run() or accessing any mir subsystems. void apply_settings(); /// The pixel formats that may be used when creating surfaces auto supported_pixel_formats() const -> std::vector; /// Run the Mir server until it exits void run(); /// Tell the Mir server to exit void stop(); /// returns true if and only if server exited normally. Otherwise false. bool exited_normally(); /** @} */ /** @name Configuration options * These functions allow customization of the handling of configuration * options. The add and set functions should be called before apply_settings() * otherwise they throw a std::logic_error. * @{ */ /// Add user configuration option(s) to Mir's option handling. /// These will be resolved during initialisation from the command line, /// environment variables, a config file or the supplied default. void add_configuration_option( std::string const& option, std::string const& description, int default_value); /// Add user configuration option(s) to Mir's option handling. /// These will be resolved during initialisation from the command line, /// environment variables, a config file or the supplied default. void add_configuration_option( std::string const& option, std::string const& description, double default_value); /// Add user configuration option(s) to Mir's option handling. /// These will be resolved during initialisation from the command line, /// environment variables, a config file or the supplied default. void add_configuration_option( std::string const& option, std::string const& description, std::string const& default_value); /// Add user configuration option(s) to Mir's option handling. /// These will be resolved during initialisation from the command line, /// environment variables, a config file or the supplied default. void add_configuration_option( std::string const& option, std::string const& description, char const* default_value); /// Add user configuration option(s) to Mir's option handling. /// These will be resolved during initialisation from the command line, /// environment variables, a config file or the supplied default. void add_configuration_option( std::string const& option, std::string const& description, bool default_value); /// Add user configuration option(s) to Mir's option handling. /// These will be resolved during initialisation from the command line, /// environment variables, a config file or the supplied default. void add_configuration_option( std::string const& option, std::string const& description, OptionType type); /// Set a handler for any command line options Mir does not recognise. /// This will be invoked if any unrecognised options are found during initialisation. /// Any unrecognised arguments are passed to this function. The pointers remain valid /// for the duration of the call only. /// If set_command_line_handler is not called the default action is to exit by /// throwing mir::AbnormalExit (which will be handled by the exception handler prior to /// exiting run(). void set_command_line_handler( std::function const& command_line_hander); /// Set the configuration filename. /// This will be searched for and parsed in the standard locations. Vis: /// 1. $XDG_CONFIG_HOME (if set, otherwise $HOME/.config (if set)) /// 2. $XDG_CONFIG_DIRS (if set, otherwise /etc/xdg) void set_config_filename(std::string const& config_file); /// Returns the configuration options. /// This will be null before initialization starts. It will be available /// when the init_callback has been invoked (and thereafter until the server exits). auto get_options() const -> std::shared_ptr; /** @} */ /** @name Using hooks into the run() logic * These allow the user to insert logic into startup or error handling. * For obvious reasons they should be called before run(). * @{ */ /// Add a callback to be invoked when the server has been initialized, /// but before it starts. This allows client code to get access Mir objects. /// If multiple callbacks are added they will be invoked in the sequence added. void add_init_callback(std::function const& init_callback); /// Set a handler for exceptions. This is invoked in a catch (...) block and /// the exception can be re-thrown to retrieve type information. /// The default action is to call mir::report_exception(std::cerr) void set_exception_handler(std::function const& exception_handler); /// Functor for processing SIGTERM or SIGINT /// This will not be called directly by a signal handler: arbitrary functions may be invoked. using Terminator = std::function; /// Set handler for termination requests. /// terminator will be called following receipt of SIGTERM or SIGINT. /// The default terminator stop()s the server, replacements should probably /// do the same in addition to any additional shutdown logic. void set_terminator(Terminator const& terminator); /// Functor for processing fatal signals for any "emergency cleanup". /// That is: SIGQUIT, SIGABRT, SIGFPE, SIGSEGV & SIGBUS /// /// \warning This will be called directly by a signal handler: /// Only async-signal-safe functions may be called using EmergencyCleanupHandler = std::function; /// Add cleanup for abnormal terminations. /// handler will be called on receipt of a fatal signal after which the /// default signal-handler will terminate the process. void add_emergency_cleanup(EmergencyCleanupHandler const& handler); /** @} */ /** @name Providing custom implementation * Provide alternative implementations of Mir subsystems: the functors will be invoked * during initialization of the Mir server (or when accessor methods are called). * They should be called before apply_settings() otherwise they throw a std::logic_error. * @{ */ /// Each of the override functions takes a builder functor of the same form /// \note If a null pointer is returned by the builder the default is used instead. template using Builder = std::function()>; /// Sets an override functor for creating the compositor. void override_the_compositor(Builder const& compositor_builder); /// Sets an override functor for creating the cursor images. void override_the_cursor_images(Builder const& cursor_images_builder); /// Sets an override functor for creating the per-display rendering code. void override_the_display_buffer_compositor_factory( Builder const& compositor_builder); /// Sets an override functor for creating the display configuration report. void override_the_display_configuration_report( Builder const& report_builder); /// Sets an override functor for creating the gl config. void override_the_gl_config(Builder const& gl_config_builder); /// Sets an override functor for creating the coordinate translator. void override_the_coordinate_translator( Builder const& coordinate_translator_builder); /// Sets an override functor for creating the host lifecycle event listener. void override_the_host_lifecycle_event_listener( Builder const& host_lifecycle_event_listener_builder); /// Sets an override functor for creating the input dispatcher. void override_the_input_dispatcher(Builder const& input_dispatcher_builder); /// Sets an override functor for creating the logger. void override_the_logger(Builder const& logger_builder); /// Sets an override functor for creating the prompt session listener. void override_the_prompt_session_listener(Builder const& prompt_session_listener_builder); /// Sets an override functor for creating the prompt session manager. void override_the_prompt_session_manager(Builder const& prompt_session_manager_builder); /// Sets an override functor for creating the status listener. void override_the_server_status_listener(Builder const& server_status_listener_builder); /// Sets an override functor for creating the session authorizer. void override_the_session_authorizer(Builder const& session_authorizer_builder); /// Sets an override functor for creating the session listener. void override_the_session_listener(Builder const& session_listener_builder); /// Sets an override functor for creating the session mediator report. void override_the_session_mediator_report(Builder const& session_mediator_builder); /// Sets an override functor for creating the shell. void override_the_shell(Builder const& wrapper); /// Sets an override functor for creating the window manager. void override_the_window_manager_builder(shell::WindowManagerBuilder const wmb); /// Sets an override functor for creating the application not responding detector. void override_the_application_not_responding_detector( Builder const& anr_detector_builder); /// Each of the wrap functions takes a wrapper functor of the same form template using Wrapper = std::function(std::shared_ptr const&)>; /// Sets a wrapper functor for creating the cursor. void wrap_cursor(Wrapper const& cursor_builder); /// Sets a wrapper functor for creating the cursor listener. void wrap_cursor_listener(Wrapper const& wrapper); /// Sets a wrapper functor for creating the per-display rendering code. void wrap_display_buffer_compositor_factory( Wrapper const& wrapper); /// Sets a wrapper functor for creating the display configuration policy. void wrap_display_configuration_policy(Wrapper const& wrapper); /// Sets a wrapper functor for creating the shell. void wrap_shell(Wrapper const& wrapper); /// Sets a wrapper functor for creating the surface stack. void wrap_surface_stack(Wrapper const& surface_stack); /** @} */ /** @name Getting access to Mir subsystems * These may be invoked by the functors that provide alternative implementations of * Mir subsystems. * They should only be used after apply_settings() is called - otherwise they throw * a std::logic_error. * @{ */ /// \return the compositor. auto the_compositor() const -> std::shared_ptr; /// \return the compositor report. auto the_compositor_report() const -> std::shared_ptr; /// \return the composite event filter. auto the_composite_event_filter() const -> std::shared_ptr; /// \return the cursor listener. auto the_cursor_listener() const -> std::shared_ptr; /// \return the cursor auto the_cursor() const -> std::shared_ptr; /// \return the focus controller. auto the_focus_controller() const -> std::shared_ptr; /// \return the graphics display. auto the_display() const -> std::shared_ptr; auto the_display_configuration_controller() const -> std::shared_ptr; /// \return the GL config. auto the_gl_config() const -> std::shared_ptr; /// \return the graphics platform. auto the_graphics_platform() const -> std::shared_ptr; /// \return the input targeter. auto the_input_targeter() const -> std::shared_ptr; /// \return the logger. auto the_logger() const -> std::shared_ptr; /// \return the main loop. auto the_main_loop() const -> std::shared_ptr; /// \return the prompt session listener. auto the_prompt_session_listener() const -> std::shared_ptr; /// \return the prompt session manager. auto the_prompt_session_manager() const ->std::shared_ptr; /// \return the session authorizer. auto the_session_authorizer() const -> std::shared_ptr; /// \return the session coordinator. auto the_session_coordinator() const -> std::shared_ptr; /// \return the session listener. auto the_session_listener() const -> std::shared_ptr; /// \return the shell. auto the_shell() const -> std::shared_ptr; /// \return the display layout. auto the_shell_display_layout() const -> std::shared_ptr; /// \return the buffer stream factory auto the_buffer_stream_factory() const -> std::shared_ptr; /// \return the surface factory auto the_surface_factory() const -> std::shared_ptr; /// \return the surface stack. auto the_surface_stack() const -> std::shared_ptr; /// \return the touch visualizer. auto the_touch_visualizer() const -> std::shared_ptr; /// \return the input device hub auto the_input_device_hub() const -> std::shared_ptr; /// \return the application not responding detector auto the_application_not_responding_detector() const -> std::shared_ptr; /** @} */ /** @name Client side support * These facilitate use of the server through the client API. * They should be called while the server is running (i.e. run() has been called and * not exited) otherwise they throw a std::logic_error. * @{ */ using ConnectHandler = std::function const& session)>; /// Get a file descriptor that can be used to connect a client /// It can be passed to another process, or used directly with mir_connect() /// using the format "fd://%d". auto open_client_socket() -> Fd; /// Get a file descriptor that can be used to connect a client /// It can be passed to another process, or used directly with mir_connect() /// using the format "fd://%d". /// \param connect_handler callback to be invoked when the client connects auto open_client_socket(ConnectHandler const& connect_handler) -> Fd; /// Get a file descriptor that can be used to connect a prompt provider /// It can be passed to another process, or used directly with mir_connect() /// using the format "fd://%d". auto open_prompt_socket() -> Fd; /** @} */ private: struct ServerConfiguration; struct Self; std::shared_ptr const self; }; } #endif /* SERVER_H_ */ ./include/server/mir/main_loop.h0000644000015600001650000000215112676616125016756 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_MAIN_LOOP_H_ #define MIR_MAIN_LOOP_H_ #include "mir/graphics/event_handler_register.h" #include "mir/time/alarm_factory.h" #include "mir/server_action_queue.h" namespace mir { class MainLoop : public graphics::EventHandlerRegister, public time::AlarmFactory, public ServerActionQueue { public: virtual void run() = 0; virtual void stop() = 0; }; } #endif /* MIR_MAIN_LOOP_H_ */ ./include/server/mir/server_status_listener.h0000644000015600001650000000226412676616125021624 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Ancell */ #ifndef MIR_SERVER_STATUS_LISTENER_H_ #define MIR_SERVER_STATUS_LISTENER_H_ namespace mir { class ServerStatusListener { public: virtual void paused() = 0; virtual void resumed() = 0; virtual void started() = 0; protected: ServerStatusListener() = default; virtual ~ServerStatusListener() = default; ServerStatusListener(ServerStatusListener const&) = delete; ServerStatusListener& operator=(ServerStatusListener const&) = delete; }; } #endif /* MIR_SERVER_STATUS_LISTENER_H_ */ ./include/server/mir/scene/0000755000015600001650000000000012676616126015727 5ustar jenkinsjenkins./include/server/mir/scene/observer.h0000644000015600001650000000360112676616125017726 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_SCENE_OBSERVER_H_ #define MIR_SCENE_OBSERVER_H_ #include namespace mir { namespace scene { class Surface; /// An observer for top level notifications of scene changes. In order /// to receive more granular change notifications a user may install /// mir::scene::SurfaceObserver in surface_added. class Observer { public: virtual void surface_added(Surface* surface) = 0; virtual void surface_removed(Surface* surface) = 0; virtual void surfaces_reordered() = 0; // Used to indicate the scene has changed in some way beyond the present surfaces // and will require full recomposition. virtual void scene_changed() = 0; // Called at observer registration to notify of already existing surfaces. virtual void surface_exists(Surface* surface) = 0; // Called when observer is unregistered, for example, to provide a place to // unregister SurfaceObservers which may have been added in surface_added/exists virtual void end_observation() = 0; protected: Observer() = default; virtual ~Observer() = default; Observer(Observer const&) = delete; Observer& operator=(Observer const&) = delete; }; } } // namespace mir #endif // MIR_SCENE_OBSERVER_H_ ./include/server/mir/scene/null_surface_observer.h0000644000015600001650000000412512676616125022472 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_SCENE_NULL_SURFACE_OBSERVER_H_ #define MIR_SCENE_NULL_SURFACE_OBSERVER_H_ #include "mir/scene/surface_observer.h" namespace mir { namespace scene { class NullSurfaceObserver : public SurfaceObserver { public: NullSurfaceObserver() = default; virtual ~NullSurfaceObserver() = default; void attrib_changed(MirSurfaceAttrib attrib, int value) override; void resized_to(geometry::Size const& size) override; void moved_to(geometry::Point const& top_left) override; void hidden_set_to(bool hide) override; void frame_posted(int frames_available, geometry::Size const& size) override; void alpha_set_to(float alpha) override; void orientation_set_to(MirOrientation orientation) override; void transformation_set_to(glm::mat4 const& t) override; void cursor_image_set_to(graphics::CursorImage const& image) override; void reception_mode_set_to(input::InputReceptionMode mode) override; void client_surface_close_requested() override; void keymap_changed(MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) override; void renamed(char const* name) override; void cursor_image_removed() override; protected: NullSurfaceObserver(NullSurfaceObserver const&) = delete; NullSurfaceObserver& operator=(NullSurfaceObserver const&) = delete; }; } } #endif // MIR_SCENE_NULL_SURFACE_OBSERVER_H_ ./include/server/mir/scene/surface_factory.h0000644000015600001650000000254312676616125021262 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_SCENE_SURFACE_FACTORY_H_ #define MIR_SCENE_SURFACE_FACTORY_H_ #include "mir/scene/surface_creation_parameters.h" #include namespace mir { namespace compositor { class BufferStream; } namespace scene { class Surface; class SurfaceFactory { public: SurfaceFactory() = default; virtual ~SurfaceFactory() = default; virtual std::shared_ptr create_surface( std::shared_ptr const&, SurfaceCreationParameters const& params) = 0; private: SurfaceFactory(const SurfaceFactory&) = delete; SurfaceFactory& operator=(const SurfaceFactory&) = delete; }; } } #endif /* MIR_SCENE_SURFACE_FACTORY_H_ */ ./include/server/mir/scene/null_session_listener.h0000644000015600001650000000307712676616125022530 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_SCENE_NULL_SESSION_LISTENER_H_ #define MIR_SCENE_NULL_SESSION_LISTENER_H_ #include "mir/scene/session_listener.h" namespace mir { namespace scene { class NullSessionListener : public SessionListener { public: NullSessionListener() = default; virtual ~NullSessionListener() noexcept(true) = default; void starting(std::shared_ptr const&) override {} void stopping(std::shared_ptr const&) override {} void focused(std::shared_ptr const&) override {} void unfocused() override {} void surface_created(Session&, std::shared_ptr const&) override {} void destroying_surface(Session&, std::shared_ptr const&) override {} protected: NullSessionListener(const NullSessionListener&) = delete; NullSessionListener& operator=(const NullSessionListener&) = delete; }; } } #endif // MIR_SCENE_NULL_SESSION_LISTENER_H_ ./include/server/mir/scene/surface.h0000644000015600001650000001075412676616125017536 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SCENE_SURFACE_H_ #define MIR_SCENE_SURFACE_H_ #include "mir/graphics/renderable.h" #include "mir/input/surface.h" #include "mir/frontend/surface.h" #include "mir/compositor/compositor_id.h" #include #include namespace mir { namespace input { class InputChannel; } namespace shell { class InputTargeter; } namespace geometry { struct Rectangle; } namespace graphics { class CursorImage; } namespace compositor { class BufferStream; } namespace scene { struct StreamInfo { std::shared_ptr stream; geometry::Displacement displacement; }; class SurfaceObserver; class Surface : public input::Surface, public frontend::Surface { public: // resolve ambiguous member function names std::string name() const override = 0; geometry::Size client_size() const override = 0; geometry::Rectangle input_bounds() const override = 0; // member functions that don't exist in base classes /// Top-left corner (of the window frame if present) virtual geometry::Point top_left() const = 0; /// Size of the surface including window frame (if any) virtual geometry::Size size() const = 0; virtual graphics::RenderableList generate_renderables(compositor::CompositorID id) const = 0; virtual int buffers_ready_for_compositor(void const* compositor_id) const = 0; virtual float alpha() const = 0; //only used in examples/ virtual MirSurfaceType type() const = 0; virtual MirSurfaceState state() const = 0; virtual void hide() = 0; virtual void show() = 0; virtual bool visible() const = 0; virtual void move_to(geometry::Point const& top_left) = 0; /** * Sets the input region for this surface. * * The input region is expressed in coordinates relative to the surface (i.e., * use (0,0) for the top left point of the surface). * * By default the input region is the whole surface. To unset a custom input region * and revert to the default set an empty input region, i.e., set_input_region({}). * To disable input set a non-empty region containing an empty rectangle, i.e., * set_input_region({geom::Rectangle{}}). */ virtual void set_input_region(std::vector const& region) = 0; virtual void resize(geometry::Size const& size) = 0; virtual void set_transformation(glm::mat4 const& t) = 0; virtual void set_alpha(float alpha) = 0; virtual void set_orientation(MirOrientation orientation) = 0; virtual void set_cursor_image(std::shared_ptr const& image) override = 0; virtual std::shared_ptr cursor_image() const override = 0; virtual void add_observer(std::shared_ptr const& observer) = 0; virtual void remove_observer(std::weak_ptr const& observer) = 0; // TODO input_channel() relates to adding and removing the surface // TODO from the scene and is probably not cleanest interface for this. virtual std::shared_ptr input_channel() const override = 0; virtual void set_reception_mode(input::InputReceptionMode mode) = 0; virtual void request_client_surface_close() = 0; virtual std::shared_ptr parent() const = 0; // TODO a legacy of old interactions and needs removing virtual int configure(MirSurfaceAttrib attrib, int value) = 0; // TODO a legacy of old interactions and needs removing virtual int query(MirSurfaceAttrib attrib) const = 0; virtual void set_keymap(MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) = 0; virtual void rename(std::string const& title) = 0; virtual void set_streams(std::list const& streams) = 0; }; } } #endif // MIR_SCENE_SURFACE_H_ ./include/server/mir/scene/prompt_session_listener.h0000644000015600001650000000343012676616125023070 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #ifndef MIR_SCENE_PROMPT_SESSION_LISTENER_H_ #define MIR_SCENE_PROMPT_SESSION_LISTENER_H_ #include namespace mir { namespace scene { class Session; class PromptSession; class PromptSessionListener { public: virtual void starting(std::shared_ptr const& prompt_session) = 0; virtual void stopping(std::shared_ptr const& prompt_session) = 0; virtual void suspending(std::shared_ptr const& prompt_session) = 0; virtual void resuming(std::shared_ptr const& prompt_session) = 0; virtual void prompt_provider_added(PromptSession const& prompt_session, std::shared_ptr const& prompt_provider) = 0; virtual void prompt_provider_removed(PromptSession const& prompt_session, std::shared_ptr const& prompt_provider) = 0; protected: PromptSessionListener() = default; virtual ~PromptSessionListener() = default; PromptSessionListener(const PromptSessionListener&) = delete; PromptSessionListener& operator=(const PromptSessionListener&) = delete; }; } } #endif // MIR_SCENE_PROMPT_SESSION_LISTENER_H_ ./include/server/mir/scene/prompt_session.h0000644000015600001650000000326212676616125021166 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #ifndef MIR_SCENE_PROMPT_SESSION_H_ #define MIR_SCENE_PROMPT_SESSION_H_ #include "mir/frontend/prompt_session.h" namespace mir { namespace scene { class Session; class PromptSession : public frontend::PromptSession { public: /** * Start a prompt session * \param [in] helper The prompt session helper session */ virtual void start(std::shared_ptr const& helper_session) = 0; /** * Stop a prompt session * \param [in] helper The prompt session helper session */ virtual void stop(std::shared_ptr const& helper_session) = 0; /** * Suspend a prompt session * \param [in] helper The prompt session helper session */ virtual void suspend(std::shared_ptr const& helper_session) = 0; /** * Resume a prompt session * \param [in] helper The prompt session helper session */ virtual void resume(std::shared_ptr const& helper_session) = 0; }; } } #endif // MIR_SHELL_PROMPT_SESSION_H_ ./include/server/mir/scene/application_not_responding_detector.h0000644000015600001650000000414412676616125025406 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Christopher James Halse Rogers */ #ifndef MIR_SCENE_APPLICATION_NOT_RESPONDING_DETECTOR_H_ #define MIR_SCENE_APPLICATION_NOT_RESPONDING_DETECTOR_H_ #include #include namespace mir { namespace frontend { class Session; } namespace scene { class Session; class ApplicationNotRespondingDetector { public: ApplicationNotRespondingDetector() = default; virtual ~ApplicationNotRespondingDetector() = default; /** * Notification object for application-not-responsive signals * * \note These signals will be called without any locks held; it's safe * to call methods on the ApplicationNotRespondingDetector from these * delegates. */ class Observer { public: Observer() = default; virtual ~Observer() = default; virtual void session_unresponsive(Session const* session) = 0; virtual void session_now_responsive(Session const* session) = 0; }; virtual void register_session(frontend::Session const* session, std::function const& pinger) = 0; virtual void unregister_session(frontend::Session const* session) = 0; virtual void pong_received(frontend::Session const* received_for) = 0; virtual void register_observer(std::shared_ptr const& observer) = 0; virtual void unregister_observer(std::shared_ptr const& observer) = 0; }; } } #endif // MIR_SCENE_APPLICATION_NOT_RESPONDING_DETECTOR_H_ ./include/server/mir/scene/snapshot.h0000644000015600001650000000211012676616125017730 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #ifndef MIR_SCENE_SNAPSHOT_H_ #define MIR_SCENE_SNAPSHOT_H_ #include "mir/geometry/size.h" #include "mir/geometry/dimensions.h" #include namespace mir { namespace scene { struct Snapshot { geometry::Size size; geometry::Stride stride; void const* pixels; }; typedef std::function SnapshotCallback; } } #endif /* MIR_SCENE_SNAPSHOT_H_ */ ./include/server/mir/scene/prompt_session_manager.h0000644000015600001650000000754512676616125022670 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #ifndef MIR_SCENE_PROMPT_SESSION_MANAGER_H_ #define MIR_SCENE_PROMPT_SESSION_MANAGER_H_ #include #include #include namespace mir { namespace scene { class Session; class PromptSession; struct PromptSessionCreationParameters; class PromptSessionManager { public: virtual ~PromptSessionManager() = default; /** * Start a new prompt session * \param [in] session The prompt helper session * \param [in] params The creation parameters for constructing the prompt session */ virtual std::shared_ptr start_prompt_session_for(std::shared_ptr const& session, PromptSessionCreationParameters const& params) const = 0; /** * Stop a started prompt session * \param [in] prompt_session The prompt session */ virtual void stop_prompt_session(std::shared_ptr const& prompt_session) const = 0; /** * Suspend a prompt session * \param [in] prompt_session The prompt session */ virtual void suspend_prompt_session(std::shared_ptr const& prompt_session) const = 0; /** * Resume a suspended prompt session * \param [in] prompt_session The prompt session */ virtual void resume_prompt_session(std::shared_ptr const& prompt_session) const = 0; /** * Add a prompt provider to an existing prompt session * \param [in] prompt_session The prompt session * \param [in] prompt_provider The prompt provider to add to the prompt session */ virtual void add_prompt_provider(std::shared_ptr const& prompt_session, std::shared_ptr const& prompt_provider) const = 0; /** * Remove a session from all associated prompt sessions * \param [in] session The new session that is to be removed */ virtual void remove_session(std::shared_ptr const& session) const = 0; /** * Retrieve the application session for a prompt session * \param [in] prompt_session The prompt session */ virtual std::shared_ptr application_for(std::shared_ptr const& prompt_session) const = 0; /** * Retrieve the helper session for a prompt session * \param [in] prompt_session The prompt session */ virtual std::shared_ptr helper_for(std::shared_ptr const& prompt_session) const = 0; /** * Iterate over all the prompt providers associated with a prompt session * \param [in] prompt_session The prompt session * \param [in] f The callback function to call for each provider */ virtual void for_each_provider_in(std::shared_ptr const& prompt_session, std::function const& prompt_provider)> const& f) const = 0; protected: PromptSessionManager() = default; PromptSessionManager(const PromptSessionManager&) = delete; PromptSessionManager& operator=(const PromptSessionManager&) = delete; }; } } #endif // MIR_SCENE_PROMPT_SESSION_MANAGER_H_ ./include/server/mir/scene/session_coordinator.h0000644000015600001650000000335112676616125022167 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SCENE_SESSION_COORDINATOR_H_ #define MIR_SCENE_SESSION_COORDINATOR_H_ #include "mir/frontend/surface_id.h" #include "mir_toolkit/common.h" #include namespace mir { namespace frontend { class EventSink; } namespace scene { class Session; class Surface; class SurfaceCreationParameters; class SessionCoordinator { public: virtual void set_focus_to(std::shared_ptr const& focus) = 0; virtual void unset_focus() = 0; virtual std::shared_ptr open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) = 0; virtual void close_session(std::shared_ptr const& session) = 0; virtual std::shared_ptr successor_of(std::shared_ptr const&) const = 0; protected: SessionCoordinator() = default; virtual ~SessionCoordinator() = default; SessionCoordinator(SessionCoordinator const&) = delete; SessionCoordinator& operator=(SessionCoordinator const&) = delete; }; } } #endif /* MIR_SCENE_SESSION_COORDINATOR_H_ */ ./include/server/mir/scene/surface_creation_parameters.h0000644000015600001650000000770412676616125023646 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SCENE_SURFACE_CREATION_PARAMETERS_H_ #define MIR_SCENE_SURFACE_CREATION_PARAMETERS_H_ #include "mir_toolkit/common.h" #include "mir/geometry/point.h" #include "mir/geometry/size.h" #include "mir/graphics/buffer_properties.h" #include "mir/graphics/display_configuration.h" #include "mir/frontend/surface_id.h" #include "mir/input/input_reception_mode.h" #include "mir/optional_value.h" #include "mir/shell/surface_specification.h" #include #include namespace mir { namespace scene { class Surface; struct SurfaceCreationParameters { SurfaceCreationParameters(); SurfaceCreationParameters& of_name(std::string const& new_name); SurfaceCreationParameters& of_size(geometry::Size new_size); SurfaceCreationParameters& of_size(geometry::Width::ValueType width, geometry::Height::ValueType height); SurfaceCreationParameters& of_position(geometry::Point const& top_left); SurfaceCreationParameters& of_buffer_usage(graphics::BufferUsage new_buffer_usage); SurfaceCreationParameters& of_pixel_format(MirPixelFormat new_pixel_format); SurfaceCreationParameters& with_input_mode(input::InputReceptionMode const& new_mode); SurfaceCreationParameters& with_output_id(graphics::DisplayConfigurationOutputId const& output_id); SurfaceCreationParameters& of_type(MirSurfaceType type); SurfaceCreationParameters& with_state(MirSurfaceState state); SurfaceCreationParameters& with_preferred_orientation(MirOrientationMode mode); SurfaceCreationParameters& with_parent_id(frontend::SurfaceId const& id); SurfaceCreationParameters& with_aux_rect(geometry::Rectangle const& rect); SurfaceCreationParameters& with_edge_attachment(MirEdgeAttachment edge); SurfaceCreationParameters& with_buffer_stream(frontend::BufferStreamId const& id); std::string name; geometry::Size size; geometry::Point top_left; graphics::BufferUsage buffer_usage; MirPixelFormat pixel_format; input::InputReceptionMode input_mode; graphics::DisplayConfigurationOutputId output_id; mir::optional_value state; mir::optional_value type; mir::optional_value preferred_orientation; mir::optional_value parent_id; mir::optional_value content_id; mir::optional_value aux_rect; mir::optional_value edge_attachment; std::weak_ptr parent; optional_value min_width; optional_value min_height; optional_value max_width; optional_value max_height; mir::optional_value width_inc; mir::optional_value height_inc; mir::optional_value min_aspect; mir::optional_value max_aspect; mir::optional_value> input_shape; mir::optional_value shell_chrome; }; bool operator==(const SurfaceCreationParameters& lhs, const SurfaceCreationParameters& rhs); bool operator!=(const SurfaceCreationParameters& lhs, const SurfaceCreationParameters& rhs); SurfaceCreationParameters a_surface(); } } #endif /* MIR_SCENE_SURFACE_CREATION_PARAMETERS_H_ */ ./include/server/mir/scene/buffer_stream_factory.h0000644000015600001650000000345712676616125022463 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Alan Griffiths * Thomas Voss */ #ifndef MIR_SCENE_BUFFER_STREAM_FACTORY_H_ #define MIR_SCENE_BUFFER_STREAM_FACTORY_H_ #include "mir/frontend/buffer_stream_id.h" #include namespace mir { namespace compositor { class BufferStream; } namespace graphics { struct BufferProperties; } namespace frontend { class BufferSink; } namespace scene { class BufferStreamFactory { public: virtual ~BufferStreamFactory() = default; virtual std::shared_ptr create_buffer_stream( frontend::BufferStreamId, std::shared_ptr const& sink, int nbuffers, graphics::BufferProperties const& buffer_properties) = 0; virtual std::shared_ptr create_buffer_stream( frontend::BufferStreamId, std::shared_ptr const& sink, graphics::BufferProperties const& buffer_properties) = 0; protected: BufferStreamFactory() = default; BufferStreamFactory(const BufferStreamFactory&) = delete; BufferStreamFactory& operator=(const BufferStreamFactory&) = delete; }; } } #endif // MIR_SCENE_BUFFER_STREAM_FACTORY_H_ ./include/server/mir/scene/session_listener.h0000644000015600001650000000306412676616125021472 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_SCENE_SESSION_LISTENER_H_ #define MIR_SCENE_SESSION_LISTENER_H_ #include namespace mir { namespace scene { class Surface; class Session; class SessionListener { public: virtual void starting(std::shared_ptr const& session) = 0; virtual void stopping(std::shared_ptr const& session) = 0; virtual void focused(std::shared_ptr const& session) = 0; virtual void unfocused() = 0; virtual void surface_created(Session& session, std::shared_ptr const& surface) = 0; virtual void destroying_surface(Session& session, std::shared_ptr const& surface) = 0; protected: SessionListener() = default; virtual ~SessionListener() = default; SessionListener(const SessionListener&) = delete; SessionListener& operator=(const SessionListener&) = delete; }; } } #endif // MIR_SCENE_SESSION_LISTENER_H_ ./include/server/mir/scene/coordinate_translator.h0000644000015600001650000000433012676616125022477 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_SCENE_COORDINATE_TRANSLATOR_H_ #define MIR_SCENE_COORDINATE_TRANSLATOR_H_ #include "mir/geometry/point.h" #include namespace mir { namespace frontend { class Surface; } namespace scene { /** * Support for the debug "surface to screen" coordinate translation interface. * \note For shells which do surface transformations the default implementation * will return incorrect results. */ class CoordinateTranslator { public: virtual ~CoordinateTranslator() = default; /** * \brief Translate a surface coordinate into the screen coordinate space * \param [in] surface A frontend::Surface. This will need to be dynamic_cast into * the scene::Surface relevant for the shell. * \param [in] x, y Coordinates to translate from the surface coordinate space * \return The coordinates in the screen coordinate space. * \throws A std::runtime_error if the translation cannot be performed * for any reason. * * \note It is acceptable for this call to unconditionally throw a std::runtime_error. * It is not required for normal functioning of the server or clients; clients which * use the debug extension will receive an appropriate failure notice. */ virtual geometry::Point surface_to_screen(std::shared_ptr surface, int32_t x, int32_t y) = 0; }; } } #endif // MIR_SCENE_COORDINATE_TRANSLATOR_H_ ./include/server/mir/scene/surface_observer.h0000644000015600001650000000445112676616125021442 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SCENE_SURFACE_OBSERVER_H_ #define MIR_SCENE_SURFACE_OBSERVER_H_ #include "mir_toolkit/common.h" #include "mir_toolkit/events/event.h" #include "mir/input/input_reception_mode.h" #include #include namespace mir { namespace geometry { struct Size; struct Point; } namespace graphics { class CursorImage; } namespace scene { class SurfaceObserver { public: virtual void attrib_changed(MirSurfaceAttrib attrib, int value) = 0; virtual void resized_to(geometry::Size const& size) = 0; virtual void moved_to(geometry::Point const& top_left) = 0; virtual void hidden_set_to(bool hide) = 0; virtual void frame_posted(int frames_available, geometry::Size const& size) = 0; virtual void alpha_set_to(float alpha) = 0; virtual void orientation_set_to(MirOrientation orientation) = 0; virtual void transformation_set_to(glm::mat4 const& t) = 0; virtual void reception_mode_set_to(input::InputReceptionMode mode) = 0; virtual void cursor_image_set_to(graphics::CursorImage const& image) = 0; virtual void client_surface_close_requested() = 0; virtual void keymap_changed(MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) = 0; virtual void renamed(char const* name) = 0; virtual void cursor_image_removed() = 0; protected: SurfaceObserver() = default; virtual ~SurfaceObserver() = default; SurfaceObserver(SurfaceObserver const&) = delete; SurfaceObserver& operator=(SurfaceObserver const&) = delete; }; } } #endif // MIR_SCENE_SURFACE_OBSERVER_H_ ./include/server/mir/scene/session.h0000644000015600001650000000533712676616125017572 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #ifndef MIR_SCENE_SESSION_H_ #define MIR_SCENE_SESSION_H_ #include "mir/frontend/session.h" #include "mir/scene/snapshot.h" #include #include namespace mir { namespace frontend { class EventSink; } namespace shell { struct StreamSpecification; } namespace input { class Device; } namespace scene { class Surface; struct SurfaceCreationParameters; class Session : public frontend::Session { public: virtual void force_requests_to_complete() = 0; virtual pid_t process_id() const = 0; virtual void take_snapshot(SnapshotCallback const& snapshot_taken) = 0; virtual std::shared_ptr default_surface() const = 0; virtual void set_lifecycle_state(MirLifecycleState state) = 0; virtual void send_display_config(graphics::DisplayConfiguration const&) = 0; virtual void hide() = 0; virtual void show() = 0; virtual void start_prompt_session() = 0; virtual void stop_prompt_session() = 0; virtual void suspend_prompt_session() = 0; virtual void resume_prompt_session() = 0; virtual frontend::SurfaceId create_surface( SurfaceCreationParameters const& params, std::shared_ptr const& sink) = 0; virtual void destroy_surface(frontend::SurfaceId surface) = 0; virtual std::shared_ptr surface(frontend::SurfaceId surface) const = 0; virtual std::shared_ptr surface_after(std::shared_ptr const&) const = 0; virtual std::shared_ptr get_buffer_stream(frontend::BufferStreamId stream) const = 0; virtual frontend::BufferStreamId create_buffer_stream(graphics::BufferProperties const& props) = 0; virtual void destroy_buffer_stream(frontend::BufferStreamId stream) = 0; virtual void configure_streams(Surface& surface, std::vector const& config) = 0; virtual void destroy_surface(std::weak_ptr const& surface) = 0; virtual void send_input_device_change(std::vector> const& devices) = 0; }; } } #endif // MIR_SCENE_SESSION_H_ ./include/server/mir/scene/prompt_session_creation_parameters.h0000644000015600001650000000175512676616125025302 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #ifndef MIR_SCENE_PROMPT_SESSION_CREATION_PARAMETERS_H_ #define MIR_SCENE_PROMPT_SESSION_CREATION_PARAMETERS_H_ #include namespace mir { namespace scene { struct PromptSessionCreationParameters { pid_t application_pid = 0; }; } } #endif /* MIR_SCENE_PROMPT_SESSION_CREATION_PARAMETERS_H_ */ ./include/server/mir/shell/0000755000015600001650000000000012676616126015741 5ustar jenkinsjenkins./include/server/mir/shell/persistent_surface_store.h0000644000015600001650000000656712676616125023253 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_SHELL_PERSISTENT_SURFACE_STORE_H_ #define MIR_SHELL_PERSISTENT_SURFACE_STORE_H_ #include #include #include #include namespace mir { namespace scene { class Surface; } namespace shell { /** * A store for Surface information divorced from the lifetime of any given Session * * This provides the backing for persistent references to Surface objects, * both across client Sessions and across shell restarts, for features such as * surface position/size restoration. * * \todo Persistence across shell restarts is as-yet unimplemented. */ class PersistentSurfaceStore { public: class Id; virtual ~PersistentSurfaceStore() = default; /** * \brief Acquire ID for a Surface * \param [in] surface Surface to query or generate an ID for * \return The ID for this surface. * \note If \arg surface has not yet had an ID generated, this generates its ID. * \note This does not extend the lifetime of \arg surface. */ virtual Id id_for_surface(std::shared_ptr const& surface) = 0; /** * \brief Lookup Surface by ID. * \param [in] id ID of surface to lookup * \return The surface with ID \arg id. If this surface has been destroyed, * but the store retains a reference, returns nullptr. * \throws std::out_of_range if the store has no reference for a surface with \arg id. */ virtual std::shared_ptr surface_for_id(Id const& id) const = 0; }; } } namespace std { template<> struct hash; } namespace mir { namespace shell { class PersistentSurfaceStore::Id final { public: /** * \brief Generate a new, unique Id. */ Id(); /** * \brief Construct an Id from its serialized string form * \param serialized_form [in] The previously-serialized Id * \throw std::invalid_argument if \arg serialized_form is not parseable as an Id. */ Id(std::string const& serialized_form); Id(Id const& rhs); Id& operator=(Id const& rhs); bool operator==(Id const& rhs) const; /** * \brief Serialize to a UTF-8 string * \return A string representation of the Id; this is guaranteed to be valid UTF-8 */ std::string serialize_to_string() const; private: friend struct std::hash; uuid_t uuid; }; } } namespace std { template<> struct hash { typedef mir::shell::PersistentSurfaceStore::Id argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& uuid) const; }; } #endif // MIR_SHELL_PERSISTENT_SURFACE_STORE_H_ ./include/server/mir/shell/surface_stack.h0000644000015600001650000000347712676616125020741 0ustar jenkinsjenkins/* * Copyright © 2013-15 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SHELL_SURFACE_COORDINATOR_H_ #define MIR_SHELL_SURFACE_COORDINATOR_H_ #include #include namespace mir { namespace geometry { class Point; } namespace input { enum class InputReceptionMode; } namespace scene { class Surface; struct SurfaceCreationParameters; class SurfaceObserver; class Session; } namespace shell { class SurfaceStack { public: using SurfaceSet = std::set, std::owner_less>>; virtual void add_surface( std::shared_ptr const&, input::InputReceptionMode new_mode) = 0; virtual void raise(std::weak_ptr const& surface) = 0; virtual void raise(SurfaceSet const& surfaces) = 0; virtual void remove_surface(std::weak_ptr const& surface) = 0; virtual auto surface_at(geometry::Point) const -> std::shared_ptr = 0; protected: SurfaceStack() = default; virtual ~SurfaceStack() = default; SurfaceStack(SurfaceStack const&) = delete; SurfaceStack& operator=(SurfaceStack const&) = delete; }; } } #endif /* MIR_SHELL_SURFACE_COORDINATOR_H_ */ ./include/server/mir/shell/window_manager_builder.h0000644000015600001650000000226412676616125022624 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef INCLUDE_SERVER_MIR_SHELL_WINDOW_MANAGER_BUILDER_H_ #define INCLUDE_SERVER_MIR_SHELL_WINDOW_MANAGER_BUILDER_H_ #include #include namespace mir { namespace shell { class WindowManager; class FocusController; /// WindowManagers are built while initializing an AbstractShell, so a builder functor is needed using WindowManagerBuilder = std::function(FocusController* focus_controller)>; } } #endif /* INCLUDE_SERVER_MIR_SHELL_WINDOW_MANAGER_BUILDER_H_ */ ./include/server/mir/shell/display_configuration_controller.h0000644000015600001650000000332012676616125024746 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_SHELL_DISPLAY_CONFIGURATION_CONTROLLER_H_ #define MIR_SHELL_DISPLAY_CONFIGURATION_CONTROLLER_H_ #include namespace mir { namespace graphics { class DisplayConfiguration; } namespace shell { class DisplayConfigurationController { public: DisplayConfigurationController() = default; virtual ~DisplayConfigurationController() = default; DisplayConfigurationController(DisplayConfigurationController const&) = delete; DisplayConfigurationController& operator=(DisplayConfigurationController const&) = delete; /** * Set the base display configuration. * * This is the display configuration that is used by default, but will be * overridden by a client's requested configuration if that client is focused. * * \param [in] conf The new display configuration to set */ virtual void set_base_configuration( std::shared_ptr const& conf) = 0; }; } } #endif //MIR_SHELL_DISPLAY_CONFIGURATION_CONTROLLER_H_ ./include/server/mir/shell/shell_report.h0000644000015600001650000000537112676616125020621 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SHELL_SHELL_REPORT_H #define MIR_SHELL_SHELL_REPORT_H #include "mir/frontend/surface_id.h" #include "mir_toolkit/common.h" #include #include namespace mir { namespace geometry { struct Rectangle; } namespace scene { class PromptSession; class Session; class Surface; struct SurfaceCreationParameters; } namespace shell { struct SurfaceSpecification; using SurfaceSet = std::set, std::owner_less>>; class ShellReport { public: virtual void opened_session(scene::Session const& session) = 0; virtual void closing_session(scene::Session const& session) = 0; virtual void created_surface( scene::Session const& session, frontend::SurfaceId surface_id) = 0; virtual void update_surface( scene::Session const& session, scene::Surface const& surface, SurfaceSpecification const& modifications) = 0; virtual void update_surface( scene::Session const& session, scene::Surface const& surface, MirSurfaceAttrib attrib, int value) = 0; virtual void destroying_surface( scene::Session const& session, frontend::SurfaceId surface) = 0; virtual void started_prompt_session( scene::PromptSession const& prompt_session, scene::Session const& session) = 0; virtual void added_prompt_provider( scene::PromptSession const& prompt_session, scene::Session const& session) = 0; virtual void stopping_prompt_session( scene::PromptSession const& prompt_session) = 0; virtual void adding_display(geometry::Rectangle const& area) = 0; virtual void removing_display(geometry::Rectangle const& area) = 0; virtual void input_focus_set_to( scene::Session const* focus_session, scene::Surface const* focus_surface) = 0; virtual void surfaces_raised(SurfaceSet const& surfaces) = 0; ShellReport() = default; virtual ~ShellReport() = default; ShellReport(ShellReport const&) = delete; ShellReport& operator=(ShellReport const&) = delete; }; } } #endif //MIR_SHELL_SHELL_REPORT_H ./include/server/mir/shell/surface_ready_observer.h0000644000015600001650000000333412676616125022637 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SHELL_SURFACE_READY_OBSERVER_H_ #define MIR_SHELL_SURFACE_READY_OBSERVER_H_ #include "mir/scene/null_surface_observer.h" #include #include namespace mir { namespace scene { class Session; class Surface; } namespace geometry { struct Size; } namespace shell { class SurfaceReadyObserver : public scene::NullSurfaceObserver, public std::enable_shared_from_this { public: using ActivateFunction = std::function const& session, std::shared_ptr const& surface)>; SurfaceReadyObserver( ActivateFunction const& activate, std::shared_ptr const& session, std::shared_ptr const& surface); ~SurfaceReadyObserver(); private: void frame_posted(int, geometry::Size const&) override; ActivateFunction const activate; std::weak_ptr const session; std::weak_ptr const surface; }; } } #endif /* MIR_SHELL_SURFACE_READY_OBSERVER_H_ */ ./include/server/mir/shell/host_lifecycle_event_listener.h0000644000015600001650000000242312676616125024214 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Cemil Azizoglu */ #ifndef MIR_HOST_LIFECYCLE_EVENT_LISTENER_H_ #define MIR_HOST_LIFECYCLE_EVENT_LISTENER_H_ #include "mir_toolkit/common.h" namespace mir { namespace shell { class HostLifecycleEventListener { public: virtual void lifecycle_event_occurred(MirLifecycleState state) = 0; protected: HostLifecycleEventListener() = default; virtual ~HostLifecycleEventListener() = default; HostLifecycleEventListener(HostLifecycleEventListener const&) = delete; HostLifecycleEventListener& operator=(HostLifecycleEventListener const&) = delete; }; } } #endif /* MIR_HOST_LIFECYCLE_EVENT_LISTENER_H_ */ ./include/server/mir/shell/shell_wrapper.h0000644000015600001650000000657512676616125020775 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SHELL_SHELL_WRAPPER_H_ #define MIR_SHELL_SHELL_WRAPPER_H_ #include "mir/shell/shell.h" namespace mir { namespace shell { class ShellWrapper : public Shell { public: explicit ShellWrapper(std::shared_ptr const& wrapped); void focus_next_session() override; std::shared_ptr focused_session() const override; void set_focus_to( std::shared_ptr const& focus_session, std::shared_ptr const& focus_surface) override; std::shared_ptr focused_surface() const override; auto surface_at(geometry::Point cursor) const -> std::shared_ptr override; void raise(SurfaceSet const& surfaces) override; std::shared_ptr open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) override; void close_session(std::shared_ptr const& session) override; std::shared_ptr start_prompt_session_for( std::shared_ptr const& session, scene::PromptSessionCreationParameters const& params) override; void add_prompt_provider_for( std::shared_ptr const& prompt_session, std::shared_ptr const& session) override; void stop_prompt_session(std::shared_ptr const& prompt_session) override; frontend::SurfaceId create_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override; void modify_surface(std::shared_ptr const& session, std::shared_ptr const& surface, SurfaceSpecification const& modifications) override; void destroy_surface(std::shared_ptr const& session, frontend::SurfaceId surface) override; int set_surface_attribute( std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) override; int get_surface_attribute( std::shared_ptr const& surface, MirSurfaceAttrib attrib) override; void raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface, uint64_t timestamp) override; void add_display(geometry::Rectangle const& area) override; void remove_display(geometry::Rectangle const& area) override; bool handle(MirEvent const& event) override; protected: std::shared_ptr const wrapped; }; } } #endif /* MIR_SHELL_SHELL_WRAPPER_H_ */ ./include/server/mir/shell/window_manager.h0000644000015600001650000000562712676616125021124 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SHELL_WINDOW_MANAGER_H_ #define MIR_SHELL_WINDOW_MANAGER_H_ #include "mir/frontend/surface_id.h" #include "mir_toolkit/common.h" #include "mir_toolkit/event.h" #include namespace mir { namespace geometry { struct Rectangle; } namespace scene { class Session; class Surface; struct SurfaceCreationParameters; } namespace shell { struct SurfaceSpecification; /// interface to provide window management logic class WindowManager { public: virtual void add_session(std::shared_ptr const& session) = 0; virtual void remove_session(std::shared_ptr const& session) = 0; virtual frontend::SurfaceId add_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::function const& session, scene::SurfaceCreationParameters const& params)> const& build) = 0; virtual void modify_surface( std::shared_ptr const& session, std::shared_ptr const& surface, SurfaceSpecification const& modifications) = 0; virtual void remove_surface( std::shared_ptr const& session, std::weak_ptr const& surface) = 0; virtual void add_display(geometry::Rectangle const& area) = 0; virtual void remove_display(geometry::Rectangle const& area) = 0; virtual bool handle_keyboard_event(MirKeyboardEvent const* event) = 0; virtual bool handle_touch_event(MirTouchEvent const* event) = 0; virtual bool handle_pointer_event(MirPointerEvent const* event) = 0; virtual int set_surface_attribute( std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) = 0; virtual void handle_raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface, uint64_t timestamp) = 0; virtual ~WindowManager() = default; WindowManager() = default; WindowManager(WindowManager const&) = delete; WindowManager& operator=(WindowManager const&) = delete; }; } } #endif /* MIR_SHELL_WINDOW_MANAGER_H_ */ ./include/server/mir/shell/focus_controller.h0000644000015600001650000000406412676616125021477 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_SHELL_FOCUS_CONTROLLER_H_ #define MIR_SHELL_FOCUS_CONTROLLER_H_ #include #include namespace mir { namespace geometry { struct Point; } namespace scene { class Session; class Surface; } namespace shell { using SurfaceSet = std::set, std::owner_less>>; // TODO I don't think this interface serves a meaningful purpose // TODO (It is referenced by a couple of example WindowManagers, and // TODO to get the active session in unity-system-compositor.) // TODO I think there's a better approach possible. class FocusController { public: virtual ~FocusController() = default; virtual void focus_next_session() = 0; virtual auto focused_session() const -> std::shared_ptr = 0; virtual void set_focus_to( std::shared_ptr const& focus_session, std::shared_ptr const& focus_surface) = 0; virtual std::shared_ptr focused_surface() const = 0; virtual auto surface_at(geometry::Point cursor) const -> std::shared_ptr = 0; virtual void raise(SurfaceSet const& surfaces) = 0; protected: FocusController() = default; FocusController(FocusController const&) = delete; FocusController& operator=(FocusController const&) = delete; }; } } // namespace mir #endif // MIR_SHELL_FOCUS_CONTROLLER_H_ ./include/server/mir/shell/surface_specification.h0000644000015600001650000000601612676616125022444 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SHELL_SURFACE_SPECIFICATION_H_ #define MIR_SHELL_SURFACE_SPECIFICATION_H_ #include "mir/optional_value.h" #include "mir_toolkit/common.h" #include "mir/frontend/surface_id.h" #include "mir/frontend/buffer_stream_id.h" #include "mir/geometry/point.h" #include "mir/geometry/displacement.h" #include "mir/graphics/buffer_properties.h" #include "mir/graphics/display_configuration.h" #include #include namespace mir { namespace scene { class Surface; } namespace shell { struct SurfaceAspectRatio { unsigned width; unsigned height; }; struct StreamSpecification { frontend::BufferStreamId stream_id; geometry::Displacement displacement; }; /// Specification of surface properties requested by client struct SurfaceSpecification { bool is_empty() const; optional_value width; optional_value height; optional_value pixel_format; optional_value buffer_usage; optional_value name; optional_value output_id; optional_value type; optional_value state; optional_value preferred_orientation; optional_value parent_id; optional_value aux_rect; optional_value edge_attachment; optional_value min_width; optional_value min_height; optional_value max_width; optional_value max_height; optional_value width_inc; optional_value height_inc; optional_value min_aspect; optional_value max_aspect; optional_value> streams; optional_value> parent; optional_value> input_shape; // TODO scene::SurfaceCreationParameters overlaps this content but has additional fields: // geometry::Point top_left; // input::InputReceptionMode input_mode; // // it also has size instead of width + height // Maybe SurfaceCreationParameters /HasA/ SurfaceSpecification? mir::optional_value shell_chrome; }; } } #endif /* MIR_SHELL_SURFACE_SPECIFICATION_H_ */ ./include/server/mir/shell/system_compositor_window_manager.h0000644000015600001650000000766512676616125025012 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SHELL_SYSTEM_COMPOSITOR_WINDOW_MANAGER_H_ #define MIR_SHELL_SYSTEM_COMPOSITOR_WINDOW_MANAGER_H_ #include "mir/shell/window_manager.h" #include "mir/graphics/display_configuration.h" #include #include namespace mir { namespace scene { class SessionCoordinator; } namespace shell { class FocusController; class DisplayLayout; /** Minimal window management for system compositing. */ class SystemCompositorWindowManager : public WindowManager { public: SystemCompositorWindowManager( FocusController* focus_controller, std::shared_ptr const& display_layout, std::shared_ptr const& session_coordinator); /** @name Customization points * These are the likely events that a system compositor will care about * @{ */ /// Called when a session first connects (before any surfaces are ready) virtual void on_session_added(std::shared_ptr const& session) const; /// Called when a session disconnects virtual void on_session_removed(std::shared_ptr const& session) const; /// Called the first time each surface owned by the session posts its first buffer virtual void on_session_ready(std::shared_ptr const& session) const; /** @} */ protected: FocusController* const focus_controller; std::shared_ptr const display_layout; std::shared_ptr const session_coordinator; private: void add_session(std::shared_ptr const& session) override; void remove_session(std::shared_ptr const& session) override; frontend::SurfaceId add_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::function const& session, scene::SurfaceCreationParameters const& params)> const& build) override; void modify_surface( std::shared_ptr const& session, std::shared_ptr const& surface, SurfaceSpecification const& modifications) override; void remove_surface( std::shared_ptr const& session, std::weak_ptr const& surface) override; void add_display(geometry::Rectangle const& area) override; void remove_display(geometry::Rectangle const& area) override; bool handle_keyboard_event(MirKeyboardEvent const* event) override; bool handle_touch_event(MirTouchEvent const* event) override; bool handle_pointer_event(MirPointerEvent const* event) override; void handle_raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface, uint64_t timestamp) override; int set_surface_attribute( std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) override; using OutputMap = std::map, graphics::DisplayConfigurationOutputId, std::owner_less>>; std::mutex mutable mutex; OutputMap output_map; }; } } #endif /* MIR_SHELL_SYSTEM_COMPOSITOR_WINDOW_MANAGER_H_ */ ./include/server/mir/shell/shell.h0000644000015600001650000000647712676616125017236 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SHELL_SHELL_H_ #define MIR_SHELL_SHELL_H_ #include "mir/shell/focus_controller.h" #include "mir/input/event_filter.h" #include "mir/frontend/surface_id.h" #include "mir/compositor/display_listener.h" #include "mir_toolkit/common.h" #include namespace mir { namespace frontend { class EventSink; } namespace geometry { struct Rectangle; } namespace scene { class PromptSession; class PromptSessionManager; class PromptSessionCreationParameters; class SessionCoordinator; class Surface; class SurfaceCreationParameters; } namespace shell { class InputTargeter; class SurfaceSpecification; class SurfaceStack; class Shell : public virtual FocusController, public virtual input::EventFilter, public virtual compositor::DisplayListener { public: /** @name these functions support frontend requests * @{ */ virtual std::shared_ptr open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) = 0; virtual void close_session(std::shared_ptr const& session) = 0; virtual std::shared_ptr start_prompt_session_for( std::shared_ptr const& session, scene::PromptSessionCreationParameters const& params) = 0; virtual void add_prompt_provider_for( std::shared_ptr const& prompt_session, std::shared_ptr const& session) = 0; virtual void stop_prompt_session(std::shared_ptr const& prompt_session) = 0; virtual frontend::SurfaceId create_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) = 0; virtual void modify_surface( std::shared_ptr const& session, std::shared_ptr const& surface, shell::SurfaceSpecification const& modifications) = 0; virtual void destroy_surface(std::shared_ptr const& session, frontend::SurfaceId surface) = 0; virtual int set_surface_attribute( std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) = 0; virtual int get_surface_attribute( std::shared_ptr const& surface, MirSurfaceAttrib attrib) = 0; virtual void raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface, uint64_t timestamp) = 0; /** @} */ }; } } #endif /* MIR_SHELL_SHELL_H_ */ ./include/server/mir/shell/abstract_shell.h0000644000015600001650000001222412676616125021104 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SHELL_ABSTRACT_SHELL_H_ #define MIR_SHELL_ABSTRACT_SHELL_H_ #include "mir/shell/shell.h" #include "mir/shell/window_manager_builder.h" #include namespace mir { namespace shell { class ShellReport; class WindowManager; /// Minimal Shell implementation with none of the necessary window management logic class AbstractShell : public virtual Shell, public virtual FocusController { public: AbstractShell( std::shared_ptr const& input_targeter, std::shared_ptr const& surface_stack, std::shared_ptr const& session_coordinator, std::shared_ptr const& prompt_session_manager, std::shared_ptr const& report, WindowManagerBuilder const& wm_builder); ~AbstractShell() noexcept; std::shared_ptr open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) override; void close_session(std::shared_ptr const& session) override; frontend::SurfaceId create_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override; void modify_surface(std::shared_ptr const& session, std::shared_ptr const& surface, SurfaceSpecification const& modifications) override; void destroy_surface(std::shared_ptr const& session, frontend::SurfaceId surface) override; int set_surface_attribute( std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) override; int get_surface_attribute( std::shared_ptr const& surface, MirSurfaceAttrib attrib) override; void raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface, uint64_t timestamp) override; std::shared_ptr start_prompt_session_for( std::shared_ptr const& session, scene::PromptSessionCreationParameters const& params) override; void add_prompt_provider_for( std::shared_ptr const& prompt_session, std::shared_ptr const& session) override; void stop_prompt_session(std::shared_ptr const& prompt_session) override; /** @name these come from FocusController * Focus changes are notified to the derived class via the private setting_focus_to() * functions. * \note I think the FocusController interface is unnecessary as: * 1. the functions are only meaningful in the context of implementing a Shell * 2. the implementation of these functions is Shell behaviour * Simply providing them as part of AbstractShell is probably adequate. * @{ */ void focus_next_session() override; std::shared_ptr focused_session() const override; // More useful than FocusController::set_focus_to()! void set_focus_to( std::shared_ptr const& focus_session, std::shared_ptr const& focus_surface) override; // The surface with focus std::shared_ptr focused_surface() const override; auto surface_at(geometry::Point cursor) const -> std::shared_ptr override; void raise(SurfaceSet const& surfaces) override; /** @} */ void add_display(geometry::Rectangle const& area) override; void remove_display(geometry::Rectangle const& area) override; bool handle(MirEvent const& event) override; protected: std::shared_ptr const input_targeter; std::shared_ptr const surface_stack; std::shared_ptr const session_coordinator; std::shared_ptr const prompt_session_manager; std::shared_ptr const window_manager; private: std::shared_ptr const report; std::mutex mutable focus_mutex; std::weak_ptr focus_surface; std::weak_ptr focus_session; void set_focus_to_locked( std::unique_lock const& lock, std::shared_ptr const& focus_session, std::shared_ptr const& focus_surface); }; } } #endif /* MIR_SHELL_ABSTRACT_SHELL_H_ */ ./include/server/mir/shell/display_layout.h0000644000015600001650000000405512676616125021157 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_SHELL_DISPLAY_LAYOUT_H_ #define MIR_SHELL_DISPLAY_LAYOUT_H_ #include "mir/graphics/display_configuration.h" namespace mir { namespace geometry { struct Rectangle; } namespace shell { /** * Interface to the layout of the display outputs. */ class DisplayLayout { public: virtual ~DisplayLayout() = default; /** * Clips a rectangle to the output it is in. * * @param [in,out] rect the rectangle to clip */ virtual void clip_to_output(geometry::Rectangle& rect) = 0; /** * Makes a rectangle take up the whole area of the output it is in. * * @param [in,out] rect the rectangle to make fullscreen */ virtual void size_to_output(geometry::Rectangle& rect) = 0; /** * Places a rectangle in an particular output if the display is known, * otherwise does nothing. * * @param [in] id the id of the output to place the rectangle in * @param [in,out] rect the rectangle to place * @return true iff the display id is recognised */ virtual bool place_in_output(graphics::DisplayConfigurationOutputId id, geometry::Rectangle& rect) = 0; protected: DisplayLayout() = default; DisplayLayout(DisplayLayout const&) = delete; DisplayLayout& operator=(DisplayLayout const&) = delete; }; } } #endif /* MIR_SHELL_DISPLAY_LAYOUT_H_ */ ./include/server/mir/shell/input_targeter.h0000644000015600001650000000246612676616125021155 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_SHELL_INPUT_TARGETER_H_ #define MIR_SHELL_INPUT_TARGETER_H_ #include namespace mir { namespace input { class Surface; } namespace shell { /// An interface used to control the selection of keyboard input focus. class InputTargeter { public: virtual ~InputTargeter() = default; virtual void set_focus(std::shared_ptr const& focus_surface) = 0; virtual void clear_focus() = 0; protected: InputTargeter() = default; InputTargeter(InputTargeter const&) = delete; InputTargeter& operator=(InputTargeter const&) = delete; }; } } // namespace mir #endif // MIR_SHELL_INPUT_TARGETER_H_ ./include/server/mir/shell/surface_stack_wrapper.h0000644000015600001650000000275112676616125022473 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SHELL_SURFACE_STACK_WRAPPER_H #define MIR_SHELL_SURFACE_STACK_WRAPPER_H #include "mir/shell/surface_stack.h" namespace mir { namespace shell { class SurfaceStackWrapper : public SurfaceStack { public: explicit SurfaceStackWrapper(std::shared_ptr const& wrapped); void add_surface( std::shared_ptr const&, input::InputReceptionMode new_mode) override; void raise(std::weak_ptr const& surface) override; void raise(SurfaceSet const& surfaces) override; void remove_surface(std::weak_ptr const& surface) override; auto surface_at(geometry::Point) const -> std::shared_ptr override; protected: std::shared_ptr const wrapped; }; } } #endif //MIR_SHELL_SURFACE_STACK_WRAPPER_H ./include/server/mir/compositor/0000755000015600001650000000000012676616125017027 5ustar jenkinsjenkins./include/server/mir/compositor/compositor.h0000644000015600001650000000214212676616125021375 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_COMPOSITOR_COMPOSITOR_H_ #define MIR_COMPOSITOR_COMPOSITOR_H_ namespace mir { namespace compositor { class Compositor { public: virtual ~Compositor() {} virtual void start() = 0; virtual void stop() = 0; protected: Compositor() = default; Compositor(Compositor const&) = delete; Compositor& operator=(Compositor const&) = delete; }; } } #endif // MIR_COMPOSITOR_COMPOSITOR_H_ ./include/server/mir/compositor/compositor_report.h0000644000015600001650000000332112676616125022770 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_COMPOSITOR_COMPOSITOR_REPORT_H_ #define MIR_COMPOSITOR_COMPOSITOR_REPORT_H_ #include "mir/graphics/renderable.h" namespace mir { namespace compositor { class CompositorReport { public: typedef const void* SubCompositorId; // e.g. thread/display buffer ID virtual void added_display(int width, int height, int x, int y, SubCompositorId id) = 0; virtual void began_frame(SubCompositorId id) = 0; virtual void renderables_in_frame(SubCompositorId id, graphics::RenderableList const& renderables) = 0; virtual void rendered_frame(SubCompositorId id) = 0; virtual void finished_frame(SubCompositorId id) = 0; virtual void started() = 0; virtual void stopped() = 0; virtual void scheduled() = 0; protected: CompositorReport() = default; virtual ~CompositorReport() = default; CompositorReport(CompositorReport const&) = delete; CompositorReport& operator=(CompositorReport const&) = delete; }; } // namespace compositor } // namespace mir #endif // MIR_COMPOSITOR_COMPOSITOR_REPORT_H_ ./include/server/mir/compositor/scene.h0000644000015600001650000000531512676616125020301 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_COMPOSITOR_SCENE_H_ #define MIR_COMPOSITOR_SCENE_H_ #include "compositor_id.h" #include #include namespace mir { namespace scene { class Observer; } namespace compositor { class SceneElement; using SceneElementSequence = std::vector>; class Scene { public: virtual ~Scene() {} /** * Generate a valid sequence of scene elements based on the current state of the Scene. * \param [in] id An arbitrary unique identifier used to distinguish * separate compositors which need to receive a sequence * for rendering. Calling with the same id will return * a new (different) sequence to that user each time. For * consistency, all callers need to determine their id * in the same way (e.g. always use "this" pointer). * \returns a sequence of mc::SceneElements for the compositor id. The * sequence is in stacking order from back to front. */ virtual SceneElementSequence scene_elements_for(CompositorID id) = 0; /** * Return the number of additional frames that you need to render to get * fully up to date with the latest data in the scene. For a generic * "scene change" this will be just 1. For surfaces that have multiple * frames queued up however, it could be greater than 1. When the result * reaches zero, you know you have consumed all the latest data from the * scene. */ virtual int frames_pending(CompositorID id) const = 0; virtual void register_compositor(CompositorID id) = 0; virtual void unregister_compositor(CompositorID id) = 0; virtual void add_observer(std::shared_ptr const& observer) = 0; virtual void remove_observer(std::weak_ptr const& observer) = 0; protected: Scene() = default; private: Scene(Scene const&) = delete; Scene& operator=(Scene const&) = delete; }; } } #endif /* MIR_COMPOSITOR_SCENE_H_ */ ./include/server/mir/compositor/display_listener.h0000644000015600001650000000240412676616125022552 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_COMPOSITOR_DISPLAY_LISTENER_H_ #define MIR_COMPOSITOR_DISPLAY_LISTENER_H_ namespace mir { namespace geometry { struct Rectangle; } namespace compositor { class DisplayListener { public: virtual void add_display(geometry::Rectangle const& area) = 0; virtual void remove_display(geometry::Rectangle const& area) = 0; protected: DisplayListener() = default; virtual ~DisplayListener() = default; DisplayListener(DisplayListener const&) = delete; DisplayListener& operator=(DisplayListener const&) = delete; }; } } #endif /* MIR_COMPOSITOR_DISPLAY_LISTENER_H_ */ ./include/server/mir/compositor/compositor_id.h0000644000015600001650000000154612676616125022060 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_COMPOSITOR_COMPOSITOR_ID_H_ #define MIR_COMPOSITOR_COMPOSITOR_ID_H_ namespace mir { namespace compositor { using CompositorID = void const*; } } #endif ./include/server/mir/compositor/display_buffer_compositor.h0000644000015600001650000000253612676616125024462 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths * Alexandros Frantzis */ #ifndef MIR_COMPOSITOR_DISPLAY_BUFFER_COMPOSITOR_H_ #define MIR_COMPOSITOR_DISPLAY_BUFFER_COMPOSITOR_H_ #include "mir/compositor/scene.h" namespace mir { namespace compositor { class DisplayBufferCompositor { public: virtual ~DisplayBufferCompositor() = default; virtual void composite(SceneElementSequence&& scene_sequence) = 0; protected: DisplayBufferCompositor() = default; DisplayBufferCompositor& operator=(DisplayBufferCompositor const&) = delete; DisplayBufferCompositor(DisplayBufferCompositor const&) = delete; }; } } #endif /* MIR_COMPOSITOR_DISPLAY_BUFFER_COMPOSITOR_H_ */ ./include/server/mir/compositor/display_buffer_compositor_factory.h0000644000015600001650000000302512676616125026203 0ustar jenkinsjenkins/* * Copyright © 2012-2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths * Alexandros Frantzis */ #ifndef MIR_COMPOSITOR_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ #define MIR_COMPOSITOR_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ #include namespace mir { namespace graphics { class DisplayBuffer; } namespace compositor { class DisplayBufferCompositor; class DisplayBufferCompositorFactory { public: virtual ~DisplayBufferCompositorFactory() = default; virtual std::unique_ptr create_compositor_for(graphics::DisplayBuffer& display_buffer) = 0; protected: DisplayBufferCompositorFactory() = default; DisplayBufferCompositorFactory& operator=(DisplayBufferCompositorFactory const&) = delete; DisplayBufferCompositorFactory(DisplayBufferCompositorFactory const&) = delete; }; } } #endif /* MIR_COMPOSITOR_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ */ ./include/server/mir/compositor/scene_element.h0000644000015600001650000000277312676616125022017 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_COMPOSITOR_SCENE_ELEMENT_H_ #define MIR_COMPOSITOR_SCENE_ELEMENT_H_ #include namespace mir { namespace graphics { class Renderable; } namespace compositor { class Decoration; class SceneElement { public: virtual ~SceneElement() = default; virtual std::shared_ptr renderable() const = 0; virtual void rendered() = 0; virtual void occluded() = 0; //TODO: Decoration is opaque on purpose. It is only used by an internal example, // and this function should be removed from the public API. virtual std::unique_ptr decoration() const = 0; protected: SceneElement() = default; SceneElement(SceneElement const&) = delete; SceneElement& operator=(SceneElement const&) = delete; }; } } #endif // MIR_COMPOSITOR_SCENE_ELEMENT_H_ ./include/server/mir/frontend/0000755000015600001650000000000012676616126016451 5ustar jenkinsjenkins./include/server/mir/frontend/session_credentials.h0000644000015600001650000000205412676616125022662 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef MIR_FRONTEND_SESSION_CREDENTIALS_ID_H_ #define MIR_FRONTEND_SESSION_CREDENTIALS_ID_H_ #include namespace mir { namespace frontend { class SessionCredentials { public: SessionCredentials(pid_t pid, uid_t uid, gid_t gid); pid_t pid() const; uid_t uid() const; gid_t gid() const; private: SessionCredentials() = delete; pid_t the_pid; uid_t the_uid; gid_t the_gid; }; } } #endif ./include/server/mir/frontend/surface.h0000644000015600001650000000346412676616125020260 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_SURFACE_H_ #define MIR_FRONTEND_SURFACE_H_ #include "mir/frontend/buffer_stream.h" #include "mir/geometry/size.h" #include "mir/geometry/displacement.h" #include "mir_toolkit/common.h" #include #include namespace mir { namespace graphics { class Buffer; class CursorImage; } namespace frontend { class ClientBufferTracker; class BufferStream; class Surface { public: virtual ~Surface() = default; /// Size of the client area of the surface (excluding any decorations) virtual geometry::Size client_size() const = 0; virtual std::shared_ptr primary_buffer_stream() const = 0; virtual bool supports_input() const = 0; virtual int client_input_fd() const = 0; virtual void set_cursor_image(std::shared_ptr const& image) = 0; virtual void set_cursor_stream(std::shared_ptr const& image, geometry::Displacement const& hotspot) = 0; protected: Surface() = default; Surface(Surface const&) = delete; Surface& operator=(Surface const&) = delete; }; } } #endif /* MIR_FRONTEND_SURFACE_H_ */ ./include/server/mir/frontend/buffer_sink.h0000644000015600001650000000244312676616125021121 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_FRONTEND_BUFFER_SINK_H_ #define MIR_FRONTEND_BUFFER_SINK_H_ #include "mir/frontend/buffer_stream_id.h" #include "mir/graphics/platform_ipc_operations.h" namespace mir { namespace graphics { class Buffer; } namespace frontend { class BufferSink { public: virtual ~BufferSink() = default; virtual void send_buffer(frontend::BufferStreamId id, graphics::Buffer& buffer, graphics::BufferIpcMsgType) = 0; protected: BufferSink() = default; BufferSink(BufferSink const&) = delete; BufferSink& operator=(BufferSink const&) = delete; }; } } // namespace mir #endif // MIR_FRONTEND_BUFFER_SINK_H_ ./include/server/mir/frontend/session_mediator_report.h0000644000015600001650000000515512676616125023571 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_SESSION_MEDIATOR_REPORT_H_ #define MIR_FRONTEND_SESSION_MEDIATOR_REPORT_H_ #include #include namespace mir { namespace frontend { // Interface for monitoring application activity class SessionMediatorReport { public: virtual ~SessionMediatorReport() = default; virtual void session_connect_called(std::string const& app_name) = 0; virtual void session_create_surface_called(std::string const& app_name) = 0; virtual void session_next_buffer_called(std::string const& app_name) = 0; virtual void session_exchange_buffer_called(std::string const& app_name) = 0; virtual void session_submit_buffer_called(std::string const& app_name) = 0; virtual void session_allocate_buffers_called(std::string const& app_name) = 0; virtual void session_release_buffers_called(std::string const& app_name) = 0; virtual void session_release_surface_called(std::string const& app_name) = 0; virtual void session_disconnect_called(std::string const& app_name) = 0; virtual void session_configure_surface_called(std::string const& app_name) = 0; virtual void session_configure_surface_cursor_called(std::string const& app_name) = 0; virtual void session_configure_display_called(std::string const& app_name) = 0; virtual void session_set_base_display_configuration_called(std::string const& app_name) = 0; virtual void session_start_prompt_session_called(std::string const& app_name, pid_t application_process) = 0; virtual void session_stop_prompt_session_called(std::string const& app_name) = 0; virtual void session_create_buffer_stream_called(std::string const& app_name) = 0; virtual void session_release_buffer_stream_called(std::string const& app_name) = 0; virtual void session_error( std::string const& app_name, char const* method, std::string const& what) = 0; }; } } #endif /* MIR_FRONTEND_SESSION_MEDIATOR_REPORT_H_ */ ./include/server/mir/frontend/session_authorizer.h0000644000015600001650000000306012676616125022557 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #ifndef MIR_FRONTEND_SESSION_AUTHORIZER_H_ #define MIR_FRONTEND_SESSION_AUTHORIZER_H_ namespace mir { namespace frontend { class SessionCredentials; class SessionAuthorizer { public: virtual ~SessionAuthorizer() = default; virtual bool connection_is_allowed(SessionCredentials const& creds) = 0; virtual bool configure_display_is_allowed(SessionCredentials const& creds) = 0; virtual bool set_base_display_configuration_is_allowed(SessionCredentials const& creds) = 0; virtual bool screencast_is_allowed(SessionCredentials const& creds) = 0; virtual bool prompt_session_is_allowed(SessionCredentials const& creds) = 0; protected: SessionAuthorizer() = default; SessionAuthorizer(SessionAuthorizer const&) = delete; SessionAuthorizer& operator=(SessionAuthorizer const&) = delete; }; } } // namespace mir #endif // MIR_FRONTEND_SESSION_AUTHORIZER_H_ ./include/server/mir/frontend/prompt_session.h0000644000015600001650000000225212676616125021706 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #ifndef MIR_FRONTEND_PROMPT_SESSION_H_ #define MIR_FRONTEND_PROMPT_SESSION_H_ #include "mir_toolkit/common.h" #include #include #include #include namespace mir { namespace frontend { class PromptSession { public: virtual ~PromptSession() = default; protected: PromptSession() = default; PromptSession(const PromptSession&) = delete; PromptSession& operator=(const PromptSession&) = delete; }; } } #endif // MIR_FRONTEND_PROMPT_SESSION_H_ ./include/server/mir/frontend/buffer_stream.h0000644000015600001650000000410412676616125021444 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_FRONTEND_BUFFER_STREAM_H_ #define MIR_FRONTEND_BUFFER_STREAM_H_ #include #include "mir/graphics/buffer_id.h" #include #include namespace mir { namespace graphics { class Buffer; struct BufferProperties; } namespace scene { class SurfaceObserver; } namespace frontend { class BufferStream { public: virtual ~BufferStream() = default; virtual void swap_buffers(graphics::Buffer* old_buffer, std::function complete) = 0; virtual void add_observer(std::shared_ptr const& observer) = 0; virtual void remove_observer(std::weak_ptr const& observer) = 0; virtual void with_most_recent_buffer_do( std::function const& exec) = 0; virtual MirPixelFormat pixel_format() const = 0; virtual graphics::BufferID allocate_buffer(graphics::BufferProperties const&) = 0; virtual void remove_buffer(graphics::BufferID) = 0; virtual void with_buffer(graphics::BufferID id, std::function const& fn) = 0; virtual void allow_framedropping(bool) = 0; virtual void set_scale(float scale) = 0; protected: BufferStream() = default; BufferStream(BufferStream const&) = delete; BufferStream& operator=(BufferStream const&) = delete; }; } } #endif /* MIR_FRONTEND_BUFFER_STREAM_H_ */ ./include/server/mir/frontend/session.h0000644000015600001650000000316412676616125020310 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #ifndef MIR_FRONTEND_SESSION_H_ #define MIR_FRONTEND_SESSION_H_ #include "mir_toolkit/common.h" #include "mir/frontend/surface_id.h" #include "mir/frontend/buffer_stream_id.h" #include #include namespace mir { namespace graphics { class DisplayConfiguration; struct BufferProperties; } namespace frontend { class Surface; class BufferStream; class Session { public: virtual ~Session() = default; virtual std::shared_ptr get_surface(SurfaceId surface) const = 0; virtual std::shared_ptr get_buffer_stream(BufferStreamId stream) const = 0; virtual BufferStreamId create_buffer_stream(graphics::BufferProperties const& props) = 0; virtual void destroy_buffer_stream(BufferStreamId stream) = 0; virtual std::string name() const = 0; protected: Session() = default; Session(Session const&) = delete; Session& operator=(Session const&) = delete; }; } } #endif // MIR_FRONTEND_SESSION_H_ ./include/server/mir/input/0000755000015600001650000000000012676616160015767 5ustar jenkinsjenkins./include/server/mir/input/pointer_configuration.h0000644000015600001650000000462112676616125022553 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_POINTER_CONFIGURATION_H_ #define MIR_INPUT_POINTER_CONFIGURATION_H_ #include "mir_toolkit/common.h" #include "mir_toolkit/client_types.h" #include "mir_toolkit/mir_input_device.h" namespace mir { namespace input { struct PointerConfiguration { PointerConfiguration() {} PointerConfiguration(MirPointerHandedness handedness, MirPointerAcceleration acceleration, double acceleration_bias, double horizontal_scroll_scale, double vertical_scroll_scale) : handedness{handedness}, acceleration{acceleration}, cursor_acceleration_bias{acceleration_bias}, horizontal_scroll_scale{horizontal_scroll_scale}, vertical_scroll_scale{vertical_scroll_scale} { } /*! * Configure which button shall be used as primary button. That way the input device is configured to be either * right or left handed. */ MirPointerHandedness handedness{mir_pointer_handedness_right}; /*! * Configure cursor acceleration profile */ MirPointerAcceleration acceleration{mir_pointer_acceleration_adaptive}; /*! * Configures the intensity of the cursor acceleration. Values within the range of [-1, 1] are allowed. * - 0: default acceleration * - [-1, 0): reduced acceleration * - (0, 1]: increased acceleration */ double cursor_acceleration_bias{0.0}; /*! * Configures a signed scale of the horizontal scrolling. Use negative values to configure 'natural scrolling' */ double horizontal_scroll_scale{1.0}; /*! * Configures a signed scale of the vertical scrolling. Use negative values to configure 'natural scrolling' */ double vertical_scroll_scale{1.0}; }; } } #endif ./include/server/mir/input/cursor_listener.h0000644000015600001650000000240012676616125021357 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_CURSOR_LISTENER_H_ #define MIR_INPUT_CURSOR_LISTENER_H_ namespace mir { namespace input { /// An interface for listening to absolute cursor events (without context): For example to update /// the position of the visible cursor. class CursorListener { public: virtual ~CursorListener() = default; virtual void cursor_moved_to(float abs_x, float abs_y) = 0; protected: CursorListener() = default; CursorListener(CursorListener const&) = delete; CursorListener& operator=(CursorListener const&) = delete; }; } } #endif // MIR_INPUT_CURSOR_LISTENER_H_ ./include/server/mir/input/surface.h0000644000015600001650000000331512676616125017573 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_INPUT_SURFACE_H_ #define MIR_INPUT_SURFACE_H_ #include "mir/geometry/point.h" #include "mir/geometry/rectangle.h" #include "mir/input/input_reception_mode.h" #include "mir_toolkit/event.h" #include #include namespace mir { namespace graphics { class CursorImage; } namespace scene { class SurfaceObserver; } namespace input { class InputChannel; class Surface { public: virtual std::string name() const = 0; virtual geometry::Rectangle input_bounds() const = 0; virtual bool input_area_contains(geometry::Point const& point) const = 0; virtual std::shared_ptr input_channel() const = 0; virtual std::shared_ptr cursor_image() const = 0; virtual InputReceptionMode reception_mode() const = 0; virtual void consume(MirEvent const* event) = 0; protected: Surface() = default; virtual ~Surface() = default; Surface(const Surface&) = delete; Surface& operator=(const Surface& ) = delete; }; } } #endif /* MIR_INPUT_SURFACE_H_ */ ./include/server/mir/input/input_dispatcher.h0000644000015600001650000000257212676616125021514 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_INPUT_DISPATCHER_H #define MIR_INPUT_INPUT_DISPATCHER_H #include #include "mir_toolkit/event.h" namespace mir { namespace input { /*! * \brief The InputDispatchers role is to decide what should happen with user input events. * * It will receive MirEvents with either MirMotionEvent or MirKeyboardEvent inside. The InputDispatcher * implementation shall either handle the input without informing any clients or pick a client * surface and send the event to it. */ class InputDispatcher { public: virtual bool dispatch(MirEvent const& event) = 0; virtual void start() = 0; virtual void stop() = 0; virtual ~InputDispatcher() = default; }; } } #endif ./include/server/mir/input/input_reception_mode.h0000644000015600001650000000162112676616125022354 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_RECEPTION_MODE_H_ #define MIR_INPUT_RECEPTION_MODE_H_ namespace mir { namespace input { enum class InputReceptionMode { normal, receives_all_input }; } } #endif /* MIR_INPUT_RECEPTION_MODE_H_ */ ./include/server/mir/input/device.h0000644000015600001650000000324112676616125017400 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_DEVICE_H_ #define MIR_INPUT_DEVICE_H_ #include "mir/input/device_capability.h" #include "mir_toolkit/event.h" #include "mir/optional_value.h" #include namespace mir { namespace input { class PointerConfiguration; class TouchpadConfiguration; class Device { public: Device() = default; virtual ~Device() = default; virtual MirInputDeviceId id() const = 0; virtual DeviceCapabilities capabilities() const = 0; virtual std::string name() const = 0; virtual std::string unique_id() const = 0; virtual mir::optional_value pointer_configuration() const = 0; virtual void apply_pointer_configuration(PointerConfiguration const&) = 0; virtual mir::optional_value touchpad_configuration() const = 0; virtual void apply_touchpad_configuration(TouchpadConfiguration const&) = 0; private: Device(Device const&) = delete; Device& operator=(Device const&) = delete; }; } } #endif ./include/server/mir/input/touchpad_configuration.h0000644000015600001650000000555512676616125022711 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_TOUCH_PAD_CONFIGURATION_H_ #define MIR_INPUT_TOUCH_PAD_CONFIGURATION_H_ #include "mir_toolkit/common.h" #include "mir_toolkit/mir_input_device.h" namespace mir { namespace input { struct TouchpadConfiguration { TouchpadConfiguration() {} TouchpadConfiguration(MirTouchpadClickModes click_mode, MirTouchpadScrollModes scroll_mode, int button_down_scroll_button, bool tap_to_click, bool disable_while_typing, bool disable_with_mouse, bool middle_mouse_button_emulation) : click_mode{click_mode}, scroll_mode{scroll_mode}, button_down_scroll_button{button_down_scroll_button}, tap_to_click{tap_to_click}, middle_mouse_button_emulation{middle_mouse_button_emulation}, disable_with_mouse{disable_with_mouse}, disable_while_typing{disable_while_typing} { } /*! * The click mode defines when the touchpad generates software emulated button events. */ MirTouchpadClickModes click_mode{mir_touchpad_click_mode_finger_count}; /*! * The scroll mode defines when the touchpad generates scroll events instead of pointer motion events. */ MirTouchpadScrollModes scroll_mode{mir_touchpad_scroll_mode_two_finger_scroll}; /*! * Configures the button used for the on-button-down scroll mode */ int button_down_scroll_button{0}; /*! * When tap to click is enabled the system will interpret short finger touch down/up sequences as button clicks. */ bool tap_to_click{true}; /*! * Emulates a middle mouse button press when the left and right buttons on a touchpad are pressed. */ bool middle_mouse_button_emulation{true}; /*! * When disable-with-mouse is enabled the touchpad will stop to emit user input events when another pointing device is plugged in. */ bool disable_with_mouse{false}; /*! * When disable-with-mouse is enabled the touchpad will stop to emit user input events when the user starts to use a keyboard and a short period after. */ bool disable_while_typing{false}; }; } } #endif ./include/server/mir/input/input_device_hub.h0000644000015600001650000000256012676616157021465 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_INPUT_DEVICE_HUB_H_ #define MIR_INPUT_INPUT_DEVICE_HUB_H_ #include namespace mir { namespace input { class Device; class InputDeviceInfo; class InputDeviceObserver; class InputDeviceHub { public: InputDeviceHub() = default; virtual ~InputDeviceHub() = default; virtual void add_observer(std::shared_ptr const&) = 0; virtual void remove_observer(std::weak_ptr const&) = 0; virtual void for_each_input_device(std::function const& callback) = 0; InputDeviceHub(InputDeviceHub const&) = delete; InputDeviceHub& operator=(InputDeviceHub const&) = delete; }; } } #endif ./include/server/mir/input/input_channel.h0000644000015600001650000000230112676616125020764 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_INPUT_CHANNEL_H_ #define MIR_INPUT_INPUT_CHANNEL_H_ namespace mir { namespace input { /// Encapsulates a paired set of fd's suitable for input communication. class InputChannel { public: virtual ~InputChannel() {} virtual int client_fd() const = 0; virtual int server_fd() const = 0; protected: InputChannel() = default; InputChannel(InputChannel const&) = delete; InputChannel& operator=(InputChannel const&) = delete; }; } } // namespace mir #endif // MIR_INPUT_INPUT_CHANNEL_H_ ./include/server/mir/input/cursor_images.h0000644000015600001650000000303712676616125021006 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_CURSOR_IMAGES_H_ #define MIR_INPUT_CURSOR_IMAGES_H_ #include "mir/geometry/size.h" #include #include namespace mir { namespace graphics { class CursorImage; } namespace input { /// CursorImages is used to lookup cursor images from the system theme. geometry::Size const default_cursor_size{geometry::Width{24}, geometry::Height{24}}; class CursorImages { public: virtual ~CursorImages() = default; /// Looks up the image for a named cursor. Cursor names /// follow the XCursor naming conventions. virtual std::shared_ptr image(std::string const& cursor_name, geometry::Size const& size) = 0; protected: CursorImages() = default; CursorImages(CursorImages const&) = delete; CursorImages& operator=(CursorImages const&) = delete; }; } } #endif /* MIR_INPUT_CURSOR_IMAGES_H_ */ ./include/server/mir/input/event_filter.h0000644000015600001650000000225712676616125020635 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_EVENT_FILTER_H_ #define MIR_INPUT_EVENT_FILTER_H_ #include "mir_toolkit/event.h" namespace mir { namespace input { class EventFilter { public: virtual ~EventFilter() = default; // \return true indicates the event was consumed by the filter virtual bool handle(MirEvent const& event) = 0; protected: EventFilter() = default; EventFilter(const EventFilter&) = delete; EventFilter& operator=(const EventFilter&) = delete; }; } } #endif // MIR_INPUT_EVENT_FILTER_H_ ./include/server/mir/input/composite_event_filter.h0000644000015600001650000000217312676616125022714 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_INPUT_COMPOSITE_EVENT_FILTER_H_ #define MIR_INPUT_COMPOSITE_EVENT_FILTER_H_ #include "mir/input/event_filter.h" #include namespace mir { namespace input { class CompositeEventFilter : public EventFilter { public: virtual void append(std::shared_ptr const& filter) = 0; virtual void prepend(std::shared_ptr const& filter) = 0; }; } } #endif /* MIR_INPUT_COMPOSITE_EVENT_FILTER_H_ */ ./include/server/mir/input/input_manager.h0000644000015600001650000000264712676616157021010 0ustar jenkinsjenkins/* * Copyright © 2012, 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr * Daniel d'Andradra * Andreas Pokorny */ #ifndef MIR_INPUT_INPUT_MANAGER_H_ #define MIR_INPUT_INPUT_MANAGER_H_ #include namespace mir { namespace input { class Platform; class InputManager { public: // TODO Remove add_platform() when we next break mirserver ABI __attribute__ ((deprecated)) virtual void add_platform(std::shared_ptr const& platform) = 0; virtual void start() = 0; virtual void stop() = 0; protected: InputManager() {}; virtual ~InputManager() {} InputManager(const InputManager&) = delete; InputManager& operator=(const InputManager&) = delete; }; } } #endif // MIR_INPUT_INPUT_MANAGER ./include/server/mir/input/touch_visualizer.h0000644000015600001650000000351312676616125021542 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_TOUCH_VISUALIZER_H_ #define MIR_INPUT_TOUCH_VISUALIZER_H_ #include "mir/geometry/point.h" #include namespace mir { namespace input { /// An interface for listening to a low level stream of touches, in order to provide // a "spot" style visualization. class TouchVisualizer { public: virtual ~TouchVisualizer() = default; struct Spot { geometry::Point touch_location; // If pressure is zero, the touch-point can be interpreted as a hover. float pressure; }; // Toggle visualization of touches virtual void enable() = 0; virtual void disable() = 0; // Visualize a given set of touches statelessly. virtual void visualize_touches(std::vector const& touches) = 0; protected: TouchVisualizer() = default; TouchVisualizer(const TouchVisualizer&) = delete; TouchVisualizer& operator=(const TouchVisualizer&) = delete; }; inline bool operator==(TouchVisualizer::Spot const &lhs, TouchVisualizer::Spot const& rhs) { return lhs.touch_location == rhs.touch_location && lhs.pressure == rhs.pressure; } } } #endif // MIR_INPUT_TOUCH_VISUALIZER_H_ ./include/server/mir/input/input_device_observer.h0000644000015600001650000000265212676616125022533 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_INPUT_DEVICE_OBSERVER_H_ #define MIR_INPUT_INPUT_DEVICE_OBSERVER_H_ #include namespace mir { namespace input { class Device; class InputDeviceObserver { public: InputDeviceObserver() = default; virtual ~InputDeviceObserver() = default; virtual void device_added(std::shared_ptr const& device) = 0; virtual void device_changed(std::shared_ptr const& device) = 0; virtual void device_removed(std::shared_ptr const& device) = 0; /*! * Called after every group of changes. */ virtual void changes_complete() = 0; InputDeviceObserver(InputDeviceObserver const&) = delete; InputDeviceObserver& operator=(InputDeviceObserver const&) = delete; }; } } #endif ./include/server/mir/lockable_callback.h0000644000015600001650000000221412676616125020371 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #ifndef MIR_LOCKABLE_CALLBACK_H_ #define MIR_LOCKABLE_CALLBACK_H_ namespace mir { class LockableCallback { public: virtual ~LockableCallback() = default; virtual void operator()() = 0; virtual void lock() = 0; virtual void unlock() = 0; protected: LockableCallback() = default; LockableCallback(LockableCallback const&) = delete; LockableCallback& operator=(LockableCallback const&) = delete; }; } #endif /* MIR_LOCKABLE_CALLBACK_H_ */ ./include/server/mir/server_action_queue.h0000644000015600001650000000252212676616125021052 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_SERVER_ACTION_QUEUE_H_ #define MIR_SERVER_ACTION_QUEUE_H_ #include namespace mir { typedef std::function ServerAction; class ServerActionQueue { public: virtual ~ServerActionQueue() = default; virtual void enqueue(void const* owner, ServerAction const& action) = 0; virtual void pause_processing_for(void const* owner) = 0; virtual void resume_processing_for(void const* owner) = 0; protected: ServerActionQueue() = default; ServerActionQueue(ServerActionQueue const&) = delete; ServerActionQueue& operator=(ServerActionQueue const&) = delete; }; } #endif /* MIR_SERVER_ACTION_QUEUE_H_ */ ./include/server/mir/graphics/0000755000015600001650000000000012676616125016431 5ustar jenkinsjenkins./include/server/mir/graphics/default_display_configuration_policy.h0000644000015600001650000000321012676616125026255 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_DEFAULT_DISPLAY_CONFIGURATION_POLICY_H_ #define MIR_GRAPHICS_DEFAULT_DISPLAY_CONFIGURATION_POLICY_H_ #include "mir/graphics/display_configuration_policy.h" namespace mir { namespace graphics { /** @name default DisplayConfigurationPolicy options. * Some simple default implementations: Mir will default to CloneDisplayConfigurationPolicy, * The others are plausible defaults for display servers that don't want to do anything * more sophisticated. * @{ */ /// All screens placed at (0, 0) class CloneDisplayConfigurationPolicy : public DisplayConfigurationPolicy { public: void apply_to(DisplayConfiguration& conf); }; /// Each screen placed to the right of the previous one class SideBySideDisplayConfigurationPolicy : public DisplayConfigurationPolicy { public: void apply_to(graphics::DisplayConfiguration& conf); }; /// Just use the first screen class SingleDisplayConfigurationPolicy : public DisplayConfigurationPolicy { public: void apply_to(graphics::DisplayConfiguration& conf); }; /** @} */ } } #endif /* MIR_GRAPHICS_DEFAULT_DISPLAY_CONFIGURATION_POLICY_H_ */ ./include/server/mir/graphics/display_configuration_report.h0000644000015600001650000000257412676616125024601 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_GRAPHICS_DISPLAY_CONFIGURATION_REPORT_H #define MIR_GRAPHICS_DISPLAY_CONFIGURATION_REPORT_H namespace mir { namespace graphics { class DisplayConfiguration; class DisplayConfigurationReport { public: virtual void initial_configuration(DisplayConfiguration const& configuration) = 0; virtual void new_configuration(DisplayConfiguration const& configuration) = 0; protected: DisplayConfigurationReport() = default; virtual ~DisplayConfigurationReport() = default; DisplayConfigurationReport(DisplayConfigurationReport const& ) = delete; DisplayConfigurationReport& operator=(DisplayConfigurationReport const& ) = delete; }; } } #endif //MIR_GRAPHICS_DISPLAY_CONFIGURATION_REPORT_H ./include/server/mir/time/0000755000015600001650000000000012676616126015570 5ustar jenkinsjenkins./include/server/mir/time/alarm_factory.h0000644000015600001650000000456012676616125020570 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Alberto Aguirre */ #ifndef MIR_TIME_ALARM_FACTORY_H_ #define MIR_TIME_ALARM_FACTORY_H_ #include "mir/time/alarm.h" #include #include #include namespace mir { class LockableCallback; namespace time { class Alarm; class AlarmFactory { public: virtual ~AlarmFactory() = default; /** * \brief Create an Alarm that will not fire until scheduled * * \param callback Function to call when the Alarm signals * * \return A handle to an Alarm that can later be scheduled */ virtual std::unique_ptr create_alarm(std::function const& callback) = 0; /** * \brief Create an Alarm that will not fire until scheduled * * A LockableCallback allows the user to preserve lock ordering * in situations where Alarm methods need to be called under external lock * and the callback implementation needs to run code protected by the same * lock. An alarm implementation may have internal locks of its own, which * maybe acquired during callback dispatching; to preserve lock ordering * LockableCallback::lock is invoked during callback dispatch before * any internal locks are acquired. * * \param callback Function to call when the Alarm signals * \return A handle to an Alarm that can later be scheduled */ virtual std::unique_ptr create_alarm(std::shared_ptr const& callback) = 0; protected: AlarmFactory() = default; AlarmFactory(AlarmFactory const&) = delete; AlarmFactory& operator=(AlarmFactory const&) = delete; }; } } #endif // MIR_TIME_ALARM_FACTORY_H_ ./include/server/mir/time/alarm.h0000644000015600001650000000475512676616125017047 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TIME_ALARM_H_ #define MIR_TIME_ALARM_H_ #include "mir/time/types.h" namespace mir { namespace time { /** * A one-shot, resettable handle to trigger a callback at a later time * \note All members of Alarm are threadsafe * \note All members of Alarm are safe to call from the Alarm's callback */ class Alarm { public: enum State { pending, /**< Will trigger the callback at some point in the future */ cancelled, /**< The callback has been cancelled before being triggered */ triggered /**< The callback has been called */ }; Alarm() = default; /** * \note Destruction of the Alarm guarantees that the callback will not subsequently be called */ virtual ~Alarm() = default; /** * Cancels a pending alarm * * \note Has no effect if the Alarm is in the Triggered state. * \note cancel() is idempotent * * \return True iff the state of the Alarm is now Cancelled */ virtual bool cancel() = 0; virtual State state() const = 0; /** * Reschedule the alarm * \param delay Delay, in milliseconds, before the Alarm will be triggered * \return True if this reschedule supersedes a previous not-yet-triggered timeout * * \note This cancels any previous timeout set. */ virtual bool reschedule_in(std::chrono::milliseconds delay) = 0; /** * Reschedule the alarm * \param timeout Time point when the alarm should be triggered * \return True if this reschedule supersedes a previous not-yet-triggered timeout * * \note This cancels any previous timeout set. */ virtual bool reschedule_for(Timestamp timeout) = 0; Alarm(Alarm const&) = delete; Alarm& operator=(Alarm const&) = delete; }; } } #endif ./include/server/mir/terminate_with_current_exception.h0000644000015600001650000000201412676616125023642 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TERMINATE_WITH_CURRENT_EXCEPTION_H_ #define MIR_TERMINATE_WITH_CURRENT_EXCEPTION_H_ namespace mir { void terminate_with_current_exception(); /// called by main thread to rethrow any termination exception void check_for_termination_exception(); void clear_termination_exception(); } #endif /* MIR_TERMINATE_WITH_CURRENT_EXCEPTION_H_ */ ./include/server/mir/report_exception.h0000644000015600001650000000215612676616125020377 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_EXCEPTION_H_ #define MIR_REPORT_EXCEPTION_H_ #include namespace mir { /** * Call this from a catch block (and only from a catch block) * to write error information to an output stream. */ void report_exception(std::ostream& out); /** * Call this from a catch block (and only from a catch block) * to write error information to std:cerr. */ void report_exception(); } #endif /* MIR_REPORT_EXCEPTION_H_ */ ./include/platforms/0000755000015600001650000000000012676616124014542 5ustar jenkinsjenkins./include/platforms/mesa/0000755000015600001650000000000012676616124015467 5ustar jenkinsjenkins./include/platforms/mesa/mir_toolkit/0000755000015600001650000000000012676616124020023 5ustar jenkinsjenkins./include/platforms/mesa/mir_toolkit/mesa/0000755000015600001650000000000012676616125020751 5ustar jenkinsjenkins./include/platforms/mesa/mir_toolkit/mesa/native_display.h0000644000015600001650000000335412676616125024142 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_TOOLKIT_MESA_NATIVE_DISPLAY_H #define MIR_TOOLKIT_MESA_NATIVE_DISPLAY_H #include #define MIR_MESA_TRUE 1 #define MIR_MESA_FALSE 0 #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif typedef struct MirMesaEGLNativeDisplay MirMesaEGLNativeDisplay; typedef struct MirMesaEGLNativeSurface MirMesaEGLNativeSurface; typedef struct MirBufferPackage MirBufferPackage; struct MirMesaEGLNativeDisplay { int (*display_get_platform)(MirMesaEGLNativeDisplay* display, MirPlatformPackage* package); void *context; }; struct MirMesaEGLNativeSurface { int (*surface_set_swapinterval)(MirMesaEGLNativeSurface* surface, int interval); int (*surface_advance_buffer)(MirMesaEGLNativeSurface* surface, MirBufferPackage* buffer_package); int (*surface_get_parameters)(MirMesaEGLNativeSurface* surface, MirSurfaceParameters* surface_parameters); }; #ifdef __cplusplus } // extern "C" /**@}*/ #endif #endif /* MIR_TOOLKIT_MESA_NATIVE_DISPLAY_H */ ./include/platforms/mesa/mir_toolkit/mesa/platform_operation.h0000644000015600001650000000311112676616125025022 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TOOLKIT_MESA_PLATFORM_OPERATION_H_ #define MIR_TOOLKIT_MESA_PLATFORM_OPERATION_H_ #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /* * Supported platform operations for the Mesa driver */ enum MirMesaPlatformOperation { auth_magic = 1, auth_fd = 2, set_gbm_device = 3 }; /* * MesaPlatformOperation::auth_magic related structures */ struct MirMesaAuthMagicRequest { unsigned int magic; }; struct MirMesaAuthMagicResponse { int status; /* 0 on success, a positive error number on failure */ }; /* * MesaPlatformOperation::set_gbm_device related structures */ struct gbm_device; struct MirMesaSetGBMDeviceRequest { struct gbm_device* device; }; struct MirMesaSetGBMDeviceResponse { int status; /* 0 on success, a positive error number on failure */ }; #ifdef __cplusplus } /**@}*/ #endif #endif ./include/test/0000755000015600001650000000000012676616124013512 5ustar jenkinsjenkins./include/test/mir/0000755000015600001650000000000012676616124014301 5ustar jenkinsjenkins./include/test/mir/test/0000755000015600001650000000000012676616160015260 5ustar jenkinsjenkins./include/test/mir/test/validity_matchers.h0000644000015600001650000000451712676616125021154 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_VALIDITY_MATCHERS_H_ #define MIR_TEST_VALIDITY_MATCHERS_H_ #include #include "mir_toolkit/mir_client_library.h" using ::testing::MakePolymorphicMatcher; using ::testing::MatchResultListener; using ::testing::NotNull; using ::testing::PolymorphicMatcher; class IsValidMatcher { public: // To implement a polymorphic matcher, first define a COPYABLE class // that has three members MatchAndExplain(), DescribeTo(), and // DescribeNegationTo(), like the following. // In this example, we want to use NotNull() with any pointer, so // MatchAndExplain() accepts a pointer of any type as its first argument. // In general, you can define MatchAndExplain() as an ordinary method or // a method template, or even overload it. template bool MatchAndExplain(T* p, MatchResultListener* listener) const; // Describes the property of a value matching this matcher. void DescribeTo(::std::ostream* os) const { *os << "is valid"; } // Describes the property of a value NOT matching this matcher. void DescribeNegationTo(::std::ostream* os) const { *os << "is not valid"; } }; template<> bool IsValidMatcher::MatchAndExplain(MirConnection* connection, MatchResultListener* listener) const; template<> bool IsValidMatcher::MatchAndExplain(MirSurface* surface, MatchResultListener* listener) const; // To construct a polymorphic matcher, pass an instance of the class // to MakePolymorphicMatcher(). Note the return type. inline PolymorphicMatcher IsValid() { return MakePolymorphicMatcher(IsValidMatcher{}); } #endif // MIR_TEST_VALIDITY_MATCHERS_H_ ./include/test/mir/test/fake_shared.h0000644000015600001650000000172612676616125017674 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_TEST_FAKE_SHARED_H_ #define MIR_TEST_FAKE_SHARED_H_ #include "mir/test/empty_deleter.h" #include namespace mir { namespace test { template std::shared_ptr fake_shared(Type& t) { return {&t, EmptyDeleter()}; } } } #endif /* MIR_TEST_FAKE_SHARED_H_ */ ./include/test/mir/test/event_factory.h0000644000015600001650000000434712676616125020312 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_EVENT_FACTORY_H #define MIR_TEST_EVENT_FACTORY_H #include "mir/geometry/point.h" namespace mir { namespace input { namespace synthesis { enum class EventAction { Down, Up }; class KeyParameters { public: KeyParameters(); KeyParameters& from_device(int device_id); KeyParameters& of_scancode(int scancode); KeyParameters& with_action(EventAction action); int device_id; int scancode; EventAction action; }; KeyParameters a_key_down_event(); KeyParameters a_key_up_event(); class ButtonParameters { public: ButtonParameters(); ButtonParameters& from_device(int device_id); ButtonParameters& of_button(int scancode); ButtonParameters& with_action(EventAction action); int device_id; int button; EventAction action; }; ButtonParameters a_button_down_event(); ButtonParameters a_button_up_event(); class MotionParameters { public: MotionParameters(); MotionParameters& from_device(int device_id); MotionParameters& with_movement(int rel_x, int rel_y); int device_id; int rel_x; int rel_y; }; MotionParameters a_pointer_event(); class TouchParameters { public: enum class Action { Tap = 0, Move, Release }; TouchParameters(); TouchParameters& from_device(int device_id); TouchParameters& at_position(geometry::Point abs_pos); TouchParameters& with_action(Action touch_action); int device_id; int abs_x; int abs_y; Action action; }; TouchParameters a_touch_event(); } } } #endif /* MIR_TEST_EVENT_FACTORY_H */ ./include/test/mir/test/popen.h0000644000015600001650000000241312676616125016553 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #ifndef MIR_TEST_POPEN_H_ #define MIR_TEST_POPEN_H_ #include #include #include namespace mir { namespace test { /** * Popen - A popen c++ wrapper */ class Popen { public: Popen(std::string const& cmd); /** * Read a line from the output of the executed command * returns false if there is nothing more to read */ bool get_line(std::string& line); private: Popen() = delete; Popen(Popen const&) = delete; Popen& operator=(Popen const&) = delete; std::unique_ptr raw_stream; }; } } #endif ./include/test/mir/test/event_matchers.h0000644000015600001650000003546012676616125020451 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr * Andreas Pokorny */ #ifndef MIR_TEST_CLIENT_EVENT_MATCHERS_H_ #define MIR_TEST_CLIENT_EVENT_MATCHERS_H_ #include "mir_toolkit/event.h" #include #include #include void PrintTo(MirEvent const& event, std::ostream *os); void PrintTo(MirEvent const* event, std::ostream *os); namespace mir { namespace test { /*! * Pointer and reference adaptors for MirEvent inside gmock matchers. * \{ */ inline MirEvent const* to_address(MirEvent const* event) { return event; } inline MirEvent const* to_address(MirEvent const& event) { return &event; } inline MirEvent const& to_ref(MirEvent const* event) { return *event; } inline MirEvent const& to_ref(MirEvent const& event) { return event; } inline MirKeyboardEvent const* maybe_key_event(MirEvent const* event) { if (mir_event_get_type(event) != mir_event_type_input) return nullptr; auto input_event = mir_event_get_input_event(event); if (mir_input_event_get_type(input_event) != mir_input_event_type_key) return nullptr; return mir_input_event_get_keyboard_event(input_event); } inline MirTouchEvent const* maybe_touch_event(MirEvent const* event) { if (mir_event_get_type(event) != mir_event_type_input) return nullptr; auto input_event = mir_event_get_input_event(event); if (mir_input_event_get_type(input_event) != mir_input_event_type_touch) return nullptr; return mir_input_event_get_touch_event(input_event); } inline MirPointerEvent const* maybe_pointer_event(MirEvent const* event) { if (mir_event_get_type(event) != mir_event_type_input) return nullptr; auto input_event = mir_event_get_input_event(event); if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer) return nullptr; return mir_input_event_get_pointer_event(input_event); } /** * \} */ MATCHER(KeyDownEvent, "") { auto kev = maybe_key_event(to_address(arg)); if (kev == nullptr) return false; if (mir_keyboard_event_action(kev) != mir_keyboard_action_down) return false; return true; } MATCHER(KeyRepeatEvent, "") { auto kev = maybe_key_event(to_address(arg)); if (kev == nullptr) return false; if (mir_keyboard_event_action(kev) != mir_keyboard_action_repeat) return false; return true; } MATCHER(KeyUpEvent, "") { auto kev = maybe_key_event(to_address(arg)); if (kev == nullptr) return false; if (mir_keyboard_event_action(kev) != mir_keyboard_action_up) return false; return true; } MATCHER_P(KeyWithModifiers, modifiers, "") { auto kev = maybe_key_event(to_address(arg)); if (kev == nullptr) return false; if(mir_keyboard_event_modifiers(kev) != modifiers) { return false; } return true; } MATCHER_P(KeyOfSymbol, keysym, "") { auto kev = maybe_key_event(to_address(arg)); if (kev == nullptr) return false; if(mir_keyboard_event_key_code(kev) != static_cast(keysym)) return false; return true; } MATCHER_P(KeyOfScanCode, code, "") { auto kev = maybe_key_event(to_address(arg)); if (kev == nullptr) return false; if(mir_keyboard_event_scan_code(kev) != code) return false; return true; } MATCHER_P(MirKeyboardEventMatches, event, "") { auto expected = maybe_key_event(to_address(event)); auto actual = maybe_key_event(to_address(arg)); if (expected == nullptr || actual == nullptr) return false; return mir_keyboard_event_action(expected) == mir_keyboard_event_action(actual) && mir_keyboard_event_key_code(expected) == mir_keyboard_event_key_code(actual) && mir_keyboard_event_scan_code(expected) == mir_keyboard_event_scan_code(actual) && mir_keyboard_event_modifiers(expected) == mir_keyboard_event_modifiers(actual); } MATCHER_P(MirTouchEventMatches, event, "") { auto expected = maybe_touch_event(to_address(event)); auto actual = maybe_touch_event(to_address(arg)); if (expected == nullptr || actual == nullptr) return false; auto tc = mir_touch_event_point_count(actual); if (mir_touch_event_point_count(expected) != tc) return false; for (unsigned i = 0; i != tc; i++) { if (mir_touch_event_id(actual, i) != mir_touch_event_id(expected, i) || mir_touch_event_action(actual, i) != mir_touch_event_action(expected, i) || mir_touch_event_tooltype(actual, i) != mir_touch_event_tooltype(expected, i) || mir_touch_event_axis_value(actual, i, mir_touch_axis_x) != mir_touch_event_axis_value(expected, i, mir_touch_axis_x) || mir_touch_event_axis_value(actual, i, mir_touch_axis_y) != mir_touch_event_axis_value(expected, i, mir_touch_axis_y)) { return false; } } return true; } MATCHER(PointerEnterEvent, "") { auto pev = maybe_pointer_event(to_address(arg)); if (pev == nullptr) return false; if (mir_pointer_event_action(pev) == mir_pointer_action_enter) return true; return false; } MATCHER(PointerLeaveEvent, "") { auto pev = maybe_pointer_event(to_address(arg)); if (pev == nullptr) return false; if (mir_pointer_event_action(pev) == mir_pointer_action_leave) return true; return false; } inline bool button_event_matches(MirPointerEvent const* pev, float x, float y, MirPointerAction action, MirPointerButtons button_state, bool check_action = true, bool check_buttons = true, bool check_axes = true) { if (pev == nullptr) return false; if (check_action && mir_pointer_event_action(pev) != action) return false; if (check_buttons && mir_pointer_event_buttons(pev) != button_state) return false; if (check_axes && mir_pointer_event_axis_value(pev, mir_pointer_axis_x) != x) return false; if (check_axes && mir_pointer_event_axis_value(pev, mir_pointer_axis_y) != y) return false; return true; } MATCHER_P2(ButtonDownEvent, x, y, "") { auto pev = maybe_pointer_event(to_address(arg)); return button_event_matches(pev, x, y, mir_pointer_action_button_down, 0, true, false); } MATCHER_P2(ButtonDownEventWithButton, pos, button, "") { auto pev = maybe_pointer_event(to_address(arg)); if (pev == nullptr) return false; if (mir_pointer_event_action(pev) != mir_pointer_action_button_down) return false; if (mir_pointer_event_button_state(pev, static_cast(button)) == false) return false; if (mir_pointer_event_axis_value(pev, mir_pointer_axis_x) != pos.x.as_float()) return false; if (mir_pointer_event_axis_value(pev, mir_pointer_axis_y) != pos.y.as_float()) return false; return true; } MATCHER_P2(ButtonUpEvent, x, y, "") { auto pev = maybe_pointer_event(to_address(arg)); return button_event_matches(pev, x, y, mir_pointer_action_button_up, 0, true, false); } MATCHER_P3(ButtonsDown, x, y, buttons, "") { auto pev = maybe_pointer_event(to_address(arg)); return button_event_matches(pev, x, y, mir_pointer_action_button_down, buttons, false); } MATCHER_P3(ButtonsUp, x, y, buttons, "") { auto pev = maybe_pointer_event(to_address(arg)); return button_event_matches(pev, x, y, mir_pointer_action_button_up, buttons, false); } MATCHER_P2(ButtonUpEventWithButton, pos, button, "") { auto pev = maybe_pointer_event(to_address(arg)); if (pev == nullptr) return false; if (mir_pointer_event_action(pev) != mir_pointer_action_button_up) return false; if (mir_pointer_event_button_state(pev, button) == true) return false; if (mir_pointer_event_axis_value(pev, mir_pointer_axis_x) != pos.x.as_float()) return false; if (mir_pointer_event_axis_value(pev, mir_pointer_axis_y) != pos.y.as_float()) return false; return true; } MATCHER_P2(PointerAxisChange, scroll_axis, value, "") { auto parg = to_address(arg); auto pev = maybe_pointer_event(parg); if (pev == nullptr) return false; if (mir_pointer_event_action(pev) != mir_pointer_action_motion) return false; if (mir_pointer_event_axis_value(pev, scroll_axis) != value) return false; return true; } MATCHER_P2(TouchEvent, x, y, "") { auto tev = maybe_touch_event(to_address(arg)); if (tev == nullptr) return false; if (mir_touch_event_action(tev, 0) != mir_touch_action_down) return false; if (std::abs(mir_touch_event_axis_value(tev, 0, mir_touch_axis_x) - x) > 0.5f) return false; if (std::abs(mir_touch_event_axis_value(tev, 0, mir_touch_axis_y) - y) > 0.5f) return false; return true; } MATCHER_P4(TouchContact, slot, action, x, y, "") { auto tev = maybe_touch_event(to_address(arg)); if (tev == nullptr) return false; if (mir_touch_event_action(tev, slot) != action) return false; if (std::abs(mir_touch_event_axis_value(tev, slot, mir_touch_axis_x) - x) > 0.5f) return false; if (std::abs(mir_touch_event_axis_value(tev, slot, mir_touch_axis_y) - y) > 0.5f) return false; return true; } MATCHER_P2(TouchUpEvent, x, y, "") { auto tev = maybe_touch_event(to_address(arg)); if (tev == nullptr) return false; if (mir_touch_event_action(tev, 0) != mir_touch_action_up) return false; if (mir_touch_event_axis_value(tev, 0, mir_touch_axis_x) != x) return false; if (mir_touch_event_axis_value(tev, 0, mir_touch_axis_y) != y) return false; return true; } MATCHER_P2(PointerEventWithPosition, x, y, "") { auto pev = maybe_pointer_event(to_address(arg)); if (pev == nullptr) return false; if (mir_pointer_event_action(pev) != mir_pointer_action_motion) return false; if (mir_pointer_event_axis_value(pev, mir_pointer_axis_x) != x) return false; if (mir_pointer_event_axis_value(pev, mir_pointer_axis_y) != y) return false; return true; } MATCHER_P(PointerEventWithModifiers, modifiers, "") { auto pev = maybe_pointer_event(to_address(arg)); if (pev && mir_pointer_event_modifiers(pev) == modifiers) return true; return false; } MATCHER_P2(PointerEventWithDiff, dx, dy, "") { auto pev = maybe_pointer_event(to_address(arg)); if (pev == nullptr) return false; if (mir_pointer_event_action(pev) != mir_pointer_action_motion) return false; if (mir_pointer_event_axis_value(pev, mir_pointer_axis_relative_x) != dx) return false; if (mir_pointer_event_axis_value(pev, mir_pointer_axis_relative_y) != dy) return false; return true; } MATCHER_P4(TouchEventInDirection, x0, y0, x1, y1, "") { auto tev = maybe_touch_event(to_address(arg)); if (tev == nullptr) return false; if (mir_touch_event_action(tev, 0) != mir_touch_action_change) return false; auto x2 = mir_touch_event_axis_value(tev, 0, mir_touch_axis_x); auto y2 = mir_touch_event_axis_value(tev, 0, mir_touch_axis_y); float dx1 = x1 - x0; float dy1 = y1 - y0; float dx2 = x2 - x0; float dy2 = y2 - y0; float dot_product = dx1 * dx2 + dy1 * dy2; // Return true if both vectors are roughly the same direction (within // 90 degrees). return dot_product > 0.0f; } MATCHER(TouchMovementEvent, "") { auto tev = maybe_touch_event(to_address(arg)); if (tev == nullptr) return false; if (mir_touch_event_action(tev, 0) != mir_touch_action_change) return false; return true; } MATCHER(PointerMovementEvent, "") { auto pev = maybe_pointer_event(to_address(arg)); if (pev == nullptr) return false; if (mir_pointer_event_action(pev) != mir_pointer_action_motion) return false; return true; } MATCHER_P2(SurfaceEvent, attrib, value, "") { auto as_address = to_address(arg); if (mir_event_get_type(as_address) != mir_event_type_surface) return false; auto surface_ev = mir_event_get_surface_event(as_address); if (mir_surface_event_get_attribute(surface_ev) != attrib) return false; if (mir_surface_event_get_attribute_value(surface_ev) != value) return false; return true; } MATCHER_P(KeymapEventForDevice, device_id, "") { auto as_address = to_address(arg); if (mir_event_get_type(as_address) != mir_event_type_keymap) return false; auto kmev = mir_event_get_keymap_event(as_address); return device_id == mir_keymap_event_get_device_id(kmev); } MATCHER_P(OrientationEvent, direction, "") { auto as_address = to_address(arg); if (mir_event_get_type(as_address) != mir_event_type_orientation) return false; auto oev = mir_event_get_orientation_event(as_address); if (mir_orientation_event_get_direction(oev) != direction) return false; return true; } MATCHER_P(InputDeviceIdMatches, device_id, "") { if (mir_event_get_type(to_address(arg)) != mir_event_type_input) return false; auto input_event = mir_event_get_input_event(to_address(arg)); return mir_input_event_get_device_id(input_event) == device_id; } MATCHER(InputConfigurationEvent, "") { auto as_address = to_address(arg); if (mir_event_get_type(as_address) != mir_event_type_input_configuration) return true; return false; } MATCHER(InputDeviceConfigurationChangedEvent, "") { auto as_address = to_address(arg); if (mir_event_get_type(as_address) != mir_event_type_input_configuration) return false; auto idev = mir_event_get_input_configuration_event(as_address); if (mir_input_configuration_event_get_action(idev) != mir_input_configuration_action_configuration_changed) return false; return true; } MATCHER(InputDeviceResetEvent, "") { auto as_address = to_address(arg); if (mir_event_get_type(as_address) != mir_event_type_input_configuration) return false; auto idev = mir_event_get_input_configuration_event(as_address); if (mir_input_configuration_event_get_action(idev) != mir_input_configuration_action_device_reset) return false; return true; } } } #endif ./include/test/mir/test/empty_deleter.h0000644000015600001650000000156712676616125020305 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_EMPTY_DELETER_H_ #define MIR_TEST_EMPTY_DELETER_H_ namespace mir { struct EmptyDeleter { void operator()(void const* ) { } }; } #endif /* MIR_TEST_EMPTY_DELETER_H_ */ ./include/test/mir/test/wait_condition.h0000644000015600001650000000364312676616157020457 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voß */ #ifndef MIR_TEST_WAIT_CONDITION_H_ #define MIR_TEST_WAIT_CONDITION_H_ #include #include #include #include namespace mir { namespace test { struct WaitCondition { WaitCondition() : woken_(false) {} void wait_for_at_most_seconds(std::chrono::seconds const& seconds) { std::unique_lock ul(guard); condition.wait_for(ul, seconds, [this] { return woken_; }); } void wait_for_at_most_seconds(int seconds) { wait_for_at_most_seconds(std::chrono::seconds(seconds)); } void wake_up_everyone() { std::unique_lock ul(guard); woken_ = true; condition.notify_all(); } bool woken() { std::unique_lock ul(guard); return woken_; } void reset() { std::lock_guard ul(guard); woken_ = false; } std::mutex guard; std::condition_variable condition; bool woken_; }; ACTION_P(ReturnFalseAndWakeUp, wait_condition) { wait_condition->wake_up_everyone(); return false; } ACTION_P(WakeUp, wait_condition) { wait_condition->wake_up_everyone(); } } } #endif // MIR_TEST_WAIT_CONDITION_H_ ./include/test/mir/test/spin_wait.h0000644000015600001650000000207112676616125017427 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_SPIN_WAIT_H_ #define MIR_TEST_SPIN_WAIT_H_ #include #include namespace mir { namespace test { bool spin_wait_for_condition_or_timeout( std::function const& condition, std::chrono::milliseconds timeout, std::chrono::milliseconds spin_period = std::chrono::milliseconds{10}); } } #endif /* MIR_TEST_SPIN_WAIT_H_ */ ./include/test/mir/test/auto_unblock_thread.h0000644000015600001650000000477512676616125021463 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ /** AutoUnblockThread is a helper thread class that can gracefully shutdown * at destruction time. This is helpul for tests that botch create * threads and use ASSERT macros for example (or any other condition that * makes the test exit early). Using naked std::thread would call std::terminate * under such conditions. */ #ifndef MIR_TEST_AUTO_UNBLOCK_THREAD_H_ #define MIR_TEST_AUTO_UNBLOCK_THREAD_H_ #include #include namespace mir { namespace test { class AutoJoinThread { public: AutoJoinThread() = default; template explicit AutoJoinThread(Callable&& f, Args&&... args) : thread{std::forward(f), std::forward(args)...} {} ~AutoJoinThread() { stop(); } void stop() { if (thread.joinable()) thread.join(); } std::thread::native_handle_type native_handle() { return thread.native_handle(); } AutoJoinThread(AutoJoinThread&& t) = default; AutoJoinThread& operator=(AutoJoinThread&& t) = default; private: std::thread thread; }; class AutoUnblockThread : public AutoJoinThread { public: AutoUnblockThread() = default; template explicit AutoUnblockThread(std::function const& unblock, Callable&& f, Args&&... args) : AutoJoinThread{std::forward(f), std::forward(args)...}, unblock{unblock} {} ~AutoUnblockThread() { stop(); } AutoUnblockThread(AutoUnblockThread&& t) = default; AutoUnblockThread& operator=(AutoUnblockThread&& t) = default; void stop() { if (unblock) unblock(); AutoJoinThread::stop(); } private: std::function unblock; }; } } #endif ./include/test/mir/test/cross_process_sync.h0000644000015600001650000000356212676616125021363 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #ifndef MIR_TEST_CROSS_PROCESS_SYNC_H_ #define MIR_TEST_CROSS_PROCESS_SYNC_H_ #include namespace mir { namespace test { // A cross-process synchronization primitive that supports simple // wait-condition-like scenarios. class CrossProcessSync { public: CrossProcessSync(); CrossProcessSync(const CrossProcessSync& rhs); ~CrossProcessSync() noexcept; CrossProcessSync& operator=(const CrossProcessSync& rhs); // Try to signal the other side that we are ready for at most duration milliseconds. // Throws a std::runtime_error if not successful. void try_signal_ready_for(const std::chrono::milliseconds& duration); void try_signal_ready_for(); // Wait for the other sides to signal readiness for at most duration milliseconds. // Returns the number of ready signals that have been collected since creation. // Throws std::runtime_error if not successful. unsigned int wait_for_signal_ready_for(const std::chrono::milliseconds& duration); unsigned int wait_for_signal_ready_for(); void signal_ready(); unsigned int wait_for_signal_ready(); private: int fds[2]; unsigned int counter; }; } } #endif // MIR_TEST_CROSS_PROCESS_SYNC_H_ ./include/test/mir/test/doubles/0000755000015600001650000000000012676616160016715 5ustar jenkinsjenkins./include/test/mir/test/doubles/nested_mock_egl.h0000644000015600001650000000230112676616125022205 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_NESTED_MOCK_EGL_H_ #define MIR_TEST_DOUBLES_NESTED_MOCK_EGL_H_ #include "mir/test/doubles/mock_egl.h" namespace mir { namespace test { namespace doubles { /// MockEGL with configuration for operating a nested server. class NestedMockEGL : public ::testing::NiceMock { public: NestedMockEGL(); private: void egl_initialize(EGLint* major, EGLint* minor); void egl_choose_config(EGLConfig* config, EGLint* num_config); }; } } } #endif /* MIR_TEST_DOUBLES_NESTED_MOCK_EGL_H_ */ ./include/test/mir/test/doubles/mock_egl.h0000644000015600001650000001327412676616125020656 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #ifndef MIR_TEST_DOUBLES_MOCK_EGL_H_ #define MIR_TEST_DOUBLES_MOCK_EGL_H_ #include #include #include #include #define GL_GLEXT_PROTOTYPES #define EGL_EGLEXT_PROTOTYPES #include #include //for GL extensions #include #include namespace mir { namespace test { namespace doubles { MATCHER_P(AttrMatches, val, std::string("matches")) { auto i = 0; while ((val[i] != EGL_NONE) && (arg[i] != EGL_NONE)) { if (val[i] != arg[i]) return false; i++; } if ((val[i] == EGL_NONE) && (arg[i] == EGL_NONE)) { return true; } return false; } MATCHER_P2(EGLConfigContainsAttrib, attrib, value, "") { bool attrib_position = true; bool attrib_found = false; while (!attrib_position || *arg != EGL_NONE) { if (attrib_position && *arg == attrib) { attrib_found = true; } else if (!attrib_position) { if (attrib_found && *arg == value) { return true; } attrib_found = false; } attrib_position = !attrib_position; ++arg; } return false; } class MockEGL { public: MockEGL(); virtual ~MockEGL(); void expect_nested_egl_usage(); void provide_egl_extensions(); // Provide a functional version of eglSwapBuffers on stubbed platforms // When enabled, if an instance of mir::client::EGLNativeSurface is passed to // eglCreateWindowSurface, then the returned EGLSurface can be used with // eglSwapBuffers to invoke EGLNativeSurface::request_and_wait_for_next_buffer void provide_stub_platform_buffer_swapping(); typedef void (*generic_function_pointer_t)(void); MOCK_METHOD1(eglGetDisplay, EGLDisplay(NativeDisplayType)); MOCK_METHOD3(eglInitialize, EGLBoolean(EGLDisplay,EGLint*,EGLint*)); MOCK_METHOD1(eglTerminate, EGLBoolean(EGLDisplay)); MOCK_METHOD2(eglQueryString,const char*(EGLDisplay, EGLint)); MOCK_METHOD1(eglBindApi, EGLBoolean(EGLenum)); MOCK_METHOD1(eglGetProcAddress,generic_function_pointer_t(const char*)); // Config management MOCK_METHOD4(eglGetConfigs, EGLBoolean(EGLDisplay,EGLConfig*,EGLint,EGLint*)); MOCK_METHOD5(eglChooseConfig, EGLBoolean(EGLDisplay, const EGLint*,EGLConfig*,EGLint,EGLint*)); MOCK_METHOD4(eglGetConfigAttrib, EGLBoolean(EGLDisplay,EGLConfig,EGLint,EGLint*)); // Surface management MOCK_METHOD4(eglCreateWindowSurface, EGLSurface(EGLDisplay,EGLConfig,NativeWindowType,const EGLint*)); MOCK_METHOD4(eglCreatePixmapSurface, EGLSurface(EGLDisplay,EGLConfig,NativePixmapType,const EGLint*)); MOCK_METHOD3(eglCreatePbufferSurface, EGLSurface(EGLDisplay,EGLConfig,const EGLint*)); MOCK_METHOD2(eglDestroySurface, EGLBoolean(EGLDisplay,EGLSurface)); MOCK_METHOD4(eglQuerySurface, EGLBoolean(EGLDisplay,EGLSurface,EGLint,EGLint*)); // EGL 1.1 render-to-texture APIs MOCK_METHOD4(eglSurfaceAttrib, EGLBoolean(EGLDisplay,EGLSurface,EGLint,EGLint)); MOCK_METHOD3(eglBindTexImage, EGLBoolean(EGLDisplay,EGLSurface,EGLint)); MOCK_METHOD3(eglReleaseTexImage, EGLBoolean(EGLDisplay,EGLSurface,EGLint)); // EGL 1.1 swap control API MOCK_METHOD2(eglSwapInterval, EGLBoolean(EGLDisplay,EGLint)); MOCK_METHOD4(eglCreateContext, EGLContext(EGLDisplay,EGLConfig,EGLContext,const EGLint*)); MOCK_METHOD2(eglDestroyContext, EGLBoolean(EGLDisplay,EGLContext)); MOCK_METHOD4(eglMakeCurrent, EGLBoolean(EGLDisplay,EGLSurface,EGLSurface,EGLContext)); MOCK_METHOD0(eglGetCurrentContext,EGLContext()); MOCK_METHOD1(eglGetCurrentSurface,EGLSurface(EGLint)); MOCK_METHOD0(eglGetCurrentDisplay, EGLDisplay()); MOCK_METHOD4(eglQueryContext, EGLBoolean(EGLDisplay,EGLContext,EGLint,EGLint*)); MOCK_METHOD0(eglWaitGL, EGLBoolean()); MOCK_METHOD1(eglWaitNative, EGLBoolean(EGLint)); MOCK_METHOD2(eglSwapBuffers, EGLBoolean(EGLDisplay,EGLSurface)); MOCK_METHOD3(eglCopyBuffers, EGLBoolean(EGLDisplay,EGLSurface,NativePixmapType)); MOCK_METHOD0(eglGetError, EGLint (void)); MOCK_METHOD5(eglCreateImageKHR, EGLImageKHR(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint*)); MOCK_METHOD2(eglDestroyImageKHR,EGLBoolean(EGLDisplay, EGLImageKHR)); MOCK_METHOD2(glEGLImageTargetTexture2DOES, void(GLenum, GLeglImageOES)); MOCK_METHOD3(eglCreateSyncKHR, EGLSyncKHR(EGLDisplay, EGLenum, EGLint const*)); MOCK_METHOD2(eglDestroySyncKHR, EGLBoolean(EGLDisplay, EGLSyncKHR)); MOCK_METHOD4(eglClientWaitSyncKHR, EGLint(EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR)); EGLDisplay const fake_egl_display; EGLConfig const* const fake_configs; EGLint const fake_configs_num; EGLSurface const fake_egl_surface; EGLContext const fake_egl_context; EGLImageKHR const fake_egl_image; int const fake_visual_id; std::mutex mutable current_contexts_mutex; std::unordered_map current_contexts; }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_EGL_H_ */ ./include/test/mir/test/doubles/null_display.h0000644000015600001650000000502212676616125021565 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_DISPLAY_H_ #define MIR_TEST_DOUBLES_NULL_DISPLAY_H_ #include "mir/graphics/display.h" #include "mir/graphics/virtual_output.h" #include "mir/test/doubles/null_gl_context.h" #include "mir/test/doubles/null_display_configuration.h" #include "mir/test/doubles/null_display_sync_group.h" namespace mir { namespace test { namespace doubles { class NullDisplay : public graphics::Display { public: void for_each_display_sync_group(std::function const& f) override { f(group); } std::unique_ptr configuration() const override { return std::unique_ptr( new NullDisplayConfiguration ); } void configure(graphics::DisplayConfiguration const&) override{} void register_configuration_change_handler( graphics::EventHandlerRegister&, graphics::DisplayConfigurationChangeHandler const&) override { } void register_pause_resume_handlers(graphics::EventHandlerRegister&, graphics::DisplayPauseHandler const&, graphics::DisplayResumeHandler const&) override { } void pause() override{} void resume() override {} std::shared_ptr create_hardware_cursor(std::shared_ptr const& /* initial_image */) override { return {}; } std::unique_ptr create_gl_context() override { return std::unique_ptr{new NullGLContext()}; } std::unique_ptr create_virtual_output(int /*width*/, int /*height*/) override { return nullptr; } NullDisplaySyncGroup group; }; } } } #endif /* MIR_TEST_DOUBLES_NULL_DISPLAY_H_ */ ./include/test/mir/test/doubles/mock_prompt_session_listener.h0000644000015600001650000000326012676616125025072 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #ifndef MIR_TEST_DOUBLES_MOCK_PROMPT_SESSION_LISTENER_H_ #define MIR_TEST_DOUBLES_MOCK_PROMPT_SESSION_LISTENER_H_ #include "mir/scene/prompt_session_listener.h" #include namespace mir { namespace test { namespace doubles { struct MockPromptSessionListener : public scene::PromptSessionListener { virtual ~MockPromptSessionListener() noexcept(true) {} MOCK_METHOD1(starting, void(std::shared_ptr const&)); MOCK_METHOD1(stopping, void(std::shared_ptr const&)); MOCK_METHOD1(suspending, void(std::shared_ptr const&)); MOCK_METHOD1(resuming, void(std::shared_ptr const&)); MOCK_METHOD2(prompt_provider_added, void(scene::PromptSession const&, std::shared_ptr const&)); MOCK_METHOD2(prompt_provider_removed, void(scene::PromptSession const&, std::shared_ptr const&)); }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_MOCK_PROMPT_SESSION_LISTENER_H_ ./include/test/mir/test/doubles/stub_cursor_image.h0000644000015600001650000000234412676616125022606 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #ifndef MIR_TEST_DOUBLES_STUB_CURSOR_IMAGE_H_ #define MIR_TEST_DOUBLES_STUB_CURSOR_IMAGE_H_ #include "mir/graphics/cursor_image.h" namespace mir { namespace test { namespace doubles { struct StubCursorImage : public mir::graphics::CursorImage { void const* as_argb_8888() const override { return nullptr; } geometry::Size size() const override { return geometry::Size{16, 16}; } geometry::Displacement hotspot() const override { return geometry::Displacement{0, 0}; } }; } } } // namespace mir #endif /* MIR_TEST_DOUBLES_STUB_CURSOR_H_ */ ./include/test/mir/test/doubles/null_logger.h0000644000015600001650000000221412676616125021377 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_DOUBLES_NULL_LOGGER_H_ #define MIR_TEST_DOUBLES_NULL_LOGGER_H_ #include "mir/logging/logger.h" namespace mir { namespace test { namespace doubles { /// Command line option to enable logging_opt extern char const* const logging_opt; extern char const* const logging_descr; class NullLogger : public mir::logging::Logger { void log(mir::logging::Severity, const std::string&, const std::string&) override; }; } } } #endif /* MIR_TEST_DOUBLES_NULL_LOGGER_H_ */ ./include/test/mir/test/doubles/null_platform_ipc_operations.h0000644000015600001650000000330312676616125025042 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_NULL_PLATFORM_IPC_OPERATIONS_H_ #define MIR_TEST_DOUBLES_NULL_PLATFORM_IPC_OPERATIONS_H_ #include "mir/graphics/platform_ipc_operations.h" #include "mir/graphics/platform_operation_message.h" namespace mir { namespace test { namespace doubles { class NullPlatformIpcOperations : public graphics::PlatformIpcOperations { public: void pack_buffer(graphics::BufferIpcMessage&, graphics::Buffer const&, graphics::BufferIpcMsgType) const override { } void unpack_buffer(graphics::BufferIpcMessage&, graphics::Buffer const&) const override { } std::shared_ptr connection_ipc_package() override { return std::make_shared(); } graphics::PlatformOperationMessage platform_operation( unsigned int const, graphics::PlatformOperationMessage const&) override { return graphics::PlatformOperationMessage(); } }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_NULL_PLATFORM_IPC_OPERATIONS_H_ ./include/test/mir/test/doubles/fake_display.h0000644000015600001650000000371112676616125021524 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_TEST_DOUBLES_FAKE_DISPLAY_H_ #define MIR_TEST_DOUBLES_FAKE_DISPLAY_H_ #include "mir/test/doubles/null_display.h" #include "mir/test/pipe.h" #include "mir/geometry/rectangle.h" #include #include namespace mir { namespace test { namespace doubles { class FakeDisplay : public NullDisplay { public: FakeDisplay(); explicit FakeDisplay(std::vector const& output_rects); void for_each_display_sync_group(std::function const& f) override; std::unique_ptr configuration() const override; void register_configuration_change_handler( mir::graphics::EventHandlerRegister& handlers, mir::graphics::DisplayConfigurationChangeHandler const& handler) override; void configure(mir::graphics::DisplayConfiguration const&) override; void emit_configuration_change_event( std::shared_ptr const& new_config); void wait_for_configuration_change_handler(); private: std::shared_ptr config; std::vector> groups; Pipe p; std::atomic handler_called; }; } } } #endif //MIR_TEST_DOUBLES_FAKE_DISPLAY_H_ ./include/test/mir/test/doubles/stub_session_authorizer.h0000644000015600001650000000316512676616125024070 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_STUB_SESSION_AUTHORIZER_H_ #define MIR_TEST_DOUBLES_STUB_SESSION_AUTHORIZER_H_ #include "mir/frontend/session_authorizer.h" namespace mir { namespace test { namespace doubles { class StubSessionAuthorizer : public frontend::SessionAuthorizer { bool connection_is_allowed(mir::frontend::SessionCredentials const&) override { return true; } bool configure_display_is_allowed(mir::frontend::SessionCredentials const&) override { return true; } bool set_base_display_configuration_is_allowed(mir::frontend::SessionCredentials const&) override { return true; } bool screencast_is_allowed(mir::frontend::SessionCredentials const&) override { return true; } bool prompt_session_is_allowed(mir::frontend::SessionCredentials const&) override { return true; } }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_STUB_SESSION_AUTHORIZER_H_ ./include/test/mir/test/doubles/wrap_shell_to_track_latest_surface.h0000644000015600001650000000305112676616125026200 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_WRAP_SHELL_TO_TRACK_LATEST_SURFACE_H #define MIR_TEST_WRAP_SHELL_TO_TRACK_LATEST_SURFACE_H #include "mir/shell/shell_wrapper.h" #include "mir/scene/session.h" namespace mir { namespace test { namespace doubles { struct WrapShellToTrackLatestSurface : mir::shell::ShellWrapper { using mir::shell::ShellWrapper::ShellWrapper; mir::frontend::SurfaceId create_surface( std::shared_ptr const& session, mir::scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override { auto const surface = mir::shell::ShellWrapper::create_surface(session, params, sink); latest_surface = session->surface(surface); return surface; } std::weak_ptr latest_surface; }; } } } #endif //MIR_TEST_WRAP_SHELL_TO_TRACK_LATEST_SURFACE_H ./include/test/mir/test/doubles/stub_cursor.h0000644000015600001650000000215612676616125021445 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_DOUBLES_STUB_CURSOR_H_ #define MIR_TEST_DOUBLES_STUB_CURSOR_H_ #include "mir/graphics/cursor.h" namespace mir { namespace test { namespace doubles { struct StubCursor : public graphics::Cursor { void show() override {} void show(graphics::CursorImage const&) override {} void hide() override {} void move_to(geometry::Point) override {} }; } } } // namespace mir #endif /* MIR_TEST_DOUBLES_STUB_CURSOR_H_ */ ./include/test/mir/test/doubles/stub_display_buffer.h0000644000015600001650000000251212676616125023122 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_STUB_DISPLAY_BUFFER_H_ #define MIR_TEST_DOUBLES_STUB_DISPLAY_BUFFER_H_ #include "mir/test/doubles/null_display_buffer.h" #include "mir/geometry/rectangle.h" namespace mir { namespace test { namespace doubles { class StubDisplayBuffer : public NullDisplayBuffer { public: StubDisplayBuffer(geometry::Rectangle const& view_area_) : view_area_(view_area_) {} StubDisplayBuffer(StubDisplayBuffer const& s) : view_area_(s.view_area_) {} geometry::Rectangle view_area() const override { return view_area_; } private: geometry::Rectangle view_area_; }; } } } #endif /* MIR_TEST_DOUBLES_STUB_DISPLAY_BUFFER_H_ */ ./include/test/mir/test/doubles/null_platform.h0000644000015600001650000000374112676616125021752 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_NULL_PLATFORM_H_ #define MIR_TEST_DOUBLES_NULL_PLATFORM_H_ #include "mir/graphics/platform.h" #include "mir/graphics/platform_ipc_package.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/test/doubles/null_display.h" #include "mir/test/doubles/null_platform_ipc_operations.h" namespace mir { namespace test { namespace doubles { class NullPlatform : public graphics::Platform { public: mir::UniqueModulePtr create_buffer_allocator() override { return nullptr; } mir::UniqueModulePtr create_display( std::shared_ptr const&, std::shared_ptr const&) override { return mir::make_module_ptr(); } std::shared_ptr connection_ipc_package() { return std::make_shared(); } mir::UniqueModulePtr make_ipc_operations() const override { return mir::make_module_ptr(); } EGLNativeDisplayType egl_native_display() const override { return EGLNativeDisplayType(); } }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_NULL_PLATFORM_ ./include/test/mir/test/doubles/stub_session.h0000644000015600001650000000540712676616125021615 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_DOUBLES_STUB_SESSION_H #define MIR_TEST_DOUBLES_STUB_SESSION_H #include namespace mir { namespace test { namespace doubles { struct StubSession : scene::Session { StubSession(pid_t pid = -1); std::shared_ptr get_surface( frontend::SurfaceId surface) const override; std::string name() const override; void force_requests_to_complete() override; pid_t process_id() const override; void take_snapshot(scene::SnapshotCallback const& snapshot_taken) override; std::shared_ptr default_surface() const override; void set_lifecycle_state(MirLifecycleState state) override; void send_display_config(graphics::DisplayConfiguration const&) override; void hide() override; void show() override; void start_prompt_session() override; void stop_prompt_session() override; void suspend_prompt_session() override; void resume_prompt_session() override; frontend::SurfaceId create_surface( scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override; void destroy_surface(frontend::SurfaceId surface) override; std::shared_ptr surface( frontend::SurfaceId surface) const override; std::shared_ptr surface_after( std::shared_ptr const&) const override; std::shared_ptr get_buffer_stream( frontend::BufferStreamId stream) const override; frontend::BufferStreamId create_buffer_stream( graphics::BufferProperties const& props) override; void destroy_buffer_stream(frontend::BufferStreamId stream) override; void configure_streams( scene::Surface& surface, std::vector const& config) override; void destroy_surface(std::weak_ptr const& surface) override; void send_input_device_change(std::vector> const& devices) override; pid_t pid; }; } } } #endif //MIR_TEST_DOUBLES_STUB_SESSION_H ./include/test/mir/test/doubles/mock_window_manager.h0000644000015600001650000000601612676616125023104 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_DOUBLES_MOCK_WINDOW_MANAGER_H_ #define MIR_TEST_DOUBLES_MOCK_WINDOW_MANAGER_H_ #include "mir/shell/window_manager.h" #include "mir/shell/surface_specification.h" #include "mir/scene/surface_creation_parameters.h" #include namespace mir { namespace test { namespace doubles { struct MockWindowManager : shell::WindowManager { MockWindowManager() { using namespace ::testing; ON_CALL(*this, add_surface(_,_,_)).WillByDefault(Invoke(add_surface_default)); } MOCK_METHOD1(add_session, void (std::shared_ptr const&)); MOCK_METHOD1(remove_session, void (std::shared_ptr const&)); MOCK_METHOD3(add_surface, frontend::SurfaceId( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::function const& session, scene::SurfaceCreationParameters const& params)> const& build)); MOCK_METHOD3(modify_surface, void(std::shared_ptr const&, std::shared_ptr const&, shell::SurfaceSpecification const&)); MOCK_METHOD2(remove_surface, void(std::shared_ptr const&, std::weak_ptr const&)); MOCK_METHOD1(add_display, void(geometry::Rectangle const&)); MOCK_METHOD1(remove_display, void(geometry::Rectangle const&)); MOCK_METHOD1(handle_keyboard_event, bool(MirKeyboardEvent const*)); MOCK_METHOD1(handle_touch_event, bool(MirTouchEvent const*)); MOCK_METHOD1(handle_pointer_event, bool(MirPointerEvent const*)); MOCK_METHOD3(handle_raise_surface, void(std::shared_ptr const&, std::shared_ptr const&, uint64_t)); MOCK_METHOD4(set_surface_attribute, int(std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value)); static frontend::SurfaceId add_surface_default( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::function const& session, scene::SurfaceCreationParameters const& params)> const& build) { return build(session, params); } }; } } } #endif /* MIR_TEST_DOUBLES_MOCK_WINDOW_MANAGER_H_ */ ./include/test/mir/test/doubles/mock_input_device_observer.h0000644000015600001650000000243712676616125024473 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_DEVICE_OBSERVER_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_DEVICE_OBSERVER_H_ #include "mir/input/input_device_observer.h" #include namespace mir { namespace test { namespace doubles { struct MockInputDeviceObserver : input::InputDeviceObserver { MOCK_METHOD1(device_added, void(std::shared_ptr const& device)); MOCK_METHOD1(device_changed, void(std::shared_ptr const& device)); MOCK_METHOD1(device_removed, void(std::shared_ptr const& device)); MOCK_METHOD0(changes_complete, void()); }; } } } #endif ./include/test/mir/test/doubles/null_gl_context.h0000644000015600001650000000206112676616125022266 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_GL_CONTEXT_H_ #define MIR_TEST_DOUBLES_NULL_GL_CONTEXT_H_ #include "mir/graphics/gl_context.h" namespace mir { namespace test { namespace doubles { class NullGLContext : public graphics::GLContext { public: void make_current() const {} void release_current() const {} }; } } } #endif /* MIR_TEST_DOUBLES_NULL_GL_CONTEXT_H_ */ ./include/test/mir/test/doubles/null_display_configuration.h0000644000015600001650000000277712676616125024532 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_NULL_DISPLAY_CONFIGURATION_H_ #define MIR_TEST_DOUBLES_NULL_DISPLAY_CONFIGURATION_H_ #include "mir/graphics/display_configuration.h" namespace mir { namespace test { namespace doubles { class NullDisplayConfiguration : public graphics::DisplayConfiguration { void for_each_card(std::function) const override { } void for_each_output(std::function) const override { } void for_each_output(std::function) override { } std::unique_ptr clone() const override { return std::make_unique(); } }; } } } #endif /* MIR_TEST_DOUBLES_NULL_DISPLAY_CONFIGURATION_H_ */ ./include/test/mir/test/doubles/null_display_sync_group.h0000644000015600001650000000502712676616125024042 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_NULL_DISPLAY_SYNC_GROUP_H_ #define MIR_TEST_DOUBLES_NULL_DISPLAY_SYNC_GROUP_H_ #include "mir/graphics/display.h" #include "mir/geometry/size.h" #include "mir/test/doubles/null_display_buffer.h" #include "mir/test/doubles/stub_display_buffer.h" #include namespace mir { namespace test { namespace doubles { struct StubDisplaySyncGroup : graphics::DisplaySyncGroup { public: StubDisplaySyncGroup(std::vector const& output_rects) : output_rects{output_rects} { for (auto const& output_rect : output_rects) display_buffers.emplace_back(output_rect); } StubDisplaySyncGroup(geometry::Size sz) : StubDisplaySyncGroup({{{0,0}, sz}}) {} void for_each_display_buffer(std::function const& f) override { for (auto& db : display_buffers) f(db); } void post() override { /* yield() is needed to ensure reasonable runtime under valgrind for some tests */ std::this_thread::yield(); } std::chrono::milliseconds recommended_sleep() const override { return std::chrono::milliseconds::zero(); } private: std::vector const output_rects; std::vector display_buffers; }; struct NullDisplaySyncGroup : graphics::DisplaySyncGroup { void for_each_display_buffer(std::function const& f) override { f(db); } virtual void post() override { /* yield() is needed to ensure reasonable runtime under valgrind for some tests */ std::this_thread::yield(); } std::chrono::milliseconds recommended_sleep() const override { return std::chrono::milliseconds::zero(); } NullDisplayBuffer db; }; } } } #endif /* MIR_TEST_DOUBLES_NULL_DISPLAY_SYNC_GROUP_H_ */ ./include/test/mir/test/doubles/null_display_buffer.h0000644000015600001650000000260012676616125023115 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_DISPLAY_BUFFER_H_ #define MIR_TEST_DOUBLES_NULL_DISPLAY_BUFFER_H_ #include "mir/graphics/display_buffer.h" namespace mir { namespace test { namespace doubles { class NullDisplayBuffer : public graphics::DisplayBuffer, public graphics::NativeDisplayBuffer { public: geometry::Rectangle view_area() const override { return geometry::Rectangle(); } bool post_renderables_if_optimizable(graphics::RenderableList const&) override { return false; } MirOrientation orientation() const override { return mir_orientation_normal; } NativeDisplayBuffer* native_display_buffer() override { return this; } }; } } } #endif /* MIR_TEST_DOUBLES_NULL_DISPLAY_BUFFER_H_ */ ./include/test/mir/test/doubles/mock_input_dispatcher.h0000644000015600001650000000223312676616125023445 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_DISPATCHER_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_DISPATCHER_H_ #include "mir/input/input_dispatcher.h" #include namespace mir { namespace test { namespace doubles { struct MockInputDispatcher : public mir::input::InputDispatcher { MOCK_METHOD1(dispatch, bool(MirEvent const&)); MOCK_METHOD0(start, void()); MOCK_METHOD0(stop, void()); }; } } } // namespace mir #endif // MIR_TEST_DOUBLES_MOCK_INPUT_DISPATCHER_H_ ./include/test/mir/test/doubles/stub_surface.h0000644000015600001650000000664412676616125021566 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_DOUBLES_STUB_SURFACE_H #define MIR_TEST_DOUBLES_STUB_SURFACE_H #include namespace mir { namespace test { namespace doubles { // scene::Surface is a horribly wide interface to expose from Mir struct StubSurface : scene::Surface { std::string name() const override; void move_to(geometry::Point const& top_left) override; float alpha() const override; geometry::Size size() const override; geometry::Size client_size() const override; std::shared_ptr primary_buffer_stream() const override; void set_streams(std::list const& streams) override; bool supports_input() const override; int client_input_fd() const override; std::shared_ptr input_channel() const override; input::InputReceptionMode reception_mode() const override; void set_reception_mode(input::InputReceptionMode mode) override; void set_input_region(std::vector const& input_rectangles) override; void resize(geometry::Size const& size) override; geometry::Point top_left() const override; geometry::Rectangle input_bounds() const override; bool input_area_contains(geometry::Point const& point) const override; void consume(MirEvent const* event) override; void set_alpha(float alpha) override; void set_orientation(MirOrientation orientation) override; void set_transformation(glm::mat4 const&) override; bool visible() const override; graphics::RenderableList generate_renderables(compositor::CompositorID id) const override; int buffers_ready_for_compositor(void const* compositor_id) const override; MirSurfaceType type() const override; MirSurfaceState state() const override; int configure(MirSurfaceAttrib attrib, int value) override; int query(MirSurfaceAttrib attrib) const override; void hide() override; void show() override; void set_cursor_image(std::shared_ptr const& image) override; std::shared_ptr cursor_image() const override; void set_cursor_stream(std::shared_ptr const& stream, geometry::Displacement const& hotspot) override; void request_client_surface_close() override; std::shared_ptr parent() const override; void add_observer(std::shared_ptr const& observer) override; void remove_observer(std::weak_ptr const& observer) override; void set_keymap(MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) override; void rename(std::string const& title) override; }; } } } #endif //MIR_TEST_DOUBLES_STUB_SURFACE_H ./include/test/mir/test/doubles/null_display_buffer_compositor_factory.h0000644000015600001650000000344312676616125027130 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_NULL_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ #define MIR_TEST_DOUBLES_NULL_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ #include "mir/compositor/display_buffer_compositor_factory.h" #include "mir/compositor/display_buffer_compositor.h" #include namespace mir { namespace test { namespace doubles { class NullDisplayBufferCompositorFactory : public compositor::DisplayBufferCompositorFactory { public: auto create_compositor_for(graphics::DisplayBuffer&) -> std::unique_ptr override { struct NullDisplayBufferCompositor : compositor::DisplayBufferCompositor { void composite(compositor::SceneElementSequence&&) { // yield() is needed to ensure reasonable runtime under // valgrind for some tests std::this_thread::yield(); } }; auto raw = new NullDisplayBufferCompositor{}; return std::unique_ptr(raw); } }; } } } #endif /* MIR_TEST_DOUBLES_NULL_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ */ ./include/test/mir/test/doubles/mock_input_device_hub.h0000644000015600001650000000233112676616157023420 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_MOCK_INPUT_DEVICE_HUB_H_ #define MIR_TEST_DOUBLES_MOCK_INPUT_DEVICE_HUB_H_ #include "mir/input/input_device_hub.h" namespace mir { namespace test { namespace doubles { struct MockInputDeviceHub : input::InputDeviceHub { MOCK_METHOD1(add_observer, void(std::shared_ptr const&)); MOCK_METHOD1(remove_observer, void(std::weak_ptr const&)); MOCK_METHOD1(for_each_input_device, void(std::function const&)); }; } } } #endif ./include/test/mir/test/doubles/stub_input_device.h0000644000015600001650000000432012676616157022606 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_DOUBLES_STUB_INPUT_DEVICE_H_ #define MIR_TEST_DOUBLES_STUB_INPUT_DEVICE_H_ #include "mir/input/device.h" #include "mir/input/device_capability.h" #include "mir/input/pointer_configuration.h" #include "mir/input/touchpad_configuration.h" #include "mir/optional_value.h" namespace mir { namespace test { namespace doubles { struct StubDevice : input::Device { StubDevice(MirInputDeviceId id, input::DeviceCapabilities caps, std::string const& name, std::string const& unique_id) : device_id(id), device_capabilities(caps), device_name(name), device_unique_id(unique_id) {} MirInputDeviceId id() const override { return device_id; } input::DeviceCapabilities capabilities() const override { return device_capabilities; } std::string name() const override { return device_name; } std::string unique_id() const override { return device_unique_id; } mir::optional_value pointer_configuration() const override { return {}; } void apply_pointer_configuration(input::PointerConfiguration const&) override { } mir::optional_value touchpad_configuration() const override { return {}; } void apply_touchpad_configuration(input::TouchpadConfiguration const&) override { } MirInputDeviceId device_id; input::DeviceCapabilities device_capabilities; std::string device_name; std::string device_unique_id; }; } } } #endif ./include/test/mir/test/doubles/stub_display_configuration.h0000644000015600001650000000557212676616157024536 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DOUBLES_STUB_DISPLAY_CONFIGURATION_H_ #define MIR_TEST_DOUBLES_STUB_DISPLAY_CONFIGURATION_H_ #include "mir/graphics/display_configuration.h" #include #include namespace mir { namespace test { namespace doubles { struct StubDisplayConfigurationOutput : public graphics::DisplayConfigurationOutput { StubDisplayConfigurationOutput( geometry::Size px_size, geometry::Size mm_size, MirPixelFormat format, double vrefresh, bool connected); StubDisplayConfigurationOutput(graphics::DisplayConfigurationOutputId id, geometry::Size px_size, geometry::Size mm_size, MirPixelFormat format, double vrefresh, bool connected); StubDisplayConfigurationOutput(graphics::DisplayConfigurationOutputId id, std::vector modes, std::vector formats); }; class StubDisplayConfig : public graphics::DisplayConfiguration { public: StubDisplayConfig(); StubDisplayConfig(StubDisplayConfig const& other); StubDisplayConfig(graphics::DisplayConfiguration const& other); StubDisplayConfig(unsigned int num_displays); StubDisplayConfig(std::vector> const& connected_used); StubDisplayConfig(unsigned int num_displays, std::vector const& pfs); StubDisplayConfig(std::vector const& rects); StubDisplayConfig(std::vector const& outputs); StubDisplayConfig( std::vector const& cards, std::vector const& outputs); void for_each_card(std::function f) const override; void for_each_output(std::function f) const override; void for_each_output(std::function f) override; std::unique_ptr clone() const override; std::vector cards; std::vector outputs; }; } } } #endif /* MIR_TEST_DOUBLES_STUB_DISPLAY_CONFIGURATION_H_ */ ./include/test/mir/test/doubles/mock_display_configuration.h0000644000015600001650000000304512676616125024476 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_DOUBLES_MOCK_DISPLAY_CONFIGURATION_H_ #define MIR_TEST_DOUBLES_MOCK_DISPLAY_CONFIGURATION_H_ #include "mir/graphics/display_configuration.h" namespace mir { namespace test { namespace doubles { struct MockDisplayConfiguration : public graphics::DisplayConfiguration { MOCK_CONST_METHOD1( for_each_card, void(std::function)); MOCK_CONST_METHOD1( for_each_output, void(std::function)); MOCK_METHOD1( for_each_output, void(std::function)); MOCK_CONST_METHOD0(valid, bool()); std::unique_ptr clone() const { throw std::runtime_error("MockDisplayConfiguration::clone is not implemented"); } }; } } } #endif ./include/test/mir/test/cross_process_action.h0000644000015600001650000000226612676616125021664 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_CROSS_PROCESS_ACTION_H_ #define MIR_TEST_CROSS_PROCESS_ACTION_H_ #include "mir/test/cross_process_sync.h" #include #include namespace mir { namespace test { class CrossProcessAction { public: void exec(std::function const& f); void operator()(std::chrono::milliseconds timeout = std::chrono::milliseconds{-1}); private: CrossProcessSync start_sync; CrossProcessSync finish_sync; }; } } #endif /* MIR_TEST_CROSS_PROCESS_ACTION_H_ */ ./include/test/mir/test/signal.h0000644000015600001650000000315712676616125016715 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_SIGNAL_H_ #define MIR_TEST_SIGNAL_H_ #include #include #include namespace mir { namespace test { /** * @brief A threadsafe, waitable signal */ class Signal { public: void raise(); bool raised(); void wait(); template bool wait_for(std::chrono::duration delay) { std::unique_lock lock(mutex); return cv.wait_for(lock, delay, [this]() { return signalled; }); } template bool wait_until(std::chrono::time_point const& time) { std::unique_lock lock(mutex); return cv.wait_until(lock, time, [this]() { return signalled; }); } void reset(); private: std::mutex mutex; std::condition_variable cv; bool signalled{false}; }; } } #endif // MIR_TEST_SIGNAL_H_ ./include/test/mir/test/display_config_matchers.h0000644000015600001650000000706212676616157022324 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_TEST_DISPLAY_CONFIG_MATCHERS_H_ #define MIR_TEST_DISPLAY_CONFIG_MATCHERS_H_ #include "mir_toolkit/client_types.h" #include //avoid a valgrind complaint by defining printer for this type static void PrintTo(MirDisplayConfiguration const&, ::std::ostream*) __attribute__ ((unused)); void PrintTo(MirDisplayConfiguration const&, ::std::ostream*) { } namespace mir { namespace protobuf { class DisplayConfiguration; class Connection; static void PrintTo(mir::protobuf::DisplayConfiguration const&, ::std::ostream*) __attribute__ ((unused)); void PrintTo(mir::protobuf::DisplayConfiguration const&, ::std::ostream*) {} static void PrintTo(mir::protobuf::Connection const&, ::std::ostream*) __attribute__ ((unused)); void PrintTo(mir::protobuf::Connection const&, ::std::ostream*) { } } namespace graphics { class DisplayConfiguration; } namespace test { bool compare_display_configurations( testing::MatchResultListener* listener, graphics::DisplayConfiguration const& display_config1, graphics::DisplayConfiguration const& display_config2); bool compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfiguration const& client_config, graphics::DisplayConfiguration const& display_config); bool compare_display_configurations( testing::MatchResultListener* listener, protobuf::DisplayConfiguration const& protobuf_config, graphics::DisplayConfiguration const& display_config); bool compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfiguration const* client_config1, MirDisplayConfiguration const* client_config2); bool compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfiguration const& client_config, protobuf::DisplayConfiguration const& protobuf_config); bool compare_display_configurations( testing::MatchResultListener* listener, graphics::DisplayConfiguration const& display_config1, MirDisplayConfiguration const* display_config2); bool compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfiguration const* display_config2, graphics::DisplayConfiguration const& display_config1); bool compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfig const* client_config, graphics::DisplayConfiguration const& server_config); bool compare_display_configurations( testing::MatchResultListener* listener, graphics::DisplayConfiguration const& server_config, MirDisplayConfig const* client_config); bool compare_display_configurations( testing::MatchResultListener* listener, MirDisplayConfig const* config1, MirDisplayConfig const* config2); MATCHER_P(DisplayConfigMatches, config, "") { return compare_display_configurations(result_listener, arg, config); } } } #endif /* MIR_TEST_DISPLAY_CONFIG_MATCHERS_H_ */ ./include/test/mir/test/pipe.h0000644000015600001650000000210412676616125016364 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_PIPE_H_ #define MIR_TEST_PIPE_H_ #include "mir/fd.h" namespace mir { namespace test { class Pipe { public: Pipe(); Pipe(int flags); ~Pipe() = default; Fd read_fd() const; Fd write_fd() const; private: Pipe(Pipe const&) = delete; Pipe& operator=(Pipe const&) = delete; Fd reader; Fd writer; }; } } #endif /* MIR_TEST_PIPE_H_ */ ./include/test/mir_test_framework/0000755000015600001650000000000012676616160017415 5ustar jenkinsjenkins./include/test/mir_test_framework/async_server_runner.h0000644000015600001650000000367612676616125023677 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TESTS_INCLUDE_MIR_TEST_FRAMEWORK_ASYNC_SERVER_RUNNER_H_ #define MIR_TESTS_INCLUDE_MIR_TEST_FRAMEWORK_ASYNC_SERVER_RUNNER_H_ #include "mir_test_framework/temporary_environment_value.h" #include "mir/test/auto_unblock_thread.h" #include "mir/server.h" #include #include #include #include namespace mir_test_framework { class AsyncServerRunner { public: AsyncServerRunner(); ~AsyncServerRunner() noexcept; void add_to_environment(char const* key, char const* value); /// Starts the server on a new thread void start_server(); /// Stops the server and joins thread void stop_server(); /// Wait for the server to exit and joins thread void wait_for_server_exit(); /// \return a connection string for a new client to connect to the server auto new_connection() -> std::string; /// \return a connection string for a client to connect to the server auto connection(int fd) -> std::string; mir::Server server; private: std::list env; mir::test::AutoJoinThread server_thread; std::mutex mutex; std::condition_variable started; bool server_running{false}; }; } #endif /* MIR_TESTS_INCLUDE_MIR_TEST_FRAMEWORK_ASYNC_SERVER_RUNNER_H_ */ ./include/test/mir_test_framework/visible_surface.h0000644000015600001650000000314712676616125022741 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Kevin DuBois */ #ifndef MIR_TEST_FRAMEWORK_VISIBLE_SURFACE_H_ #define MIR_TEST_FRAMEWORK_VISIBLE_SURFACE_H_ #include "mir_toolkit/mir_client_library.h" #include #include #include namespace mir_test_framework { class VisibleSurface { public: explicit VisibleSurface(MirSurfaceSpec* spec); VisibleSurface(VisibleSurface&&); VisibleSurface& operator=(VisibleSurface&&); VisibleSurface(VisibleSurface const&) = delete; VisibleSurface& operator=(VisibleSurface const&) = delete; ~VisibleSurface(); static void event_callback(MirSurface* surf, MirEvent const* ev, void* context); void set_visibility(MirSurface* surf, bool vis); operator MirSurface*() const; private: std::mutex mutex; std::condition_variable cv; MirSurface* surface; bool visible; }; std::ostream& operator<<(std::ostream& os, VisibleSurface const& s); } #endif /* MIR_TEST_FRAMEWORK_VISIBLE_SURFACE_H_ */ ./include/test/mir_test_framework/stub_server_platform_factory.h0000644000015600001650000000322112676616125025563 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_FRAMEWORK_STUB_SERVER_PLATFORM_FACTORY_ #define MIR_TEST_FRAMEWORK_STUB_SERVER_PLATFORM_FACTORY_ #include "mir/geometry/rectangle.h" #include "mir/graphics/platform.h" #include "mir/module_deleter.h" #include #include #include namespace geom = mir::geometry; namespace mir { namespace graphics { class Platform; } namespace input { class InputDeviceInfo; } } namespace mg = mir::graphics; namespace mir_test_framework { class FakeInputDevice; std::shared_ptr make_stubbed_server_graphics_platform(std::vector const& display_rects); void set_next_display_rects(std::unique_ptr>&& display_rects); void set_next_preset_display(std::shared_ptr const& display); mir::UniqueModulePtr add_fake_input_device(mir::input::InputDeviceInfo const& info); } #endif /* MIR_TEST_FRAMEWORK_STUB_SERVER_PLATFORM_FACTORY_ */ ./include/test/mir_test_framework/interprocess_client_server_test.h0000644000015600001650000000445712676616125026304 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_INTERPROCESS_CLIENT_SERVER_TEST_H_ #define MIR_TEST_FRAMEWORK_INTERPROCESS_CLIENT_SERVER_TEST_H_ #include "mir_test_framework/headless_test.h" #include "mir/test/cross_process_sync.h" namespace mir_test_framework { class Process; class Result; class InterprocessClientServerTest : public HeadlessTest { public: char const* const mir_test_socket = test_socket_file().c_str(); ~InterprocessClientServerTest(); void init_server(std::function const& init_code); void run_in_server(std::function const& exec_code); void run_in_client(std::function const& client_code); auto new_client_process(std::function const& client_code) -> std::shared_ptr; bool is_test_process() const; pid_t client_pid() const { return client_process_id; } void TearDown() override; // Convenient [test|server|client] identifier if adding debug messages auto process_type() const -> char const* { return process_tag; } void expect_server_signalled(int signal); void stop_server(); bool sigkill_server_process(); Result wait_for_shutdown_server_process(); private: pid_t test_process_id{getpid()}; pid_t server_process_id{0}; pid_t client_process_id{0}; std::shared_ptr server_process; std::unique_ptr shutdown_sync{new mir::test::CrossProcessSync()}; char const* process_tag = "test"; std::function server_setup = []{}; bool server_signal_expected{false}; int expected_server_failure_signal{0}; }; } #endif /* MIR_TEST_FRAMEWORK_INTERPROCESS_CLIENT_SERVER_TEST_H_ */ ./include/test/mir_test_framework/declarative_placement_window_manage_policy.h0000644000015600001650000000403512676616157030367 0ustar jenkinsjenkins/* * Copyright © 2014-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TEST_FRAMEWORK_DECLARATIVE_PLACEMENT_WINDOW_MANAGER_POLICY_H_ #define MIR_TEST_FRAMEWORK_DECLARATIVE_PLACEMENT_WINDOW_MANAGER_POLICY_H_ #include "mir/shell/canonical_window_manager.h" #include "mir/geometry/rectangle.h" #include #include #include namespace mir_test_framework { typedef std::map SurfaceGeometries; /// DeclarativePlacementWindowManagerPolicy is a test utility server component for specifying /// a static list of surface geometries and relative depths. Used, for example, /// in input tests where it is necessary to set up scenarios depending on /// multiple surfaces geometry and stacking. class DeclarativePlacementWindowManagerPolicy : public mir::shell::CanonicalWindowManagerPolicy { public: DeclarativePlacementWindowManagerPolicy( mir::shell::WindowManagerTools* const tools, SurfaceGeometries const& positions_by_name, std::shared_ptr const& display_layout); auto handle_place_new_surface( std::shared_ptr const& session, mir::scene::SurfaceCreationParameters const& request_parameters) -> mir::scene::SurfaceCreationParameters; private: SurfaceGeometries const& surface_geometries_by_name; }; } #endif // MIR_TEST_FRAMEWORK_DECLARATIVE_PLACEMENT_WINDOW_MANAGER_POLICY_H_ ./include/test/mir_test_framework/headless_in_process_server.h0000644000015600001650000000207412676616125025174 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_HEADLESS_IN_PROCESS_SERVER_H_ #define MIR_TEST_FRAMEWORK_HEADLESS_IN_PROCESS_SERVER_H_ #include "mir_test_framework/headless_test.h" namespace mir_test_framework { struct HeadlessInProcessServer : HeadlessTest { HeadlessInProcessServer(); void SetUp() override; void TearDown() override; }; } #endif /* MIR_TEST_FRAMEWORK_HEADLESS_IN_PROCESS_SERVER_H_ */ ./include/test/mir_test_framework/in_process_server.h0000644000015600001650000000313312676616125023321 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_IN_PROCESS_SERVER_H_ #define MIR_TEST_FRAMEWORK_IN_PROCESS_SERVER_H_ #include "mir_test_framework/server_runner.h" #include namespace mir_test_framework { /// Fixture for running Mir server in test process struct InProcessServer : testing::Test, private ServerRunner { /// Starts the server /// \warning don't forget to call this if you override SetUp() void SetUp() override { ServerRunner::start_server(); } /// Stops the server /// \warning don't forget to call this if you override TearDown() void TearDown() override { ServerRunner::stop_server(); } /// \return a connection string for a new client to connect to the server using ServerRunner::new_connection; /// \return a connection string for a new client to connect to the prompt server using ServerRunner::new_prompt_connection; }; } #endif /* MIR_TEST_FRAMEWORK_IN_PROCESS_SERVER_H_ */ ./include/test/mir_test_framework/detect_server.h0000644000015600001650000000172712676616125022434 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_DETECT_SERVER_H_ #define MIR_TEST_FRAMEWORK_DETECT_SERVER_H_ #include #include namespace mir_test_framework { bool detect_server(std::string const& name, std::chrono::milliseconds const& timeout); } #endif /* MIR_TEST_FRAMEWORK_DETECT_SERVER_H_ */ ./include/test/mir_test_framework/server_runner.h0000644000015600001650000000361712676616125022475 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_SERVER_RUNNER_H_ #define MIR_TEST_FRAMEWORK_SERVER_RUNNER_H_ #include #include #include #include #include namespace mir { class MainLoop; class DefaultServerConfiguration; } namespace mir_test_framework { /// Utility for running Mir server in test process struct ServerRunner { ServerRunner(); virtual ~ServerRunner(); /// Starts the server /// The method is synchronous, i.e., it returns only after the server has started void start_server(); /// Stops the server /// The method is synchronous, i.e., it returns only after the server has stopped void stop_server(); /// \return a connection string for a new client to connect to the server std::string new_connection(); /// \return a connection string for a new client to connect to the prompt server std::string new_prompt_connection(); private: std::shared_ptr start_mir_server(); virtual mir::DefaultServerConfiguration& server_config() = 0; char const* const old_env; std::thread server_thread; std::mutex main_loop_mutex; std::shared_ptr main_loop; }; } #endif /* MIR_TEST_FRAMEWORK_SERVER_RUNNER_H_ */ ./include/test/mir_test_framework/connected_client_headless_server.h0000644000015600001650000000224212676616125026325 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_CONNECTED_CLIENT_HEADLESS_SERVER_H_ #define MIR_TEST_FRAMEWORK_CONNECTED_CLIENT_HEADLESS_SERVER_H_ #include "mir_test_framework/headless_in_process_server.h" #include "mir_toolkit/mir_client_library.h" namespace mir_test_framework { struct ConnectedClientHeadlessServer : HeadlessInProcessServer { MirConnection* connection{nullptr}; void SetUp() override; void TearDown() override; }; } #endif /* MIR_TEST_FRAMEWORK_CONNECTED_CLIENT_HEADLESS_SERVER_H_ */ ./include/test/mir_test_framework/process.h0000644000015600001650000000545312676616125021254 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss * Thomas Guest */ #ifndef MIR_TEST_FRAMEWORK_PROCESS_H_ #define MIR_TEST_FRAMEWORK_PROCESS_H_ #include #include #include #include #include #include #include namespace mir_test_framework { enum class TerminationReason { unknown, child_terminated_normally, child_terminated_by_signal, child_terminated_with_core_dump, child_stopped_by_signal, child_resumed_by_signal }; // Aggregated results of running a process to completion struct Result { Result(); // Did the process exit without error? bool succeeded() const; // Was the process terminated by a signal? bool signalled() const; TerminationReason reason; int exit_code; int signal; }; // Posix process control class. class Process { public: // Construct a process with the supplied pid Process(pid_t pid); // Destroy the process cleanly, by terminating it and waiting for // the pid. ~Process(); // Wait for the process to terminate, and return the results. Result wait_for_termination(const std::chrono::milliseconds& timeout = std::chrono::milliseconds(60 * 1000)); void kill(); void terminate(); void stop(); void cont(); void detach(); protected: Process() = delete; Process(const Process&) = delete; Process& operator=(const Process&) = delete; private: pid_t pid; bool terminated; bool detached; }; // Stream print helper std::ostream& operator<<(std::ostream& out, const Result& result); // Fork a child process to run the supplied main function, calling // the exit function when done. // Returns the parent process. template std::shared_ptr fork_and_run_in_a_different_process( Callable&& main_fn, std::function exit_fn) { pid_t pid = fork(); if (pid < 0) { throw std::runtime_error("Failed to fork process"); } if (pid == 0) { main_fn(); exit(exit_fn()); } return std::shared_ptr(new Process(pid)); } } #endif // MIR_TEST_FRAMEWORK_PROCESS_H_ ./include/test/mir_test_framework/temporary_environment_value.h0000644000015600001650000000222512676616125025432 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_ENVIRONMENT_VALUE_H_ #define MIR_TEST_FRAMEWORK_ENVIRONMENT_VALUE_H_ #include namespace mir_test_framework { class TemporaryEnvironmentValue { public: TemporaryEnvironmentValue(char const* name, char const* value); ~TemporaryEnvironmentValue(); private: static int const overwrite = 1; std::string const name; bool const has_old_value; std::string const old_value; }; } #endif /* MIR_TEST_FRAMEWORK_ENVIRONMENT_VALUE_H_ */ ./include/test/mir_test_framework/headless_nested_server_runner.h0000644000015600001650000000211512676616125025677 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #ifndef MIR_TEST_FRAMEWORK_HEADLESS_NESTED_SERVER_RUNNER_H_ #define MIR_TEST_FRAMEWORK_HEADLESS_NESTED_SERVER_RUNNER_H_ #include "mir_test_framework/async_server_runner.h" namespace mir_test_framework { class HeadlessNestedServerRunner : public AsyncServerRunner { public: HeadlessNestedServerRunner(std::string const& connect_string); }; } #endif /* MIR_TEST_FRAMEWORK_HEADLESS_NESTED_SERVER_RUNNER_H_ */ ./include/test/mir_test_framework/using_stub_client_platform.h0000644000015600001650000000234112676616125025213 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TEST_FRAMEWORK_USING_STUB_CLIENT_PLATFORM_H_ #define MIR_TEST_FRAMEWORK_USING_STUB_CLIENT_PLATFORM_H_ #include namespace mir_test_framework { class UsingStubClientPlatform { public: UsingStubClientPlatform(); ~UsingStubClientPlatform(); UsingStubClientPlatform(UsingStubClientPlatform const&) = delete; UsingStubClientPlatform operator=(UsingStubClientPlatform const&) = delete; private: class Impl; std::unique_ptr impl; }; } #endif /* MIR_TEST_FRAMEWORK_USING_STUB_CLIENT_PLATFORM_H_ */ ./include/test/mir_test_framework/executable_path.h0000644000015600001650000000223612676616157022734 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #ifndef MIR_TEST_FRAMEWORK_EXECUTABLE_PATH_H_ #define MIR_TEST_FRAMEWORK_EXECUTABLE_PATH_H_ #include namespace mir_test_framework { std::string executable_path(); std::string library_path(); std::string server_platform_path(); std::string test_data_path(); std::string server_platform(std::string const& name); std::string server_input_platform(std::string const& name); std::string client_platform(std::string const& name); } #endif /* MIR_TEST_FRAMEWORK_EXECUTABLE_PATH_H_ */ ./include/test/mir_test_framework/headless_test.h0000644000015600001650000000355712676616125022430 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_HEADLESS_TEST_H_ #define MIR_TEST_FRAMEWORK_HEADLESS_TEST_H_ #include "mir_test_framework/async_server_runner.h" #include namespace mir { class SharedLibrary; } namespace mir { namespace graphics { class Display; }} namespace mir { namespace geometry { class Rectangle; }} namespace mir_test_framework { /** Basic fixture for tests that don't use graphics or input hardware. * This provides a mechanism for temporarily setting environment variables. * It automatically sets "MIR_SERVER_PLATFORM_GRAPHICS_LIB" to "graphics-dummy.so" * and MIR_SERVER_PLATFORM_INPUT_LIB to "input-stub.so" * as the tests do not hit the graphics hardware. */ class HeadlessTest : public ::testing::Test, public AsyncServerRunner { public: HeadlessTest(); ~HeadlessTest() noexcept; void preset_display(std::shared_ptr const& display); /// Override initial display layout void initial_display_layout(std::vector const& display_rects); private: std::unique_ptr server_platform_graphics_lib; }; std::string const& test_socket_file(); } #endif /* MIR_TEST_FRAMEWORK_HEADLESS_TEST_H_ */ ./include/test/mir_test_framework/stub_graphics_platform_operation.h0000644000015600001650000000170612676616125026414 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #ifndef MIR_TEST_FRAMEWORK_STUB_GRAPHICS_PLATFORM_OPERATION_H_ #define MIR_TEST_FRAMEWORK_STUB_GRAPHICS_PLATFORM_OPERATION_H_ namespace mir_test_framework { enum class StubGraphicsPlatformOperation : unsigned int { add = 13, echo_fd = 15 }; } #endif ./include/test/mir_test_framework/stub_platform_helpers.h0000644000015600001650000000321112676616125024167 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_FRAMEWORK_STUB_PLATFORM_HELPERS_H_ #define MIR_TEST_FRAMEWORK_STUB_PLATFORM_HELPERS_H_ #include "mir/graphics/platform_ipc_package.h" #include "mir_toolkit/client_types.h" #include namespace mir_test_framework { constexpr int stub_data_size{21}; constexpr int stub_data_guard{0x0eadbeef}; static inline void pack_stub_ipc_package(mir::graphics::PlatformIPCPackage& package) { package.ipc_data = std::vector(stub_data_size, -1); package.ipc_data[0] = stub_data_guard; } static inline void create_stub_platform_package(MirPlatformPackage& package) { ::memset(&package, 0, sizeof(package)); package.data_items = stub_data_size; package.data[0] = stub_data_guard; } MATCHER(IsStubPlatformPackage, "") { return (arg.data_items == stub_data_size) && (arg.data[0] == stub_data_guard) && (arg.fd_items == 0); } } #endif // MIR_TEST_FRAMEWORK_STUB_PLATFORM_HELPERS_H_ ./include/test/mir_test_framework/any_surface.h0000644000015600001650000000167112676616125022073 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #ifndef MIR_TEST_FRAMEWORK_ANY_SURFACE_H_ #define MIR_TEST_FRAMEWORK_ANY_SURFACE_H_ #include "mir_toolkit/mir_client_library.h" namespace mir_test_framework { MirSurface *make_any_surface(MirConnection *connection); } #endif // MIR_TEST_FRAMEWORK_ANY_SURFACE_H_ ./include/test/mir_test_framework/fake_input_device.h0000644000015600001650000000351312676616157023242 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_FRAMEWORK_FAKE_INPUT_DEVICE_H_ #define MIR_TEST_FRAMEWORK_FAKE_INPUT_DEVICE_H_ #include "mir_toolkit/events/event.h" #include "mir/test/event_factory.h" namespace mir_test_framework { namespace synthesis = mir::input::synthesis; class FakeInputDevice { public: /** * Valid value range of simulated touch coordinates. The simulated coordinates will be remapped to * the coordinates of the given input sink. * \{ */ static const int maximum_touch_axis_value = 0xFFFF; static const int minimum_touch_axis_value = 0; /// \} FakeInputDevice() = default; virtual ~FakeInputDevice() = default; virtual void emit_device_removal() = 0; virtual void emit_runtime_error() = 0; virtual void emit_event(synthesis::KeyParameters const& key) = 0; virtual void emit_event(synthesis::ButtonParameters const& button) = 0; virtual void emit_event(synthesis::MotionParameters const& motion) = 0; virtual void emit_event(synthesis::TouchParameters const& touch) = 0; FakeInputDevice(FakeInputDevice const&) = delete; FakeInputDevice& operator=(FakeInputDevice const&) = delete; }; } #endif ./include/test/mir_test_framework/connected_client_with_a_surface.h0000644000015600001650000000231512676616125026133 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_CONNECTED_CLIENT_WITH_A_SURFACE_H_ #define MIR_TEST_FRAMEWORK_CONNECTED_CLIENT_WITH_A_SURFACE_H_ #include "mir_test_framework/connected_client_headless_server.h" #include "mir/geometry/size.h" namespace mir_test_framework { struct ConnectedClientWithASurface : ConnectedClientHeadlessServer { MirSurface* surface{nullptr}; void SetUp() override; void TearDown() override; mir::geometry::Size const surface_size {640, 480}; }; } #endif /* MIR_TEST_FRAMEWORK_CONNECTED_CLIENT_WITH_A_SURFACE_H_ */ ./include/test/mir_test_framework/main.h0000644000015600001650000000250212676616125020512 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for moredetails. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_TEST_FRAMEWORK_MAIN_H #define MIR_TEST_FRAMEWORK_MAIN_H namespace mir_test_framework { /** * Initialize and run the mir test framework as follows: * * ::testing::InitGoogleTest(&argc, argv); * set_commandline(argc, argv); * return RUN_ALL_TESTS(); * * \attention If you override main() for your own purposes call this or do * something equivalent to run the tests. */ int main(int argc, char* argv[]); /** * Note the commandline for use in the mir test framework. The parameter list * referenced by argv must remain valid during the tests. */ void set_commandline(int argc, char* argv[]); } #endif //MIR_TEST_FRAMEWORK_MAIN_H ./include/test/mir_test_framework/placement_applying_shell.h0000644000015600001650000000440012676616157024634 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_TEST_FRAMEWORK_PLACEMENT_APPLYING_SHELL_H_ #define MIR_TEST_FRAMEWORK_PLACEMENT_APPLYING_SHELL_H_ #include "mir/shell/shell_wrapper.h" #include "mir/geometry/rectangle.h" #include "mir/scene/session.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" #include #include #include #include #include namespace mir_test_framework { using ClientInputRegions = std::map>; using ClientPositions = std::map; struct PlacementApplyingShell : mir::shell::ShellWrapper { PlacementApplyingShell( std::shared_ptr wrapped_coordinator, ClientInputRegions const& client_input_regions, ClientPositions const& client_positions); ~PlacementApplyingShell(); mir::frontend::SurfaceId create_surface( std::shared_ptr const& session, mir::scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override; void modify_surface( std::shared_ptr const& session, std::shared_ptr const& surface, mir::shell::SurfaceSpecification const& modifications) override; bool wait_for_modify_surface(std::chrono::seconds timeout); private: ClientInputRegions const& client_input_regions; ClientPositions const& client_positions; std::mutex mutex; std::condition_variable cv; bool modified {false}; }; } #endif ./include/renderers/0000755000015600001650000000000012676616124014524 5ustar jenkinsjenkins./include/renderers/gl/0000755000015600001650000000000012676616124015126 5ustar jenkinsjenkins./include/renderers/gl/mir/0000755000015600001650000000000012676616124015715 5ustar jenkinsjenkins./include/renderers/gl/mir/renderer/0000755000015600001650000000000012676616124017523 5ustar jenkinsjenkins./include/renderers/gl/mir/renderer/gl/0000755000015600001650000000000012676616126020127 5ustar jenkinsjenkins./include/renderers/gl/mir/renderer/gl/texture_source.h0000644000015600001650000000325712676616125023366 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_RENDERER_GL_TEXTURE_SOURCE_H_ #define MIR_RENDERER_GL_TEXTURE_SOURCE_H_ namespace mir { namespace renderer { namespace gl { //FIXME: (kdub) we're not hiding the differences in texture upload approaches between our platforms // very well with this interface. class TextureSource { public: virtual ~TextureSource() = default; // \warning deprecated, provided for convenience and legacy transition. //will call bind() and then secure_for_render() virtual void gl_bind_to_texture() = 0; //Uploads texture. virtual void bind() = 0; //add synchronization points to the command stream to ensure resources //are present during the draw. Will not upload texture. //should be called if an already uploaded texture is reused. virtual void secure_for_render() = 0; protected: TextureSource() = default; TextureSource(TextureSource const&) = delete; TextureSource& operator=(TextureSource const&) = delete; }; } } } #endif ./include/renderers/gl/mir/renderer/gl/render_target.h0000644000015600001650000000274212676616125023131 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_RENDERER_GL_RENDER_TARGET_H_ #define MIR_RENDERER_GL_RENDER_TARGET_H_ namespace mir { namespace renderer { namespace gl { class RenderTarget { public: virtual ~RenderTarget() = default; /** Makes the the current GL render target. */ virtual void make_current() = 0; /** Releases the current GL render target. */ virtual void release_current() = 0; /** * Swap buffers for OpenGL rendering. * After this method returns is the earliest time that it is safe to * free GL-related resources such as textures and buffers. */ virtual void swap_buffers() = 0; protected: RenderTarget() = default; RenderTarget(RenderTarget const&) = delete; RenderTarget& operator=(RenderTarget const&) = delete; }; } } } #endif ./include/platform/0000755000015600001650000000000012676616124014357 5ustar jenkinsjenkins./include/platform/mir/0000755000015600001650000000000012676616125015147 5ustar jenkinsjenkins./include/platform/mir/abnormal_exit.h0000644000015600001650000000173712676616125020154 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_ABNORMAL_EXIT_H_ #define MIR_ABNORMAL_EXIT_H_ #include namespace mir { class AbnormalExit : public std::runtime_error { public: AbnormalExit(std::string const& what) : std::runtime_error(what) { } }; } #endif /* MIR_ABNORMAL_EXIT_H_ */ ./include/platform/mir/module_properties.h0000644000015600001650000000246512676616125021070 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_PLATFORM_MODULE_PROPERTIES_H_ #define MIR_PLATFORM_MODULE_PROPERTIES_H_ namespace mir { /** * Describes a platform module. Mir provides the following graphics platforms: * "mir:mesa-kms", "mir:mesa-x11" and "mir:android". * Mir provides "mir:evdev-input" input platform. * * Third party platforms should be named according to the vendor and platform: * ":" */ struct ModuleProperties { char const* name; int major_version; int minor_version; int micro_version; char const* file; }; } #endif /* MIR_PLATFORM_MODULE_PROPERTIES_H_ */ ./include/platform/mir/options/0000755000015600001650000000000012676616125016642 5ustar jenkinsjenkins./include/platform/mir/options/option.h0000644000015600001650000000320512676616125020323 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_OPTIONS_OPTION_H_ #define MIR_OPTIONS_OPTION_H_ #include #include namespace mir { /// System options. Interface for extracting configuration options from wherever /// they may be (e.g. program arguments, config files or environment variables). namespace options { class Option { public: virtual bool is_set(char const* name) const = 0; virtual bool get(char const* name, bool default_) const = 0; virtual std::string get(char const* name, char const* default_) const = 0; virtual int get(char const* name, int default_) const = 0; virtual boost::any const& get(char const* name) const = 0; template Type get(char const* name) const { return boost::any_cast(get(name)); } protected: Option() = default; virtual ~Option() = default; Option(Option const&) = delete; Option& operator=(Option const&) = delete; }; } } #endif /* MIR_OPTIONS_OPTION_H_ */ ./include/platform/mir/input/0000755000015600001650000000000012676616126016307 5ustar jenkinsjenkins./include/platform/mir/input/platform.h0000644000015600001650000001155412676616125020311 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_PLATFORM_H_ #define MIR_INPUT_PLATFORM_H_ #include "mir/module_properties.h" #include #include #include #include namespace mir { class EmergencyCleanupRegistry; namespace dispatch { class Dispatchable; } namespace input { class InputDevice; class InputReport; class InputDeviceRegistry; class InputPlatformPolicy; enum class PlatformPriority : uint32_t { unsupported = 0, dummy = 1, supported = 128, best = 256, }; /** * Input Platform is used to discover and access available input devices. * * A platform implementation is supposed to handle device occurance events by * opening new device and registering them at the server's InputDeviceRegistry. * Likewise the InputDeviceRegistry shall be informed about removed input devices. * * The actual processing of user input is controlled through the mir::input::InputDevice interface. */ class Platform { public: Platform() = default; virtual ~Platform() = default; /*! * The dispatchable of the platform shall be used to monitor for devices. */ virtual std::shared_ptr dispatchable() = 0; /*! * Request the platform to start monitoring for devices. * * \param input_device_registry should be informed about available input devices * \param trigger_registry should be used to register event sources that may indicate a changes of the available devices */ virtual void start() = 0; /*! * Request the platform to stop monitoring for devices. */ virtual void stop() = 0; private: Platform(Platform const&) = delete; Platform& operator=(Platform const&) = delete; }; typedef mir::UniqueModulePtr(*CreatePlatform)( options::Option const& options, std::shared_ptr const& emergency_cleanup_registry, std::shared_ptr const& input_device_registry, std::shared_ptr const& report); typedef void(*AddPlatformOptions)( boost::program_options::options_description& config); typedef PlatformPriority(*ProbePlatform)( options::Option const& options); typedef ModuleProperties const*(*DescribeModule)(); } } extern "C" { /** * Function used to initialize an input platform. * * \param [in] options options to use for this platform * \param [in] emergency_cleanup_registry object to register emergency shutdown handlers with * \param [in] input_device_registry object to register input devices handled by this platform * \param [in] report the object to use to report interesting events from the input subsystem * \param [in] platform_policy object to use to obtain input related configuration and input event filtering interfaces * * This factory function needs to be implemented by each platform. * * \ingroup platform_enablement */ mir::UniqueModulePtr create_input_platform( mir::options::Option const& options, std::shared_ptr const& emergency_cleanup_registry, std::shared_ptr const& input_device_registry, std::shared_ptr const& report); /** * Function used to add additional configuration options * * \param [in] config program option description that the platform may extend * * This function needs to be implemented by each platform. * * \ingroup platform_enablement */ void add_input_platform_options(boost::program_options::options_description& config); /** * probe_platform should indicate whether the platform is able to work within * the current environment. * * \param [in] options program options of the mir server * * This function needs to be implemented by each platform. * * \ingroup platform_enablement */ mir::input::PlatformPriority probe_input_platform(mir::options::Option const& options); /** * describe_input_module should return a description of the input platform. * * This function needs to be implemented by each platform. * * \ingroup platform_enablement */ mir::ModuleProperties const* describe_input_module(); } #endif // MIR_INPUT_PLATFORM_H_ ./include/platform/mir/input/pointer_settings.h0000644000015600001650000000317712676616125022067 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_POINTER_SETTINGS_H_ #define MIR_INPUT_POINTER_SETTINGS_H_ #include "mir_toolkit/client_types.h" #include "mir_toolkit/mir_input_device.h" namespace mir { namespace input { struct PointerSettings { PointerSettings() {} /** * Configure left and right handed mode by selecting a primary button */ MirPointerHandedness handedness{mir_pointer_handedness_right}; /** * Bias cursor acceleration. * - [-1, 0): reduced acceleration * - 0: default acceleration * - (0, 1]: increased acceleration */ double cursor_acceleration_bias{0.0}; /** * Acceleration profile */ MirPointerAcceleration acceleration{mir_pointer_acceleration_adaptive}; /** * Scale horizontal scrolling linearly */ double horizontal_scroll_scale{1.0}; /** * Scale vertical scrolling linearly */ double vertical_scroll_scale{1.0}; }; } } #endif ./include/platform/mir/input/touchpad_settings.h0000644000015600001650000000247712676616125022220 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_TOUCH_PAD_SETTINGS_H_ #define MIR_INPUT_TOUCH_PAD_SETTINGS_H_ #include "mir_toolkit/mir_input_device.h" namespace mir { namespace input { int const no_scroll_button = 0; struct TouchpadSettings { TouchpadSettings() {} MirTouchpadClickModes click_mode{mir_touchpad_click_mode_finger_count}; MirTouchpadScrollModes scroll_mode{mir_touchpad_scroll_mode_two_finger_scroll}; int button_down_scroll_button{no_scroll_button}; bool tap_to_click{true}; bool disable_while_typing{false}; bool disable_with_mouse{false}; bool middle_mouse_button_emulation{true}; }; } } #endif ./include/platform/mir/input/device_capability.h0000644000015600001650000000251112676616125022116 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_DEVICE_CAPABILITY_H_ #define MIR_INPUT_DEVICE_CAPABILITY_H_ #include "mir/flags.h" #include namespace mir { namespace input { enum class DeviceCapability : uint32_t { unknown = 0, pointer = 1<<1, keyboard = 1<<2, touchpad = 1<<3, touchscreen = 1<<4, gamepad = 1<<5, joystick = 1<<6, switch_ = 1<<7, multitouch = 1<<8, // multitouch capable alpha_numeric = 1<<9 // enough keys for text entry }; DeviceCapability mir_enable_enum_bit_operators(DeviceCapability); using DeviceCapabilities = mir::Flags; } } #endif ./include/platform/mir/input/event_builder.h0000644000015600001650000000411012676616125021302 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_EVENT_BUILDER_H_ #define MIR_INPUT_EVENT_BUILDER_H_ #include "mir_toolkit/event.h" #include #include namespace mir { using EventUPtr = std::unique_ptr; namespace input { class EventBuilder { public: EventBuilder() = default; virtual ~EventBuilder() = default; using Timestamp = std::chrono::nanoseconds; virtual EventUPtr key_event(Timestamp timestamp, MirKeyboardAction action, xkb_keysym_t key_code, int scan_code) = 0; virtual EventUPtr touch_event(Timestamp timestamp) = 0; virtual void add_touch(MirEvent& event, MirTouchId touch_id, MirTouchAction action, MirTouchTooltype tooltype, float x_axis_value, float y_axis_value, float pressure_value, float touch_major_value, float touch_minor_value, float size_value) = 0; virtual EventUPtr pointer_event(Timestamp timestamp, MirPointerAction action, MirPointerButtons buttons_pressed, float hscroll_value, float vscroll_value, float relative_x_value, float relative_y_value) = 0; virtual EventUPtr configuration_event(Timestamp timestamp, MirInputConfigurationAction action) = 0; protected: EventBuilder(EventBuilder const&) = delete; EventBuilder& operator=(EventBuilder const&) = delete; }; } } #endif ./include/platform/mir/input/input_device.h0000644000015600001650000000364512676616125021145 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_INPUT_DEVICE_H_ #define MIR_INPUT_INPUT_DEVICE_H_ #include "mir/module_deleter.h" #include "mir/optional_value.h" #include namespace mir { namespace dispatch { class Dispatchable; } namespace input { class InputSink; class InputDeviceInfo; class EventBuilder; class PointerSettings; class TouchpadSettings; /** * Represents an input device. */ class InputDevice { public: InputDevice() = default; virtual ~InputDevice() = default; /*! * Allow the input device to provide its input events to the given InputSink */ virtual void start(InputSink* destination, EventBuilder* builder) = 0; /*! * Stop the input device from sending input events, to the InputSink. */ virtual void stop() = 0; virtual InputDeviceInfo get_device_info() = 0; virtual optional_value get_pointer_settings() const = 0; virtual void apply_settings(PointerSettings const&) = 0; virtual optional_value get_touchpad_settings() const = 0; virtual void apply_settings(TouchpadSettings const&) = 0; protected: InputDevice(InputDevice const&) = delete; InputDevice& operator=(InputDevice const&) = delete; }; } } #endif ./include/platform/mir/input/input_device_registry.h0000644000015600001650000000245712676616125023075 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_INPUT_DEVICE_REGISTRY_H_ #define MIR_INPUT_INPUT_DEVICE_REGISTRY_H_ #include namespace mir { namespace input { class InputDevice; class InputDeviceRegistry { public: InputDeviceRegistry() = default; virtual ~InputDeviceRegistry() = default; virtual void add_device(std::shared_ptr const& device) = 0; virtual void remove_device(std::shared_ptr const& device) = 0; protected: InputDeviceRegistry(InputDeviceRegistry const&) = delete; InputDeviceRegistry& operator=(InputDeviceRegistry const&) = delete; }; } } #endif ./include/platform/mir/input/input_sink.h0000644000015600001650000000252112676616125020642 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_INPUT_SINK_H_ #define MIR_INPUT_INPUT_SINK_H_ #include "mir_toolkit/event.h" #include "mir/geometry/rectangle.h" #include "mir/geometry/displacement.h" namespace mir { namespace input { class InputSink { public: InputSink() = default; virtual ~InputSink() = default; virtual void handle_input(MirEvent& event) = 0; /**! * Obtain the bounding rectangle of the destination area for this input sink */ virtual mir::geometry::Rectangle bounding_rectangle() const = 0; private: InputSink(InputSink const&) = delete; InputSink& operator=(InputSink const&) = delete; }; } } #endif ./include/platform/mir/input/input_device_info.h0000644000015600001650000000202212676616125022144 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_INPUT_DEVICE_INFO_H_ #define MIR_INPUT_INPUT_DEVICE_INFO_H_ #include "mir/input/device_capability.h" #include #include namespace mir { namespace input { struct InputDeviceInfo { std::string name; std::string unique_id; DeviceCapabilities capabilities; }; } } #endif ./include/platform/mir/input/input_report.h0000644000015600001650000000305212676616125021211 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_INPUT_INPUT_REPORT_H_ #define MIR_INPUT_INPUT_REPORT_H_ #include namespace mir { namespace input { class InputReport { public: virtual ~InputReport() = default; virtual void received_event_from_kernel(int64_t when, int type, int code, int value) = 0; virtual void published_key_event(int dest_fd, uint32_t seq_id, int64_t event_time) = 0; virtual void published_motion_event(int dest_fd, uint32_t seq_id, int64_t event_time) = 0; virtual void opened_input_device(char const* device_name, char const* input_platform) = 0; virtual void failed_to_open_input_device(char const* device_name, char const* input_platform) = 0; protected: InputReport() = default; InputReport(InputReport const&) = delete; InputReport& operator=(InputReport const&) = delete; }; } } #endif /* MIR_INPUT_INPUT_REPORT_H_ */ ./include/platform/mir/graphics/0000755000015600001650000000000012676616126016750 5ustar jenkinsjenkins./include/platform/mir/graphics/platform.h0000644000015600001650000001464412676616125020755 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: * Thomas Guest */ #ifndef MIR_GRAPHICS_PLATFORM_H_ #define MIR_GRAPHICS_PLATFORM_H_ #include #include #include "mir/module_properties.h" #include "mir/module_deleter.h" namespace mir { class EmergencyCleanupRegistry; namespace frontend { class Surface; } namespace options { class Option; class ProgramOption; } /// Graphics subsystem. Mediates interaction between core system and /// the graphics environment. namespace graphics { class Buffer; class Display; class DisplayReport; class DisplayConfigurationPolicy; class GraphicBufferAllocator; class GLConfig; class PlatformIpcOperations; class NestedContext; /** * \defgroup platform_enablement Mir platform enablement * * Classes and functions that need to be implemented to add support for a graphics platform. */ /** * Interface to platform specific support for graphics operations. * \ingroup platform_enablement */ class Platform { public: Platform() = default; Platform(const Platform& p) = delete; Platform& operator=(const Platform& p) = delete; virtual ~Platform() = default; /** * Creates the buffer allocator subsystem. */ virtual UniqueModulePtr create_buffer_allocator() = 0; /** * Creates the display subsystem. */ virtual UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, std::shared_ptr const& gl_config) = 0; /** * Creates an object capable of doing platform specific processing of buffers * before they are sent or after they are recieved accross IPC */ virtual UniqueModulePtr make_ipc_operations() const = 0; virtual EGLNativeDisplayType egl_native_display() const = 0; }; /** * A measure of how well a platform supports a device * * \note This is compared as an integer; best + 1 is a valid PlatformPriority that * will be used in preference to a module that reports best. * Platform modules distributed with Mir will never use a priority higher * than best. */ enum PlatformPriority : uint32_t { unsupported = 0, /**< Unable to function at all on this device */ dummy = 1, /**< Used only for dummy or stub platforms. */ supported = 128, /**< Capable of providing a functioning Platform on this device, * possibly with degraded performance or features. */ best = 256 /**< Capable of providing a Platform with the best features and * performance this device is capable of. */ }; typedef mir::UniqueModulePtr(*CreateHostPlatform)( std::shared_ptr const& options, std::shared_ptr const& emergency_cleanup_registry, std::shared_ptr const& report); typedef mir::UniqueModulePtr(*CreateGuestPlatform)( std::shared_ptr const& report, std::shared_ptr const& nested_context); typedef void(*AddPlatformOptions)( boost::program_options::options_description& config); typedef mir::graphics::PlatformPriority(*PlatformProbe)(mir::options::ProgramOption const& options); typedef mir::ModuleProperties const*(*DescribeModule)(); } } extern "C" { /** * Function prototype used to return a new host graphics platform. The host graphics platform * is the system entity that owns the physical display and is a mir host server. * * \param [in] options options to use for this platform * \param [in] emergency_cleanup_registry object to register emergency shutdown handlers with * \param [in] report the object to use to report interesting events from the display subsystem * * This factory function needs to be implemented by each platform. * * \ingroup platform_enablement */ mir::UniqueModulePtr create_host_platform( std::shared_ptr const& options, std::shared_ptr const& emergency_cleanup_registry, std::shared_ptr const& report); /** * Function prototype used to return a new guest graphics platform. The guest graphics platform * exists alongside the host platform and do not output or control the physical displays * * \param [in] nested_context the object that contains resources needed from the host platform * \param [in] report the object to use to report interesting events from the display subsystem * * This factory function needs to be implemented by each platform. * * \ingroup platform_enablement */ mir::UniqueModulePtr create_guest_platform( std::shared_ptr const& report, std::shared_ptr const& nested_context); /** * Function prototype used to add platform specific options to the platform-independent server options. * * \param [in] config a boost::program_options that can be appended with new options * * This factory function needs to be implemented by each platform. * * \ingroup platform_enablement */ void add_graphics_platform_options( boost::program_options::options_description& config); // TODO: We actually need to be more granular here; on a device with more // than one graphics system we may need a different platform per GPU, // so we should be associating platforms with graphics devices in some way mir::graphics::PlatformPriority probe_graphics_platform(mir::options::ProgramOption const& options); mir::ModuleProperties const* describe_graphics_module(); } #endif // MIR_GRAPHICS_PLATFORM_H_ ./include/platform/mir/graphics/virtual_output.h0000644000015600001650000000343712676616125022235 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #ifndef MIR_GRAPHICS_VIRTUAL_OUTPUT_H_ #define MIR_GRAPHICS_VIRTUAL_OUTPUT_H_ namespace mir { namespace graphics { /** * Interface to a virtual output */ class VirtualOutput { public: /** Enables the virtual output. * * This will initiate the same response as when a physical display is * hotplugged into a system. A configuration change notification will be * generated and the DisplayConfiguration will contain one new output. **/ virtual void enable() = 0; /** Disable the virtual output. * * This will initiate the same response as when a physical display is * unplugged from a system. A configuration change notification will be * generated and the DisplayConfiguration will no longer contain an output * associated with the virtual display. **/ virtual void disable() = 0; VirtualOutput() = default; virtual ~VirtualOutput() = default; private: VirtualOutput(VirtualOutput const&) = delete; VirtualOutput& operator=(VirtualOutput const&) = delete; }; } } #endif /* MIR_GRAPHICS_VIRTUAL_OUTPUT_H_ */ ./include/platform/mir/graphics/cursor.h0000644000015600001650000000243012676616125020434 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_GRAPHICS_CURSOR_H_ #define MIR_GRAPHICS_CURSOR_H_ #include "mir/geometry/size.h" #include "mir/geometry/point.h" #include namespace mir { namespace graphics { class CursorImage; class Cursor { public: virtual void show() = 0; virtual void show(CursorImage const& cursor_image) = 0; virtual void hide() = 0; virtual void move_to(geometry::Point position) = 0; protected: Cursor() = default; virtual ~Cursor() = default; Cursor(Cursor const&) = delete; Cursor& operator=(Cursor const&) = delete; }; } } #endif /* MIR_GRAPHICS_CURSOR_H_ */ ./include/platform/mir/graphics/buffer_properties.h0000644000015600001650000000405612676616125022652 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_BUFFER_PROPERTIES_H_ #define MIR_GRAPHICS_BUFFER_PROPERTIES_H_ #include "mir/geometry/size.h" #include "mir_toolkit/common.h" namespace mir { namespace graphics { /** * How a buffer is going to be used. * * The usage is not just a hint; for example, depending on the platform, a * 'hardware' buffer may not support direct pixel access. */ enum class BufferUsage { undefined, /** rendering using GL */ hardware, /** rendering using direct pixel access */ software }; /** * Buffer creation properties. */ struct BufferProperties { BufferProperties() : size(), format(mir_pixel_format_invalid), usage(BufferUsage::undefined) { } BufferProperties(const geometry::Size& size, const MirPixelFormat& format, BufferUsage usage) : size{size}, format{format}, usage{usage} { } geometry::Size size; MirPixelFormat format; BufferUsage usage; }; inline bool operator==(BufferProperties const& lhs, BufferProperties const& rhs) { return lhs.size == rhs.size && lhs.format == rhs.format && lhs.usage == rhs.usage; } inline bool operator!=(BufferProperties const& lhs, BufferProperties const& rhs) { return !(lhs == rhs); } } } #endif // MIR_GRAPHICS_BUFFER_PROPERTIES_H_ ./include/platform/mir/graphics/buffer_id.h0000644000015600001650000000171012676616125021044 0ustar jenkinsjenkins/* * Copyright © 2012, 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_GRAPHICS_BUFFER_ID_H_ #define MIR_GRAPHICS_BUFFER_ID_H_ #include "mir/int_wrapper.h" namespace mir { namespace graphics { struct BufferIdTag; typedef IntWrapper BufferID; } } #endif /* MIR_GRAPHICS_BUFFER_ID_H_ */ ./include/platform/mir/graphics/display_configuration.h0000644000015600001650000001457712676616125023532 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_DISPLAY_CONFIGURATION_H_ #define MIR_GRAPHICS_DISPLAY_CONFIGURATION_H_ #include "mir/int_wrapper.h" #include "mir/geometry/size.h" #include "mir/geometry/rectangle.h" #include "mir/geometry/point.h" #include "mir_toolkit/common.h" #include #include #include namespace mir { namespace graphics { namespace detail { struct GraphicsConfCardIdTag; struct GraphicsConfOutputIdTag; } typedef IntWrapper DisplayConfigurationCardId; typedef IntWrapper DisplayConfigurationOutputId; /** * Configuration information for a display card. */ struct DisplayConfigurationCard { DisplayConfigurationCardId id; size_t max_simultaneous_outputs; }; /** * The type of a display output. */ enum class DisplayConfigurationOutputType { unknown, vga, dvii, dvid, dvia, composite, svideo, lvds, component, ninepindin, displayport, hdmia, hdmib, tv, edp }; /** * Configuration information for a display output mode. */ struct DisplayConfigurationMode { geometry::Size size; double vrefresh_hz; }; /** * Configuration information for a display output. */ struct DisplayConfigurationOutput { /** The output's id. */ DisplayConfigurationOutputId id; /** The id of the card the output is connected to. */ DisplayConfigurationCardId card_id; /** The type of the output. */ DisplayConfigurationOutputType type; /** The pixel formats supported by the output */ std::vector pixel_formats; /** The modes supported by the output. */ std::vector modes; /** The index in the 'modes' vector of the preferred output mode. */ uint32_t preferred_mode_index; /** The physical size of the output. */ geometry::Size physical_size_mm; /** Whether the output is connected. */ bool connected; /** Whether the output is used in the configuration. */ bool used; /** The top left point of this output in the virtual coordinate space. */ geometry::Point top_left; /** The index in the 'modes' vector of the current output mode. */ uint32_t current_mode_index; /** The current output pixel format. A matching entry should be found in the 'pixel_formats' vector*/ MirPixelFormat current_format; /** Current power mode **/ MirPowerMode power_mode; MirOrientation orientation; /** Requested scale factor for this output, for HiDPI support */ float scale; /** Form factor of this output; phone display, tablet, monitor, TV, projector... */ MirFormFactor form_factor; /** The logical rectangle occupied by the output, based on its position, current mode and orientation (rotation) */ geometry::Rectangle extents() const; bool valid() const; }; /** * Mirror of a DisplayConfigurationOutput, with some fields limited to * being read-only, preventing users from changing things they shouldn't. */ struct UserDisplayConfigurationOutput { DisplayConfigurationOutputId const& id; DisplayConfigurationCardId const& card_id; DisplayConfigurationOutputType const& type; std::vector const& pixel_formats; std::vector const& modes; uint32_t const& preferred_mode_index; geometry::Size const& physical_size_mm; bool const& connected; bool& used; geometry::Point& top_left; uint32_t& current_mode_index; MirPixelFormat& current_format; MirPowerMode& power_mode; MirOrientation& orientation; float& scale; MirFormFactor& form_factor; UserDisplayConfigurationOutput(DisplayConfigurationOutput& master); geometry::Rectangle extents() const; }; std::ostream& operator<<(std::ostream& out, DisplayConfigurationCard const& val); bool operator==(DisplayConfigurationCard const& val1, DisplayConfigurationCard const& val2); bool operator!=(DisplayConfigurationCard const& val1, DisplayConfigurationCard const& val2); std::ostream& operator<<(std::ostream& out, DisplayConfigurationMode const& val); bool operator==(DisplayConfigurationMode const& val1, DisplayConfigurationMode const& val2); bool operator!=(DisplayConfigurationMode const& val1, DisplayConfigurationMode const& val2); std::ostream& operator<<(std::ostream& out, DisplayConfigurationOutput const& val); bool operator==(DisplayConfigurationOutput const& val1, DisplayConfigurationOutput const& val2); bool operator!=(DisplayConfigurationOutput const& val1, DisplayConfigurationOutput const& val2); /** * Interface to a configuration of display cards and outputs. */ class DisplayConfiguration { public: virtual ~DisplayConfiguration() = default; /** Executes a function object for each card in the configuration. */ virtual void for_each_card(std::function f) const = 0; /** Executes a function object for each output in the configuration. */ virtual void for_each_output(std::function f) const = 0; virtual void for_each_output(std::function f) = 0; virtual std::unique_ptr clone() const = 0; virtual bool valid() const; protected: DisplayConfiguration() = default; DisplayConfiguration(DisplayConfiguration const& c) = delete; DisplayConfiguration& operator=(DisplayConfiguration const& c) = delete; }; bool operator==(DisplayConfiguration const& lhs, DisplayConfiguration const& rhs); bool operator!=(DisplayConfiguration const& lhs, DisplayConfiguration const& rhs); std::ostream& operator<<(std::ostream& out, DisplayConfiguration const& val); } } #endif /* MIR_GRAPHICS_DISPLAY_CONFIGURATION_H_ */ ./include/platform/mir/graphics/gl_config.h0000644000015600001650000000260112676616125021046 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_GL_CONFIG_H_ #define MIR_GRAPHICS_GL_CONFIG_H_ namespace mir { namespace graphics { /** * Interface for customizing aspects of the GL config used by the server. */ class GLConfig { public: virtual ~GLConfig() = default; /** * Gets the bits to use for each pixel in the depth buffer. */ virtual int depth_buffer_bits() const = 0; /** * Gets the bits to use for each pixel in the stencil buffer. */ virtual int stencil_buffer_bits() const = 0; protected: GLConfig() = default; GLConfig(GLConfig const&) = delete; GLConfig& operator=(GLConfig const&) = delete; }; } } #endif /* MIR_GRAPHICS_GL_CONFIG_H_ */ ./include/platform/mir/graphics/platform_operation_message.h0000644000015600001650000000177112676616125024536 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_PLATFORM_OPERATION_MESSAGE_H_ #define MIR_GRAPHICS_PLATFORM_OPERATION_MESSAGE_H_ #include #include namespace mir { namespace graphics { struct PlatformOperationMessage { std::vector data; std::vector fds; }; } } #endif ./include/platform/mir/graphics/graphic_buffer_allocator.h0000644000015600001650000000326312676616125024132 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_GRAPHICS_GRAPHIC_BUFFER_ALLOCATOR_H_ #define MIR_GRAPHICS_GRAPHIC_BUFFER_ALLOCATOR_H_ #include "mir/graphics/buffer.h" #include #include namespace mir { namespace graphics { struct BufferProperties; /** * Interface to graphic buffer allocation. */ class GraphicBufferAllocator { public: virtual ~GraphicBufferAllocator() = default; /** * Allocates a buffer. * * \param [in] buffer_properties the properties the allocated buffer should have */ virtual std::shared_ptr alloc_buffer( BufferProperties const& buffer_properties) = 0; /** * The supported buffer pixel formats. */ virtual std::vector supported_pixel_formats() = 0; protected: GraphicBufferAllocator() = default; GraphicBufferAllocator(const GraphicBufferAllocator&) = delete; GraphicBufferAllocator& operator=(const GraphicBufferAllocator&) = delete; }; } } #endif // MIR_GRAPHICS_GRAPHIC_BUFFER_ALLOCATOR_H_ ./include/platform/mir/graphics/display.h0000644000015600001650000001305012676616125020564 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_GRAPHICS_DISPLAY_H_ #define MIR_GRAPHICS_DISPLAY_H_ #include #include #include namespace mir { namespace graphics { class DisplayBuffer; class DisplayConfiguration; class Cursor; class CursorImage; class GLContext; class EventHandlerRegister; class VirtualOutput; typedef std::function DisplayPauseHandler; typedef std::function DisplayResumeHandler; typedef std::function DisplayConfigurationChangeHandler; /** * DisplaySyncGroup represents a group of displays that need to be output * in unison as a single post() call. * This is only appropriate for platforms whose post() calls are non-blocking * and not synchronous with the screen hardware (e.g. virtual machines or * Android). * Using a DisplaySyncGroup with multiple screens on a platform whose post() * blocks for vsync often results in stuttering, and so should be avoided. * Although using DisplaySyncGroup with a single DisplayBuffer remains safe * for any platform. */ class DisplaySyncGroup { public: /** * Executes a functor that allows the DisplayBuffer contents to be updated **/ virtual void for_each_display_buffer(std::function const& f) = 0; /** Post the content of the DisplayBuffers associated with this DisplaySyncGroup. * The content of all the DisplayBuffers in this DisplaySyncGroup are guaranteed to be onscreen * in the near future. On some platforms, this may wait a potentially long time for vsync. **/ virtual void post() = 0; /** * Returns a recommendation to the compositor as to how long it should * wait before sampling the scene for the next frame. Sampling the * scene too early results in up to one whole frame of extra lag if * rendering is fast or skipped altogether (bypass/overlays). But sampling * too late and we might miss the deadline. If unsure just return zero. */ virtual std::chrono::milliseconds recommended_sleep() const = 0; virtual ~DisplaySyncGroup() = default; protected: DisplaySyncGroup() = default; DisplaySyncGroup(DisplaySyncGroup const&) = delete; DisplaySyncGroup& operator=(DisplaySyncGroup const&) = delete; }; /** * Interface to the display subsystem. */ class Display { public: /** * Executes a functor for each output group. */ virtual void for_each_display_sync_group(std::function const& f) = 0; /** * Gets a copy of the current output configuration. */ virtual std::unique_ptr configuration() const = 0; /** * Sets a new output configuration. */ virtual void configure(DisplayConfiguration const& conf) = 0; /** * Registers a handler for display configuration changes. * * Note that the handler is called only for hardware changes (e.g. monitor * plugged/unplugged), not for changes initiated by software (e.g. modesetting). * * The implementation should use the functionality provided by the MainLoop * to register the handlers in a way appropriate for the platform. */ virtual void register_configuration_change_handler( EventHandlerRegister& handlers, DisplayConfigurationChangeHandler const& conf_change_handler) = 0; /** * Registers handlers for pausing and resuming the display subsystem. * * The implementation should use the functionality provided by the EventHandlerRegister * to register the handlers in a way appropriate for the platform. */ virtual void register_pause_resume_handlers( EventHandlerRegister& handlers, DisplayPauseHandler const& pause_handler, DisplayResumeHandler const& resume_handler) = 0; /** * Pauses the display. * * This method may temporarily (until resumed) release any resources * associated with the display subsystem. */ virtual void pause() = 0; /** * Resumes the display. */ virtual void resume() = 0; /** * Create a hardware cursor object. */ virtual std::shared_ptr create_hardware_cursor(std::shared_ptr const& initial_image) = 0; /** * Creates a GLContext object that shares resources with the Display's GL context. * * This is usually implemented as a shared EGL context. This object can be used * to access graphics resources from an arbitrary thread. */ virtual std::unique_ptr create_gl_context() = 0; /** * Creates a virtual output * \returns null if the implementation does not support virtual outputs */ virtual std::unique_ptr create_virtual_output(int width, int height) = 0; Display() = default; virtual ~Display() = default; private: Display(Display const&) = delete; Display& operator=(Display const&) = delete; }; } } #endif /* MIR_GRAPHICS_DISPLAY_H_ */ ./include/platform/mir/graphics/buffer_basic.h0000644000015600001650000000214012676616125021527 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_GRAPHICS_BUFFER_BASIC_H_ #define MIR_GRAPHICS_BUFFER_BASIC_H_ #include "mir/graphics/buffer.h" #include "mir/graphics/buffer_id.h" namespace mir { namespace graphics { class BufferBasic : public Buffer { public: BufferBasic(); graphics::BufferID id() const { return buffer_id; } private: BufferID const buffer_id; }; } } #endif /* MIR_GRAPHICS_BUFFER_BASIC_H_ */ ./include/platform/mir/graphics/display_configuration_policy.h0000644000015600001650000000247712676616125025105 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_DISPLAY_CONFIGURATION_POLICY_H_ #define MIR_GRAPHICS_DISPLAY_CONFIGURATION_POLICY_H_ namespace mir { namespace graphics { class DisplayConfiguration; class DisplayConfigurationPolicy { public: virtual ~DisplayConfigurationPolicy() = default; virtual void apply_to(DisplayConfiguration& conf) = 0; protected: DisplayConfigurationPolicy() = default; DisplayConfigurationPolicy(DisplayConfigurationPolicy const& c) = delete; DisplayConfigurationPolicy& operator=(DisplayConfigurationPolicy const& c) = delete; }; } } #endif /* MIR_GRAPHICS_DISPLAY_CONFIGURATION_POLICY_H_ */ ./include/platform/mir/graphics/display_buffer.h0000644000015600001650000000671012676616125022122 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_DISPLAY_BUFFER_H_ #define MIR_GRAPHICS_DISPLAY_BUFFER_H_ #include #include #include #include namespace mir { namespace graphics { class Buffer; class NativeDisplayBuffer { protected: NativeDisplayBuffer() = default; virtual ~NativeDisplayBuffer() = default; NativeDisplayBuffer(NativeDisplayBuffer const&) = delete; NativeDisplayBuffer operator=(NativeDisplayBuffer const&) = delete; }; /** * Interface to an output framebuffer. */ class DisplayBuffer { public: virtual ~DisplayBuffer() = default; /** The area the DisplayBuffer occupies in the virtual screen space. */ virtual geometry::Rectangle view_area() const = 0; /** This will render renderlist to the screen and post the result to the * screen if there is a hardware optimization that can be done. * \param [in] renderlist * The renderables that should appear on the screen if the hardware * is capable of optmizing that list somehow. If what you want * displayed on the screen cannot be represented by a RenderableList, * then you should draw using OpenGL and use post_update() * \returns * true if the hardware can optimize the rendering of the list. * When this call completes, the renderlist will have been posted * to the screen. * false if the hardware platform cannot optimize the list. The screen * will not be updated. The caller should render the list another way, * and post using post_update() **/ virtual bool post_renderables_if_optimizable(RenderableList const& renderlist) = 0; /** Returns the orientation of the display buffer relative to how the * user should see it (the orientation of the output). * This tells us how much (if any) rotation the renderer needs to do. * If your DisplayBuffer can do the rotation itself then this will * always return mir_orientation_normal. If the DisplayBuffer does not * implement the rotation itself then this function will return the * amount of rotation the renderer must do to make things "look right". */ virtual MirOrientation orientation() const = 0; /** Returns a pointer to the native display buffer object backing this * display buffer. * * The pointer to the native display buffer remains valid as long as the * display buffer object is valid. */ virtual NativeDisplayBuffer* native_display_buffer() = 0; protected: DisplayBuffer() = default; DisplayBuffer(DisplayBuffer const& c) = delete; DisplayBuffer& operator=(DisplayBuffer const& c) = delete; }; } } #endif /* MIR_GRAPHICS_DISPLAY_BUFFER_H_ */ ./include/platform/mir/graphics/cursor_image.h0000644000015600001650000000266312676616125021606 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_GRAPHICS_CURSOR_IMAGE_H_ #define MIR_GRAPHICS_CURSOR_IMAGE_H_ #include "mir/geometry/size.h" #include "mir/geometry/displacement.h" namespace mir { namespace graphics { class CursorImage { public: virtual ~CursorImage() = default; virtual void const* as_argb_8888() const = 0; virtual geometry::Size size() const = 0; // We use "hotspot" to mean the offset within a cursor image // which should be placed at the onscreen // location of the pointer. virtual geometry::Displacement hotspot() const = 0; protected: CursorImage() = default; CursorImage(CursorImage const&) = delete; CursorImage& operator=(CursorImage const&) = delete; }; } } #endif /* MIR_GRAPHICS_CURSOR_IMAGE_H_ */ ./include/platform/mir/graphics/gl_context.h0000644000015600001650000000222112676616125021263 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_GL_CONTEXT_H_ #define MIR_GRAPHICS_GL_CONTEXT_H_ namespace mir { namespace graphics { class GLContext { public: virtual ~GLContext() = default; virtual void make_current() const = 0; virtual void release_current() const = 0; protected: GLContext() = default; GLContext(GLContext const&) = delete; GLContext& operator=(GLContext const&) = delete; }; } } #endif /* MIR_GRAPHICS_GL_CONTEXT_H_ */ ./include/platform/mir/graphics/buffer.h0000644000015600001650000000402712676616125020374 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_GRAPHICS_BUFFER_H_ #define MIR_GRAPHICS_BUFFER_H_ #include "mir/graphics/native_buffer.h" #include "mir/graphics/buffer_id.h" #include "mir/geometry/size.h" #include "mir_toolkit/common.h" #include #include namespace mir { namespace graphics { class NativeBufferBase { protected: NativeBufferBase() = default; virtual ~NativeBufferBase() = default; NativeBufferBase(NativeBuffer const&) = delete; NativeBufferBase operator=(NativeBuffer const&) = delete; }; class Buffer { public: virtual ~Buffer() {} virtual std::shared_ptr native_buffer_handle() const = 0; virtual BufferID id() const = 0; virtual geometry::Size size() const = 0; virtual geometry::Stride stride() const = 0; virtual MirPixelFormat pixel_format() const = 0; //FIXME: correct mg::Buffer::write, it requires that the user does too much to use it correctly, // (ie, it forces them to figure out what size is proper, alloc a buffer, fill it, and then // copy the data into the buffer) virtual void write(unsigned char const* pixels, size_t size) = 0; virtual void read(std::function const& do_with_pixels) = 0; virtual NativeBufferBase* native_buffer_base() = 0; protected: Buffer() = default; }; } } #endif // MIR_GRAPHICS_BUFFER_H_ ./include/platform/mir/graphics/platform_ipc_package.h0000644000015600001650000000250712676616125023256 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_PLATFORM_IPC_PACKAGE_H_ #define MIR_GRAPHICS_PLATFORM_IPC_PACKAGE_H_ #include #include namespace mir { struct ModuleProperties; namespace graphics { /** * Platform data to be sent to the clients over IPC. */ struct PlatformIPCPackage { PlatformIPCPackage() : graphics_module(nullptr) {} explicit PlatformIPCPackage(ModuleProperties const* graphics_module) : graphics_module(graphics_module) {} std::vector ipc_data; std::vector ipc_fds; ModuleProperties const* graphics_module; }; } } #endif /* MIR_GRAPHICS_PLATFORM_IPC_PACKAGE_H_ */ ./include/platform/mir/graphics/platform_ipc_operations.h0000644000015600001650000000633212676616125024046 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_GRAPHICS_PLATFORM_IPC_OPERATIONS_H_ #define MIR_GRAPHICS_PLATFORM_IPC_OPERATIONS_H_ #include "platform_ipc_package.h" #include namespace mir { namespace graphics { enum class BufferIpcMsgType { full_msg, //pack the full ipc representation of the buffer update_msg //assume the client has a full representation, and pack only updates to the buffer }; class Buffer; class BufferIpcMessage; struct PlatformOperationMessage; class PlatformIpcOperations { public: virtual ~PlatformIpcOperations() = default; /** * Arranges the IPC package for a buffer that is to be sent through * the frontend from server to client. This should be called every * time a buffer is to be sent cross-process. * * Pack the platform specific contents of Buffer into BufferIpcMessage for sending to the client * * \param [in] message the message that will be sent * \param [in] buffer the buffer to be put in the message * \param [in] ipc_type what sort of ipc message is needed */ virtual void pack_buffer(BufferIpcMessage& message, Buffer const& buffer, BufferIpcMsgType msg_type) const = 0; /** * Arranges the IPC package for a buffer that was sent over IPC * client to server. This must be called every time a buffer is * received, as some platform specific processing has to be done on * the incoming buffer. * \param [in] message the message that was sent to the server * \param [in] buffer the buffer associated with the message */ virtual void unpack_buffer(BufferIpcMessage& message, Buffer const& buffer) const = 0; /** * Gets the connection package for the platform. * * The IPC package will be sent to clients when they connect. */ virtual std::shared_ptr connection_ipc_package() = 0; /** * Arranges a platform specific operation triggered by an IPC call * \returns the response that will be sent to the client * \param [in] opcode the opcode that indicates the action to be performed * \param [in] request the message that was sent to the server */ virtual PlatformOperationMessage platform_operation( unsigned int const opcode, PlatformOperationMessage const& message) = 0; protected: PlatformIpcOperations() = default; PlatformIpcOperations(PlatformIpcOperations const&) = delete; PlatformIpcOperations& operator=(PlatformIpcOperations const&) = delete; }; } } #endif /* MIR_GRAPHICS_BUFFER_IPC_PACKER_H_ */ ./include/platform/mir/graphics/event_handler_register.h0000644000015600001650000000364012676616125023645 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_GRAPHICS_EVENT_HANDLER_REGISTER_H_ #define MIR_GRAPHICS_EVENT_HANDLER_REGISTER_H_ #include #include #include "mir/module_deleter.h" namespace mir { namespace graphics { class EventHandlerRegister { public: virtual void register_signal_handler( std::initializer_list signals, std::function const& handler) = 0; virtual void register_signal_handler( std::initializer_list signals, mir::UniqueModulePtr> handler) = 0; virtual void register_fd_handler( std::initializer_list fds, void const* owner, std::function const& handler) = 0; virtual void register_fd_handler( std::initializer_list fds, void const* owner, mir::UniqueModulePtr> handler) = 0; virtual void unregister_fd_handler(void const* owner) = 0; protected: EventHandlerRegister() = default; virtual ~EventHandlerRegister() = default; EventHandlerRegister(EventHandlerRegister const&) = delete; EventHandlerRegister& operator=(EventHandlerRegister const&) = delete; }; } } #endif /* MIR_GRAPHICS_EVENT_HANDLER_REGISTER_H_ */ ./include/platform/mir/graphics/renderable.h0000644000015600001650000000512512676616125021226 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_GRAPHICS_RENDERABLE_H_ #define MIR_GRAPHICS_RENDERABLE_H_ #include #include #include #include namespace mir { namespace graphics { class Buffer; class Renderable { public: virtual ~Renderable() = default; typedef void const* ID; // Mostly opaque, but zero is reserved as "invalid" /** * Return a unique ID for the renderable, which may or may not be based * on the underlying surface ID. You should not assume they are related. */ virtual ID id() const = 0; /** * Return the buffer that should be composited/rendered. */ virtual std::shared_ptr buffer() const = 0; virtual geometry::Rectangle screen_position() const = 0; // These are from the old CompositingCriteria. There is a little bit // of function overlap with the above functions still. virtual float alpha() const = 0; /** * Transformation returns the transformation matrix that should be applied * to the surface. By default when there are no transformations this will * be the identity matrix. * * \warning As this functionality is presently only used by * mir_demo_standalone_render_surfaces for rotations it may be * deprecated in future. It is expected that real transformations * may become more transient things (e.g. applied by animation * logic externally instead of being a semi-permanent attribute of * the surface itself). */ virtual glm::mat4 transformation() const = 0; virtual bool shaped() const = 0; // meaning the pixel format has alpha protected: Renderable() = default; Renderable(Renderable const&) = delete; Renderable& operator=(Renderable const&) = delete; }; typedef std::vector> RenderableList; } } #endif /* MIR_GRAPHICS_RENDERABLE_H_ */ ./include/common/0000755000015600001650000000000012676616124014023 5ustar jenkinsjenkins./include/common/mir/0000755000015600001650000000000012676616125014613 5ustar jenkinsjenkins./include/common/mir/libname.h0000644000015600001650000000171512676616125016377 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_LIBNAME_H #define MIR_LIBNAME_H namespace mir { namespace detail { char const* libname_impl(void* libname); } namespace { inline char const* libname() { return detail::libname_impl(reinterpret_cast(&libname)); } } } #endif //MIR_LIBNAME_H_H ./include/common/mir/shared_library.h0000644000015600001650000000366312676616125017766 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SHARED_LIBRARY_H_ #define MIR_SHARED_LIBRARY_H_ #include namespace mir { class SharedLibrary { public: explicit SharedLibrary(char const* library_name); explicit SharedLibrary(std::string const& library_name); ~SharedLibrary() noexcept; template FunctionPtr load_function(char const* function_name) const { FunctionPtr result{}; (void*&)result = load_symbol(function_name); return result; } template FunctionPtr load_function(std::string const& function_name) const { return load_function(function_name.c_str()); } template FunctionPtr load_function(std::string const& function_name, std::string const& version) const { FunctionPtr result{}; (void*&)result = load_symbol(function_name.c_str(), version.c_str()); return result; } private: void* const so; void* load_symbol(char const* function_name) const; void* load_symbol(char const* function_name, char const* version) const; SharedLibrary(SharedLibrary const&) = delete; SharedLibrary& operator=(SharedLibrary const&) = delete; }; } #endif /* MIR_SHARED_LIBRARY_H_ */ ./include/common/mir/flags.h0000644000015600001650000001045412676616125016064 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored By: Andreas Pokorny */ #ifndef MIR_FLAGS_H_ #define MIR_FLAGS_H_ #include namespace mir { /*! * Treat an enumeration, scoped and unscoped, like a set of flags. * * For scoped enumerations, there are optional bitwise operators available * that can be enabled by declaring a function within the namespace of the * enumeration (here Enum): * \begincode * Enum mir_enable_enum_bit_operators(Enum); * \endcode */ template struct Flags { using value_type = typename std::underlying_type::type; explicit constexpr Flags(value_type flag_value = 0) noexcept : flag_value{flag_value} {} constexpr Flags(Enum flag_value) noexcept : flag_value{static_cast(flag_value)} {} constexpr Flags operator|(Flags other) const noexcept { return Flags(flag_value|other.flag_value); } constexpr Flags operator&(Flags other) const noexcept { return Flags(flag_value & other.flag_value); } constexpr Flags operator^(Flags other) const noexcept { return Flags(flag_value ^ other.flag_value); } // those mutating operators could be trated as constexpr with c++14 Flags& operator|=(Flags other) noexcept { flag_value |= other.flag_value; return *this; } Flags operator&=(Flags other) noexcept { flag_value &= other.flag_value; return *this; } Flags operator^=(Flags other) noexcept { flag_value ^= other.flag_value; return *this; } constexpr bool operator==(Flags other) const noexcept { return flag_value == other.flag_value; } constexpr value_type value() const noexcept { return flag_value; } private: value_type flag_value; }; template constexpr Flags operator|(Flags flags, Enum e) noexcept { return Flags(flags.value() | static_cast(e)); } template constexpr Flags operator|(Enum e, Flags flags) noexcept { return Flags(flags.value() | static_cast(e)); } template constexpr Enum operator&(Enum e, Flags flags) noexcept { return static_cast(flags.value() & static_cast(e)); } template constexpr Enum operator&(Flags flags, Enum e) noexcept { return static_cast(flags.value() & static_cast(e)); } template constexpr bool operator==(Flags flags, Enum e) noexcept { return e == static_cast(flags.value()); } template constexpr bool operator==(Enum e, Flags flags) noexcept { return e == static_cast(flags.value()); } template constexpr bool contains(Flags flags, Enum e) noexcept { return e == static_cast(flags.value() & static_cast(e)); } } template constexpr mir::Flags(0)))> operator|(Enum lhs, Enum rhs) noexcept { return mir::Flags(lhs) | mir::Flags(rhs); } template constexpr mir::Flags(0)))> operator&(Enum lhs, Enum rhs) noexcept { return mir::Flags(lhs) & mir::Flags(rhs); } template constexpr mir::Flags(0)))> operator^(Enum lhs, Enum rhs) noexcept { return mir::Flags(lhs) ^ mir::Flags(rhs); } #endif ./include/common/mir/optional_value.h0000644000015600001650000000462012676616125020007 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #ifndef MIR_OPTIONAL_VALUE_H_ #define MIR_OPTIONAL_VALUE_H_ #include "mir/fatal.h" #include namespace mir { template class optional_value { public: optional_value() = default; optional_value(T const& value) : value_{value}, is_set_{true} {} optional_value& operator=(T const& value) { value_ = value; is_set_ = true; return *this; } bool is_set() const { return is_set_; } T value() const { die_if_unset(); return value_; } T&& consume() { die_if_unset(); is_set_ = false; return std::move(value_); } private: void die_if_unset() const { if (!is_set()) { (*fatal_error)("Accessing value of unset optional"); } } T value_; bool is_set_{false}; }; template inline bool operator == (optional_value const& lhs, optional_value const& rhs) { return lhs.is_set() == rhs.is_set() && (!lhs.is_set() || lhs.value() == rhs.value()); } template inline bool operator != (optional_value const& lhs, optional_value const& rhs) { return !(lhs == rhs); } template inline bool operator == (optional_value const& lhs, T const& rhs) { return lhs.is_set() && (lhs.value() == rhs); } template inline bool operator != (optional_value const& lhs, T const& rhs) { return !(lhs == rhs); } template inline bool operator == (T const& lhs, optional_value const& rhs) { return rhs == lhs; } template inline bool operator != (T const& lhs, optional_value const& rhs) { return rhs != lhs; } } #endif /* MIR_OPTIONAL_VALUE_H_ */ ./include/common/mir/fatal.h0000644000015600001650000000475612676616125016067 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * --- * Fatal error handling - Fatal errors are situations we don't expect to ever * happen and don't have logic to gracefully recover from. The most useful * thing you can do in that situation is abort to get a clean core file and * stack trace to maximize the chances of it being readable. * * Author: Daniel van Vugt * Alan Griffiths */ #ifndef MIR_FATAL_H_ #define MIR_FATAL_H_ namespace mir { /** * fatal_error() is strictly for "this should never happen" situations that * you cannot recover from. By default it points at fatal_error_except(). * Note the reason parameter is a simple char* so its value is clearly visible * in stack trace output. * \param [in] reason A printf-style format string. */ extern void (*fatal_error)(char const* reason, ...); /** * Throws an exception that will typically kill the Mir server and propagate from * mir::run_mir. * \param [in] reason A printf-style format string. */ void fatal_error_except(char const* reason, ...); /** * An alternative to fatal_error_except() that kills the program and dump core * as cleanly as possible. * \param [in] reason A printf-style format string. */ void fatal_error_abort(char const* reason, ...); // Utility class to override & restore existing error handler class FatalErrorStrategy { public: explicit FatalErrorStrategy(void (*fatal_error_handler)(char const* reason, ...)) : old_fatal_error_handler(fatal_error) { fatal_error = fatal_error_handler; } ~FatalErrorStrategy() { fatal_error = old_fatal_error_handler; } private: void (*old_fatal_error_handler)(char const* reason, ...); FatalErrorStrategy(FatalErrorStrategy const&) = delete; FatalErrorStrategy& operator=(FatalErrorStrategy const&) = delete; }; } // namespace mir #endif // MIR_FATAL_H_ ./include/common/mir/dispatch/0000755000015600001650000000000012676616125016412 5ustar jenkinsjenkins./include/common/mir/dispatch/readable_fd.h0000644000015600001650000000235312676616125020776 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_DISPATCH_READABLE_FD_H_ #define MIR_DISPATCH_READABLE_FD_H_ #include "mir/dispatch/dispatchable.h" #include "mir/fd.h" #include namespace mir { namespace dispatch { class ReadableFd : public Dispatchable { public: ReadableFd(Fd fd, std::function const& on_readable); Fd watch_fd() const override; bool dispatch(FdEvents events) override; FdEvents relevant_events() const override; private: mir::Fd fd; std::function readable; }; } } #endif // MIR_DISPATCH_READABLE_FD_H_ ./include/common/mir/dispatch/action_queue.h0000644000015600001650000000252312676616125021246 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_DISPATCH_ACTION_QUEUE_H_ #define MIR_DISPATCH_ACTION_QUEUE_H_ #include "mir/fd.h" #include "mir/dispatch/dispatchable.h" #include #include namespace mir { namespace dispatch { class ActionQueue : public Dispatchable { public: ActionQueue(); Fd watch_fd() const override; void enqueue(std::function const& action); bool dispatch(FdEvents events) override; FdEvents relevant_events() const override; private: bool consume(); void wake(); mir::Fd event_fd; std::mutex list_lock; std::list> actions; }; } } #endif // MIR_DISPATCH_ACTION_QUEUE_H_ ./include/common/mir/dispatch/multiplexing_dispatchable.h0000644000015600001650000000666712676616125024026 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_DISPATCH_MULTIPLEXING_DISPATCHABLE_H_ #define MIR_DISPATCH_MULTIPLEXING_DISPATCHABLE_H_ #include "mir/dispatch/dispatchable.h" #include #include #include #include #include namespace mir { namespace dispatch { /** * \brief How concurrent dispatch should be handled */ enum class DispatchReentrancy { sequential, /**< The dispatch function is guaranteed not to be called * while a thread is currently running it. */ reentrant /**< The dispatch function may be called on multiple threads * simultaneously */ }; /** * \brief An adaptor that combines multiple Dispatchables into a single Dispatchable * \note Instances are fully thread-safe. */ class MultiplexingDispatchable final : public Dispatchable { public: MultiplexingDispatchable(); MultiplexingDispatchable(std::initializer_list> dispatchees); virtual ~MultiplexingDispatchable() noexcept; MultiplexingDispatchable& operator=(MultiplexingDispatchable const&) = delete; MultiplexingDispatchable(MultiplexingDispatchable const&) = delete; Fd watch_fd() const override; bool dispatch(FdEvents events) override; FdEvents relevant_events() const override; /** * \brief Add a dispatchable to the adaptor * \param [in] dispatchee Dispatchable to add. The Dispatchable's dispatch() * function will not be called reentrantly. */ void add_watch(std::shared_ptr const& dispatchee); /** * \brief Add a dispatchable to the adaptor, specifying the reentrancy of dispatch() */ void add_watch(std::shared_ptr const& dispatchee, DispatchReentrancy reentrancy); /** * \brief Add a simple callback to the adaptor * \param [in] fd File descriptor to monitor for readability * \param [in] callback Callback to fire when \ref fd becomes readable. * This callback is not called reentrantly. */ void add_watch(Fd const& fd, std::function const& callback); /** * \brief Remove a watch from the dispatchable * \param [in] dispatchee Dispatchable to remove */ void remove_watch(std::shared_ptr const& dispatchee); /** * \brief Remove a watch by file-descriptor * \param [in] fd File descriptor of watch to remove. */ void remove_watch(Fd const& fd); private: pthread_rwlock_t lifetime_mutex; std::list, bool>> dispatchee_holder; Fd epoll_fd; }; } } #endif // MIR_DISPATCH_MULTIPLEXING_DISPATCHABLE_H_ ./include/common/mir/dispatch/threaded_dispatcher.h0000644000015600001650000000354512676616125022560 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_ #define MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_ #include #include #include #include #include #include #include "mir/dispatch/multiplexing_dispatchable.h" #include "mir/fd.h" namespace mir { namespace dispatch { class Dispatchable; class ThreadedDispatcher { public: ThreadedDispatcher(std::string const& name, std::shared_ptr const& dispatchee); ThreadedDispatcher(std::string const& name, std::shared_ptr const& dispatchee, std::function const& exception_handler); ~ThreadedDispatcher() noexcept; void add_thread(); void remove_thread(); class ThreadShutdownRequestHandler; private: std::string const name_base; std::shared_ptr thread_exiter; std::shared_ptr dispatcher; std::mutex thread_pool_mutex; std::vector threadpool; std::function const exception_handler; }; } } #endif // MIR_DISPATCH_SIMPLE_DISPATCH_THREAD_H_ ./include/common/mir/dispatch/dispatchable.h0000644000015600001650000000476712676616125021224 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_DISPATCH_DISPATCHABLE_H_ #define MIR_DISPATCH_DISPATCHABLE_H_ #include "mir/fd.h" namespace mir { namespace dispatch { enum FdEvent : uint32_t { readable = 1<<0, writable = 1<<1, remote_closed = 1<<2, error = 1<<3 }; using FdEvents = uint32_t; class Dispatchable { public: Dispatchable() = default; virtual ~Dispatchable() = default; Dispatchable& operator=(Dispatchable const&) = delete; Dispatchable(Dispatchable const&) = delete; /** * \brief Get a poll()able file descriptor * \return A file descriptor usable with poll() or equivalent function calls. * relevant_events() contains the set of event types to watch for. */ virtual Fd watch_fd() const = 0; /** * \brief Dispatch one pending event * \param [in] event The set of events current on the file-descriptor * \returns False iff no more events will be produced by this Dispatchable. * Dispatch should no longer be called. * \note This will dispatch at most one event. If there are multiple events * specified in \ref event (eg: readable | remote_closed) then dispatch * will process only one. * \note It is harmless to call dispatch() with an event that does not contain * any of the events from relevant_events(). The function will do * nothing in such a case. * \note An implementation of dispatch() MUST handle FdEvent::error, * if only to return false and terminate further event dispatch. */ virtual bool dispatch(FdEvents events) = 0; /** * \brief The set of file-descriptor events this Dispatchable handles. */ virtual FdEvents relevant_events() const = 0; }; } } #endif // MIR_DISPATCH_DISPATCHABLE_H_ ./include/common/mir/frontend/0000755000015600001650000000000012676616125016432 5ustar jenkinsjenkins./include/common/mir/frontend/buffer_stream_id.h0000644000015600001650000000203112676616125022077 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_FRONTEND_BUFFER_STREAM_ID_H_ #define MIR_FRONTEND_BUFFER_STREAM_ID_H_ #include "mir/int_wrapper.h" namespace mir { namespace frontend { namespace detail { struct SessionsBufferStreamIdTag; } typedef IntWrapper BufferStreamId; } } // namespace mir #endif // MIR_FRONTEND_BUFFER_STREAM_ID_H_ ./include/common/mir/frontend/surface_id.h0000644000015600001650000000177012676616125020714 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_FRONTEND_SURFACE_ID_H_ #define MIR_FRONTEND_SURFACE_ID_H_ #include "mir/int_wrapper.h" namespace mir { namespace frontend { namespace detail { struct SessionsSurfaceIdTag; } typedef IntWrapper SurfaceId; } } // namespace mir #endif // MIR_FRONTEND_SURFACE_ID_H_ ./include/common/mir/module_deleter.h0000644000015600001650000000530212676616125017755 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_MODULE_DELETER_H_ #define MIR_MODULE_DELETER_H_ #include namespace mir { class SharedLibrary; namespace detail { class RefCountedLibrary { public: RefCountedLibrary(void* address); RefCountedLibrary(RefCountedLibrary const&); ~RefCountedLibrary(); RefCountedLibrary& operator=(RefCountedLibrary const&); private: std::shared_ptr internal_state; }; } template struct ModuleDeleter : std::default_delete { ModuleDeleter() : library(nullptr) {} template ModuleDeleter(ModuleDeleter const& other) : std::default_delete{other}, library{other.get_library()} { } detail::RefCountedLibrary get_library() const { return library; } protected: ModuleDeleter(void *address_in_module) : library{address_in_module} { } private: detail::RefCountedLibrary library; }; /*! * \brief Use UniqueModulePtr to ensure that your loadable libray outlives * instances created within it. * * Use mir::make_module_ptr(...) or pass a function from your library to the * constructor, to increase the lifetime of your library: * \code * mir::UniqueModulePtr library_entry_point() * { * return mir::UniqueModulePtr(new Implementation, &library_entry_point); * } * \endcode * * The default constructor will not try to infer the dynamic library. */ template using UniqueModulePtr = std::unique_ptr>; namespace { /*! * \brief make_unique like creation function for UniqueModulePtr */ template inline auto make_module_ptr(Args&&... args) -> UniqueModulePtr { struct Deleter : ModuleDeleter { Deleter(void* address) : ModuleDeleter(address) {} } deleter(reinterpret_cast(&make_module_ptr)); return UniqueModulePtr(new Type(std::forward(args)...), std::move(deleter)); } } } #endif ./include/common/mir/log.h0000644000015600001650000000560112676616125015547 0ustar jenkinsjenkins/* * Convenience functions to make logging in Mir easy * ~~~ * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Daniel van Vugt */ #ifndef MIR_LOG_H_ #define MIR_LOG_H_ #include "mir/logging/logger.h" // for Severity #include #include namespace mir { void logv(logging::Severity sev, const char *component, char const* fmt, va_list va); void log(logging::Severity sev, const char *component, char const* fmt, ...); void log(logging::Severity sev, const char *component, std::string const& message); #ifndef MIR_LOG_COMPONENT #ifdef MIR_LOG_COMPONENT_FALLBACK #define MIR_LOG_COMPONENT MIR_LOG_COMPONENT_FALLBACK #endif #endif #ifdef MIR_LOG_COMPONENT namespace { // Isolated namespace so that the component string is always correct for // where it's used. inline void log_info(std::string const& message) { ::mir::log(::mir::logging::Severity::informational, MIR_LOG_COMPONENT, message); } template void log_info(char const* fmt, Args&&... args) { ::mir::log(::mir::logging::Severity::informational, MIR_LOG_COMPONENT, fmt, std::forward(args)...); } template void log_error(char const* fmt, Args&&... args) { ::mir::log(::mir::logging::Severity::error, MIR_LOG_COMPONENT, fmt, std::forward(args)...); } template inline void log_debug(char const* fmt, Args&&... args) { ::mir::log(::mir::logging::Severity::debug, MIR_LOG_COMPONENT, fmt, std::forward(args)...); } inline void log_critical(std::string const& message) { ::mir::log(::mir::logging::Severity::critical, MIR_LOG_COMPONENT, message); } inline void log_error(std::string const& message) { ::mir::log(::mir::logging::Severity::error, MIR_LOG_COMPONENT, message); } inline void log_warning(std::string const& message) { ::mir::log(::mir::logging::Severity::warning, MIR_LOG_COMPONENT, message); } template void log_warning(char const* fmt, Args&&... args) { ::mir::log(::mir::logging::Severity::warning, MIR_LOG_COMPONENT, fmt, std::forward(args)...); } } // (nested anonymous) namespace #endif } // namespace mir #endif // MIR_LOG_H_ ./include/common/mir/int_wrapper.h0000644000015600001650000000472012676616125017321 0ustar jenkinsjenkins/* * Copyright © 2012, 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_INT_WRAPPER_H_ #define MIR_INT_WRAPPER_H_ #include namespace mir { template class IntWrapper { public: IntWrapper() : value(0) {} explicit IntWrapper(ValueType value) : value(value) {} ValueType as_value() const { return value; } private: ValueType value; }; template std::ostream& operator<<(std::ostream& out, IntWrapper const& value) { out << value.as_value(); return out; } template inline bool operator == (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_value() == rhs.as_value(); } template inline bool operator != (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_value() != rhs.as_value(); } template inline bool operator <= (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_value() <= rhs.as_value(); } template inline bool operator >= (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_value() >= rhs.as_value(); } template inline bool operator < (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_value() < rhs.as_value(); } } #include namespace std { template struct hash< ::mir::IntWrapper > { std::hash self; std::size_t operator()(::mir::IntWrapper const& id) const { return self(id.as_value()); } }; } #endif // MIR_INT_WRAPPER_H_ ./include/common/mir/logging/0000755000015600001650000000000012676616160016240 5ustar jenkinsjenkins./include/common/mir/logging/logger.h0000644000015600001650000000374012676616157017702 0ustar jenkinsjenkins/* * Copyright © 2012-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Thomas Voß */ #ifndef MIR_LOGGING_LOGGER_H_ #define MIR_LOGGING_LOGGER_H_ #include #include namespace mir { namespace logging { enum class Severity { critical = 0, error = 1, warning = 2, informational = 3, debug = 4 }; // A facade to shield the inner core of mir to prevent an actual // logging framework from leaking implementation detail. class Logger { public: virtual void log(Severity severity, const std::string& message, const std::string& component) = 0; /* * Those playing at home may wonder why we're saying the 4th argument is the format string, * when it's the 3rd argument in the signature. * * The answer, of course, is that the attribute doesn't know about the implicit * 'this' first parameter of C++! */ virtual void log(char const* component, Severity severity, char const* format, ...) __attribute__ ((format (printf, 4, 5))); protected: Logger() {} virtual ~Logger() = default; Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete; }; void log(Severity severity, const std::string& message, const std::string& component); void set_logger(std::shared_ptr const& new_logger); } } #endif // MIR_LOGGING_LOGGER_H_ ./include/common/mir/fd.h0000644000015600001650000000320712676616125015357 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_FD_H_ #define MIR_FD_H_ #include namespace mir { //TODO: remove once mir::Fd is used more pervasively. // some existing code does not really allow us to transfer or share the ownership // of the fd. Constructing using mir::Fd(IntOwnedFd(int)) will help transition the existing // code to using the mir::Fd type properly. struct IntOwnedFd { int int_owned_fd; }; class Fd { public: //transfer ownership of the POD-int to the object. The int no longer needs close()ing, //and has the lifetime of the Fd object. explicit Fd(int fd); explicit Fd(IntOwnedFd); static int const invalid{-1}; Fd(); //Initializes fd to the mir::Fd::invalid; Fd(Fd&&); Fd(Fd const&) = default; Fd& operator=(Fd); //bit of a convenient kludge. take care not to close or otherwise destroy the FD. operator int() const; private: std::shared_ptr fd; }; } // namespace mir #endif // MIR_FD_H_ ./include/common/mir/assert_module_entry_point.h0000644000015600001650000000211412676616125022262 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_ASSERT_MODULE_ENTRY_POINT_H_ #define MIR_ASSERT_MODULE_ENTRY_POINT_H_ #include namespace mir { template void assert_entry_point_signature(EntryPoint) { static_assert(std::is_same::value, "Signature of platform entry point does not match."); } } #endif ./include/common/mir/cached_ptr.h0000644000015600001650000000243412676616125017063 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_CACHED_PTR_H_ #define MIR_CACHED_PTR_H_ #include #include namespace mir { template class CachedPtr { std::weak_ptr cache; CachedPtr(CachedPtr const&) = delete; CachedPtr& operator=(CachedPtr const&) = delete; public: CachedPtr() = default; std::shared_ptr operator()(std::function()> make) { auto result = cache.lock(); if (!result) { cache = result = make(); } return result; } }; } // namespace mir #endif // MIR_CACHED_PTR_H_ ./include/common/mir/graphics/0000755000015600001650000000000012676616125016413 5ustar jenkinsjenkins./include/common/mir/graphics/native_buffer.h0000644000015600001650000000203512676616125021403 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_GRAPHICS_NATIVE_BUFFER_H_ #define MIR_GRAPHICS_NATIVE_BUFFER_H_ #ifndef ANDROID #include #endif namespace mir { namespace graphics { #ifdef ANDROID //just a fwd dcl class NativeBuffer; #else typedef struct MirBufferPackage NativeBuffer; #endif } } #endif /* MIR_GRAPHICS_NATIVE_BUFFER_H_ */ ./include/common/mir/time/0000755000015600001650000000000012676616126015552 5ustar jenkinsjenkins./include/common/mir/time/types.h0000644000015600001650000000167312676616125017075 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_TIME_TYPES_H_ #define MIR_TIME_TYPES_H_ #include namespace mir { namespace time { using Timestamp = std::chrono::steady_clock::time_point; using Duration = std::chrono::steady_clock::duration; } } #endif ./include/common/mir/geometry/0000755000015600001650000000000012676616125016446 5ustar jenkinsjenkins./include/common/mir/geometry/forward.h0000644000015600001650000000171312676616125020265 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_GEOMETRY_FORWARD_H_ #define MIR_GEOMETRY_FORWARD_H_ namespace mir { namespace geometry { // Declarations of geometric concepts I think we'll need struct Point; struct Size; class Displacement; struct Rectangle; class Region; } } #endif /* FORWARD_H_ */ ./include/common/mir/geometry/rectangle.h0000644000015600001650000000433212676616125020565 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths * Alexandros Frantzis */ #ifndef MIR_GEOMETRY_RECTANGLE_H_ #define MIR_GEOMETRY_RECTANGLE_H_ #include "mir/geometry/point.h" #include "size.h" #include namespace mir { namespace geometry { struct Rectangle { Rectangle() = default; Rectangle(Point const& top_left, Size const& size) : top_left{top_left}, size{size} { } Point top_left; Size size; /** * The bottom right boundary point of the rectangle. * * Note that the returned point is *not* included in the rectangle * area, that is, the rectangle is represented as [top_left,bottom_right). */ Point bottom_right() const; Point top_right() const; Point bottom_left() const; bool contains(Point const& p) const; /** * Test if the rectangle contains another. * * Note that an empty rectangle can still contain other empty rectangles, * which are treated as points or lines of thickness zero. */ bool contains(Rectangle const& r) const; bool overlaps(Rectangle const& r) const; Rectangle intersection_with(Rectangle const& r) const; }; inline bool operator == (Rectangle const& lhs, Rectangle const& rhs) { return lhs.top_left == rhs.top_left && lhs.size == rhs.size; } inline bool operator != (Rectangle const& lhs, Rectangle const& rhs) { return lhs.top_left != rhs.top_left || lhs.size != rhs.size; } std::ostream& operator<<(std::ostream& out, Rectangle const& value); } } #endif /* MIR_GEOMETRY_RECTANGLE_H_ */ ./include/common/mir/geometry/size.h0000644000015600001650000000374312676616125017600 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_GEOMETRY_SIZE_H_ #define MIR_GEOMETRY_SIZE_H_ #include "mir/geometry/dimensions.h" #include namespace mir { namespace geometry { struct Size { Size() {} Size(Size const&) = default; Size& operator=(Size const&) = default; template Size(WidthType&& width, HeightType&& height) : width(width), height(height) {} Width width; Height height; }; inline bool operator == (Size const& lhs, Size const& rhs) { return lhs.width == rhs.width && lhs.height == rhs.height; } inline bool operator != (Size const& lhs, Size const& rhs) { return lhs.width != rhs.width || lhs.height != rhs.height; } std::ostream& operator<<(std::ostream& out, Size const& value); template inline Size operator*(Scalar scale, Size const& size) { return Size{scale*size.width, scale*size.height}; } template inline Size operator*(Size const& size, Scalar scale) { return scale*size; } #ifdef MIR_GEOMETRY_DISPLACEMENT_H_ inline Displacement as_displacement(Size const& size) { return Displacement{size.width.as_int(), size.height.as_int()}; } inline Size as_size(Displacement const& disp) { return Size{disp.dx.as_int(), disp.dy.as_int()}; } #endif } } #endif /* MIR_GEOMETRY_SIZE_H_ */ ./include/common/mir/geometry/displacement.h0000644000015600001650000000620112676616125021266 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GEOMETRY_DISPLACEMENT_H_ #define MIR_GEOMETRY_DISPLACEMENT_H_ #include "mir/geometry/dimensions.h" #include "mir/geometry/point.h" #include namespace mir { namespace geometry { struct Displacement { Displacement() {} Displacement(Displacement const&) = default; Displacement& operator=(Displacement const&) = default; template Displacement(DeltaXType&& dx, DeltaYType&& dy) : dx{dx}, dy{dy} {} long long length_squared() const { long long x = dx.as_int(), y = dy.as_int(); return x * x + y * y; } DeltaX dx; DeltaY dy; }; inline bool operator==(Displacement const& lhs, Displacement const& rhs) { return lhs.dx == rhs.dx && lhs.dy == rhs.dy; } inline bool operator!=(Displacement const& lhs, Displacement const& rhs) { return lhs.dx != rhs.dx || lhs.dy != rhs.dy; } std::ostream& operator<<(std::ostream& out, Displacement const& value); inline Displacement operator+(Displacement const& lhs, Displacement const& rhs) { return Displacement{lhs.dx + rhs.dx, lhs.dy + rhs.dy}; } inline Displacement operator-(Displacement const& lhs, Displacement const& rhs) { return Displacement{lhs.dx - rhs.dx, lhs.dy - rhs.dy}; } inline Point operator+(Point const& lhs, Displacement const& rhs) { return Point{lhs.x + rhs.dx, lhs.y + rhs.dy}; } inline Point operator+(Displacement const& lhs, Point const& rhs) { return Point{rhs.x + lhs.dx, rhs.y + lhs.dy}; } inline Point operator-(Point const& lhs, Displacement const& rhs) { return Point{lhs.x - rhs.dx, lhs.y - rhs.dy}; } inline Displacement operator-(Point const& lhs, Point const& rhs) { return Displacement{lhs.x - rhs.x, lhs.y - rhs.y}; } inline bool operator<(Displacement const& lhs, Displacement const& rhs) { return lhs.length_squared() < rhs.length_squared(); } template inline Displacement operator*(Scalar scale, Displacement const& disp) { return Displacement{scale*disp.dx, scale*disp.dy}; } template inline Displacement operator*(Displacement const& disp, Scalar scale) { return scale*disp; } #ifdef MIR_GEOMETRY_SIZE_H_ inline Displacement as_displacement(Size const& size) { return Displacement{size.width.as_int(), size.height.as_int()}; } inline Size as_size(Displacement const& disp) { return Size{disp.dx.as_int(), disp.dy.as_int()}; } #endif } } #endif /* MIR_GEOMETRY_DISPLACEMENT_H_ */ ./include/common/mir/geometry/rectangles.h0000644000015600001650000000366012676616125020753 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GEOMETRY_RECTANGLES_H_ #define MIR_GEOMETRY_RECTANGLES_H_ #include "mir/geometry/point.h" #include "mir/geometry/rectangle.h" #include #include namespace mir { namespace geometry { /// A collection of rectangles (with possible duplicates). class Rectangles { public: Rectangles(); Rectangles(std::initializer_list const& rects); /* We want to keep implicit copy and move methods */ void add(Rectangle const& rect); /// removes at most one matching rectangle void remove(Rectangle const& rect); void clear(); Rectangle bounding_rectangle() const; void confine(Point& point) const; typedef std::vector::const_iterator const_iterator; typedef std::vector::size_type size_type; const_iterator begin() const; const_iterator end() const; size_type size() const; bool operator==(Rectangles const& rect) const; bool operator!=(Rectangles const& rect) const; private: std::vector rectangles; Rectangle bounding_rectangle_; // TODO unused: delete on next ABI break }; std::ostream& operator<<(std::ostream& out, Rectangles const& value); } } #endif /* MIR_GEOMETRY_RECTANGLES_H_ */ ./include/common/mir/geometry/dimensions.h0000644000015600001650000001362312676616125020774 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_GEOMETRY_DIMENSIONS_H_ #define MIR_GEOMETRY_DIMENSIONS_H_ #include #include namespace mir { /// Basic geometry types. Types for dimensions, displacements, etc. /// and the operations that they support. namespace geometry { namespace detail { template class IntWrapper { public: typedef int ValueType; IntWrapper() : value(0) {} template explicit IntWrapper(AnyInteger value) : value(static_cast(value)) {} uint32_t as_uint32_t() const // TODO: Deprecate this later { return (uint32_t)value; } int as_int() const { return value; } float as_float() const { return value; } private: ValueType value; }; template std::ostream& operator<<(std::ostream& out, IntWrapper const& value) { out << value.as_int(); return out; } template inline bool operator == (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_int() == rhs.as_int(); } template inline bool operator != (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_int() != rhs.as_int(); } template inline bool operator <= (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_int() <= rhs.as_int(); } template inline bool operator >= (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_int() >= rhs.as_int(); } template inline bool operator < (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_int() < rhs.as_int(); } template inline bool operator > (IntWrapper const& lhs, IntWrapper const& rhs) { return lhs.as_int() > rhs.as_int(); } } // namespace detail typedef detail::IntWrapper Width; typedef detail::IntWrapper Height; // Just to be clear, mir::geometry::Stride is the stride of the buffer in bytes typedef detail::IntWrapper Stride; typedef detail::IntWrapper X; typedef detail::IntWrapper Y; typedef detail::IntWrapper DeltaX; typedef detail::IntWrapper DeltaY; // Adding deltas is fine inline DeltaX operator+(DeltaX lhs, DeltaX rhs) { return DeltaX(lhs.as_int() + rhs.as_int()); } inline DeltaY operator+(DeltaY lhs, DeltaY rhs) { return DeltaY(lhs.as_int() + rhs.as_int()); } inline DeltaX operator-(DeltaX lhs, DeltaX rhs) { return DeltaX(lhs.as_int() - rhs.as_int()); } inline DeltaY operator-(DeltaY lhs, DeltaY rhs) { return DeltaY(lhs.as_int() - rhs.as_int()); } // Adding deltas to co-ordinates is fine inline X operator+(X lhs, DeltaX rhs) { return X(lhs.as_int() + rhs.as_int()); } inline Y operator+(Y lhs, DeltaY rhs) { return Y(lhs.as_int() + rhs.as_int()); } inline X operator-(X lhs, DeltaX rhs) { return X(lhs.as_int() - rhs.as_int()); } inline Y operator-(Y lhs, DeltaY rhs) { return Y(lhs.as_int() - rhs.as_int()); } inline X& operator+=(X& lhs, DeltaX rhs) { return lhs = X(lhs.as_int() + rhs.as_int()); } inline Y& operator+=(Y& lhs, DeltaY rhs) { return lhs = Y(lhs.as_int() + rhs.as_int()); } inline X& operator-=(X& lhs, DeltaX rhs) { return lhs = X(lhs.as_int() - rhs.as_int()); } inline Y& operator-=(Y& lhs, DeltaY rhs) { return lhs = Y(lhs.as_int() - rhs.as_int()); } // Adding deltas to Width and Height is fine inline Width operator+(Width lhs, DeltaX rhs) { return Width(lhs.as_int() + rhs.as_int()); } inline Height operator+(Height lhs, DeltaY rhs) { return Height(lhs.as_int() + rhs.as_int()); } inline Width operator-(Width lhs, DeltaX rhs) { return Width(lhs.as_int() - rhs.as_int()); } inline Height operator-(Height lhs, DeltaY rhs) { return Height(lhs.as_int() - rhs.as_int()); } // Subtracting coordinates is fine inline DeltaX operator-(X lhs, X rhs) { return DeltaX(lhs.as_int() - rhs.as_int()); } inline DeltaY operator-(Y lhs, Y rhs) { return DeltaY(lhs.as_int() - rhs.as_int()); } //Subtracting Width and Height is fine inline DeltaX operator-(Width lhs, Width rhs) { return DeltaX(lhs.as_int() - rhs.as_int()); } inline DeltaY operator-(Height lhs, Height rhs) { return DeltaY(lhs.as_int() - rhs.as_int()); } // Multiplying by a scalar value is fine template inline Width operator*(Scalar scale, Width const& w) { return Width{scale*w.as_int()}; } template inline Height operator*(Scalar scale, Height const& h) { return Height{scale*h.as_int()}; } template inline DeltaX operator*(Scalar scale, DeltaX const& dx) { return DeltaX{scale*dx.as_int()}; } template inline DeltaY operator*(Scalar scale, DeltaY const& dy) { return DeltaY{scale*dy.as_int()}; } template inline Width operator*(Width const& w, Scalar scale) { return scale*w; } template inline Height operator*(Height const& h, Scalar scale) { return scale*h; } template inline DeltaX operator*(DeltaX const& dx, Scalar scale) { return scale*dx; } template inline DeltaY operator*(DeltaY const& dy, Scalar scale) { return scale*dy; } template inline Target dim_cast(Source s) { return Target(s.as_int()); } } } #endif /* MIR_GEOMETRY_DIMENSIONS_H_ */ ./include/common/mir/geometry/length.h0000644000015600001650000000451412676616125020104 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_GEOMETRY_LENGTH_H_ #define MIR_GEOMETRY_LENGTH_H_ namespace mir { namespace geometry { /// Length represents a physical length in the real world. The number of pixels /// this equates to can then be calculated based on a given DPI. class Length { public: enum Units { micrometres = 1, millimetres = 1000, centimetres = 10000, inches = 25400 }; Length() : magnitude(0) {} Length(Length const&) = default; Length& operator=(Length const&) = default; Length(float mag, Units units) : magnitude(mag * units) {} float as(Units units) const { return static_cast(magnitude) / units; } float as_pixels(float dpi) const { return as(inches) * dpi; } bool operator==(Length const& rhs) const { return magnitude == rhs.magnitude; } bool operator!=(Length const& rhs) const { return magnitude != rhs.magnitude; } private: float magnitude; }; inline Length operator"" _mm(long double mag) { return Length(mag, Length::millimetres); } inline Length operator"" _mm(unsigned long long mag) { return Length(mag, Length::millimetres); } inline Length operator"" _cm(long double mag) { return Length(mag, Length::centimetres); } inline Length operator"" _cm(unsigned long long mag) { return Length(mag, Length::centimetres); } inline Length operator"" _in(long double mag) { return Length(mag, Length::inches); } inline Length operator"" _in(unsigned long long mag) { return Length(mag, Length::inches); } } // namespace geometry } // namespace mir #endif // MIR_GEOMETRY_LENGTH_H_ ./include/common/mir/geometry/point.h0000644000015600001650000000401412676616125017747 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GEOMETRY_POINT_H_ #define MIR_GEOMETRY_POINT_H_ #include "dimensions.h" #include namespace mir { namespace geometry { struct Point { Point() = default; Point(Point const&) = default; Point& operator=(Point const&) = default; template Point(XType&& x, YType&& y) : x(x), y(y) {} X x; Y y; }; inline bool operator == (Point const& lhs, Point const& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; } inline bool operator != (Point const& lhs, Point const& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y; } inline Point operator+(Point lhs, DeltaX rhs) { return{lhs.x + rhs, lhs.y}; } inline Point operator+(Point lhs, DeltaY rhs) { return{lhs.x, lhs.y + rhs}; } inline Point operator-(Point lhs, DeltaX rhs) { return{lhs.x - rhs, lhs.y}; } inline Point operator-(Point lhs, DeltaY rhs) { return{lhs.x, lhs.y - rhs}; } inline Point& operator+=(Point& lhs, DeltaX rhs) { lhs.x += rhs; return lhs; } inline Point& operator+=(Point& lhs, DeltaY rhs) { lhs.y += rhs; return lhs; } inline Point& operator-=(Point& lhs, DeltaX rhs) { lhs.x -= rhs; return lhs; } inline Point& operator-=(Point& lhs, DeltaY rhs) { lhs.y -= rhs; return lhs; } std::ostream& operator<<(std::ostream& out, Point const& value); } } #endif /* MIR_GEOMETRY_POINT_H_ */ ./include/common/mir_toolkit/0000755000015600001650000000000012676616125016360 5ustar jenkinsjenkins./include/common/mir_toolkit/common.h0000644000015600001650000001633512676616125020031 0ustar jenkinsjenkins/* * Simple definitions common to client and server. * * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Daniel van Vugt */ #ifndef MIR_COMMON_H_ #define MIR_COMMON_H_ /** * \addtogroup mir_toolkit * @{ */ /* This is C code. Not C++. */ /** * Attributes of a surface that the client and server/shell may wish to * get or set over the wire. */ typedef enum MirSurfaceAttrib { /* Do not specify values...code relies on 0...N ordering. */ mir_surface_attrib_type, mir_surface_attrib_state, mir_surface_attrib_swapinterval, mir_surface_attrib_focus, mir_surface_attrib_dpi, mir_surface_attrib_visibility, mir_surface_attrib_preferred_orientation, /* Must be last */ mir_surface_attribs } MirSurfaceAttrib; typedef enum MirSurfaceType { mir_surface_type_normal, /**< AKA "regular" */ mir_surface_type_utility, /**< AKA "floating" */ mir_surface_type_dialog, mir_surface_type_overlay, /**< \deprecated Use "gloss" instead. */ mir_surface_type_gloss = mir_surface_type_overlay, mir_surface_type_freestyle, mir_surface_type_popover, /**< \deprecated Choose "menu" or "tip" */ mir_surface_type_menu = mir_surface_type_popover, mir_surface_type_inputmethod, /**< AKA "OSK" or handwriting etc. */ mir_surface_type_satellite, /**< AKA "toolbox"/"toolbar" */ mir_surface_type_tip, /**< AKA "tooltip" */ mir_surface_types } MirSurfaceType; typedef enum MirSurfaceState { mir_surface_state_unknown, mir_surface_state_restored, mir_surface_state_minimized, mir_surface_state_maximized, mir_surface_state_vertmaximized, /* mir_surface_state_semimaximized, Omitted for now, since it's functionally a subset of vertmaximized and differs only in the X coordinate. */ mir_surface_state_fullscreen, mir_surface_state_horizmaximized, mir_surface_state_hidden, mir_surface_states } MirSurfaceState; /* TODO: MirSurfaceFocusState MirSurfaceVisibility and MirLifecycleState use an inconsistent naming convention. */ typedef enum MirSurfaceFocusState { mir_surface_unfocused = 0, mir_surface_focused } MirSurfaceFocusState; typedef enum MirSurfaceVisibility { mir_surface_visibility_occluded = 0, mir_surface_visibility_exposed } MirSurfaceVisibility; typedef enum MirLifecycleState { mir_lifecycle_state_will_suspend, mir_lifecycle_state_resumed, mir_lifecycle_connection_lost } MirLifecycleState; typedef enum MirPowerMode { mir_power_mode_on, /* Display in use. */ mir_power_mode_standby, /* Blanked, low power. */ mir_power_mode_suspend, /* Blanked, lowest power. */ mir_power_mode_off /* Powered down. */ } MirPowerMode; typedef enum MirPromptSessionState { mir_prompt_session_state_stopped = 0, mir_prompt_session_state_started, mir_prompt_session_state_suspended } MirPromptSessionState; /** * 32-bit pixel formats (8888): * The order of components in the enum matches the order of the components * as they would be written in an integer representing a pixel value of that * format. For example; abgr_8888 should be coded as 0xAABBGGRR, which will * end up as R,G,B,A in memory on a little endian system, and as A,B,G,R on a * big endian system. * * 24-bit pixel formats (888): * These are in literal byte order, regardless of CPU architecture it's always * the same. Writing these 3-byte pixels is typically slower than other formats * but uses less memory than 32-bit and is endian-independent. * * 16-bit pixel formats (565/5551/4444): * Always interpreted as one 16-bit integer per pixel with components in * high-to-low bit order following the format name. These are the fastest * formats, however colour quality is visibly lower. */ typedef enum MirPixelFormat { mir_pixel_format_invalid = 0, mir_pixel_format_abgr_8888 = 1, mir_pixel_format_xbgr_8888 = 2, mir_pixel_format_argb_8888 = 3, mir_pixel_format_xrgb_8888 = 4, mir_pixel_format_bgr_888 = 5, mir_pixel_format_rgb_888 = 6, mir_pixel_format_rgb_565 = 7, mir_pixel_format_rgba_5551 = 8, mir_pixel_format_rgba_4444 = 9, /* * TODO: Big endian support would require additional formats in order to * composite software surfaces using OpenGL (GL_RGBA/GL_BGRA_EXT): * mir_pixel_format_rgb[ax]_8888 * mir_pixel_format_bgr[ax]_8888 */ mir_pixel_formats /* Note: This is always max format + 1 */ } MirPixelFormat; /* This could be improved... https://bugs.launchpad.net/mir/+bug/1236254 */ #define MIR_BYTES_PER_PIXEL(f) ((f) == mir_pixel_format_bgr_888 ? 3 : \ (f) == mir_pixel_format_rgb_888 ? 3 : \ (f) == mir_pixel_format_rgb_565 ? 2 : \ (f) == mir_pixel_format_rgba_5551 ? 2 : \ (f) == mir_pixel_format_rgba_4444 ? 2 : \ 4) /** Direction relative to the "natural" orientation of the display */ typedef enum MirOrientation { mir_orientation_normal = 0, mir_orientation_left = 90, mir_orientation_inverted = 180, mir_orientation_right = 270 } MirOrientation; typedef enum MirOrientationMode { mir_orientation_mode_portrait = 1 << 0, mir_orientation_mode_landscape = 1 << 1, mir_orientation_mode_portrait_inverted = 1 << 2, mir_orientation_mode_landscape_inverted = 1 << 3, mir_orientation_mode_portrait_any = mir_orientation_mode_portrait | mir_orientation_mode_portrait_inverted, mir_orientation_mode_landscape_any = mir_orientation_mode_landscape | mir_orientation_mode_landscape_inverted, mir_orientation_mode_any = mir_orientation_mode_portrait_any | mir_orientation_mode_landscape_any } MirOrientationMode; typedef enum MirEdgeAttachment { mir_edge_attachment_vertical = 1 << 0, mir_edge_attachment_horizontal = 1 << 1, mir_edge_attachment_any = mir_edge_attachment_vertical | mir_edge_attachment_horizontal } MirEdgeAttachment; /** * Form factor associated with a physical output */ typedef enum MirFormFactor { mir_form_factor_unknown, mir_form_factor_phone, mir_form_factor_tablet, mir_form_factor_monitor, mir_form_factor_tv, mir_form_factor_projector, } MirFormFactor; /** * Shell chrome */ typedef enum MirShellChrome { mir_shell_chrome_normal, mir_shell_chrome_low, } MirShellChrome; /**@}*/ #endif ./include/common/mir_toolkit/mir_native_buffer.h0000644000015600001650000000341112676616125022216 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #ifndef MIR_CLIENT_MIR_NATIVE_BUFFER_H_ #define MIR_CLIENT_MIR_NATIVE_BUFFER_H_ enum { mir_buffer_package_max = 30 }; typedef enum { mir_buffer_flag_can_scanout = 1 } MirBufferFlag; typedef struct MirBufferPackage { int data_items; int fd_items; int data[mir_buffer_package_max]; int width; /* These must come after data[] to keep ABI compatibility */ int height; int fd[mir_buffer_package_max]; int unused0; /* Retain ABI compatibility (avoid rebuilding Mesa) */ unsigned int flags; /* MirBufferFlag's */ int stride; int age; /**< Number of frames submitted by the client since the client has rendered to this buffer. */ /**< This has the same semantics as the EGL_EXT_buffer_age extension */ /**< \see http://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt */ } MirBufferPackage; #ifdef ANDROID struct ANativeWindowBuffer; typedef struct ANativeWindowBuffer MirNativeBuffer; #else typedef struct MirBufferPackage MirNativeBuffer; #endif #endif /* MIR_CLIENT_MIR_NATIVE_BUFFER_H_ */ ./include/common/mir_toolkit/mir_version_number.h0000644000015600001650000000255412676616125022443 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_VERSION_NUMBER_H_ #define MIR_VERSION_NUMBER_H_ /** * MIR_VERSION_NUMBER * \param major [in] The major version (eg: 3 for version 3.2.33) * \param minor [in] The minor version (eg: 2 for version 3.2.33) * \param micro [in] The micro version (eg: 33 for version 3.2.33) * * Returns the combined version information as a single 32-bit value for * logical comparisons. For example: * #if MIR_CLIENT_VERSION >= MIR_VERSION_NUMBER(2,3,4) * * This can be useful to conditionally build code depending on new features or * specific bugfixes in the Mir client library. */ #define MIR_VERSION_NUMBER(major,minor,micro) \ (((major) << 22) + ((minor) << 12) + (micro)) #endif /* MIR_VERSION_NUMBER_H_ */ ./include/client/0000755000015600001650000000000012676616124014011 5ustar jenkinsjenkins./include/client/mir/0000755000015600001650000000000012676616125014601 5ustar jenkinsjenkins./include/client/mir/event_printer.h0000644000015600001650000000441412676616125017641 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_EVENT_PRINTER_H_ #define MIR_EVENT_PRINTER_H_ #include "mir_toolkit/event.h" #include namespace mir { std::ostream& operator<<(std::ostream& out, MirInputEventModifier modifier); std::ostream& operator<<(std::ostream& out, MirKeyboardAction action); std::ostream& operator<<(std::ostream& out, MirTouchAction action); std::ostream& operator<<(std::ostream& out, MirTouchTooltype tool); std::ostream& operator<<(std::ostream& out, MirPointerAction action); std::ostream& operator<<(std::ostream& out, MirPromptSessionState state); std::ostream& operator<<(std::ostream& out, MirOrientation orientation); std::ostream& operator<<(std::ostream& out, MirSurfaceAttrib attribute); std::ostream& operator<<(std::ostream& out, MirSurfaceFocusState state); std::ostream& operator<<(std::ostream& out, MirSurfaceVisibility state); std::ostream& operator<<(std::ostream& out, MirSurfaceType type); std::ostream& operator<<(std::ostream& out, MirSurfaceState state); std::ostream& operator<<(std::ostream& out, MirPromptSessionEvent const& event); std::ostream& operator<<(std::ostream& out, MirResizeEvent const& event); std::ostream& operator<<(std::ostream& out, MirOrientationEvent const& event); std::ostream& operator<<(std::ostream& out, MirInputEvent const& event); std::ostream& operator<<(std::ostream& out, MirCloseSurfaceEvent const& event); std::ostream& operator<<(std::ostream& out, MirKeymapEvent const& event); std::ostream& operator<<(std::ostream& out, MirSurfaceEvent const& event); std::ostream& operator<<(std::ostream& out, MirEvent const& event); } #endif ./include/client/mir/events/0000755000015600001650000000000012676616125016105 5ustar jenkinsjenkins./include/client/mir/events/event_builders.h0000644000015600001650000001343612676616125021277 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Robert Carr */ #ifndef MIR_EVENT_BUILDERS_H_ #define MIR_EVENT_BUILDERS_H_ #include "mir_toolkit/event.h" #include "mir/geometry/size.h" #include "mir/geometry/point.h" #include "mir/frontend/surface_id.h" #include #include #include #include namespace mir { typedef std::unique_ptr EventUPtr; namespace events { // Surface orientation change event EventUPtr make_event(frontend::SurfaceId const& surface_id, MirOrientation orientation); // Prompt session state change event EventUPtr make_event(MirPromptSessionState state); // Surface resize event EventUPtr make_event(frontend::SurfaceId const& surface_id, geometry::Size const& size); // Surface configure event EventUPtr make_event(frontend::SurfaceId const& surface_id, MirSurfaceAttrib attribute, int value); // Close surface event EventUPtr make_event(frontend::SurfaceId const& surface_id); // Keymap event EventUPtr make_event(frontend::SurfaceId const& surface_id, MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options); // Surface output event EventUPtr make_event( frontend::SurfaceId const& surface_id, int dpi, float scale, MirFormFactor form_factor, uint32_t id); // Key event EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, std::vector const& cookie, MirKeyboardAction action, xkb_keysym_t key_code, int scan_code, MirInputEventModifiers modifiers); void set_modifier(MirEvent& event, MirInputEventModifiers modifiers); void set_cursor_position(MirEvent& event, mir::geometry::Point const& pos); void set_button_state(MirEvent& event, MirPointerButtons button_state); // Deprecated version with uint64_t mac EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, uint64_t mac, MirKeyboardAction action, xkb_keysym_t key_code, int scan_code, MirInputEventModifiers modifiers) __attribute__ ((deprecated)); // Deprecated version without mac EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, MirKeyboardAction action, xkb_keysym_t key_code, int scan_code, MirInputEventModifiers modifiers) __attribute__ ((deprecated)); // Touch event EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, std::vector const& mac, MirInputEventModifiers modifiers); // Deprecated version with uint64_t mac EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, uint64_t mac, MirInputEventModifiers modifiers) __attribute__ ((deprecated)); // Deprecated version without mac EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, MirInputEventModifiers modifiers) __attribute__ ((deprecated)); void add_touch(MirEvent &event, MirTouchId touch_id, MirTouchAction action, MirTouchTooltype tooltype, float x_axis_value, float y_axis_value, float pressure_value, float touch_major_value, float touch_minor_value, float size_value); // Pointer event // Deprecated version without relative axis EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, uint64_t mac, MirInputEventModifiers modifiers, MirPointerAction action, MirPointerButtons buttons_pressed, float x_axis_value, float y_axis_value, float hscroll_value, float vscroll_value) __attribute__ ((deprecated)); // Deprecated version without relative axis and mac EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, MirInputEventModifiers modifiers, MirPointerAction action, MirPointerButtons buttons_pressed, float x_axis_value, float y_axis_value, float hscroll_value, float vscroll_value) __attribute__ ((deprecated)); EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, std::vector const& mac, MirInputEventModifiers modifiers, MirPointerAction action, MirPointerButtons buttons_pressed, float x_axis_value, float y_axis_value, float hscroll_value, float vscroll_value, float relative_x_value, float relative_y_value); // Deprecated version with uint64_t mac EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, uint64_t mac, MirInputEventModifiers modifiers, MirPointerAction action, MirPointerButtons buttons_pressed, float x_axis_value, float y_axis_value, float hscroll_value, float vscroll_value, float relative_x_value, float relative_y_value) __attribute__ ((deprecated)); // Deprecated version without mac EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp, MirInputEventModifiers modifiers, MirPointerAction action, MirPointerButtons buttons_pressed, float x_axis_value, float y_axis_value, float hscroll_value, float vscroll_value, float relative_x_value, float relative_y_value) __attribute__ ((deprecated)); // Input configuration event EventUPtr make_event(MirInputConfigurationAction action, MirInputDeviceId id, std::chrono::nanoseconds time); } } #endif // MIR_EVENT_BUILDERS_H_ ./include/client/mir_toolkit/0000755000015600001650000000000012676616160016345 5ustar jenkinsjenkins./include/client/mir_toolkit/mir_buffer_stream.h0000644000015600001650000002073012676616125022214 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_TOOLKIT_MIR_BUFFER_STREAM_H_ #define MIR_TOOLKIT_MIR_BUFFER_STREAM_H_ #include #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Test for a valid buffer stream * * \param [in] buffer_stream The buffer stream * \return True if the supplied buffer_stream is valid, or * false otherwise. */ bool mir_buffer_stream_is_valid(MirBufferStream *buffer_stream); /** * Retrieve a text description of the error. The returned string is owned by * the library and remains valid until the stream or the associated * connection has been released. * \param [in] buffer_stream The buffer stream * \return A text description of any error resulting in an * invalid stream, or the empty string "" if the * connection is valid. */ char const *mir_buffer_stream_get_error_message(MirBufferStream *surface); /** * Create a new buffer stream. * * For example, the resulting buffer stream may be used * with mir_cursor_configuration_from_buffer_stream, * in order to post images to the system cursor. * * \param [in] connection A valid connection * \param [in] width Requested buffer width * \param [in] height Requested buffer height * \param [in] buffer_usage Requested buffer usage, use * mir_buffer_usage_software for cursor image streams * \param [in] callback Callback to be invoked when the request completes * The callback is guaranteed to be called and called with a * non-null MirBufferStream*, but the stream may be invalid in * case of an error. * \param [in] context Userdata to pass to callback function * * \return A handle that can be supplied to mir_wait_for */ MirWaitHandle* mir_connection_create_buffer_stream(MirConnection *connection, int width, int height, MirPixelFormat format, MirBufferUsage buffer_usage, mir_buffer_stream_callback callback, void* context); /** * Create a new buffer stream unattached to a surface and wait for the result. * The resulting buffer stream may be used with * mir_cursor_configuration_from_buffer_stream in order to post images * to the system cursor. * * \param [in] connection A valid connection * \param [in] width Requested buffer width * \param [in] height Requested buffer height * \param [in] buffer_usage Requested buffer usage, use * mir_buffer_usage_software for cursor image streams * * \return The new buffer stream. This is guaranteed non-null, * but may be invalid in the case of error. */ MirBufferStream* mir_connection_create_buffer_stream_sync(MirConnection *connection, int width, int height, MirPixelFormat format, MirBufferUsage buffer_usage); /** * Release the supplied stream and any associated buffer. The returned wait * handle remains valid until the connection to the server is released. * \warning callback could be called from another thread. You must do any * locking appropriate to protect your data accessed in the * callback. * \param [in] stream The stream * \param [in] callback Callback function to be invoked when the request * completes * \param [in,out] context User data passed to the callback function * \return A handle that can be passed to mir_wait_for */ MirWaitHandle *mir_buffer_stream_release( MirBufferStream * buffer_stream, mir_buffer_stream_callback callback, void *context); /** * Release the specified buffer stream like in mir,_buffer_stream_release(), * but also wait for the operation to complete. * \param [in] buffer stream The buffer stream to be released */ void mir_buffer_stream_release_sync(MirBufferStream *buffer_stream); /** * Get the underlying platform type so the buffer obtained in "raw" * representation in mir_buffer_stream_get_current_buffer() * may be understood * * \deprecated Use of this function is inherently non-portable in the presence * of plug-in platform modules as these need not correspond to the available * types. To identify the graphics platform use * mir_connection_get_graphics_module(). To safely interpret the * buffer contents use mir_buffer_stream_get_graphics_region(). * * \todo This should be removed from the public API at the next API break. * * \pre The surface is valid * \param [in] surface The surface * \return One of mir_platform_type_android or * mir_platform_type_gbm */ __attribute__ ((deprecated)) MirPlatformType mir_buffer_stream_get_platform_type(MirBufferStream *stream); /** * Retrieve the current buffer in "raw" representation. * \pre The buffer stream is valid * \param [in] surface The buffer stream * \param [out] buffer_package Structure to be populated */ void mir_buffer_stream_get_current_buffer(MirBufferStream *buffer_stream, MirNativeBuffer **buffer_package); /** * Advance a buffer stream's buffer. The returned handle remains valid until the * next call to mir_buffer_stream_swap_buffers, until the buffer stream has been * released or the connection to the server has been released. * \warning callback could be called from another thread. You must do any * locking appropriate to protect your data accessed in the * callback. * \param [in] buffer_stream The buffer stream * \param [in] callback Callback function to be invoked when the request * completes * \param [in,out] context User data passed to the callback function * \return A handle that can be passed to mir_wait_for */ MirWaitHandle *mir_buffer_stream_swap_buffers( MirBufferStream *buffer_stream, mir_buffer_stream_callback callback, void *context); /** * Advance a buffer stream's buffer as in mir_buffer stream_swap_buffers(), * but also wait for the operation to complete. * \param [in] buffer stream The buffer stream whose buffer to advance */ void mir_buffer_stream_swap_buffers_sync(MirBufferStream *buffer_stream); /** * Retrieve a buffer stream's graphics region * \warning Depending on platform, this can map the graphics buffer each * time its called. The region remains mapped until * mir_buffer_stream_swap_buffers(). * \pre The buffer stream is valid * \param [in] buffer stream The buffer stream * \param [out] graphics_region Structure to be populated */ void mir_buffer_stream_get_graphics_region( MirBufferStream *buffer_stream, MirGraphicsRegion *graphics_region); /** * Retrieve a window type which may be used by EGL. * \param [in] buffer_stream The buffer stream * \return An EGLNativeWindowType that the client can use */ MirEGLNativeWindowType mir_buffer_stream_get_egl_native_window(MirBufferStream *buffer_stream); /** * Set the scale associated with all buffers in the stream * \param [in] buffer_stream The buffer stream * \param [in] scale The scale * \return A handle that can be passed to mir_wait_for */ MirWaitHandle *mir_buffer_stream_set_scale(MirBufferStream* stream, float scale); /** * Set the scale as in mir_buffer_stream_set_scale(), but also wait for the * operation to complete. * \param [in] buffer_stream The buffer stream * \param [in] scale The scale */ void mir_buffer_stream_set_scale_sync(MirBufferStream* stream, float scale); #ifdef __cplusplus } /**@}*/ #endif #endif // MIR_TOOLKIT_MIR_BUFFER_STREAM_H_ ./include/client/mir_toolkit/cursors.h0000644000015600001650000000663012676616125020224 0ustar jenkinsjenkins/* * Cursor name definitions. * * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Robert Carr */ #ifndef MIR_CURSORS_H_ #define MIR_CURSORS_H_ /** * \addtogroup mir_toolkit * @{ */ /* This is C code. Not C++. */ #ifdef __cplusplus extern "C" { #endif /** * A special cursor name for use with mir_cursor_configuration_from_name * representing the system default cursor. */ extern char const *const mir_default_cursor_name; /** * A special cursor name for use with mir_cursor_configuration_from_name * representing a disabled cursor image. */ extern char const *const mir_disabled_cursor_name; /** * The standard arrow cursor (typically the system default) */ extern char const* const mir_arrow_cursor_name; /** * The "wait" cursor, typically an hourglass or watch used during operations * which prevent the user from interacting. */ extern char const* const mir_busy_cursor_name; /** * The caret or ibeam cursor, indicating acceptance of text input */ extern char const* const mir_caret_cursor_name; /** * The pointing hand cursor, typically used for clickable elements such * as hyperlinks. */ extern char const* const mir_pointing_hand_cursor_name; /** * The open handed cursor, typically used to indicate that the area beneath * the cursor may be clicked and dragged around. */ extern char const* const mir_open_hand_cursor_name; /** * The close handed cursor, typically used to indicate that a drag operation is in process * which involves scrolling. */ extern char const* const mir_closed_hand_cursor_name; /** * The cursor used to indicate a horizontal resize operation. */ extern char const* const mir_horizontal_resize_cursor_name; /** * The cursor used to indicate a vertical resize operation. */ extern char const* const mir_vertical_resize_cursor_name; /** * The cursor used to indicate diagonal resize from top-right and bottom-left corners. */ extern char const* const mir_diagonal_resize_bottom_to_top_cursor_name; /** * The cursor used to indicate diagonal resize from bottom-left and top-right corners. */ extern char const* const mir_diagonal_resize_top_to_bottom_cursor_name; /** * The cursor used to indicate resize with no directional constraint. */ extern char const* const mir_omnidirectional_resize_cursor_name; /** * The cursor used for vertical splitters, indicating that a handle may be * dragged to adjust vertical space. */ extern char const* const mir_vsplit_resize_cursor_name; /** * The cursor used for horizontal splitters, indicating that a handle may be * dragged to adjust horizontal space. */ extern char const* const mir_hsplit_resize_cursor_name; /** * The cursor used for crosshair, which may be used for picking colors or * finer precision. */ extern char const* const mir_crosshair_cursor_name; #ifdef __cplusplus } #endif /**@}*/ #endif ./include/client/mir_toolkit/mir_wait.h0000644000015600001650000000303212676616125020330 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_TOOLKIT_MIR_WAIT_H_ #define MIR_TOOLKIT_MIR_WAIT_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif struct MirWaitHandle; /** * Wait on the supplied handle until all instances of the associated request * have completed. * \param [in] wait_handle Handle returned by an asynchronous request */ void mir_wait_for(MirWaitHandle *wait_handle); /** * Wait on the supplied handle until one instance of the associated request * has completed. Use this instead of mir_wait_for in a threaded environment * to ensure that the act of waiting does not clear all results associated * with the wait handle; only one. * \param [in] wait_handle Handle returned by an asynchronous request */ void mir_wait_for_one(MirWaitHandle *wait_handle); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_MIR_WAIT_H_ */ ./include/client/mir_toolkit/debug/0000755000015600001650000000000012676616125017434 5ustar jenkinsjenkins./include/client/mir_toolkit/debug/surface.h0000644000015600001650000000554612676616125021247 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_CLIENT_LIBRARY_DEBUG_H #define MIR_CLIENT_LIBRARY_DEBUG_H #include /* This header defines debug interfaces that aren't expected to be generally useful * and do not have the same API-stability guarantees that the main API has */ #ifdef __cplusplus extern "C" { #endif /** * Return the ID of a surface (only useful for debug output). * \pre The surface is valid * \param [in] surface The surface * \return An internal ID that identifies the surface */ int mir_debug_surface_id(MirSurface *surface); /** * Get the ID of the surface's current buffer (only useful for debug purposes) * \pre The surface is valid * \param [in] surface The surface * \return The internal buffer ID of the surface's current buffer. * This is the buffer that is currently being drawn to, * and would be returned by mir_surface_get_current_buffer. */ uint32_t mir_debug_surface_current_buffer_id(MirSurface *surface); /** * Get the screen coordinates corresponding to a pair of surface coordinates * \pre The surface is valid * \param [in] surface The surface * \param [in] x, y Surface coordinates to map to screen coordinates * \param [out] screen_x, screen_y The screen coordinates corresponding to x, y. * \return True if screen_x and screen_y contain values * \note There are many cases where such a mapping does not exist or would be expensive * to calculate. Only Mir servers started with the --debug option will ever return * values for this call, and even when --debug is enabled servers are free to * return nothing. * * This call will only be interesting for automated testing, where both the client * and shell state is known and constrained. */ bool mir_debug_surface_coords_to_screen(MirSurface* surface, int x, int y, int* screen_x, int* screen_y); #ifdef __cplusplus } #endif #endif /* MIR_CLIENT_LIBRARY_DEBUG_H */ ./include/client/mir_toolkit/mir_blob.h0000644000015600001650000000423312676616125020306 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_TOOLKIT_MIR_BLOB_H_ #define MIR_TOOLKIT_MIR_BLOB_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Create a blob from a display configuration * * \param [in] configuration The display configuration * \return A blob */ MirBlob* mir_blob_from_display_configuration(MirDisplayConfiguration* configuration); /** * Create a blob from a buffer. * \note this does not copy the data, the buffer is assumed to be available * until the blob is released. * * \param [in] buffer the buffer * \param [in] buffer_size the buffer size * \return A blob */ MirBlob* mir_blob_onto_buffer(void const* buffer, size_t buffer_size); /** * Create a blob from a display configuration * * \warning will abort() if the blob doesn't represent a meaningful display configuration * * \param [in] blob The blob * \return A display configuration */ MirDisplayConfiguration* mir_blob_to_display_configuration(MirBlob* blob); /** * Get the size of a blob * \param [in] blob The blob * \return the size */ size_t mir_blob_size(MirBlob* blob); /** * Get the data of a blob * \param [in] blob The blob * \return the data */ void const* mir_blob_data(MirBlob* blob); /** * Release a blob object * \param [in] blob The blob */ void mir_blob_release(MirBlob* blob); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_MIR_BLOB_H_ */ ./include/client/mir_toolkit/mir_prompt_session.h0000644000015600001650000000662212676616125022460 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MIR_TOOLKIT_MIR_PROMPT_SESSION_H_ #define MIR_TOOLKIT_MIR_PROMPT_SESSION_H_ #include "mir_toolkit/mir_client_library.h" #include #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Create and start a new prompt session * \param [in] connection The connection * \param [in] application_pid The process id of the initiating application * \param [in] state_change_callback The function to be called when a prompt session state change occurs * \param [in,out] context User data passed to the callback functions * \return A handle that can be passed to mir_wait_for */ MirPromptSession *mir_connection_create_prompt_session_sync( MirConnection* connection, pid_t application_pid, mir_prompt_session_state_change_callback state_change_callback, void *context); /** * Allocate some FDs for prompt providers to connect on * * Prompt helpers need to allocate connection FDs it will pass to * prompt providers to use when connecting to the server. The server can * then associate them with the prompt session. * * \warning This API is tentative until the implementation of prompt sessions is complete * \param [in] prompt_session The prompt session * \param [in] no_of_fds The number of fds to allocate * \param [in] callback Callback invoked when request completes * \param [in,out] context User data passed to the callback function * \return A handle that can be passed to mir_wait_for */ MirWaitHandle* mir_prompt_session_new_fds_for_prompt_providers( MirPromptSession *prompt_session, unsigned int no_of_fds, mir_client_fd_callback callback, void * context); /** * Stop and release the specified prompt session * \param [in] prompt_session The prompt session */ void mir_prompt_session_release_sync(MirPromptSession *prompt_session); /** * Test for a valid prompt session * \param [in] prompt_session The prompt session * \return True if prompt_session is valid, false otherwise */ bool mir_prompt_session_is_valid(MirPromptSession *prompt_session); /** * Retrieve a text description of the last error. The returned string is owned * by the library and remains valid until the prompt session has been released. * \param [in] prompt_session The prompt session * \return A text description of any error resulting in an * invalid connection, or the empty string "" if the * connection is valid. */ char const *mir_prompt_session_error_message(MirPromptSession *prompt_session); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_MIR_PROMPT_SESSION_H_ */ ./include/client/mir_toolkit/version.h0000644000015600001650000000364412676616157020220 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_TOOLKIT_VERSION_H_ #define MIR_TOOLKIT_VERSION_H_ #include "mir_toolkit/mir_version_number.h" /** * \addtogroup mir_toolkit * @{ */ /** * MIR_CLIENT_MAJOR_VERSION * * The major client API version. This will increase once per API incompatible release. * * See also: http://semver.org/ */ #define MIR_CLIENT_MAJOR_VERSION (3) /** * MIR_CLIENT_MINOR_VERSION * * The minor client API version. This will increase each time new backwards-compatible API * is added, and will reset to 0 each time MIR_CLIENT_MAJOR_VERSION is incremented. * * See also: http://semver.org/ */ #define MIR_CLIENT_MINOR_VERSION (3) /** * MIR_CLIENT_MICRO_VERSION * * The micro client API version. This will increment each release. * This is usually uninteresting for client code, but may be of use in selecting * whether to use a feature that has previously been buggy. * * This corresponds to the PATCH field of http://semver.org/ */ #define MIR_CLIENT_MICRO_VERSION (0) /** * MIR_CLIENT_VERSION * * The current version of the Mir client headers in use. */ #define MIR_CLIENT_VERSION \ MIR_VERSION_NUMBER(MIR_CLIENT_MAJOR_VERSION, \ MIR_CLIENT_MINOR_VERSION, \ MIR_CLIENT_MICRO_VERSION) /**@}*/ #endif /* MIR_TOOLKIT_VERSION_H_ */ ./include/client/mir_toolkit/mir_client_library.h0000644000015600001650000000217312676616157022400 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_CLIENT_LIBRARY_H #define MIR_CLIENT_LIBRARY_H #include #include #include #include #include #include #include #include #include #include #endif /* MIR_CLIENT_LIBRARY_H */ ./include/client/mir_toolkit/mir_cookie.h0000644000015600001650000000435312676616157020651 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Brandon Schaefer */ #ifndef MIR_TOOLKIT_MIR_COOKIE_H_ #define MIR_TOOLKIT_MIR_COOKIE_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Queries the size needed to serialize a given cookie * * \params[in] cookie A cookie instance * \return The size of the serialized representation of the given cookie */ size_t mir_cookie_buffer_size(MirCookie const* cookie); /** * Serializes a cookie into the given buffer * * \pre The size must be equal to mir_cookie_size * \params[in] cookie A cookie instance * \params[in] buffer A buffer which is filled with the serialized representation of the given cookie * \params[in] size The size of the given buffer */ void mir_cookie_to_buffer(MirCookie const* cookie, void* buffer, size_t size); /** * Create a cookie from a serialized representation * * \params[in] buffer The buffer containing a serialized cookie. * The buffer may be freed immediately after this call. * \return A MirCookie instance. The instance must be released * with a call to mir_cookie_release. * NULL will be returned if the buffer and size don't describe * the contents of a MirCookie. */ MirCookie const* mir_cookie_from_buffer(void const* buffer, size_t size); /** * Release the MirCookie * * \params[in] cookie The cookie to release */ void mir_cookie_release(MirCookie const* cookie); #ifdef __cplusplus } /**@}*/ #endif #endif // MIR_TOOLKIT_MIR_COOKIE_H_ ./include/client/mir_toolkit/event.h0000644000015600001650000000156312676616125017645 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_EVENT_COMPAT_HEADER_H_ #define MIR_TOOLKIT_EVENT_COMPAT_HEADER_H_ #include "mir_toolkit/events/event.h" #endif /* MIR_TOOLKIT_EVENT_COMPAT_HEADER_H_ */ ./include/client/mir_toolkit/client_types.h0000644000015600001650000003360412676616157021234 0ustar jenkinsjenkins/* * client_types.h: Type definitions used in client apps and libmirclient. * * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_CLIENT_TYPES_H_ #define MIR_TOOLKIT_CLIENT_TYPES_H_ #include #include #include #ifdef __cplusplus /** * \defgroup mir_toolkit MIR graphics tools API * @{ */ extern "C" { #endif /* Display server connection API */ typedef void* MirEGLNativeWindowType; typedef void* MirEGLNativeDisplayType; typedef struct MirConnection MirConnection; typedef struct MirSurface MirSurface; typedef struct MirSurfaceSpec MirSurfaceSpec; typedef struct MirScreencast MirScreencast; typedef struct MirPromptSession MirPromptSession; typedef struct MirBufferStream MirBufferStream; typedef struct MirPersistentId MirPersistentId; typedef struct MirBlob MirBlob; typedef struct MirDisplayConfig MirDisplayConfig; /** * Descriptor for an output connection. * * Each MirOutput corresponds to a video output. This may be a physical connection on the system, * like HDMI or DisplayPort, or may be a virtual output such as a remote display or screencast display. */ typedef struct MirOutput MirOutput; /** * Returned by asynchronous functions. Must not be free'd by * callers. See the individual function documentation for information * on the lifetime of wait handles. */ typedef struct MirWaitHandle MirWaitHandle; typedef struct MirPlatformMessage MirPlatformMessage; /** * Callback to be passed when issuing a mir_connect request. * \param [in] connection the new connection * \param [in,out] client_context context provided by client in calling * mir_connect */ typedef void (*mir_connected_callback)(MirConnection *connection, void *client_context); /** * Callback to be passed when calling: * - mir_connection_create_surface * - mir_surface_swap_buffers * - mir_surface_release * \param [in] surface the surface being updated * \param [in,out] client_context context provided by client in calling * mir_connect */ typedef void (*mir_surface_callback)(MirSurface *surface, void *client_context); /** * Callback to be passed when calling: * - mir_buffer_stream_* functions requiring a callback. * \param [in] stream the buffer stream being updated * \param [in,out] client_context context provided by client in calling * mir_connect */ typedef void (*mir_buffer_stream_callback)(MirBufferStream *stream, void *client_context); /** * Callback for handling of surface events. * \param [in] surface The surface on which an event has occurred * \param [in] event The event to be handled * \param [in,out] context The context provided by client */ typedef void (*mir_surface_event_callback)( MirSurface* surface, MirEvent const* event, void* context); /** * Callback called when a lifecycle event/callback is requested * from the running server. * \param [in] connection The connection associated with the lifecycle event * \param [in] cb The callback requested * \param [in,out] context The context provided by the client */ typedef void (*mir_lifecycle_event_callback)( MirConnection* connection, MirLifecycleState state, void* context); /** * Callback called when the server pings for responsiveness testing. * \param [in] connection The connection associated with this ping * \param [in] serial Identifier of this ping, to be passed to * mir_connection_pong() * \param [in,out] context The context provided by the client */ typedef void (*mir_ping_event_callback)( MirConnection* connection, int32_t serial, void* context); /** * Callback called when a display config change has occurred * \param [in] connection The connection associated with the display change * \param [in,out] context The context provided by client */ typedef void (*mir_display_config_callback)( MirConnection* connection, void* context); /** * Callback called when a request for client file descriptors completes * \param [in] prompt_session The prompt session * \param [in] count The number of FDs allocated * \param [in] fds Array of FDs * \param [in,out] context The context provided by client * * \note Copy the FDs as the array will be invalidated after callback completes */ typedef void (*mir_client_fd_callback)( MirPromptSession *prompt_session, size_t count, int const* fds, void* context); typedef void (*mir_surface_id_callback)( MirSurface* surface, MirPersistentId* id, void* context); /** * MirBufferUsage specifies how a surface can and will be used. A "hardware" * surface can be used for OpenGL accelerated rendering. A "software" surface * is one that can be addressed in main memory and blitted to directly. */ typedef enum MirBufferUsage { mir_buffer_usage_hardware = 1, mir_buffer_usage_software } MirBufferUsage; /** * MirSurfaceParameters is the structure of minimum required information that * you must provide to Mir in order to create a surface. */ typedef struct MirSurfaceParameters { char const *name; int width; int height; MirPixelFormat pixel_format; MirBufferUsage buffer_usage; /** * The id of the output to place the surface in. * * Use one of the output ids from MirDisplayConfiguration/MirDisplayOutput * to place a surface on that output. Only fullscreen placements are * currently supported. If you don't have special placement requirements, * use the value mir_display_output_id_invalid. */ uint32_t output_id; } MirSurfaceParameters; enum { mir_platform_package_max = 32 }; /** * The native buffer type for the system the client is connected on * * \deprecated Use of this type is inherently non-portable in the presence * of plug-in platform modules as these need not correspond to the available * types. * \todo This should be removed from the public API at the next API break. */ #ifndef __cplusplus __attribute__ ((deprecated)) #endif typedef enum MirPlatformType { mir_platform_type_gbm, mir_platform_type_android } MirPlatformType; typedef struct MirPlatformPackage { int data_items; int fd_items; int data[mir_platform_package_max]; int fd[mir_platform_package_max]; } MirPlatformPackage; /** * Retrieved information about a loadable module. This allows clients to * identify the underlying platform. E.g. whether the graphics are * "mir:android" or "mir:mesa". * Third party graphics platforms do not currently exist but should be * named according to the vendor and platform. Vis: ":" */ typedef struct MirModuleProperties { char const *name; int major_version; int minor_version; int micro_version; char const *filename; } MirModuleProperties; /** * Retrieved information about a MirSurface. This is most useful for learning * how and where to write to a 'mir_buffer_usage_software' surface. */ typedef struct MirGraphicsRegion { int width; int height; int stride; MirPixelFormat pixel_format; char *vaddr; } MirGraphicsRegion; /** * DEPRECATED. use MirDisplayConfiguration */ enum { mir_supported_pixel_format_max = 32 }; typedef struct MirDisplayInfo { uint32_t width; uint32_t height; int supported_pixel_format_items; MirPixelFormat supported_pixel_format[mir_supported_pixel_format_max]; } MirDisplayInfo; /** * MirDisplayConfiguration provides details of the graphics environment. */ typedef struct MirDisplayCard { uint32_t card_id; uint32_t max_simultaneous_outputs; } MirDisplayCard; typedef enum MirDisplayOutputType { mir_display_output_type_unknown, mir_display_output_type_vga, mir_display_output_type_dvii, mir_display_output_type_dvid, mir_display_output_type_dvia, mir_display_output_type_composite, mir_display_output_type_svideo, mir_display_output_type_lvds, mir_display_output_type_component, mir_display_output_type_ninepindin, mir_display_output_type_displayport, mir_display_output_type_hdmia, mir_display_output_type_hdmib, mir_display_output_type_tv, mir_display_output_type_edp } MirDisplayOutputType; typedef enum MirOutputType { mir_output_type_unknown, mir_output_type_vga, mir_output_type_dvii, mir_output_type_dvid, mir_output_type_dvia, mir_output_type_composite, mir_output_type_svideo, mir_output_type_lvds, mir_output_type_component, mir_output_type_ninepindin, mir_output_type_displayport, mir_output_type_hdmia, mir_output_type_hdmib, mir_output_type_tv, mir_output_type_edp } MirOutputType; typedef enum MirOutputConnectionState { mir_output_connection_state_disconnected = 0, mir_output_connection_state_connected, mir_output_connection_state_unknown } MirOutputConnectionState; typedef struct MirDisplayMode { uint32_t vertical_resolution; uint32_t horizontal_resolution; double refresh_rate; } MirDisplayMode; enum { mir_display_output_id_invalid = 0 }; typedef struct MirDisplayOutput { uint32_t num_modes; MirDisplayMode* modes; uint32_t preferred_mode; /**< There might be no preferred mode, which is indicated by a value >=num_modes. */ uint32_t current_mode; uint32_t num_output_formats; MirPixelFormat* output_formats; MirPixelFormat current_format; uint32_t card_id; uint32_t output_id; MirDisplayOutputType type; int32_t position_x; int32_t position_y; uint32_t connected; uint32_t used; uint32_t physical_width_mm; uint32_t physical_height_mm; MirPowerMode power_mode; MirOrientation orientation; } MirDisplayOutput; typedef struct MirDisplayConfiguration { uint32_t num_outputs; MirDisplayOutput* outputs; uint32_t num_cards; MirDisplayCard *cards; } MirDisplayConfiguration; /** * The displacement from the top-left corner of the surface. */ typedef struct MirBufferStreamInfo { MirBufferStream* stream; int displacement_x; int displacement_y; } MirBufferStreamInfo; typedef struct MirRectangle { int left; int top; unsigned int width; unsigned int height; } MirRectangle; typedef struct MirInputConfig MirInputConfig; typedef struct MirInputDevice MirInputDevice; /** * MirScreencastParameters is the structure of required information that * you must provide to Mir in order to create a MirScreencast. * The width and height parameters can be used to down-scale the screencast * For no scaling set them to the region width and height. */ typedef struct MirScreencastParameters { /** * The rectangular region of the screen to capture - * The region is specified in virtual screen space hence multiple screens can be captured simultaneously */ MirRectangle region; /** The width of the screencast which can be different than the screen region capture width */ unsigned int width; /** The height of the screencast which can be different than the screen region capture height */ unsigned int height; /** * The pixel format of the screencast. * It must be a supported format obtained from mir_connection_get_available_surface_formats. */ MirPixelFormat pixel_format; } MirScreencastParameters; /** * Callback to be passed when calling MirScreencast functions. * \param [in] screencast the screencast being updated * \param [in,out] client_context context provided by the client */ typedef void (*mir_screencast_callback)(MirScreencast *screencast, void *client_context); /** * Callback member of MirPromptSession for handling of prompt sessions. * \param [in] prompt_provider The prompt session associated with the callback * \param [in,out] context The context provided by the client */ typedef void (*mir_prompt_session_callback)(MirPromptSession* prompt_provider, void* context); /** * Callback member of MirPromptSession for handling of prompt sessions events. * \param [in] prompt_provider The prompt session associated with the callback * \param [in] state The state of the prompt session * \param [in,out] context The context provided by the client */ typedef void (*mir_prompt_session_state_change_callback)( MirPromptSession* prompt_provider, MirPromptSessionState state, void* context); /** * Callback called when a platform operation completes. * * \warning The reply is owned by the callee, who should release it when it's * not needed any more. * * \param [in] connection The connection associated with the platform operation * \param [in] reply The platform operation reply * \param [in,out] context The context provided by the client */ typedef void (*mir_platform_operation_callback)( MirConnection* connection, MirPlatformMessage* reply, void* context); /** * Callback called when a change of input devices has occurred * \param [in] connection The connection associated with the input device * change * \param [in,out] context The context provided by client */ typedef void (*mir_input_config_callback)( MirConnection* connection, void* context); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_CLIENT_TYPES_H_ */ ./include/client/mir_toolkit/mir_platform_message.h0000644000015600001650000000774612676616125022734 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_TOOLKIT_MIR_PLATFORM_MESSAGE_H_ #define MIR_TOOLKIT_MIR_PLATFORM_MESSAGE_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif struct MirPlatformMessage; typedef struct { void const* const data; size_t const size; } MirPlatformMessageData; typedef struct { int const* const fds; size_t const num_fds; } MirPlatformMessageFds; /** * Create a platform message to use with mir_connection_platform_operation(). * * Each call to mir_platform_message_create() should be matched by * a call to mir_platform_message_release() to avoid memory leaks. * * \param [in] opcode The platform message opcode * \return The created MirPlatformMessage */ MirPlatformMessage* mir_platform_message_create(unsigned int opcode); /** * Release a platform message. * * \param [in] message The MirPlatformMessage */ void mir_platform_message_release(MirPlatformMessage const* message); /** * Set the data associated with a message. * * The data is copied into the message. * * \param [in] message The MirPlatformMessage * \param [in] data Pointer to the data * \param [in] data_size The size of the data in bytes */ void mir_platform_message_set_data(MirPlatformMessage* message, void const* data, size_t data_size); /** * Sets the fds associated with a message. * * The fd array is copied into the message, but the message does not take * ownership of the fds, i.e., the caller is responsible for keeping * the fds open for as long as this message needs to remain valid. * * Note that the fds associated with a message are not closed when the message * is released. The caller is responsible for closing the fds when the message * doesn't need them anymore (see also mir_platform_message_get_fds()). * * \param [in] message The MirPlatformMessage * \param [in] fds Pointer to the array of fds * \param [in] num_fds The number of fds */ void mir_platform_message_set_fds(MirPlatformMessage* message, int const* fds, size_t num_fds); /** * Get the opcode of a message. * * \param [in] message The MirPlatformMessage * \return The opcode */ unsigned int mir_platform_message_get_opcode(MirPlatformMessage const* message); /** * Get the data associated with a message. * * The memory holding the returned data array is owned by the message and is * valid only as long as the message is valid and mir_platform_set_data() is * not called. You must not change or free the returned data array. * * \param [in] message The MirPlatformMessage * \return The data */ MirPlatformMessageData mir_platform_message_get_data(MirPlatformMessage const* message); /** * Gets the fds associated with a message. * * The memory of the returned fd array is owned by the message and is valid * only as long as the message is valid and mir_platform_set_fds() is not * called. You must not change or free the returned fd array. * * Note that the fds associated with a message will not be closed when the * message is released. Users are responsible for getting and closing the * fds to avoid leaks. * * \param [in] message The MirPlatformMessage * \return The fds */ MirPlatformMessageFds mir_platform_message_get_fds(MirPlatformMessage const* message); #ifdef __cplusplus } /**@}*/ #endif #endif ./include/client/mir_toolkit/mir_surface.h0000644000015600001650000010152112676616157021023 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_TOOLKIT_MIR_SURFACE_H_ #define MIR_TOOLKIT_MIR_SURFACE_H_ #include #include #include #include #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Create a surface specification for a normal surface. * * A normal surface is suitable for most application windows. It has no special semantics. * On a desktop shell it will typically have a title-bar, be movable, resizeable, etc. * * \param [in] connection Connection the surface will be created on * \param [in] width Requested width. The server is not guaranteed to return a surface of this width. * \param [in] height Requested height. The server is not guaranteed to return a surface of this height. * \param [in] format Pixel format for the surface. * \return A handle that can be passed to mir_surface_create() to complete construction. */ MirSurfaceSpec* mir_connection_create_spec_for_normal_surface(MirConnection* connection, int width, int height, MirPixelFormat format); /** * Create a surface specification for a menu surface. * * Positioning of the surface is specified with respect to the parent surface * via an adjacency rectangle. The server will attempt to choose an edge of the * adjacency rectangle on which to place the surface taking in to account * screen-edge proximity or similar constraints. In addition, the server can use * the edge affinity hint to consider only horizontal or only vertical adjacency * edges in the given rectangle. * * \param [in] connection Connection the surface will be created on * \param [in] width Requested width. The server is not guaranteed to * return a surface of this width. * \param [in] height Requested height. The server is not guaranteed to * return a surface of this height. * \param [in] format Pixel format for the surface. * \param [in] parent A valid parent surface for this menu. * \param [in] rect The adjacency rectangle. The server is not * guaranteed to create a surface at the requested * location. * \param [in] edge The preferred edge direction to attach to. Use * mir_edge_attachment_any for no preference. * \return A handle that can be passed to mir_surface_create() * to complete construction. */ MirSurfaceSpec* mir_connection_create_spec_for_menu(MirConnection* connection, int width, int height, MirPixelFormat format, MirSurface* parent, MirRectangle* rect, MirEdgeAttachment edge); /** * Create a surface specification for a tooltip surface. * * A tooltip surface becomes visible when the pointer hovers the specified * target zone. A tooltip surface has no input focus and will be closed when * the pointer moves out of the target zone or the parent closes, moves or hides * * The tooltip parent cannot be another tooltip surface. * * The tooltip position is decided by the server but typically it will appear * near the pointer. * * \param [in] connection Connection the surface will be created on * \param [in] width Requested width. The server is not guaranteed to * return a surface of this width. * \param [in] height Requested height. The server is not guaranteed to * return a surface of this height. * \param [in] format Pixel format for the surface. * \param [in] parent A valid parent surface for this tooltip. * \param [in] rect A target zone relative to parent. * \return A handle that can be passed to mir_surface_create() * to complete construction. */ MirSurfaceSpec* mir_connection_create_spec_for_tooltip(MirConnection* connection, int width, int height, MirPixelFormat format, MirSurface* parent, MirRectangle* zone); /** * Create a surface specification for a modal dialog surface. * * The dialog surface will have input focus; the parent can still be moved, * resized or hidden/minimized but no interaction is possible until the dialog * is dismissed. * * A dialog will typically have no close/maximize button decorations. * * During surface creation, if the specified parent is another dialog surface * the server may choose to close the specified parent in order to show this * new dialog surface. * * \param [in] connection Connection the surface will be created on * \param [in] width Requested width. The server is not guaranteed to * return a surface of this width. * \param [in] height Requested height. The server is not guaranteed to * return a surface of this height. * \param [in] format Pixel format for the surface. * \param [in] parent A valid parent surface. * */ MirSurfaceSpec* mir_connection_create_spec_for_modal_dialog(MirConnection* connection, int width, int height, MirPixelFormat format, MirSurface* parent); /** * Create a surface specification for a parentless dialog surface. * * A parentless dialog surface is similar to a normal surface, but it cannot * be fullscreen and typically won't have any maximize/close button decorations. * * A parentless dialog is not allowed to have other dialog children. The server * may decide to close the parent and show the child dialog only. * * \param [in] connection Connection the surface will be created on * \param [in] width Requested width. The server is not guaranteed to * return a surface of this width. * \param [in] height Requested height. The server is not guaranteed to * return a surface of this height. * \param [in] format Pixel format for the surface. * */ MirSurfaceSpec* mir_connection_create_spec_for_dialog(MirConnection* connection, int width, int height, MirPixelFormat format); /** * Create a surface specification. * This can be used with mir_surface_create() to create a surface or with * mir_surface_apply_spec() to change an existing surface. * \remark For use with mir_surface_create() at least the type, width, height, * format and buffer_usage must be set. (And for types requiring a parent that * too must be set.) * * \param [in] connection a valid mir connection * \return A handle that can ultimately be passed to * mir_surface_create() or mir_surface_apply_spec() */ MirSurfaceSpec* mir_create_surface_spec(MirConnection* connection); /** * Create a surface specification for updating a surface. * * This can be applied to one or more target surfaces using * mir_surface_apply_spec(...). * * \param [in] connection a valid mir connection * */ MirSurfaceSpec* mir_connection_create_spec_for_changes(MirConnection* connection); /** * Create a surface from a given specification * * * \param [in] requested_specification Specification of the attributes for the created surface * \param [in] callback Callback function to be invoked when creation is complete * \param [in, out] context User data passed to callback function. * This callback is guaranteed to be called, and called with a * non-null MirSurface*, but the surface may be invalid in * case of an error. * \return A handle that can be passed to mir_wait_for() */ MirWaitHandle* mir_surface_create(MirSurfaceSpec* requested_specification, mir_surface_callback callback, void* context); /** * Create a surface from a given specification and wait for the result. * \param [in] requested_specification Specification of the attributes for the created surface * \return The new surface. This is guaranteed non-null, but may be invalid * in the case of error. */ MirSurface* mir_surface_create_sync(MirSurfaceSpec* requested_specification); /** * Set the requested parent. * * \param [in] spec Specification to mutate * \param [in] parent A valid parent surface. */ void mir_surface_spec_set_parent(MirSurfaceSpec* spec, MirSurface* parent); /** * Update a surface specification with a surface type. * This can be used with mir_surface_create() to create a surface or with * mir_surface_apply_spec() to change an existing surface. * \remark For use with mir_surface_apply_spec() the shell need not support * arbitrary changes of type and some target types may require the setting of * properties such as "parent" that are not already present on the surface. * The type transformations the server is required to support are:\n * regular => utility, dialog or satellite\n * utility => regular, dialog or satellite\n * dialog => regular, utility or satellite\n * satellite => regular, utility or dialog\n * popup => satellite * * \param [in] spec Specification to mutate * \param [in] type the target type of the surface */ void mir_surface_spec_set_type(MirSurfaceSpec* spec, MirSurfaceType type); /** * Set the requested name. * * The surface name helps the user to distinguish between multiple surfaces * from the same application. A typical desktop shell may use it to provide * text in the window titlebar, in an alt-tab switcher, or equivalent. * * \param [in] spec Specification to mutate * \param [in] name Requested name. This must be valid UTF-8. * Copied into spec; clients can free the buffer passed after this call. */ void mir_surface_spec_set_name(MirSurfaceSpec* spec, char const* name); /** * Set the requested width, in pixels * * \param [in] spec Specification to mutate * \param [in] width Requested width. * * \note The requested dimensions are a hint only. The server is not guaranteed to create a * surface of any specific width or height. */ void mir_surface_spec_set_width(MirSurfaceSpec* spec, unsigned width); /** * Set the requested height, in pixels * * \param [in] spec Specification to mutate * \param [in] height Requested height. * * \note The requested dimensions are a hint only. The server is not guaranteed to create a * surface of any specific width or height. */ void mir_surface_spec_set_height(MirSurfaceSpec* spec, unsigned height); /** * Set the requested width increment, in pixels. * Defines an arithmetic progression of sizes starting with min_width (if set, otherwise 0) * into which the surface prefers to be resized. * * \param [in] spec Specification to mutate * \param [in] width_inc Requested width increment. * * \note The requested dimensions are a hint only. The server is not guaranteed to * create a surface of any specific width or height. */ void mir_surface_spec_set_width_increment(MirSurfaceSpec* spec, unsigned width_inc); /** * Set the requested height increment, in pixels * Defines an arithmetic progression of sizes starting with min_height (if set, otherwise 0) * into which the surface prefers to be resized. * * \param [in] spec Specification to mutate * \param [in] height_inc Requested height increment. * * \note The requested dimensions are a hint only. The server is not guaranteed to * create a surface of any specific width or height. */ void mir_surface_spec_set_height_increment(MirSurfaceSpec* spec, unsigned height_inc); /** * Set the minimum width, in pixels * * \param [in] spec Specification to mutate * \param [in] width Minimum width. * * \note The requested dimensions are a hint only. The server is not guaranteed to create a * surface of any specific width or height. */ void mir_surface_spec_set_min_width(MirSurfaceSpec* spec, unsigned min_width); /** * Set the minimum height, in pixels * * \param [in] spec Specification to mutate * \param [in] height Minimum height. * * \note The requested dimensions are a hint only. The server is not guaranteed to create a * surface of any specific width or height. */ void mir_surface_spec_set_min_height(MirSurfaceSpec* spec, unsigned min_height); /** * Set the maximum width, in pixels * * \param [in] spec Specification to mutate * \param [in] width Maximum width. * * \note The requested dimensions are a hint only. The server is not guaranteed to create a * surface of any specific width or height. */ void mir_surface_spec_set_max_width(MirSurfaceSpec* spec, unsigned max_width); /** * Set the maximum height, in pixels * * \param [in] spec Specification to mutate * \param [in] height Maximum height. * * \note The requested dimensions are a hint only. The server is not guaranteed to create a * surface of any specific width or height. */ void mir_surface_spec_set_max_height(MirSurfaceSpec* spec, unsigned max_height); /** * Set the minimum aspect ratio. This is the minimum ratio of surface width to height. * It is independent of orientation changes and/or preferences. * * \param [in] spec Specification to mutate * \param [in] width numerator * \param [in] height denominator * * \note The requested aspect ratio is a hint only. The server is not guaranteed * to create a surface of any specific aspect. */ void mir_surface_spec_set_min_aspect_ratio(MirSurfaceSpec* spec, unsigned width, unsigned height); /** * Set the maximum aspect ratio. This is the maximum ratio of surface width to height. * It is independent of orientation changes and/or preferences. * * \param [in] spec Specification to mutate * \param [in] width numerator * \param [in] height denominator * * \note The requested aspect ratio is a hint only. The server is not guaranteed * to create a surface of any specific aspect. */ void mir_surface_spec_set_max_aspect_ratio(MirSurfaceSpec* spec, unsigned width, unsigned height); /** * Set the requested pixel format. * \param [in] spec Specification to mutate * \param [in] format Requested pixel format * * \note If this call returns %true then the server is guaranteed to honour this request. * If the server is unable to create a surface with this pixel format at * the point mir_surface_create() is called it will instead return an invalid surface. */ void mir_surface_spec_set_pixel_format(MirSurfaceSpec* spec, MirPixelFormat format); /** * Set the requested buffer usage. * \param [in] spec Specification to mutate * \param [in] usage Requested buffer usage * * \note If this call returns %true then the server is guaranteed to honour this request. * If the server is unable to create a surface with this buffer usage at * the point mir_surface_create() is called it will instead return an invalid surface. */ void mir_surface_spec_set_buffer_usage(MirSurfaceSpec* spec, MirBufferUsage usage); /** * \param [in] spec Specification to mutate * \param [in] output_id ID of output to place surface on. From MirDisplayOutput.output_id * * \note If this call returns %true then a valid surface returned from mir_surface_create() is * guaranteed to be fullscreen on the specified output. An invalid surface is returned * if the server unable to, or policy prevents it from, honouring this request. */ void mir_surface_spec_set_fullscreen_on_output(MirSurfaceSpec* spec, uint32_t output_id); /** * Set the requested preferred orientation mode. * \param [in] spec Specification to mutate * \param [in] mode Requested preferred orientation * * \note If the server is unable to create a surface with the preferred orientation at * the point mir_surface_create() is called it will instead return an invalid surface. */ void mir_surface_spec_set_preferred_orientation(MirSurfaceSpec* spec, MirOrientationMode mode); /** * Request that the created surface be attached to a surface of a different client. * * This is restricted to input methods, which need to attach their suggestion surface * to text entry widgets of other processes. * * \param [in] spec Specification to mutate * \param [in] parent A MirPersistentId reference to the parent surface * \param [in] attachment_rect A rectangle specifying the region (in parent surface coordinates) * that the created surface should be attached to. * \param [in] edge The preferred edge direction to attach to. Use * mir_edge_attachment_any for no preference. * \return False if the operation is invalid for this surface type. * * \note If the parent surface becomes invalid before mir_surface_create() is processed, * it will return an invalid surface. If the parent surface is valid at the time * mir_surface_create() is called but is later closed, this surface will also receive * a close event. */ bool mir_surface_spec_attach_to_foreign_parent(MirSurfaceSpec* spec, MirPersistentId* parent, MirRectangle* attachment_rect, MirEdgeAttachment edge); /** * Set the requested state. * \param [in] spec Specification to mutate * \param [in] mode Requested state * * \note If the server is unable to create a surface with the requested state at * the point mir_surface_create() is called it will instead return an invalid surface. */ void mir_surface_spec_set_state(MirSurfaceSpec* spec, MirSurfaceState state); /** * Release the resources held by a MirSurfaceSpec. * * \param [in] spec Specification to release */ void mir_surface_spec_release(MirSurfaceSpec* spec); /** * Set the streams associated with the spec. * streams[0] is the bottom-most stream, and streams[size-1] is the topmost. * On application of the spec, a stream that is present in the surface, * but is not in the list will be disassociated from the surface. * On application of the spec, a stream that is not present in the surface, * but is in the list will be associated with the surface. * Streams set a displacement from the top-left corner of the surface. * * \warning disassociating streams from the surface will not release() them. * \warning It is wiser to arrange the streams within the bounds of the * surface the spec is applied to. Shells can define their own * behavior as to what happens to an out-of-bound stream. * * \param [in] spec The spec to accumulate the request in. * \param [in] streams An array of non-null streams info. * \param [in] num_streams The number of elements in the streams array. */ void mir_surface_spec_set_streams(MirSurfaceSpec* spec, MirBufferStreamInfo* streams, unsigned int num_streams); /** * Set a collection of input rectangles assosciated with the spec. * Rectangles are specified as a list of regions relative to the top left * of the specified surface. If the server applies this specification * to a surface input which would normally go to the surface but is not * contained within any of the input rectangles instead passes * on to the next client. * * \param [in] spec The spec to accumulate the request in. * \param [in] rectangles An array of MirRectangles specifying the input shape. * \param [in] num_streams The number of elements in the rectangles array. */ void mir_surface_spec_set_input_shape(MirSurfaceSpec* spec, MirRectangle const *rectangles, size_t n_rects); /** * Set the event handler to be called when events arrive for a surface. * \warning event_handler could be called from another thread. You must do * any locking appropriate to protect your data accessed in the * callback. There is also a chance that different events will be * called back in different threads, for the same surface, * simultaneously. * \param [in] spec The spec to accumulate the request in. * \param [in] callback The callback function * \param [in] context Additional argument to be passed to callback */ void mir_surface_spec_set_event_handler( MirSurfaceSpec* spec, mir_surface_event_callback callback, void* context); /** * Ask the shell to customize "chrome" for this surface. * For example, on the phone hide indicators when this surface is active. * * \param [in] spec The spec to accumulate the request in. * \param [in] style The requested level of "chrome" */ void mir_surface_spec_set_shell_chrome(MirSurfaceSpec* spec, MirShellChrome style); /** * Set the event handler to be called when events arrive for a surface. * \warning event_handler could be called from another thread. You must do * any locking appropriate to protect your data accessed in the * callback. There is also a chance that different events will be * called back in different threads, for the same surface, * simultaneously. * \param [in] surface The surface * \param [in] callback The callback function * \param [in] context Additional argument to be passed to callback */ void mir_surface_set_event_handler(MirSurface *surface, mir_surface_event_callback callback, void* context); /** * Retrieve the primary MirBufferStream associated with a surface (to advance buffers, * obtain EGLNativeWindow, etc...) * * \param[in] surface The surface */ MirBufferStream* mir_surface_get_buffer_stream(MirSurface *surface); /** * Test for a valid surface * \param [in] surface The surface * \return True if the supplied surface is valid, or * false otherwise. */ bool mir_surface_is_valid(MirSurface *surface); /** * Retrieve a text description of the error. The returned string is owned by * the library and remains valid until the surface or the associated * connection has been released. * \param [in] surface The surface * \return A text description of any error resulting in an * invalid surface, or the empty string "" if the * connection is valid. */ char const *mir_surface_get_error_message(MirSurface *surface); /** * Get a surface's parameters. * \pre The surface is valid * \param [in] surface The surface * \param [out] parameters Structure to be populated */ void mir_surface_get_parameters(MirSurface *surface, MirSurfaceParameters *parameters); /** * Release the supplied surface and any associated buffer. The returned wait * handle remains valid until the connection to the server is released. * \warning callback could be called from another thread. You must do any * locking appropriate to protect your data accessed in the * callback. * \param [in] surface The surface * \param [in] callback Callback function to be invoked when the request * completes * \param [in,out] context User data passed to the callback function * \return A handle that can be passed to mir_wait_for */ MirWaitHandle *mir_surface_release( MirSurface *surface, mir_surface_callback callback, void *context); /** * Release the specified surface like in mir_surface_release(), but also wait * for the operation to complete. * \param [in] surface The surface to be released */ void mir_surface_release_sync(MirSurface *surface); /** * Get the type (purpose) of a surface. * \param [in] surface The surface to query * \return The type of the surface */ MirSurfaceType mir_surface_get_type(MirSurface *surface); /** * Change the state of a surface. * \param [in] surface The surface to operate on * \param [in] state The new state of the surface * \return A wait handle that can be passed to mir_wait_for */ MirWaitHandle* mir_surface_set_state(MirSurface *surface, MirSurfaceState state); /** * Get the current state of a surface. * \param [in] surface The surface to query * \return The state of the surface */ MirSurfaceState mir_surface_get_state(MirSurface *surface); /** * Set the swapinterval for mir_surface_swap_buffers. EGL users should use * eglSwapInterval directly. * At the time being, only swapinterval of 0 or 1 is supported. * \param [in] surface The surface to operate on * \param [in] interval The number of vblank signals that * mir_surface_swap_buffers will wait for * \return A wait handle that can be passed to mir_wait_for, * or NULL if the interval could not be supported */ MirWaitHandle* mir_surface_set_swapinterval(MirSurface* surface, int interval); /** * Query the swapinterval that the surface is operating with. * The default interval is 1. * \param [in] surface The surface to operate on * \return The swapinterval value that the client is operating with. * Returns -1 if surface is invalid. */ int mir_surface_get_swapinterval(MirSurface* surface); /** * Query the DPI value of the surface (dots per inch). This will vary depending * on the physical display configuration and where the surface is within it. * \return The DPI of the surface, or zero if unknown. */ int mir_surface_get_dpi(MirSurface* surface); /** * Query the focus state for a surface. * \param [in] surface The surface to operate on * \return The focus state of said surface */ MirSurfaceFocusState mir_surface_get_focus(MirSurface *surface); /** * Query the visibility state for a surface. * \param [in] surface The surface to operate on * \return The visibility state of said surface */ MirSurfaceVisibility mir_surface_get_visibility(MirSurface *surface); /** * Choose the cursor state for a surface: whether a cursor is shown, * and which cursor if so. * \param [in] surface The surface to operate on * \param [in] parameters The configuration parameters obtained * from mir_cursor* family of functions. * \return A wait handle that can be passed to mir_wait_for, * or NULL if parameters is invalid. * */ MirWaitHandle* mir_surface_configure_cursor(MirSurface *surface, MirCursorConfiguration const* parameters); /** * Get the orientation of a surface. * \param [in] surface The surface to query * \return The orientation of the surface */ MirOrientation mir_surface_get_orientation(MirSurface *surface); /** * Request to set the preferred orientations of a surface. * The request may be rejected by the server; to check wait on the * result and check the applied value using mir_surface_get_preferred_orientation * \param [in] surface The surface to operate on * \param [in] orientation The preferred orientation modes * \return A wait handle that can be passed to mir_wait_for */ MirWaitHandle* mir_surface_set_preferred_orientation(MirSurface *surface, MirOrientationMode orientation); /** * Get the preferred orientation modes of a surface. * \param [in] surface The surface to query * \return The preferred orientation modes */ MirOrientationMode mir_surface_get_preferred_orientation(MirSurface *surface); /** * Create a surface specification for an input method surface. * * Currently this is only appropriate for the Unity On-Screen-Keyboard. * * \param [in] connection Connection the surface will be created on * \param [in] width Requested width. The server is not guaranteed to return a surface of this width. * \param [in] height Requested height. The server is not guaranteed to return a surface of this height. * \param [in] format Pixel format for the surface. * \return A handle that can be passed to mir_surface_create() to complete construction. */ MirSurfaceSpec* mir_connection_create_spec_for_input_method(MirConnection* connection, int width, int height, MirPixelFormat format); /** * Request changes to the specification of a surface. The server will decide * whether and how the request can be honoured. * * \param [in] surface The surface to rename * \param [in] spec Spec with the requested changes applied */ void mir_surface_apply_spec(MirSurface* surface, MirSurfaceSpec* spec); /** * \brief Request an ID for the surface that can be shared cross-process and * across restarts. * * This call acquires a MirPersistentId for this MirSurface. This MirPersistentId * can be serialized to a string, stored or sent to another process, and then * later deserialized to refer to the same surface. * * \param [in] surface The surface to acquire a persistent reference to. * \param [in] callback Callback to invoke when the request completes. * \param [in,out] context User data passed to completion callback. * \return A MirWaitHandle that can be used in mir_wait_for to await completion. */ MirWaitHandle* mir_surface_request_persistent_id(MirSurface* surface, mir_surface_id_callback callback, void* context); /** * \brief Request a persistent ID for a surface and wait for the result * \param [in] surface The surface to acquire a persistent ID for. * \return A MirPersistentId. This MirPersistentId is owned by the calling code, and must * be freed with a call to mir_persistent_id_release() */ MirPersistentId* mir_surface_request_persistent_id_sync(MirSurface *surface); /** * \brief Check the validity of a MirPersistentId * \param [in] id The MirPersistentId * \return True iff the MirPersistentId contains a valid ID value. * * \note This does not guarantee that the ID refers to a currently valid object. */ bool mir_persistent_id_is_valid(MirPersistentId* id); /** * \brief Free a MirPersistentId * \param [in] id The MirPersistentId to free * \note This frees only the client-side representation; it has no effect on the * object referred to by \arg id. */ void mir_persistent_id_release(MirPersistentId* id); /** * \brief Get a string representation of a MirSurfaceId * \param [in] id The ID to serialise * \return A string representation of id. This string is owned by the MirSurfaceId, * and must not be freed by the caller. * * \see mir_surface_id_from_string */ char const* mir_persistent_id_as_string(MirPersistentId* id); /** * \brief Deserialise a string representation of a MirSurfaceId * \param [in] string_representation Serialised representation of the ID * \return The deserialised MirSurfaceId */ MirPersistentId* mir_persistent_id_from_string(char const* string_representation); /** * Attempts to raise the surface to the front. * * \param [in] surface The surface to raise * \param [in] cookie A cookie instance obtained from an input event. * An invalid cookie will terminate the client connection. */ void mir_surface_raise(MirSurface* surface, MirCookie const* cookie); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_MIR_SURFACE_H_ */ ./include/client/mir_toolkit/mir_display_configuration.h0000644000015600001650000003252012676616157023771 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TOOLKIT_MIR_DISPLAY_CONFIGURATION_H_ #define MIR_TOOLKIT_MIR_DISPLAY_CONFIGURATION_H_ #include "client_types.h" #ifdef __cplusplus extern "C" { #endif /** * \addtogroup mir_toolkit * @{ */ /** * A descriptor for a display mode. * * A display mode contains all the information necessary to drive a display. It * includes resolution and refresh rate, but also pixel clock, vsync and hsync * timings, and so on. */ typedef struct MirOutputMode MirOutputMode; /** * Release resources associated with a MirDisplayConfig handle. * * \param [in] config The handle to release */ void mir_display_config_release(MirDisplayConfig* config); /** * Get the maximum possible number of simultaneously active outputs this system * supports. * * \note There may be restrictions on the configuration required to achieve this * many active outputs. Typically the achievable number of simultaneously active * outputs is lower than this number. * * \param [in] config The configuration to query * \returns The maximum number of simultaneously active outputs * supportable at this time. */ int mir_display_config_get_max_simultaneous_outputs( MirDisplayConfig const* config); /** * Get the number of outputs available in this display configuration. * * This returns the total number of outputs the system has. This includes both * enabled and disabled output connections, and is typically larger than the * value returned from mir_display_config_get_max_simultaneous_outputs(). * * Typically this will be constant over the lifetime of a client as devices * usually do not gain extra physical ports at runtime. However, hotpluggable * display devices exist and the number of virtual outputs may change at * runtime, so this should always be called to determine the number of outputs * to iterate over. * * \param [in] config The configuration to query * \returns The number of outputs available in this configuration. */ int mir_display_config_get_num_outputs(MirDisplayConfig const* config); /** * Get a read-only handle to the index 'th output of this configuration * * \note The MirOutput handle is only valid while config is valid. * \pre 0 <= index < mir_display_config_get_num_outputs(config) * \param [in] config The configuration to query * \param [in] index The index of the output to get * \returns A read-only handle to a MirOutput within config which is valid * until mir_display_config_release(config) is called. */ MirOutput const* mir_display_config_get_output(MirDisplayConfig const* config, size_t index); /** * Get the number of modes in the supported mode list of this output. * * The list of supported modes is retrieved from the hardware, possibly modified * by any applicable quirk tables, and may not be exhaustive. * * \param [in] output The MirOutput to query * \returns The number of modes in the supported mode list of output. */ int mir_output_get_num_modes(MirOutput const* output); /** * Get a handle for a mode descriptor from the list of supported modes. * * The list of supported modes is retrieved from the hardware, possibly modified * by any applicable quirk tables, and may not be exhaustive. * * \pre 0 <= index < mir_output_get_num_modes(output) * \note The handle remains valid as long as output is valid. * * \param [in] output The MirOutput to query * \param [in] index The index of the mode to retrieve. * \returns A handle for a description of the supported mode. This is valid * for as long as output is valid. The return value is never null. */ MirOutputMode const* mir_output_get_mode(MirOutput const* output, size_t index); /** * Get a handle to the output's preferred mode. * * This is provided by the output itself. For modern displays (LCD, OLED, etc) * it is typically a mode with the native resolution. * * An output may not have a preferred mode, in which case this call will return * NULL. * * \note The handle remains valid as long as output is valid. * * \param [in] output The MirOutput to query * \returns A handle for a description of the supported mode. This is valid * for as long as output is valid. If the output does not have a * preferred mode, it returns NULL. */ MirOutputMode const* mir_output_get_preferred_mode(MirOutput const* output); /** * Get a handle to the output's current mode. * * An output may not have a current mode (for example, if it is disabled), in * which case this call will return NULL. * * \note The handle remains valid as long as output is valid. * * \param [in] output The MirOutput to query * \returns A handle for a description of the supported mode. This is valid * for as long as output is valid. If the output does not have a * current mode, it returns NULL. */ MirOutputMode const* mir_output_get_current_mode(MirOutput const* output); /** * Get the number of pixel formats supported by this output * * \param [in] output The MirOutput to query * \returns The number of pixel formats for output. */ int mir_output_get_num_pixel_formats(MirOutput const* output); /** * Get a pixel format from the list of supported formats * * \pre 0 <= index < mir_output_get_num_pixel_formats(output) * * \param [in] output The MirOutput to query * \param [in] index The index of the format to query * \returns The index 'th supported pixel format. */ MirPixelFormat mir_output_get_pixel_format(MirOutput const* output, size_t index); /** * Get the current pixel format. * * \param [in] output The MirOutput to query * \returns The current pixel format. This may be mir_pixel_format_invalid * (for example, if the output is not currently enabled). */ MirPixelFormat mir_output_get_current_pixel_format(MirOutput const* output); /** * Set the output format * * \param [in] output The MirOutput to modify * \param [in] format The MirPixelFormat to set */ void mir_output_set_pixel_format(MirOutput* output, MirPixelFormat format); /** * Get the ID of an output * * This can be used to refer to the output in other parts of the API, such as * mir_surface_spec_set_fullscreen_on_output(). * * \param [in] output The MirOutput to query. * \returns The ID of output, which may be used to refer to it in other * parts of the API. */ int mir_output_get_id(MirOutput const* output); /** * Get the physical connection type of an output. * * This is a best-effort determination, and may be incorrect. * * \param [in] output The MirOutput to query * \returns The type of the display connection, or mir_output_type_unknown * if it cannot be determined. */ MirOutputType mir_output_get_type(MirOutput const* output); /** * Get the x coordinate of the top-left point of the output in the virtual * display space. * * Outputs can be thought of as viewports into a virtual display space. They may * freely overlap, coincide, or be disjoint as desired. * * Output orientation changes the orientation of the output rectangle in virtual * display space, but does not change its top-left corner. * * \param [in] output The MirOutput to query * \returns The x coordinate of the top-left point of the output in the * virtual display space. */ int mir_output_get_position_x(MirOutput const* output); /** * Get the y coordinate of the top-left point of the output in the virtual * display space. * * Outputs can be thought of as viewports into a virtual display space. They may * freely overlap, coincide, or be disjoint as desired. * * Output orientation changes the orientation of the output rectangle in virtual * display space, but does not change its top-left corner. * * \param [in] output The MirOutput to query * \returns The y coordinate of the top-left point of the output in the * virtual display space. */ int mir_output_get_position_y(MirOutput const* output); /** * Get whether there is a display physically connected to the output. * * This gives a best-effort determination of whether or not enabling this output * will result in an image being displayed to the user. * * The accuracy of this determination varies with connection type - for example, * for DisplayPort and HDMI connections a return value of * mir_output_connection_state_connected is usually a reliable indicator that * there is a powered-on display connected. * * VGA and DVI connectors can usually determine whether or not there is a * physically connected display, but cannot distinguish between a powered or * unpowered display. * * It is not always possible to determine whether or not there is a display * connected; in such cases mir_output_connection_state_unknown is returned. * * \param [in] output The MirOutput to query * \returns Whether there is a display connected to this output. */ MirOutputConnectionState mir_output_get_connection_state( MirOutput const* output); /** * Get whether this output is enabled in the current configuration. * * \param [in] output the MirOutput to query * \returns Whether the output is enabled. */ bool mir_output_is_enabled(MirOutput const* output); /** * Get the physical width of the connected display, in millimetres. * * A best-effort report of the physical width of the display connected to this * output. This is retrieved from the display hardware, possibly modified by any * applicable quirk tables. * * Where this information is unavailable or inapplicable (for example, * projectors), 0 is returned. * * \param [in] output the MirOutput to query * \returns Physical width of the connected display, in mm. */ int mir_output_get_physical_width_mm(MirOutput const* output); /** * Get the physical height of the connected display, in millimetres. * * A best-effort report of the physical height of the display connected to this * output. This is retrieved from the display hardware, possibly modified by any * applicable quirk tables. * * Where this information is unavailable or inapplicable (for example, * projectors), 0 is returned. * * \param [in] output the MirOutput to query * \returns Physical height of the connected display, in mm. */ int mir_output_get_physical_height_mm(MirOutput const* output); /** * Get the power state of a connected display. * * It is undefined which power state is returned for an output which is not * connected. * * \param [in] output The MirOutput to query * \returns The power state of the display connected to output. */ MirPowerMode mir_output_get_power_mode(MirOutput const* output); /** * Get the orientation of a display. * * \param [in] output The MirOutput to query * \returns The orientation of output */ MirOrientation mir_output_get_orientation(MirOutput const* output); /** * Get the scale-factor of a display * * The scale-factor specifies the conversion between logical pixels and physical pixels on this output. * * A surface with dimensions 200×100 on an output with scale-factor 2.0 will display 400x200 pixels * on this output, will display 300x150 pixels on an output with scale-factor 1.5, and so on. * * Where this calculation would result in a fractional number of pixels the floor is used, so a surface with * dimensions 101x100 on an output with scale-factor of 1.5 will display 151x150 pixels, not 152x150. * * \param [in] output The MirOutput to query * \returns The scale-factor of this monitor */ float mir_output_get_scale_factor(MirOutput const* output); /** * Get the form-factor of a connected output. * * This call succeeds even if the output is not connected, but may return nonsense values. * * \param [in] output The MirOutput to query * \returns The form factor of this output */ MirFormFactor mir_output_get_form_factor(MirOutput const* output); /** * Get the width, in pixels, of a MirOutputMode * * \note This is unaffected by the orientation of the output * * \param [in] mode The MirOutputMode to query * \returns The width, in pixels, of mode. */ int mir_output_mode_get_width(MirOutputMode const* mode); /** Get the height, in pixels, of a MirOutputMode * * \note This is unaffected by the orientation of the output * * \param [in] mode The MirOutputMode to query * \returns The height, in pixels, of mode. */ int mir_output_mode_get_height(MirOutputMode const* mode); /** Get the refresh rate, in Hz, of a MirOutputMode * * \param [in] mode The MirOutputMode to query * \returns The refresh rate, in Hz, of mode */ double mir_output_mode_get_refresh_rate(MirOutputMode const* mode); /**@}*/ #ifdef __cplusplus } #endif #endif //MIR_TOOLKIT_MIR_DISPLAY_CONFIGURATION_H_ ./include/client/mir_toolkit/events/0000755000015600001650000000000012676616160017651 5ustar jenkinsjenkins./include/client/mir_toolkit/events/orientation_event.h0000644000015600001650000000234112676616157023564 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_EVENTS_ORIENTATION_EVENT_H_ #define MIR_TOOLKIT_EVENTS_ORIENTATION_EVENT_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Retrieve the new orientation reported by this MirOrientationEvent * * \param[in] ev The orientation event * \return The new orientation */ MirOrientation mir_orientation_event_get_direction(MirOrientationEvent const* ev); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_ORIENTATION_EVENT_H_ */ ./include/client/mir_toolkit/events/input_configuration_event.h0000644000015600001650000000502212676616157025316 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_EVENTS_INPUT_CONFIGURATION_EVENT_H_ #define MIR_TOOLKIT_EVENTS_INPUT_CONFIGURATION_EVENT_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /// MirInputConfigurationEvent indicates a configuration change in the input device subsystem. Eventually /// it's usage will be required to properly interpret MirInputEvent, for example: /// If we receive a button down, and then a device reset, we should not expect /// to receive the button up. /// /// Another example, the maximum/minimum axis values for a device may have been reconfigured and /// need to be required. /// /// Of course as things stand there is no client input-device introspection API so these events /// are difficult to use. typedef enum { mir_input_configuration_action_configuration_changed, mir_input_configuration_action_device_reset } MirInputConfigurationAction; /** * Retrieve the input configuration action which occurred. * * \param[in] ev The input configuration event * \return The action */ MirInputConfigurationAction mir_input_configuration_event_get_action(MirInputConfigurationEvent const* ev); /** * Retreive the time associated with a MirInputConfiguration event * \param[in] ev The input configuration event * \return The time in nanoseconds since epoch */ int64_t mir_input_configuration_event_get_time(MirInputConfigurationEvent const* ev); /** * Retreive the device id associated with a MirInputConfiguration event * \param[in] ev The input configuration event * \return The device id or -1 if not applicable to events of this action */ MirInputDeviceId mir_input_configuration_event_get_device_id(MirInputConfigurationEvent const* ev); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_INPUT_CONFIGURATION_EVENT_H_ */ ./include/client/mir_toolkit/events/prompt_session_event.h0000644000015600001650000000240512676616157024316 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_EVENTS_PROMPT_SESSION_EVENT_H_ #define MIR_TOOLKIT_EVENTS_PROMPT_SESSION_EVENT_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Retrieve the new prompt session state reported by a given MirPromptSessionEvent * * \param [in] event The prompt session event * \return The new state */ MirPromptSessionState mir_prompt_session_event_get_state(MirPromptSessionEvent const* ev); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_PROMPT_SESSION_EVENT_H_ */ ./include/client/mir_toolkit/events/surface_output_event.h0000644000015600001650000000426612676616125024304 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TOOLKIT_SURFACE_OUTPUT_EVENT_H_ #define MIR_TOOLKIT_SURFACE_OUTPUT_EVENT_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Retrieve the DPI of the new output configuration of a MirSurfaceOutputEvent * * \param [in] ev The event * \return The new DPI value for the surface is primarily on. */ int mir_surface_output_event_get_dpi(MirSurfaceOutputEvent const* ev); /** * Retrieve the form factor of the new output configuration of a MirSurfaceOutputEvent * * \param [in] ev The event * \return The new form factor of the output the surface is primarily on. */ MirFormFactor mir_surface_output_event_get_form_factor(MirSurfaceOutputEvent const* ev); /** * Retrieve the form factor of the new output configuration of a MirSurfaceOutputEvent * * \param [in] ev The event * \return The new form factor of the output the surface is primarily on. */ float mir_surface_output_event_get_scale(MirSurfaceOutputEvent const* ev); /** * Retrieve the ID of the output this surface is on from a MirSurfaceOutputEvent * * \param [in] ev The event * \return The ID of the output the surface is currently considered to be on. * (From MirDisplayOutput::output_id) */ uint32_t mir_surface_output_event_get_output_id(MirSurfaceOutputEvent const *ev); #ifdef __cplusplus } /**@}*/ #endif #endif //MIR_TOOLKIT_SURFACE_OUTPUT_EVENT_H_ ./include/client/mir_toolkit/events/keymap_event.h0000644000015600001650000000417312676616157022524 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_EVENTS_KEYMAP_EVENT_H_ #define MIR_TOOLKIT_EVENTS_KEYMAP_EVENT_H_ #include #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Retrieve the new keymap reported by this MirKeymapEvent * * \deprecated keymap credentials are no longer available use * mir_keymap_event_get_keymap_buffer instead. * * \param[in] ev The keymap event * \param[out] rules XKB rules describing the new keymap. */ void mir_keymap_event_get_rules(MirKeymapEvent const* ev, struct xkb_rule_names* names) __attribute__ ((deprecated)); /** * Retrieve the new keymap reported by this MirKeymapEvent * * The keymap buffer is only valid while the MirKeymapEvent is valid. * The buffer can be used via xkb_keymap_new_from_buffer * \param[in] ev The keymap event * \param[out] buffer the xkbcommon keymap * \param[out] length of the buffer */ void mir_keymap_event_get_keymap_buffer(MirKeymapEvent const* ev, char const** buffer, size_t *length); /** * Retrieve the device id the keymap reported by this MirKeymapEvent applies to * * \param[in] ev The keymap event * \param[out] rules XKB rules describing the new keymap. */ MirInputDeviceId mir_keymap_event_get_device_id(MirKeymapEvent const* ev); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_KEYMAP_EVENT_H_ */ ./include/client/mir_toolkit/events/input/0000755000015600001650000000000012676616160021010 5ustar jenkinsjenkins./include/client/mir_toolkit/events/input/pointer_event.h0000644000015600001650000001053712676616157024056 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_POINTER_EVENT_H_ #define MIR_TOOLKIT_POINTER_EVENT_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * An event type describing a change in pointer device state. */ typedef struct MirPointerEvent MirPointerEvent; /** * Possible pointer actions */ typedef enum { /* A pointer button has come up */ mir_pointer_action_button_up = 0, /* A pointer button has gone down */ mir_pointer_action_button_down = 1, /* The pointer has entered the surface to which this event was delivered */ mir_pointer_action_enter = 2, /* The pointer has left the surface to which this event was delivered */ mir_pointer_action_leave = 3, /* Axis values have changed for the pointer */ mir_pointer_action_motion = 4 } MirPointerAction; /** * Identifiers for pointer axis */ typedef enum { /* Absolute axis containing the x coordinate of the pointer */ mir_pointer_axis_x = 0, /* Absolute axis containing the y coordinate of the pointer */ mir_pointer_axis_y = 1, /* Relative axis containing ticks reported by the vertical scroll wheel */ mir_pointer_axis_vscroll = 2, /* Relative axis containing ticks reported by the horizontal scroll wheel */ mir_pointer_axis_hscroll = 3, /* Relative axis containing the last reported x differential from the pointer */ mir_pointer_axis_relative_x = 4, /* Relative axis containing the last reported y differential from the pointer */ mir_pointer_axis_relative_y = 5 } MirPointerAxis; /* * Identifiers for pointer buttons */ typedef enum { mir_pointer_button_primary = 1 << 0, mir_pointer_button_secondary = 1 << 1, mir_pointer_button_tertiary = 1 << 2, mir_pointer_button_back = 1 << 3, mir_pointer_button_forward = 1 << 4, mir_pointer_button_side = 1 << 5, mir_pointer_button_extra = 1 << 6, mir_pointer_button_task = 1 << 7 } MirPointerButton; typedef unsigned int MirPointerButtons; /** * Retrieve the modifier keys pressed when the pointer action occured. * * \param [in] event The pointer event * \return The modifier mask */ MirInputEventModifiers mir_pointer_event_modifiers(MirPointerEvent const* event); /** * Retrieve the action which occured to generate a given pointer event. * * \param [in] event The pointer event * \return Action performed by the pointer */ MirPointerAction mir_pointer_event_action(MirPointerEvent const* event); /** * Retrieve the state of a given pointer button when the action occurred. * * \param [in] event The pointer event * \param [in] button The button to check * * \return Whether the given button is depressed */ bool mir_pointer_event_button_state(MirPointerEvent const* event, MirPointerButton button); /** * Retreive the pointer button state as a masked set of values. * * \param [in] event The pointer event * * \return The button state */ MirPointerButtons mir_pointer_event_buttons(MirPointerEvent const* event); /** * Retrieve the axis value reported by a given pointer event. * * \param [in] event The pointer event * \param [in] axis The axis to retreive a value from * \return The value of the given axis */ float mir_pointer_event_axis_value(MirPointerEvent const* event, MirPointerAxis axis); /** * Retrieve the corresponding input event. * * \param [in] event The pointer event * \return The input event */ MirInputEvent const* mir_pointer_event_input_event(MirPointerEvent const* event); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_POINTER_EVENT_H_ */ ./include/client/mir_toolkit/events/input/keyboard_event.h0000644000015600001650000000570112676616157024173 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_KEYBOARD_EVENT_H_ #define MIR_TOOLKIT_KEYBOARD_EVENT_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * An event type describing a change in keyboard state * * Apology #1: Keyboard events almost always come from a keyboard, except they * can also come from system buttons (power, volume, home). This is an issue * we've inherited from the Linux kernel and Android, but could be solved in * future by giving such system switch events their own input group such as * MirSwitchEvent. */ typedef struct MirKeyboardEvent MirKeyboardEvent; /** * Possible actions for changing key state */ typedef enum { /* A key has come up (released) */ mir_keyboard_action_up, /* A key has gone down (pressed) */ mir_keyboard_action_down, /* System policy has triggered a key repeat on a key which was already down */ mir_keyboard_action_repeat } MirKeyboardAction; /** * Retrieve the action which triggered a given key event. * * \param [in] event The key event * \return The associated action */ MirKeyboardAction mir_keyboard_event_action(MirKeyboardEvent const* event); /** * Retrieve the xkb mapped keycode associated with the key acted on.. May * be interpreted as per * * \param [in] event The key event * \return The xkb_keysym */ xkb_keysym_t mir_keyboard_event_key_code(MirKeyboardEvent const* event); /** * Retrieve the raw hardware scan code associated with the key acted on. May * be interpreted as per * * \param [in] event The key event * \return The scancode */ int mir_keyboard_event_scan_code(MirKeyboardEvent const* event); /** * Retrieve the modifier keys pressed when the key action occured. * * \param [in] event The key event * \return The modifier mask */ MirInputEventModifiers mir_keyboard_event_modifiers(MirKeyboardEvent const* event); /** * Retrieve the corresponding input event. * * \param [in] event The keyboard event * \return The input event */ MirInputEvent const* mir_keyboard_event_input_event(MirKeyboardEvent const* event); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_KEYBOARD_EVENT_H_ */ ./include/client/mir_toolkit/events/input/touch_event.h0000644000015600001650000001134412676616157023515 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_TOUCH_EVENT_H_ #define MIR_TOOLKIT_TOUCH_EVENT_H_ #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * An event type describing a change in touch device state. */ typedef struct MirTouchEvent MirTouchEvent; /** * An identifier for a touch-point. TouchId's are unique per-gesture. * That is to say, once a touch has gone down at time T, no other touch will * use that touch's ID until all touches at time T have come up. */ typedef int32_t MirTouchId; /** * Possible per touch actions for state changing */ typedef enum { /* This touch point is going up */ mir_touch_action_up = 0, /* This touch point is going down */ mir_touch_action_down = 1, /* Axis values have changed on this touch point */ mir_touch_action_change = 2 } MirTouchAction; /** * Identifiers for touch axis */ typedef enum { /* Axis representing the x coordinate for the touch */ mir_touch_axis_x = 0, /* Axis representing the y coordinate for the touch */ mir_touch_axis_y = 1, /* Axis representing pressure of the touch */ mir_touch_axis_pressure = 2, /* Axis representing the length of the major axis of an ellipse centered at the touch point */ mir_touch_axis_touch_major = 3, /* Axis representing the length of the minor axis of an ellipse centered at the touch point */ mir_touch_axis_touch_minor = 4, /* Axis representing the diameter of a circle centered on the touch point */ mir_touch_axis_size = 5 } MirTouchAxis; /** * Identifiers for per-touch tool types */ typedef enum { // Tool type could not be determined mir_touch_tooltype_unknown = 0, // Touch is made with a finger mir_touch_tooltype_finger = 1, // Touch is made with a stylus mir_touch_tooltype_stylus = 2 } MirTouchTooltype; /** * Retrieve the modifier keys pressed when the touch action occured. * * \param [in] event The key event * \return The modifier mask */ MirInputEventModifiers mir_touch_event_modifiers(MirTouchEvent const* event); /** * Retrieve the number of touches reported for a given touch event. Each touch * is said to be index in the event and may be accessed by index 0, 1, ... , (touch_count - 1) * * \param [in] event The touch event * \return The number of touches */ unsigned int mir_touch_event_point_count(MirTouchEvent const* event); /** * Retrieve the TouchID for a touch at given index. * * \param [in] event The touch event * \param [in] touch_index The touch index. Must be less than (touch_count - 1). * \return ID of the touch at index */ MirTouchId mir_touch_event_id(MirTouchEvent const* event, size_t touch_index); /** * Retrieve the action which occured for a touch at given index. * * \param [in] event The touch event * \param [in] touch_index The touch index. Must be less than (touch_count - 1). * \return Action performed for the touch at index. */ MirTouchAction mir_touch_event_action(MirTouchEvent const* event, size_t touch_index); /** * Retrieve the tooltype for touch at given index. * * \param [in] event The touch event * \param [in] touch_index The touch index. Must be less than (touch_count - 1). * \return Tooltype used for the touch at index */ MirTouchTooltype mir_touch_event_tooltype(MirTouchEvent const* event, size_t touch_index); /** * Retrieve the axis value for a given axis on an indexed touch. * * \param [in] event The touch event * \param [in] touch_index The touch index. Must be less than (touch_count - 1). * \param [in] axis The axis to retreive a value from * \return The value of the given axis */ float mir_touch_event_axis_value(MirTouchEvent const* event, size_t touch_index, MirTouchAxis axis); /** * Retrieve the corresponding input event. * * \param [in] event The touch event * \return The input event */ MirInputEvent const* mir_touch_event_input_event(MirTouchEvent const* event); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_TOUCH_EVENT_H_ */ ./include/client/mir_toolkit/events/input/input_event.h0000644000015600001650000001120212676616157023523 0ustar jenkinsjenkins/* * Copyright © 2014-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_INPUT_EVENT_H_ #define MIR_TOOLKIT_INPUT_EVENT_H_ #include "mir_toolkit/events/event.h" #include #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif typedef int64_t MirInputDeviceId; typedef enum { mir_input_event_type_key = 0, mir_input_event_type_touch = 1, mir_input_event_type_pointer = 2 } MirInputEventType; /** * Description of key modifier state. */ typedef enum { mir_input_event_modifier_none = 1 << 0, mir_input_event_modifier_alt = 1 << 1, mir_input_event_modifier_alt_left = 1 << 2, mir_input_event_modifier_alt_right = 1 << 3, mir_input_event_modifier_shift = 1 << 4, mir_input_event_modifier_shift_left = 1 << 5, mir_input_event_modifier_shift_right = 1 << 6, mir_input_event_modifier_sym = 1 << 7, mir_input_event_modifier_function = 1 << 8, mir_input_event_modifier_ctrl = 1 << 9, mir_input_event_modifier_ctrl_left = 1 << 10, mir_input_event_modifier_ctrl_right = 1 << 11, mir_input_event_modifier_meta = 1 << 12, mir_input_event_modifier_meta_left = 1 << 13, mir_input_event_modifier_meta_right = 1 << 14, mir_input_event_modifier_caps_lock = 1 << 15, mir_input_event_modifier_num_lock = 1 << 16, mir_input_event_modifier_scroll_lock = 1 << 17 } MirInputEventModifier; typedef unsigned int MirInputEventModifiers; #ifdef __cplusplus } /**@}*/ #endif #include "mir_toolkit/events/input/touch_event.h" #include "mir_toolkit/events/input/keyboard_event.h" #include "mir_toolkit/events/input/pointer_event.h" #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Retrieves the device id responsible for generating an input event. * * \param [in] event The input event * \return The id of the generating device */ MirInputDeviceId mir_input_event_get_device_id(MirInputEvent const* ev); /** * Retrieve the time at which an input event occurred. * * \param [in] event The input event * \return A timestamp in nanoseconds-since-epoch */ int64_t mir_input_event_get_event_time(MirInputEvent const* ev); /** * Retrieve the type of an input event. E.g. key, touch... * * \param [in] event The input event * \return The input event type */ MirInputEventType mir_input_event_get_type(MirInputEvent const* ev); /** * Retrieve the MirKeyboardEvent associated with a given input event. * * \param[in] event The input event * \return The MirKeyboardEvent or NULL if event type is not * mir_input_event_type_key */ MirKeyboardEvent const* mir_input_event_get_keyboard_event(MirInputEvent const* ev); /** * Retrieve the MirTouchEvent associated with a given input event. * * \param[in] event The input event * \return The MirTouchEvent or NULL if event type is not * mir_input_event_type_touch */ MirTouchEvent const* mir_input_event_get_touch_event(MirInputEvent const* ev); /** * Retrieve the MirPointerEvent associated with a given input event. * * \param[in] event The input event * \return The MirPointerEvent or NULL if event type is not * mir_input_event_type_pointer */ MirPointerEvent const* mir_input_event_get_pointer_event(MirInputEvent const* ev); /** * Query if an input event contains a cookie * * \params[in] ev The input event * \return True if the input event contains a cookie */ bool mir_input_event_has_cookie(MirInputEvent const* ev); /** * Returns the cookie associated with an input event. * * \pre The input event must have a MirCookie * \params[in] ev An input event * \return The cookie associated with the given input event * The cookie must be released by calling mir_cookie_release */ MirCookie const* mir_input_event_get_cookie(MirInputEvent const* ev); #ifdef __cplusplus } /**@}*/ #endif #endif // MIR_TOOLKIT_INPUT_EVENT_H_ ./include/client/mir_toolkit/events/event.h0000644000015600001650000001676012676616157021163 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_EVENT_H_ #define MIR_TOOLKIT_EVENT_H_ #include #include #include "mir_toolkit/common.h" #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif typedef enum { mir_event_type_key, mir_event_type_motion, mir_event_type_surface, mir_event_type_resize, mir_event_type_prompt_session_state_change, mir_event_type_orientation, mir_event_type_close_surface, /* Type for new style input event will be returned from mir_event_get_type when old style event type was mir_event_type_key or mir_event_type_motion */ mir_event_type_input, mir_event_type_keymap, mir_event_type_input_configuration, mir_event_type_surface_output, } MirEventType; typedef struct MirSurfaceEvent MirSurfaceEvent; typedef struct MirResizeEvent MirResizeEvent; typedef struct MirPromptSessionEvent MirPromptSessionEvent; typedef struct MirOrientationEvent MirOrientationEvent; typedef struct MirCloseSurfaceEvent MirCloseSurfaceEvent; typedef struct MirInputEvent MirInputEvent; typedef struct MirKeymapEvent MirKeymapEvent; typedef struct MirInputConfigurationEvent MirInputConfigurationEvent; typedef struct MirSurfaceOutputEvent MirSurfaceOutputEvent; typedef struct MirCookie MirCookie; typedef union MirEvent MirEvent; #ifdef __cplusplus } /**@}*/ #endif #include "mir_toolkit/events/input/input_event.h" #include "mir_toolkit/events/resize_event.h" #include "mir_toolkit/events/surface_event.h" #include "mir_toolkit/events/orientation_event.h" #include "mir_toolkit/events/prompt_session_event.h" #include "mir_toolkit/events/keymap_event.h" #include "mir_toolkit/events/input_configuration_event.h" #include "mir_toolkit/events/surface_output_event.h" #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Retrieves the type of a MirEvent. Now preferred over direct access to ev->type. * In particular ev->type will never be mir_event_type_input and mir_event_get_type * is the only way to ensure mir_event_get_input_event will succeed. * * \param [in] event The event * \return The event type */ MirEventType mir_event_get_type(MirEvent const* ev); /** * Retrieve the MirInputEvent associated with a MirEvent of * type mir_event_type_input. See * for accessors. * * \param [in] event The event * \return The associated MirInputEvent */ MirInputEvent const* mir_event_get_input_event(MirEvent const* ev); /** * Retrieve the MirSurfaceEvent associated with a MirEvent of * type mir_event_type_surface. See * for accessors. * * \param [in] event The event * \return The associated MirSurfaceEvent */ MirSurfaceEvent const* mir_event_get_surface_event(MirEvent const* ev); /** * Retrieve the MirResizeEvent associated with a MirEvent of * type mir_event_type_resize. See * for accessors. * * \param [in] event The event * \return The associated MirResizeEvent */ MirResizeEvent const* mir_event_get_resize_event(MirEvent const* ev); /** * Retrieve the MirPromptSessionEvent associated with a MirEvent of * type mir_event_type_prompt_session_state_change. See * for accessors. * * \param [in] event The event * \return The associated MirPromptSessionEvent */ MirPromptSessionEvent const* mir_event_get_prompt_session_event(MirEvent const* ev); /** * Retrieve the MirOrientationEvent associated with a MirEvent of * type mir_event_type_orientation. See * for accessors. * * \param [in] event The event * \return The associated MirOrientationEvent */ MirOrientationEvent const* mir_event_get_orientation_event(MirEvent const* ev); /** * Retrieve the MirCloseSurfaceEvent associated with a MirEvent of * type mir_event_type_close_surface. The event is a request to close * the surface it is delivered to and has no accessors. * * \deprecated Use of this function is pointless as there is no way to use the * return value. * * \todo This should be removed from the public API at the next API break. * * \param [in] event The event * \return The associated MirCloseSurfaceEvent */ __attribute__ ((deprecated)) MirCloseSurfaceEvent const* mir_event_get_close_surface_event(MirEvent const* ev); /** * Retrieve the MirKeymapEvent associated with a MirEvent of * type mir_event_type_keymap. The event signifies that the keymap * applied for the relevant surface has changed. * * \param [in] event The event * \return The associated MirKeymapEvent */ MirKeymapEvent const* mir_event_get_keymap_event(MirEvent const* ev); /** * Retrieve the MirInputConfiguration associated with a MirEvent of * type mir_event_type_input_configuration. The event signifies that the * input device configuration has changed. * * \param [in] event The event * \return The associated MirInputConfigurationEvent */ MirInputConfigurationEvent const* mir_event_get_input_configuration_event(MirEvent const* ev); /** * Retrieve the MirSurfaceOutputEvent associated with a MirEvent of type * mir_event_type_surface_output. The event signifies that the properties * of the output the surface is displayed upon have changed. * * A MirSurfaceOutputEvent is generated either when the properties of the * output the surface is primarily on change (for example: by user configuration * of resolution) or when the output the surface is primarily on changes * (for example: when a user moves the surface from one monitor to another). * * \param [in] event The event * \return The associated MirSurfaceOutputEvent */ MirSurfaceOutputEvent const* mir_event_get_surface_output_event(MirEvent const* ev); /* * * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * _________________________ *< Don't use mir_event_ref > *------------------------- * \ ^__^ * \ (oo)\_______ * (__)\ )\/\ * ||----w | * || || * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * NOTICE: mir_event_ref and mir_event_unref are implemented in terms of copy until * such point whereas direct MirEvent access as deprecated. This means you must * use the return value as your new reference * */ /** * Reference this MirEvent and return a pointer to the * newly referenced instance * * \param[in] The event to reference * \return The event pointer to now use */ MirEvent const* mir_event_ref(MirEvent const* ev) __attribute__((warn_unused_result)); /** * Release a reference to a MirEvent. * * \param[in] The event to un-reference */ void mir_event_unref(MirEvent const* ev); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_EVENT_H_ */ ./include/client/mir_toolkit/events/resize_event.h0000644000015600001650000000257112676616157022537 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_EVENTS_RESIZE_EVENT_H_ #define MIR_TOOLKIT_EVENTS_RESIZE_EVENT_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Retrieve the new width reported by a given MirResizeEvent * * \param[in] ev The resize event * \return The reported width */ int mir_resize_event_get_width(MirResizeEvent const* ev); /** * Retrieve the new height reported by a given MirResizeEvent * * \param[in] ev The resize event * \return The reported height */ int mir_resize_event_get_height(MirResizeEvent const* ev); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_RESIZE_EVENT_H_ */ ./include/client/mir_toolkit/events/surface_event.h0000644000015600001650000000271212676616157022663 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_TOOLKIT_EVENTS_SURFACE_EVENT_H_ #define MIR_TOOLKIT_EVENTS_SURFACE_EVENT_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Retrieve the attribute index configured with a given MirSurfaceEvent * * \param [in] Event The event * \return The associated attribute */ MirSurfaceAttrib mir_surface_event_get_attribute(MirSurfaceEvent const* ev); /** * Retrieve the new value of the associated attribute for a given MirSurfaceEvent * * \param [in] Event The event * \return The associated attribute value */ int mir_surface_event_get_attribute_value(MirSurfaceEvent const* ev); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_SURFACE_EVENT_H_ */ ./include/client/mir_toolkit/mir_input_device.h0000644000015600001650000001443712676616157022062 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_TOOLKIT_MIR_INPUT_DEVICE_H_ #define MIR_TOOLKIT_MIR_INPUT_DEVICE_H_ #include "mir_toolkit/client_types.h" /** * \addtogroup mir_toolkit * @{ */ #ifdef __cplusplus extern "C" { #endif typedef enum MirPointerHandedness { mir_pointer_handedness_right = 0, mir_pointer_handedness_left = 1 } MirPointerHandedness; /** * MirPointerAcceleration describes the way pointer movement is filtered: * - mir_pointer_acceleration_none: (acceleration bias + 1.0) is applied as * a factor to the current velocity of the pointer. So a bias of 0 to results * to no change of velocity. * - mir_pointer_acceleration_adaptive: acceleration bias selects an * acceleration function based on the current velocity that usually consists * of two linear inclines separated by a plateau. */ typedef enum MirPointerAcceleration { mir_pointer_acceleration_none = 1, mir_pointer_acceleration_adaptive = 2 } MirPointerAcceleration; typedef enum MirTouchpadClickMode { mir_touchpad_click_mode_none = 0, mir_touchpad_click_mode_area_to_click = 1 << 0, mir_touchpad_click_mode_finger_count = 1 << 1 } MirTouchpadClickMode; typedef unsigned int MirTouchpadClickModes; typedef enum MirTouchpadScrollMode { mir_touchpad_scroll_mode_none = 0, mir_touchpad_scroll_mode_two_finger_scroll = 1 << 0, mir_touchpad_scroll_mode_edge_scroll = 1 << 1, mir_touchpad_scroll_mode_button_down_scroll = 1 << 2 } MirTouchpadScrollMode; typedef unsigned int MirTouchpadScrollModes; enum MirInputDeviceCapability { mir_input_device_capability_none = 0, mir_input_device_capability_pointer = 1<<1, mir_input_device_capability_keyboard = 1<<2, mir_input_device_capability_touchpad = 1<<3, mir_input_device_capability_touchscreen = 1<<4, mir_input_device_capability_gamepad = 1<<5, mir_input_device_capability_joystick = 1<<6, mir_input_device_capability_switch = 1<<7, mir_input_device_capability_multitouch = 1<<8, //! capable to detect multiple contacts mir_input_device_capability_alpha_numeric = 1<<9 //! offers enough keys for text entry }; typedef unsigned int MirInputDeviceCapabilities; /** * Retrieve the number of available input devices. * * \param [in] config The input configuration snapshot * * \return Number of input devices */ size_t mir_input_config_device_count(MirInputConfig const* config); /** * Retrieve the input device at given \a index. * * The pointer returned stays valid until mir_input_config_destroy * is called with \a config. * * \param [in] config The input configuration snapshot * \param [in] index The index of the input device to return. * \return input device */ MirInputDevice const* mir_input_config_get_device( MirInputConfig const* config, size_t index); /** * Retrieve the input device by \a id. * * The MirInputDevice returned stays valid until mir_input_config_destroy * is called with \a config. If no device with the given \a id is found * NULL will be returned. * * \param [in] config The input configuration snapshot * \param [in] id The input device id to search for * * \return input device */ MirInputDevice const* mir_input_config_get_device_by_id( MirInputConfig const* config, MirInputDeviceId id); /** * Retrieve the input device at given \a index. * * The pointer returned stays valid until mir_input_config_destroy * is called with \a config. * * \param [in] config The input configuration snapshot * \param [in] index The index of the input device to return. * \return input device */ MirInputDevice* mir_input_config_get_mutable_device( MirInputConfig* config, size_t index); /** * Retrieve the input device by \a id. * * The MirInputDevice returned stays valid until mir_input_config_destroy * is called with \a config. If no device with the given \a id is found * NULL will be returned. * * \param [in] config The input configuration snapshot * \param [in] id The input device id to search for * * \return input device */ MirInputDevice* mir_input_config_get_mutable_device_by_id( MirInputConfig* config, MirInputDeviceId id); /** * Retrieve the capabilities of the input device at the given index. * * \param [in] device The input device * * \return The capability flags of the input device */ MirInputDeviceCapabilities mir_input_device_get_capabilities( MirInputDevice const* device); /** * Retrieve the device id of the input device. * The device id is a unique integer value, only valid while the device is * attached. The device id matches the device id attached every input event. * * \param [in] device The input device * * \return The device id of the input device */ MirInputDeviceId mir_input_device_get_id(MirInputDevice const* device); /** * Retrieve the name of the input device. * The string pointed to will be valid as long as MirInputDevice is valid. * The name may be empty but never NULL. * * \param [in] device The input device * * \return The name of the input device */ char const* mir_input_device_get_name(MirInputDevice const* device); /** * Retrieve the unique id of the input device. * The string pointed to will be valid as long as \a device is valid. * The value of the unique id of a given device should be valid across mir * connections session and servers of the same version. * * \param [in] device The input device * * \return The unique id of the input device */ char const* mir_input_device_get_unique_id(MirInputDevice const* device); #ifdef __cplusplus } #endif #endif ./include/client/mir_toolkit/mir_connection.h0000644000015600001650000003142512676616157021537 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_TOOLKIT_MIR_CONNECTION_H_ #define MIR_TOOLKIT_MIR_CONNECTION_H_ #include #include #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Request a connection to the Mir server. The supplied callback is called when * the connection is established, or fails. The returned wait handle remains * valid until the connection has been released. * \warning callback could be called from another thread. You must do any * locking appropriate to protect your data accessed in the * callback. * \param [in] server File path of the server socket to connect to, or * NULL to choose the default server (can be set by * the $MIR_SOCKET environment variable) * \param [in] app_name A name referring to the application * \param [in] callback Callback function to be invoked when request * completes * \param [in,out] context User data passed to the callback function * \return A handle that can be passed to mir_wait_for */ MirWaitHandle *mir_connect( char const *server, char const *app_name, mir_connected_callback callback, void *context); /** * Perform a mir_connect() but also wait for and return the result. * \param [in] server File path of the server socket to connect to, or * NULL to choose the default server * \param [in] app_name A name referring to the application * \return The resulting MirConnection */ MirConnection *mir_connect_sync(char const *server, char const *app_name); /** * Test for a valid connection * \param [in] connection The connection * \return True if the supplied connection is valid, or * false otherwise. */ bool mir_connection_is_valid(MirConnection *connection); /** * Retrieve a text description of the last error. The returned string is owned * by the library and remains valid until the connection has been released. * \param [in] connection The connection * \return A text description of any error resulting in an * invalid connection, or the empty string "" if the * connection is valid. */ char const *mir_connection_get_error_message(MirConnection *connection); /** * Release a connection to the Mir server * \param [in] connection The connection */ void mir_connection_release(MirConnection *connection); /** * Query platform-specific data and/or file descriptors that are required to * initialize GL/EGL features. * \param [in] connection The connection * \param [out] platform_package Structure to be populated */ void mir_connection_get_platform(MirConnection *connection, MirPlatformPackage *platform_package); /** * Query graphics platform module. * * \note The char pointers in MirModuleProperties are owned by the connection and should not be * freed. They remain valid until the connection is released. * * \param [in] connection The connection * \param [out] properties Structure to be populated */ void mir_connection_get_graphics_module(MirConnection *connection, MirModuleProperties *properties); /** * Register a callback to be called when a Lifecycle state change occurs. * \param [in] connection The connection * \param [in] callback The function to be called when the state change occurs * \param [in,out] context User data passed to the callback function */ void mir_connection_set_lifecycle_event_callback(MirConnection* connection, mir_lifecycle_event_callback callback, void* context); /** * Register a callback for server ping events. * * The server may send ping requests to detect unresponsive applications. Clients should * process this with their regular event handling, and call mir_connection_pong() in response. * * The shell may treat a client which fails to pong in a timely fashion differently; a common * response is to overlay the surface with an unresponsive application message. * * A default implementation that immediately calls pong is provided; toolkits SHOULD override * this default implementation to more accurately reflect the state of their event processing * loop. * * \param [in] connection The connection * \param [in] callback The function to be called on ping events. * \param [in] context User data passed to the callback function */ void mir_connection_set_ping_event_callback(MirConnection* connection, mir_ping_event_callback callback, void* context); /** * Respond to a ping event * \param [in] connection The connection * \param [in] serial Serial from the ping event */ void mir_connection_pong(MirConnection* connection, int32_t serial); /** * Query the display * * \deprecated Use mir_connection_create_display_configuration() instead. * * \warning return value must be destroyed via mir_display_config_destroy() * \warning may return null if connection is invalid * \param [in] connection The connection * \return structure that describes the display configuration */ MirDisplayConfiguration* mir_connection_create_display_config(MirConnection *connection); /** * Query the display * * \pre mir_connection_is_valid(connection) == true * \warning return value must be destroyed via mir_display_config_release() * * \param [in] connection The connection * \return structure that describes the display configuration */ MirDisplayConfig* mir_connection_create_display_configuration(MirConnection* connection); /** * Register a callback to be called when the hardware display configuration changes * * Once a change has occurred, you can use mir_connection_create_display_configuration to see * the new configuration. * * \param [in] connection The connection * \param [in] callback The function to be called when a display change occurs * \param [in,out] context User data passed to the callback function */ void mir_connection_set_display_config_change_callback( MirConnection* connection, mir_display_config_callback callback, void* context); /** * Destroy the DisplayConfiguration resource acquired from mir_connection_create_display_config * \param [in] display_configuration The display_configuration information resource to be destroyed */ void mir_display_config_destroy(MirDisplayConfiguration* display_configuration); /** * Apply the display configuration * * The display configuration is applied to this connection only (per-connection * configuration) and is invalidated when a hardware change occurs. Clients should * register a callback with mir_connection_set_display_config_change_callback() * to get notified about hardware changes, so that the can apply a new configuration. * * \warning This request may be denied. Check that the request succeeded with mir_connection_get_error_message. * \param [in] connection The connection * \param [in] display_configuration The display_configuration to apply * \return A handle that can be passed to mir_wait_for */ MirWaitHandle* mir_connection_apply_display_config(MirConnection *connection, MirDisplayConfiguration* display_configuration); /** * Set the base display configuration * * The base display configuration is the configuration the server applies when * there is no active per-connection configuration. * * When the wait handle returned by this function becomes ready, clients can use * mir_connection_get_error_message() to check if an authorization error occurred. * Only authorization errors are guaranteed to return an error message for this * operation. * * A successful result (i.e. no error) does not guarantee that the base display * configuration has been changed to the desired value. Clients should register * a callback with mir_connection_set_display_config_change_callback() to monitor * actual base display configuration changes. * * \warning This request may be denied. Check that the request succeeded with mir_connection_get_error_message. * \param [in] connection The connection * \param [in] display_configuration The display_configuration to set as base * \return A handle that can be passed to mir_wait_for */ MirWaitHandle* mir_connection_set_base_display_config( MirConnection* connection, MirDisplayConfiguration const* display_configuration); /** * Get a display type that can be used for OpenGL ES 2.0 acceleration. * \param [in] connection The connection * \return An EGLNativeDisplayType that the client can use */ MirEGLNativeDisplayType mir_connection_get_egl_native_display(MirConnection *connection); /** * Get the exact MirPixelFormat to use in creating a surface for a chosen * EGLConfig. * \param [in] connection The connection * \param [in] egldisplay The EGLDisplay for the given config * \param [in] eglconfig The EGLConfig you have chosen to use * \return The MirPixelFormat to use in surface creation */ MirPixelFormat mir_connection_get_egl_pixel_format( MirConnection *connection, void *egldisplay, void *eglconfig); /** * Get the list of possible formats that a surface can be created with. * \param [in] connection The connection * \param [out] formats List of valid formats to create surfaces with * \param [in] formats_size size of formats list * \param [out] num_valid_formats number of valid formats returned in formats * * \note Users of EGL should call mir_connection_get_egl_pixel_format instead, * as it will take the guesswork out of choosing between similar pixel * formats. At the moment, this function returns a compatible list of * formats likely to work for either software or hardware rendering. * However it is not the full or accurate list and will be replaced in * future by a function that takes the intended MirBufferUsage into * account. */ void mir_connection_get_available_surface_formats( MirConnection* connection, MirPixelFormat* formats, unsigned const int format_size, unsigned int *num_valid_formats); /** * Perform a platform specific operation. * * The MirPlatformMessage used for the request needs to remain valid * until this operation finishes. * * \param [in] connection The connection * \param [in] request The message used for this operation * \param [in] callback The callback to call when the operation finishes * \param [in,out] context User data passed to the callback function * \return A handle that can be passed to mir_wait_for */ MirWaitHandle* mir_connection_platform_operation( MirConnection* connection, MirPlatformMessage const* request, mir_platform_operation_callback callback, void* context); /** * Create a snapshot of the attached input devices and device configurations. * \warning return value must be destroyed via mir_input_config_destroy() * \warning may return null if connection is invalid * \param [in] connection The connection * \return structure that describes the input configuration */ MirInputConfig* mir_connection_create_input_config(MirConnection *connection); /** * Release this snapshot of the input configuration. * This invalidates any pointers retrieved from this structure. * * \param [in] devices The input configuration */ void mir_input_config_destroy(MirInputConfig const* config); /** * Register a callback to be called when the input devices change. * * Once a change has occurred, you can use mir_connection_create_input_config * to get an updated snapshot of the input device configuration. * * \param [in] connection The connection * \param [in] callback The function to be called when a change occurs * \param [in,out] context User data passed to the callback function */ void mir_connection_set_input_config_change_callback( MirConnection* connection, mir_input_config_callback callback, void* context); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_MIR_CONNECTION_H_ */ ./include/client/mir_toolkit/mir_screencast.h0000644000015600001650000000342012676616125021517 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MIR_TOOLKIT_MIR_SCREENCAST_H_ #define MIR_TOOLKIT_MIR_SCREENCAST_H_ #include #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Create a screencast on the supplied connection. * * A screencast allows clients to read the contents of the screen. * * \warning This request may be denied. * \param [in] connection The connection * \param [in] parameters The screencast parameters * \return The resulting screencast */ MirScreencast *mir_connection_create_screencast_sync( MirConnection *connection, MirScreencastParameters *parameters); /** * Release the specified screencast. * \param [in] screencast The screencast to be released */ void mir_screencast_release_sync( MirScreencast *screencast); /** * Retrieve the MirBufferStream associated with a screencast * (to advance buffers, obtain EGLNativeWindowType, etc...) * * \param[in] screencast The screencast */ MirBufferStream* mir_screencast_get_buffer_stream(MirScreencast *screencast); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_MIR_SCREENCAST_H_ */ ./include/client/mir_toolkit/mir_cursor_configuration.h0000644000015600001650000000463012676616125023635 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef MIR_TOOLKIT_MIR_CURSOR_H_ #define MIR_TOOLKIT_MIR_CURSOR_H_ #include #include /** * Opaque structure containing cursor parameterization. Create with mir_cursor* family. * Used with mir_surface_configure_cursor. */ typedef struct MirCursorConfiguration MirCursorConfiguration; #ifdef __cplusplus /** * \addtogroup mir_toolkit * @{ */ extern "C" { #endif /** * Release resources assosciated with cursor parameters * \param [in] parameters The operand */ void mir_cursor_configuration_destroy(MirCursorConfiguration *parameters); /** * Returns a new MirCursorConfiguration representing a named cursor * from the system cursor theme. Symbolic cursor names, such as * mir_default_cursor_name and mir_caret_cursor_name are available * see (mir_toolkit/cursors.h). * as input. * \param [in] name The cursor name * \return A cursor parameters object which must be passed * to_mir_cursor_configuration_destroy */ MirCursorConfiguration *mir_cursor_configuration_from_name(char const* name); /** * Returns a new cursor configuration tied to a given buffer stream. * If the configuration is successfully applied buffers from the stream will be used * to fill the system cursor. * \param [in] name The buffer stream * \param [in] hotspot_x The x-coordinate to use as the cursor's hotspot. * \param [in] hotspot_y The y-coordinate to use as the cursor's hotspot. * \return A cursor parameters object which must be passed * to_mir_cursor_configuration_destroy */ MirCursorConfiguration *mir_cursor_configuration_from_buffer_stream(MirBufferStream const* stream, int hotspot_x, int hotspot_y); #ifdef __cplusplus } /**@}*/ #endif #endif /* MIR_TOOLKIT_MIR_CURSOR_H_ */ ./include/cookie/0000755000015600001650000000000012676616124014004 5ustar jenkinsjenkins./include/cookie/mir/0000755000015600001650000000000012676616124014573 5ustar jenkinsjenkins./include/cookie/mir/cookie/0000755000015600001650000000000012676616125016045 5ustar jenkinsjenkins./include/cookie/mir/cookie/cookie.h0000644000015600001650000000254312676616125017473 0ustar jenkinsjenkins/* * Copyright © 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Brandon Schaefer */ #ifndef MIR_COOKIE_COOKIE_H_ #define MIR_COOKIE_COOKIE_H_ #include #include namespace mir { namespace cookie { class Cookie { public: Cookie() = default; virtual ~Cookie() = default; Cookie(Cookie const& cookie) = delete; Cookie& operator=(Cookie const& cookie) = delete; /** * Returns the timestamp that the cookie is built with * * \return The timestamp */ virtual uint64_t timestamp() const = 0; /** * Converts the cookie into a stream of bytes. * * \return The stream of bytes formatted */ virtual std::vector serialize() const = 0; }; } } #endif // MIR_COOKIE_COOKIE_H_ ./include/cookie/mir/cookie/authority.h0000644000015600001650000000745112676616125020255 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Brandon Schaefer */ #ifndef MIR_COOKIE_AUTHORITY_H_ #define MIR_COOKIE_AUTHORITY_H_ #include #include #include #include "mir/cookie/cookie.h" namespace mir { namespace cookie { using Secret = std::vector; struct SecurityCheckError : std::runtime_error { SecurityCheckError(); }; /** * \brief A source of moderately-difficult-to-spoof cookies. * * The primary motivation for this is to provide event timestamps that clients find it difficult to spoof. * This is useful for focus grant and similar operations where shell behaviour should be dependent on * the timestamp of the client event that caused the request. * * Some spoofing protection is desirable; experience with X clients shows that they will go to some effort * to attempt to bypass focus stealing prevention. * */ class Authority { public: /** * Optimal size for the provided Secret. * * This is the maximum useful size of the secret key. Keys of greater size * will be reduced to this size internally, and keys of smaller size may be * internally extended to this size. */ static size_t optimal_secret_size(); /** * Construction function used to create an Authority. The secret size must be * no less then minimum_secret_size otherwise an exception will be thrown * * \param [in] secret A secret used to set the key for the hash function * \return An Authority */ static std::unique_ptr create_from(Secret const& secret); /** * Construction function used to create an Authority as well as a secret. * * \param [out] save_secret The secret that was created. * \return An Authority */ static std::unique_ptr create_saving(Secret& save_secret); /** * Construction function used to create an Authority and a secret which it keeps internally. * * \return An Authority */ static std::unique_ptr create(); Authority(Authority const& authority) = delete; Authority& operator=(Authority const& authority) = delete; virtual ~Authority() noexcept = default; /** * Creates a cookie from a timestamp. * * \param [in] timestamp A timestamp * \return A cookie instance */ virtual std::unique_ptr make_cookie(uint64_t const& timestamp) = 0; /** * Creates a cookie from a serialized representation * * \param [in] blob A blob of bytes representing a serialized cookie * \return A cookie instance */ virtual std::unique_ptr make_cookie(std::vector const& raw_cookie) = 0; /** * Absolute minimum size of secret key the Authority will accept. * * Code should be using optimum_secret_size(); this minimum size is provided * as a user convenience to guard against catastrophically bad initialisation. */ static unsigned const minimum_secret_size = 8; protected: Authority() = default; }; } } #endif // MIR_COOKIE_COOKIE_AUTHORITY_H_ ./src/0000755000015600001650000000000012676616160011677 5ustar jenkinsjenkins./src/gl/0000755000015600001650000000000012676616126012303 5ustar jenkinsjenkins./src/gl/texture.cpp0000644000015600001650000000300012676616125014477 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/gl/texture.h" namespace mgl = mir::gl; mgl::Texture::Texture() : id(generate_id()) { //If you need to tweak the TexParameters, best to do it within the class //so the object has a cogent idea of what the texture it represents is doing glBindTexture(GL_TEXTURE_2D, id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } void mgl::Texture::bind() const { glBindTexture(GL_TEXTURE_2D, id); } mgl::Texture::~Texture() { glDeleteTextures(1, &id); } GLuint mgl::Texture::generate_id() const { GLuint id; glGenTextures(1, &id); return id; } ./src/gl/program.cpp0000644000015600001650000000610212676616125014454 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/gl/program.h" #include #include namespace mgl = mir::gl; namespace { typedef void(*MirGLGetObjectInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *); typedef void(*MirGLGetObjectiv)(GLuint, GLenum, GLint *); void GetObjectLogAndThrow(MirGLGetObjectInfoLog getObjectInfoLog, MirGLGetObjectiv getObjectiv, std::string const & msg, GLuint object) { GLint object_log_length = 0; (*getObjectiv)(object, GL_INFO_LOG_LENGTH, &object_log_length); const GLuint object_log_buffer_length = object_log_length + 1; std::string object_info_log; object_info_log.resize(object_log_buffer_length); (*getObjectInfoLog)(object, object_log_length, NULL, const_cast(object_info_log.data())); std::string object_info_err(msg + "\n"); object_info_err += object_info_log; BOOST_THROW_EXCEPTION(std::runtime_error(object_info_err)); } } mgl::Shader::Shader(GLchar const* shader_src, GLuint type) : shader(glCreateShader(type)) { GLint param{0}; glShaderSource(shader, 1, &shader_src, 0); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, ¶m); if (param == GL_FALSE) { glDeleteShader(shader); GetObjectLogAndThrow(glGetShaderInfoLog, glGetShaderiv, "Failed to compile shader:", shader); } } mgl::Shader::~Shader() { glDeleteShader(shader); } mgl::Shader::operator GLuint() const { return shader; } mgl::SimpleProgram::SimpleProgram( GLchar const* vertex_shader_src, GLchar const* fragment_shader_src) : vertex_shader(vertex_shader_src, GL_VERTEX_SHADER), fragment_shader(fragment_shader_src, GL_FRAGMENT_SHADER), program(glCreateProgram()) { glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); glLinkProgram(program); GLint param = 0; glGetProgramiv(program, GL_LINK_STATUS, ¶m); if (param == GL_FALSE) { glDeleteProgram(program); GetObjectLogAndThrow(glGetProgramInfoLog, glGetProgramiv, "Failed to link program:", program); } } mgl::SimpleProgram::~SimpleProgram() { glDeleteProgram(program); } mgl::SimpleProgram::operator GLuint() const { return program; } ./src/gl/recently_used_cache.h0000644000015600001650000000324512676616125016447 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt * Kevin DuBois */ #ifndef MIR_GL_RECENTLY_USED_CACHE_H_ #define MIR_GL_RECENTLY_USED_CACHE_H_ #include "mir/gl/texture_cache.h" #include "mir/gl/texture.h" #include "mir/graphics/buffer_id.h" #include "mir/graphics/renderable.h" #include namespace mir { namespace graphics { class Buffer; } namespace gl { class RecentlyUsedCache : public TextureCache { public: std::shared_ptr load(graphics::Renderable const& renderable) override; void invalidate() override; void drop_unused() override; private: struct Entry { Entry() : texture(std::make_shared()) {} std::shared_ptr texture; graphics::BufferID last_bound_buffer; bool used{true}; bool valid_binding{false}; std::shared_ptr resource; }; std::unordered_map textures; }; } } #endif /* MIR_GL_RECENTLY_USED_CACHE_H_ */ ./src/gl/recently_used_cache.cpp0000644000015600001650000000440112676616125016775 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt * Kevin DuBois */ #include "recently_used_cache.h" #include "mir/graphics/buffer.h" #include "mir/renderer/gl/texture_source.h" #include #include namespace mg = mir::graphics; namespace mgl = mir::gl; namespace geom = mir::geometry; namespace mrgl = mir::renderer::gl; std::shared_ptr mgl::RecentlyUsedCache::load(mg::Renderable const& renderable) { auto const& buffer = renderable.buffer(); auto buffer_id = buffer->id(); auto& texture = textures[renderable.id()]; texture.texture->bind(); auto const texture_source = dynamic_cast(buffer->native_buffer_base()); if (!texture_source) BOOST_THROW_EXCEPTION(std::logic_error("Buffer does not support GL rendering")); if ((texture.last_bound_buffer != buffer_id) || (!texture.valid_binding)) { texture_source->bind(); texture.resource = buffer; texture.last_bound_buffer = buffer_id; } texture_source->secure_for_render(); texture.valid_binding = true; texture.used = true; return texture.texture; } void mgl::RecentlyUsedCache::invalidate() { for (auto &t : textures) t.second.valid_binding = false; } void mgl::RecentlyUsedCache::drop_unused() { auto t = textures.begin(); while (t != textures.end()) { auto& tex = t->second; tex.resource.reset(); if (tex.used) { tex.used = false; ++t; } else { t = textures.erase(t); } } } ./src/gl/CMakeLists.txt0000644000015600001650000000044312676616125015043 0ustar jenkinsjenkinsinclude_directories( ${PROJECT_SOURCE_DIR}/include/platform ${PROJECT_SOURCE_DIR}/src/include/gl ${PROJECT_SOURCE_DIR}/include/renderers/gl ) ADD_LIBRARY( mirgl OBJECT default_program_factory.cpp program.cpp recently_used_cache.cpp tessellation_helpers.cpp texture.cpp ) ./src/gl/default_program_factory.cpp0000644000015600001650000000242212676616125017710 0ustar jenkinsjenkins /* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/gl/default_program_factory.h" #include "mir/gl/program.h" #include "recently_used_cache.h" namespace mgl = mir::gl; std::unique_ptr mgl::DefaultProgramFactory::create_gl_program( std::string const& vertex_shader, std::string const& fragment_shader) const { std::lock_guard lock(mutex); return std::make_unique( vertex_shader.c_str(), fragment_shader.c_str()); } std::unique_ptr mgl::DefaultProgramFactory::create_texture_cache() const { return std::make_unique(); } ./src/gl/tessellation_helpers.cpp0000644000015600001650000000410512676616125017236 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt * Kevin DuBois */ #include "mir/gl/tessellation_helpers.h" #include "mir/graphics/renderable.h" #include "mir/graphics/buffer.h" namespace mg = mir::graphics; namespace mgl = mir::gl; namespace geom = mir::geometry; mgl::Primitive mgl::tessellate_renderable_into_rectangle( mg::Renderable const& renderable, geom::Displacement const& offset) { auto const& buf_size = renderable.buffer()->size(); auto rect = renderable.screen_position(); rect.top_left = rect.top_left - offset; GLfloat left = rect.top_left.x.as_int(); GLfloat right = left + rect.size.width.as_int(); GLfloat top = rect.top_left.y.as_int(); GLfloat bottom = top + rect.size.height.as_int(); mgl::Primitive rectangle; rectangle.tex_id = 0; rectangle.type = GL_TRIANGLE_STRIP; GLfloat tex_right = static_cast(rect.size.width.as_int()) / buf_size.width.as_int(); GLfloat tex_bottom = static_cast(rect.size.height.as_int()) / buf_size.height.as_int(); auto& vertices = rectangle.vertices; vertices[0] = {{left, top, 0.0f}, {0.0f, 0.0f}}; vertices[1] = {{left, bottom, 0.0f}, {0.0f, tex_bottom}}; vertices[2] = {{right, top, 0.0f}, {tex_right, 0.0f}}; vertices[3] = {{right, bottom, 0.0f}, {tex_right, tex_bottom}}; return rectangle; } ./src/server/0000755000015600001650000000000012676616160013205 5ustar jenkinsjenkins./src/server/server.cpp0000644000015600001650000004517712676616125015236 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/server.h" #include "mir/emergency_cleanup.h" #include "mir/fd.h" #include "mir/frontend/connector.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/display_buffer.h" #include "mir/options/default_configuration.h" #include "mir/renderer/gl/render_target.h" #include "mir/default_server_configuration.h" #include "mir/logging/logger.h" #include "mir/log.h" #include "mir/main_loop.h" #include "mir/report_exception.h" #include "mir/run_mir.h" #include "mir/cookie/authority.h" // TODO these are used to frig a stub renderer when running headless #include "mir/compositor/renderer.h" #include "mir/graphics/renderable.h" #include "mir/compositor/renderer_factory.h" #include namespace mo = mir::options; #define FOREACH_WRAPPER(MACRO)\ MACRO(cursor)\ MACRO(cursor_listener)\ MACRO(display_buffer_compositor_factory)\ MACRO(display_configuration_policy)\ MACRO(shell)\ MACRO(surface_stack) #define FOREACH_OVERRIDE(MACRO)\ MACRO(compositor)\ MACRO(cursor_images)\ MACRO(display_buffer_compositor_factory)\ MACRO(display_configuration_report)\ MACRO(gl_config)\ MACRO(host_lifecycle_event_listener)\ MACRO(input_dispatcher)\ MACRO(logger)\ MACRO(prompt_session_listener)\ MACRO(prompt_session_manager)\ MACRO(server_status_listener)\ MACRO(session_authorizer)\ MACRO(session_listener)\ MACRO(session_mediator_report)\ MACRO(shell)\ MACRO(application_not_responding_detector)\ MACRO(cookie_authority)\ MACRO(coordinate_translator) #define FOREACH_ACCESSOR(MACRO)\ MACRO(the_buffer_stream_factory)\ MACRO(the_compositor)\ MACRO(the_compositor_report)\ MACRO(the_composite_event_filter)\ MACRO(the_cursor_listener)\ MACRO(the_cursor)\ MACRO(the_display)\ MACRO(the_display_configuration_controller)\ MACRO(the_focus_controller)\ MACRO(the_gl_config)\ MACRO(the_graphics_platform)\ MACRO(the_input_targeter)\ MACRO(the_logger)\ MACRO(the_main_loop)\ MACRO(the_prompt_session_listener)\ MACRO(the_session_authorizer)\ MACRO(the_session_coordinator)\ MACRO(the_session_listener)\ MACRO(the_surface_factory)\ MACRO(the_prompt_session_manager)\ MACRO(the_shell)\ MACRO(the_shell_display_layout)\ MACRO(the_surface_stack)\ MACRO(the_touch_visualizer)\ MACRO(the_input_device_hub)\ MACRO(the_application_not_responding_detector) #define MIR_SERVER_BUILDER(name)\ std::function::type()> name##_builder; #define MIR_SERVER_WRAPPER(name)\ std::function::type\ (std::result_of::type const&)> name##_wrapper; struct mir::Server::Self { bool exit_status{false}; std::weak_ptr options; std::string config_file; std::shared_ptr server_config; std::function init_callback{[]{}}; int argc{0}; char const** argv{nullptr}; std::function exception_handler{}; Terminator terminator{}; EmergencyCleanupHandler emergency_cleanup_handler; std::function command_line_hander{}; /// set a callback to introduce additional configuration options. /// this will be invoked by run() before server initialisation starts void set_add_configuration_options( std::function const& add_configuration_options); std::function add_configuration_options{ [](options::DefaultConfiguration&){}}; FOREACH_OVERRIDE(MIR_SERVER_BUILDER) shell::WindowManagerBuilder wmb; FOREACH_WRAPPER(MIR_SERVER_WRAPPER) }; #undef MIR_SERVER_BUILDER #undef MIR_SERVER_WRAPPER #define MIR_SERVER_CONFIG_OVERRIDE(name)\ auto the_##name()\ -> decltype(mir::DefaultServerConfiguration::the_##name()) override\ {\ if (self->name##_builder)\ {\ if (auto const result = name([this]{ return self->name##_builder(); }))\ return result;\ }\ \ return mir::DefaultServerConfiguration::the_##name();\ } #define MIR_SERVER_CONFIG_WRAP(name)\ auto wrap_##name(decltype(Self::name##_wrapper)::result_type const& wrapped)\ -> decltype(mir::DefaultServerConfiguration::wrap_##name({})) override\ {\ if (self->name##_wrapper)\ return name(\ [&] { return self->name##_wrapper(wrapped); });\ \ return mir::DefaultServerConfiguration::wrap_##name(wrapped);\ } // TODO these are used to frig a stub renderer when running headless namespace { class StubGLRenderer : public mir::compositor::Renderer { public: StubGLRenderer(mir::graphics::DisplayBuffer& display_buffer) : render_target{ dynamic_cast( display_buffer.native_display_buffer())} { } void set_viewport(mir::geometry::Rectangle const&) override {} void set_rotation(float) override {} void render(mir::graphics::RenderableList const& renderables) const override { // Invoke GL renderer specific functions if the DisplayBuffer supports them if (render_target) render_target->make_current(); for (auto const& r : renderables) r->buffer(); // We need to consume a buffer to unblock client tests // Invoke GL renderer specific functions if the DisplayBuffer supports them if (render_target) render_target->swap_buffers(); } void suspend() override {} mir::renderer::gl::RenderTarget* const render_target; }; class StubGLRendererFactory : public mir::compositor::RendererFactory { public: auto create_renderer_for(mir::graphics::DisplayBuffer& display_buffer) -> std::unique_ptr { return std::make_unique(display_buffer); } }; } struct mir::Server::ServerConfiguration : mir::DefaultServerConfiguration { ServerConfiguration( std::shared_ptr const& configuration_options, std::shared_ptr const& self) : DefaultServerConfiguration(configuration_options), self(self.get()) { } // TODO this is an ugly frig to avoid exposing the render factory to end users and tests running headless auto the_renderer_factory() -> std::shared_ptr override { auto const& options = the_options(); if (options->is_set(options::platform_graphics_lib)) { auto const graphics_lib = options->get(options::platform_graphics_lib); if (graphics_lib.find("graphics-dummy.so") != std::string::npos) return std::make_shared(); } return mir::DefaultServerConfiguration::the_renderer_factory(); } using mir::DefaultServerConfiguration::the_options; FOREACH_OVERRIDE(MIR_SERVER_CONFIG_OVERRIDE) FOREACH_WRAPPER(MIR_SERVER_CONFIG_WRAP) auto the_window_manager_builder() -> shell::WindowManagerBuilder override { if (self->wmb) return self->wmb; return mir::DefaultServerConfiguration::the_window_manager_builder(); } Self* const self; }; #undef MIR_SERVER_CONFIG_OVERRIDE #undef MIR_SERVER_CONFIG_WRAP namespace { std::shared_ptr configuration_options( int argc, char const** argv, std::function const& command_line_hander, std::string const& config_file) { std::shared_ptr result; if (command_line_hander) result = std::make_shared(argc, argv, command_line_hander, config_file); else result = std::make_shared(argc, argv, config_file); return result; } template void verify_setting_allowed(ConfigPtr const& initialized) { if (initialized) BOOST_THROW_EXCEPTION(std::logic_error("Cannot amend configuration after apply_settings() call")); } template void verify_accessing_allowed(ConfigPtr const& initialized) { if (!initialized) BOOST_THROW_EXCEPTION(std::logic_error("Cannot use configuration before apply_settings() call")); } } mir::Server::Server() : self(std::make_shared()) { } void mir::Server::Self::set_add_configuration_options( std::function const& add_configuration_options) { this->add_configuration_options = add_configuration_options; } void mir::Server::set_command_line(int argc, char const* argv[]) { verify_setting_allowed(self->server_config); self->argc = argc; self->argv = argv; } void mir::Server::add_init_callback(std::function const& init_callback) { auto const& existing = self->init_callback; auto const updated = [=] { existing(); init_callback(); }; self->init_callback = updated; } void mir::Server::set_command_line_handler( std::function const& command_line_hander) { verify_setting_allowed(self->server_config); self->command_line_hander = command_line_hander; } void mir::Server::set_config_filename(std::string const& config_file) { verify_setting_allowed(self->server_config); self->config_file = config_file; } auto mir::Server::get_options() const -> std::shared_ptr { verify_accessing_allowed(self->server_config); return self->options.lock(); } void mir::Server::set_exception_handler(std::function const& exception_handler) { self->exception_handler = exception_handler; } void mir::Server::set_terminator(Terminator const& terminator) { self->terminator = terminator; } void mir::Server::add_emergency_cleanup(EmergencyCleanupHandler const& handler) { if (auto const& existing = self->emergency_cleanup_handler) { self->emergency_cleanup_handler = [=] { existing(); handler(); }; } else { self->emergency_cleanup_handler = handler; } } void mir::Server::apply_settings() { if (self->server_config) return; auto const options = configuration_options(self->argc, self->argv, self->command_line_hander, self->config_file); self->add_configuration_options(*options); auto const config = std::make_shared(options, self); self->server_config = config; self->options = config->the_options(); mir::logging::set_logger(config->the_logger()); } void mir::Server::run() try { mir::log_info("Starting"); verify_accessing_allowed(self->server_config); auto const emergency_cleanup = self->server_config->the_emergency_cleanup(); if (self->emergency_cleanup_handler) emergency_cleanup->add(self->emergency_cleanup_handler); run_mir( *self->server_config, [&](DisplayServer&) { self->init_callback(); }, self->terminator); self->exit_status = true; self->server_config.reset(); } catch (...) { self->server_config = nullptr; if (self->exception_handler) self->exception_handler(); else mir::report_exception(std::cerr); } auto mir::Server::supported_pixel_formats() const -> std::vector { return self->server_config->the_buffer_allocator()->supported_pixel_formats(); } void mir::Server::stop() { mir::log_info("Stopping"); if (self->server_config) if (auto const main_loop = the_main_loop()) main_loop->stop(); } bool mir::Server::exited_normally() { return self->exit_status; } auto mir::Server::open_client_socket() -> Fd { if (auto const config = self->server_config) return Fd{config->the_connector()->client_socket_fd()}; BOOST_THROW_EXCEPTION(std::logic_error("Cannot open connection when not running")); } auto mir::Server::open_prompt_socket() -> Fd { if (auto const config = self->server_config) return Fd{config->the_prompt_connector()->client_socket_fd()}; BOOST_THROW_EXCEPTION(std::logic_error("Cannot open connection when not running")); } auto mir::Server::open_client_socket(ConnectHandler const& connect_handler) -> Fd { if (auto const config = self->server_config) return Fd{config->the_connector()->client_socket_fd(connect_handler)}; BOOST_THROW_EXCEPTION(std::logic_error("Cannot open connection when not running")); } #define MIR_SERVER_ACCESSOR(name)\ auto mir::Server::name() const -> decltype(self->server_config->name())\ {\ verify_accessing_allowed(self->server_config);\ return self->server_config->name();\ } FOREACH_ACCESSOR(MIR_SERVER_ACCESSOR) #undef MIR_SERVER_ACCESSOR #define MIR_SERVER_OVERRIDE(name)\ void mir::Server::override_the_##name(decltype(Self::name##_builder) const& value)\ {\ verify_setting_allowed(self->server_config);\ self->name##_builder = value;\ } FOREACH_OVERRIDE(MIR_SERVER_OVERRIDE) #undef MIR_SERVER_OVERRIDE void mir::Server::override_the_window_manager_builder(shell::WindowManagerBuilder const wmb) { verify_setting_allowed(self->server_config); self->wmb = wmb; } #define MIR_SERVER_WRAP(name)\ void mir::Server::wrap_##name(decltype(Self::name##_wrapper) const& value)\ {\ verify_setting_allowed(self->server_config);\ self->name##_wrapper = value;\ } FOREACH_WRAPPER(MIR_SERVER_WRAP) #undef MIR_SERVER_WRAP void mir::Server::add_configuration_option( std::string const& option, std::string const& description, int default_) { verify_setting_allowed(self->server_config); namespace po = boost::program_options; auto const& existing = self->add_configuration_options; auto const option_adder = [=](options::DefaultConfiguration& config) { existing(config); config.add_options() (option.c_str(), po::value()->default_value(default_), description.c_str()); }; self->set_add_configuration_options(option_adder); } void mir::Server::add_configuration_option( std::string const& option, std::string const& description, double default_) { verify_setting_allowed(self->server_config); namespace po = boost::program_options; auto const& existing = self->add_configuration_options; auto const option_adder = [=](options::DefaultConfiguration& config) { existing(config); config.add_options() (option.c_str(), po::value()->default_value(default_), description.c_str()); }; self->set_add_configuration_options(option_adder); } void mir::Server::add_configuration_option( std::string const& option, std::string const& description, std::string const& default_) { verify_setting_allowed(self->server_config); namespace po = boost::program_options; auto const& existing = self->add_configuration_options; auto const option_adder = [=](options::DefaultConfiguration& config) { existing(config); config.add_options() (option.c_str(), po::value()->default_value(default_), description.c_str()); }; self->set_add_configuration_options(option_adder); } void mir::Server::add_configuration_option( std::string const& option, std::string const& description, char const* default_value) { add_configuration_option(option, description, std::string{default_value}); } void mir::Server::add_configuration_option( std::string const& option, std::string const& description, bool default_) { verify_setting_allowed(self->server_config); namespace po = boost::program_options; auto const& existing = self->add_configuration_options; auto const option_adder = [=](options::DefaultConfiguration& config) { existing(config); config.add_options() (option.c_str(), po::value()->default_value(default_), description.c_str()); }; self->set_add_configuration_options(option_adder); } void mir::Server::add_configuration_option( std::string const& option, std::string const& description, OptionType type) { verify_setting_allowed(self->server_config); namespace po = boost::program_options; auto const& existing = self->add_configuration_options; switch (type) { case OptionType::null: { auto const option_adder = [=](options::DefaultConfiguration& config) { existing(config); config.add_options() (option.c_str(), description.c_str()); }; self->set_add_configuration_options(option_adder); } break; case OptionType::integer: { auto const option_adder = [=](options::DefaultConfiguration& config) { existing(config); config.add_options() (option.c_str(), po::value(), description.c_str()); }; self->set_add_configuration_options(option_adder); } break; case OptionType::string: { auto const option_adder = [=](options::DefaultConfiguration& config) { existing(config); config.add_options() (option.c_str(), po::value(), description.c_str()); }; self->set_add_configuration_options(option_adder); } break; case OptionType::boolean: { auto const option_adder = [=](options::DefaultConfiguration& config) { existing(config); config.add_options() (option.c_str(), po::value(), description.c_str()); }; self->set_add_configuration_options(option_adder); } break; } } ./src/server/mirserver.pc.in0000644000015600001650000000041412676616125016154 0ustar jenkinsjenkinsprefix=@PREFIX@ exec_prefix=@EXEC_PREFIX@ libdir=@LIBDIR@ includedir=@INCLUDEDIR@ Name: mirserver Description: Mir server library Version: @MIR_VERSION@ Requires.private: mirclient Requires: mirplatform Libs: -L@LIBDIR@ -lmirserver -lmircommon Cflags: -I@INCLUDEDIR@ ./src/server/thread/0000755000015600001650000000000012676616125014455 5ustar jenkinsjenkins./src/server/thread/basic_thread_pool.cpp0000644000015600001650000001357412676616125020634 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #include "mir/thread/basic_thread_pool.h" #include "mir/terminate_with_current_exception.h" #include #include #include namespace mt = mir::thread; namespace { class Task { public: Task(std::function const& task) : task{task} {} void execute() { try { task(); } catch (...) { task_exception = std::current_exception(); } } void notify_done() { if (task_exception) promise.set_exception(task_exception); else promise.set_value(); } std::future get_future() { return promise.get_future(); } private: std::function const task; std::promise promise; std::exception_ptr task_exception; }; class Worker { public: Worker() : exiting{false} { } ~Worker() { exit(); } void operator()() noexcept try { std::unique_lock lock{state_mutex}; while (!exiting) { task_available_cv.wait(lock, [&]{ return exiting || !tasks.empty(); }); if (!exiting) { auto task = std::move(tasks.front()); lock.unlock(); task.execute(); lock.lock(); tasks.pop_front(); task.notify_done(); } } } catch(...) { mir::terminate_with_current_exception(); } void queue_task(Task task) { std::lock_guard lock{state_mutex}; tasks.push_back(std::move(task)); task_available_cv.notify_one(); } void exit() { std::lock_guard lock{state_mutex}; exiting = true; task_available_cv.notify_one(); } bool is_idle() const { std::lock_guard lock{state_mutex}; return tasks.empty(); } private: std::deque tasks; bool exiting; std::mutex mutable state_mutex; std::condition_variable task_available_cv; }; } namespace mir { namespace thread { class WorkerThread { public: WorkerThread(mt::BasicThreadPool::TaskId an_id) : thread{std::ref(worker)}, id_{an_id} {} ~WorkerThread() { worker.exit(); if (thread.joinable()) thread.join(); } void queue_task(Task task, mt::BasicThreadPool::TaskId the_id) { worker.queue_task(std::move(task)); id_ = the_id; } bool is_idle() const { return worker.is_idle(); } mt::BasicThreadPool::TaskId current_id() const { return id_; } private: ::Worker worker; std::thread thread; mt::BasicThreadPool::TaskId id_; }; } } mt::BasicThreadPool::BasicThreadPool(int min_threads) : min_threads{min_threads} { } mt::BasicThreadPool::~BasicThreadPool() = default; std::future mt::BasicThreadPool::run(std::function const& task) { std::lock_guard lock{mutex}; TaskId const generic_id = nullptr; WorkerThread* no_preferred_thread = nullptr; return run(no_preferred_thread, task, generic_id); } std::future mt::BasicThreadPool::run(std::function const& task, TaskId id) { std::lock_guard lock{mutex}; return run(find_thread_by(id), task, id); } std::future mt::BasicThreadPool::run(WorkerThread* worker_thread, std::function const& task, TaskId id) { // No pre-selected thread to execute task, find an idle thread if (worker_thread == nullptr) worker_thread = find_idle_thread(); if (worker_thread == nullptr) { // No idle threads available so create a new one auto new_worker_thread = std::make_unique(id); threads.push_back(std::move(new_worker_thread)); worker_thread = threads.back().get(); } Task a_task{task}; auto future = a_task.get_future(); worker_thread->queue_task(std::move(a_task), id); return future; } void mt::BasicThreadPool::shrink() { std::lock_guard lock{mutex}; int max_threads_to_remove = threads.size() - min_threads; auto it = std::remove_if(threads.begin(), threads.end(), [&max_threads_to_remove](std::unique_ptr const& worker_thread) { bool remove = worker_thread->is_idle() && max_threads_to_remove > 0; if (remove) max_threads_to_remove--; return remove; } ); threads.erase(it, threads.end()); } mt::WorkerThread* mt::BasicThreadPool::find_thread_by(TaskId id) { auto it = std::find_if(threads.begin(), threads.end(), [id](std::unique_ptr const& worker_thread) { return worker_thread->current_id() == id; } ); return it == threads.end() ? nullptr : it->get(); } mt::WorkerThread *mt::BasicThreadPool::find_idle_thread() { auto it = std::find_if(threads.begin(), threads.end(), [](std::unique_ptr const& worker_thread) { return worker_thread->is_idle(); } ); return it == threads.end() ? nullptr : it->get(); } ./src/server/thread/CMakeLists.txt0000644000015600001650000000015412676616125017215 0ustar jenkinsjenkinsset( MIR_THREAD_SRCS basic_thread_pool.cpp ) ADD_LIBRARY( mirthread OBJECT ${MIR_THREAD_SRCS} ) ./src/server/scene/0000755000015600001650000000000012676616160014302 5ustar jenkinsjenkins./src/server/scene/default_session_container.cpp0000644000015600001650000000422612676616125022244 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #include "default_session_container.h" #include #include #include namespace ms = mir::scene; void ms::DefaultSessionContainer::insert_session(std::shared_ptr const& session) { std::unique_lock lk(guard); apps.push_back(session); } void ms::DefaultSessionContainer::remove_session(std::shared_ptr const& session) { std::unique_lock lk(guard); auto it = std::find(apps.begin(), apps.end(), session); if (it != apps.end()) { apps.erase(it); } else { BOOST_THROW_EXCEPTION(std::logic_error("Invalid Session")); } } void ms::DefaultSessionContainer::for_each(std::function const&)> f) const { std::unique_lock lk(guard); for (auto const ptr : apps) { f(ptr); } } std::shared_ptr ms::DefaultSessionContainer::successor_of(std::shared_ptr const& session) const { std::shared_ptr result, first; if (!session && apps.size()) return apps.back(); else if(!session) return std::shared_ptr(); for (auto it = apps.begin(); it != apps.end(); it++) { if (*it == session) { auto successor = ++it; if (successor == apps.end()) return *apps.begin(); else return *successor; } } BOOST_THROW_EXCEPTION(std::logic_error("Invalid session")); } ./src/server/scene/output_properties_cache.cpp0000644000015600001650000000632712676616125021756 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "output_properties_cache.h" #include "mir/geometry/rectangle.h" #include "mir/graphics/display_configuration.h" #include #include namespace ms = mir::scene; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { int calculate_dpi(mir::geometry::Size const& resolution, mir::geometry::Size const& size) { float constexpr mm_per_inch = 25.4f; auto diagonal_mm = sqrt(size.height.as_int()*size.height.as_int() + size.width.as_int()*size.width.as_int()); auto diagonal_px = sqrt(resolution.height.as_int() * resolution.height.as_int() + resolution.width.as_int() * resolution.width.as_int()); return diagonal_px / diagonal_mm * mm_per_inch; } } void ms::OutputPropertiesCache::update_from(mg::DisplayConfiguration const &config) { auto new_properties = std::make_shared>(); config.for_each_output( [&new_properties](mg::DisplayConfigurationOutput const& output) { if (output.current_mode_index < output.modes.size()) { auto const mode = output.modes[output.current_mode_index]; new_properties->emplace_back(OutputProperties { output.extents(), calculate_dpi(mode.size, output.physical_size_mm), output.scale, output.form_factor, output.id}); } }); std::lock_guard lk(mutex); cache = new_properties; } auto ms::OutputPropertiesCache::get_cache() const -> std::shared_ptr> { std::lock_guard lk(mutex); return cache; } auto ms::OutputPropertiesCache::properties_for(geom::Rectangle const& extents) const -> std::shared_ptr { auto temp_properties = get_cache(); std::shared_ptr matching_output_properties{}; if (temp_properties) { for (auto const &output : *temp_properties) { if (output.extents.overlaps(extents)) { if (!matching_output_properties || (output.scale > matching_output_properties->scale)) { matching_output_properties = std::shared_ptr( temp_properties, &output ); } } } } return matching_output_properties; } ./src/server/scene/basic_surface.cpp0000644000015600001650000006144112676616157017613 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Alan Griffiths * Thomas Voss */ #include "basic_surface.h" #include "mir/compositor/buffer_stream.h" #include "mir/frontend/event_sink.h" #include "mir/input/input_channel.h" #include "mir/shell/input_targeter.h" #include "mir/input/input_sender.h" #include "mir/graphics/buffer.h" #include "mir/graphics/cursor_image.h" #include "mir/geometry/displacement.h" #include "mir/scene/scene_report.h" #include "mir/scene/null_surface_observer.h" #include #include #include #include // memcpy namespace mc = mir::compositor; namespace ms = mir::scene; namespace msh = mir::shell; namespace mg = mir::graphics; namespace mi = mir::input; namespace mf = mir::frontend; namespace geom = mir::geometry; void ms::SurfaceObservers::attrib_changed(MirSurfaceAttrib attrib, int value) { for_each([&](std::shared_ptr const& observer) { observer->attrib_changed(attrib, value); }); } void ms::SurfaceObservers::resized_to(geometry::Size const& size) { for_each([&](std::shared_ptr const& observer) { observer->resized_to(size); }); } void ms::SurfaceObservers::moved_to(geometry::Point const& top_left) { for_each([&](std::shared_ptr const& observer) { observer->moved_to(top_left); }); } void ms::SurfaceObservers::hidden_set_to(bool hide) { for_each([&](std::shared_ptr const& observer) { observer->hidden_set_to(hide); }); } void ms::SurfaceObservers::frame_posted(int frames_available, geometry::Size const& size) { for_each([&](std::shared_ptr const& observer) { observer->frame_posted(frames_available, size); }); } void ms::SurfaceObservers::alpha_set_to(float alpha) { for_each([&](std::shared_ptr const& observer) { observer->alpha_set_to(alpha); }); } void ms::SurfaceObservers::orientation_set_to(MirOrientation orientation) { for_each([&](std::shared_ptr const& observer) { observer->orientation_set_to(orientation); }); } void ms::SurfaceObservers::transformation_set_to(glm::mat4 const& t) { for_each([&](std::shared_ptr const& observer) { observer->transformation_set_to(t); }); } void ms::SurfaceObservers::cursor_image_set_to(mg::CursorImage const& image) { for_each([&](std::shared_ptr const& observer) { observer->cursor_image_set_to(image); }); } void ms::SurfaceObservers::reception_mode_set_to(mi::InputReceptionMode mode) { for_each([&](std::shared_ptr const& observer) { observer->reception_mode_set_to(mode); }); } void ms::SurfaceObservers::client_surface_close_requested() { for_each([](std::shared_ptr const& observer) { observer->client_surface_close_requested(); }); } void ms::SurfaceObservers::keymap_changed(MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) { for_each([&](std::shared_ptr const& observer) { observer->keymap_changed(id, model, layout, variant, options); }); } void ms::SurfaceObservers::renamed(char const* name) { for_each([name](std::shared_ptr const& observer) { observer->renamed(name); }); } void ms::SurfaceObservers::cursor_image_removed() { for_each([](std::shared_ptr const& observer) { observer->cursor_image_removed(); }); } struct ms::CursorStreamImageAdapter { CursorStreamImageAdapter(ms::BasicSurface &surface) : surface(surface), observer{std::make_shared(this)} { } ~CursorStreamImageAdapter() { reset(); } void reset() { if (stream) { stream->remove_observer(observer); stream.reset(); } } void update(std::shared_ptr const& new_stream, geom::Displacement const& new_hotspot) { if (new_stream == stream && new_hotspot == hotspot) { return; } else if (new_stream != stream) { if (stream) stream->remove_observer(observer); stream = std::dynamic_pointer_cast(new_stream); stream->add_observer(observer); } hotspot = new_hotspot; post_cursor_image_from_current_buffer(); } private: struct FramePostObserver : public ms::NullSurfaceObserver { FramePostObserver(CursorStreamImageAdapter const* self) : self(self) { } void frame_posted(int /* available */, geometry::Size const& /* size */) { self->post_cursor_image_from_current_buffer(); } CursorStreamImageAdapter const* const self; }; void post_cursor_image_from_current_buffer() const { surface.set_cursor_from_buffer(*stream->lock_compositor_buffer(this), hotspot); } ms::BasicSurface& surface; std::shared_ptr const observer; std::shared_ptr stream; geom::Displacement hotspot; }; ms::BasicSurface::BasicSurface( std::string const& name, geometry::Rectangle rect, std::weak_ptr const& parent, bool nonrectangular, std::shared_ptr const& buffer_stream, std::shared_ptr const& input_channel, std::shared_ptr const& input_sender, std::shared_ptr const& cursor_image, std::shared_ptr const& report) : surface_name(name), surface_rect(rect), surface_alpha(1.0f), hidden(false), input_mode(mi::InputReceptionMode::normal), nonrectangular(nonrectangular), custom_input_rectangles(), surface_buffer_stream(buffer_stream), server_input_channel(input_channel), input_sender(input_sender), cursor_image_(cursor_image), report(report), parent_(parent), layers({StreamInfo{buffer_stream, {0,0}}}), cursor_stream_adapter{std::make_unique(*this)}, input_validator([this](MirEvent const& ev) { this->input_sender->send_event(ev, server_input_channel); }) { report->surface_created(this, surface_name); } ms::BasicSurface::BasicSurface( std::string const& name, geometry::Rectangle rect, bool nonrectangular, std::shared_ptr const& buffer_stream, std::shared_ptr const& input_channel, std::shared_ptr const& input_sender, std::shared_ptr const& cursor_image, std::shared_ptr const& report) : BasicSurface(name, rect, std::shared_ptr{nullptr}, nonrectangular,buffer_stream, input_channel, input_sender, cursor_image, report) { } ms::BasicSurface::~BasicSurface() noexcept { report->surface_deleted(this, surface_name); } std::shared_ptr ms::BasicSurface::buffer_stream() const { return surface_buffer_stream; } std::string ms::BasicSurface::name() const { return surface_name; } void ms::BasicSurface::move_to(geometry::Point const& top_left) { { std::unique_lock lk(guard); surface_rect.top_left = top_left; } observers.moved_to(top_left); } float ms::BasicSurface::alpha() const { std::unique_lock lk(guard); return surface_alpha; } void ms::BasicSurface::set_hidden(bool hide) { { std::unique_lock lk(guard); hidden = hide; } observers.hidden_set_to(hide); } mir::geometry::Size ms::BasicSurface::size() const { std::unique_lock lk(guard); return surface_rect.size; } mir::geometry::Size ms::BasicSurface::client_size() const { // TODO: In future when decorated, client_size() would be smaller than size return size(); } std::shared_ptr ms::BasicSurface::primary_buffer_stream() const { return surface_buffer_stream; } bool ms::BasicSurface::supports_input() const { if (server_input_channel && server_input_channel->client_fd() != -1) return true; return false; } int ms::BasicSurface::client_input_fd() const { if (!supports_input()) BOOST_THROW_EXCEPTION(std::logic_error("Surface does not support input")); return server_input_channel->client_fd(); } std::shared_ptr ms::BasicSurface::input_channel() const { return server_input_channel; } void ms::BasicSurface::set_input_region(std::vector const& input_rectangles) { std::unique_lock lock(guard); custom_input_rectangles = input_rectangles; } void ms::BasicSurface::resize(geom::Size const& desired_size) { geom::Size new_size = desired_size; if (new_size.width <= geom::Width{0}) new_size.width = geom::Width{1}; if (new_size.height <= geom::Height{0}) new_size.height = geom::Height{1}; if (new_size == size()) return; /* * Other combinations may still be invalid (like dimensions too big or * insufficient resources), but those are runtime and platform-specific, so * not predictable here. Such critical exceptions would arise from * the platform buffer allocator as a runtime_error via: */ surface_buffer_stream->resize(new_size); // Now the buffer stream has successfully resized, update the state second; { std::unique_lock lock(guard); surface_rect.size = new_size; } observers.resized_to(new_size); } geom::Point ms::BasicSurface::top_left() const { std::unique_lock lk(guard); return surface_rect.top_left; } geom::Rectangle ms::BasicSurface::input_bounds() const { std::unique_lock lk(guard); return surface_rect; } bool ms::BasicSurface::input_area_contains(geom::Point const& point) const { std::unique_lock lock(guard); if (!visible(lock)) return false; // Restrict to bounding rectangle if (!surface_rect.contains(point)) return false; // No custom input region means effective input region is whole surface if (custom_input_rectangles.empty()) return true; // TODO: Perhaps creates some issues with transformation. auto local_point = geom::Point{0, 0} + (point-surface_rect.top_left); for (auto const& rectangle : custom_input_rectangles) { if (rectangle.contains(local_point)) { return true; } } return false; } void ms::BasicSurface::set_alpha(float alpha) { { std::unique_lock lk(guard); surface_alpha = alpha; } observers.alpha_set_to(alpha); } void ms::BasicSurface::set_orientation(MirOrientation orientation) { observers.orientation_set_to(orientation); } void ms::BasicSurface::set_transformation(glm::mat4 const& t) { { std::unique_lock lk(guard); transformation_matrix = t; } observers.transformation_set_to(t); } bool ms::BasicSurface::visible() const { std::unique_lock lk(guard); return visible(lk); } bool ms::BasicSurface::visible(std::unique_lock&) const { bool visible{false}; for (auto const& info : layers) visible |= info.stream->has_submitted_buffer(); return !hidden && visible; } mi::InputReceptionMode ms::BasicSurface::reception_mode() const { return input_mode; } void ms::BasicSurface::set_reception_mode(mi::InputReceptionMode mode) { { std::lock_guard lk(guard); input_mode = mode; } observers.reception_mode_set_to(mode); } MirSurfaceType ms::BasicSurface::type() const { std::unique_lock lg(guard); return type_; } MirSurfaceType ms::BasicSurface::set_type(MirSurfaceType t) { std::unique_lock lg(guard); if (t < 0 || t > mir_surface_types) { BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface " "type.")); } if (type_ != t) { type_ = t; lg.unlock(); observers.attrib_changed(mir_surface_attrib_type, type_); } return t; } MirSurfaceState ms::BasicSurface::state() const { std::unique_lock lg(guard); return state_; } MirSurfaceState ms::BasicSurface::set_state(MirSurfaceState s) { if (s < mir_surface_state_unknown || s > mir_surface_states) BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface state.")); std::unique_lock lg(guard); if (state_ != s) { state_ = s; lg.unlock(); observers.attrib_changed(mir_surface_attrib_state, s); } return s; } int ms::BasicSurface::set_swap_interval(int interval) { if (interval < 0) { BOOST_THROW_EXCEPTION(std::logic_error("Invalid swapinterval")); } std::unique_lock lg(guard); if (swapinterval_ != interval) { swapinterval_ = interval; bool allow_dropping = (interval == 0); for (auto& info : layers) info.stream->allow_framedropping(allow_dropping); lg.unlock(); observers.attrib_changed(mir_surface_attrib_swapinterval, interval); } return interval; } MirSurfaceFocusState ms::BasicSurface::set_focus_state(MirSurfaceFocusState new_state) { if (new_state != mir_surface_focused && new_state != mir_surface_unfocused) { BOOST_THROW_EXCEPTION(std::logic_error("Invalid focus state.")); } std::unique_lock lg(guard); if (focus_ != new_state) { focus_ = new_state; lg.unlock(); observers.attrib_changed(mir_surface_attrib_focus, new_state); } return new_state; } MirOrientationMode ms::BasicSurface::set_preferred_orientation(MirOrientationMode new_orientation_mode) { if ((new_orientation_mode & mir_orientation_mode_any) == 0) { BOOST_THROW_EXCEPTION(std::logic_error("Invalid orientation mode")); } std::unique_lock lg(guard); if (pref_orientation_mode != new_orientation_mode) { pref_orientation_mode = new_orientation_mode; lg.unlock(); observers.attrib_changed(mir_surface_attrib_preferred_orientation, new_orientation_mode); } return new_orientation_mode; } int ms::BasicSurface::configure(MirSurfaceAttrib attrib, int value) { int result = value; switch (attrib) { case mir_surface_attrib_type: result = set_type(static_cast(result)); break; case mir_surface_attrib_state: result = set_state(static_cast(result)); break; case mir_surface_attrib_focus: result = set_focus_state(static_cast(result)); break; case mir_surface_attrib_swapinterval: result = set_swap_interval(result); break; case mir_surface_attrib_dpi: result = set_dpi(result); break; case mir_surface_attrib_visibility: result = set_visibility(static_cast(result)); break; case mir_surface_attrib_preferred_orientation: result = set_preferred_orientation(static_cast(result)); break; default: BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface attribute.")); } return result; } int ms::BasicSurface::query(MirSurfaceAttrib attrib) const { std::unique_lock lg(guard); switch (attrib) { case mir_surface_attrib_type: return type_; case mir_surface_attrib_state: return state_; case mir_surface_attrib_swapinterval: return swapinterval_; case mir_surface_attrib_focus: return focus_; case mir_surface_attrib_dpi: return dpi_; case mir_surface_attrib_visibility: return visibility_; case mir_surface_attrib_preferred_orientation: return pref_orientation_mode; default: BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface " "attribute.")); } } void ms::BasicSurface::hide() { set_hidden(true); } void ms::BasicSurface::show() { set_hidden(false); } void ms::BasicSurface::set_cursor_image(std::shared_ptr const& image) { { std::unique_lock lock(guard); cursor_stream_adapter->reset(); cursor_image_ = image; } if (image) observers.cursor_image_set_to(*image); else observers.cursor_image_removed(); } std::shared_ptr ms::BasicSurface::cursor_image() const { std::unique_lock lock(guard); return cursor_image_; } namespace { struct CursorImageFromBuffer : public mg::CursorImage { CursorImageFromBuffer(mg::Buffer &buffer, geom::Displacement const& hotspot) : buffer_size(buffer.size()), hotspot_(hotspot) { buffer.read([&](unsigned char const* buffer_pixels) { size_t buffer_size_bytes = buffer_size.width.as_int() * buffer_size.height.as_int() * MIR_BYTES_PER_PIXEL(buffer.pixel_format()); pixels = std::unique_ptr( new unsigned char[buffer_size_bytes] ); memcpy(pixels.get(), buffer_pixels, buffer_size_bytes); }); } void const* as_argb_8888() const { return pixels.get(); } geom::Size size() const { return buffer_size; } geom::Displacement hotspot() const { return hotspot_; } geom::Size const buffer_size; geom::Displacement const hotspot_; std::unique_ptr pixels; }; } void ms::BasicSurface::set_cursor_from_buffer(mg::Buffer& buffer, geom::Displacement const& hotspot) { auto image = std::make_shared(buffer, hotspot); { std::unique_lock lock(guard); cursor_image_ = image; } observers.cursor_image_set_to(*image); } // In order to set the cursor image from a buffer stream, we use an adapter pattern, // which observes buffers from the stream and copies them 1 by 1 to cursor images. // We must be careful, when setting a new cursor image with ms::BasicSurface::set_cursor_image // we need to reset the stream adapter (to halt the observation and allow the new static image // to be set). Likewise from the adapter we must use set_cursor_from_buffer as // opposed to the public set_cursor_from_image in order to avoid resetting the stream // adapter. void ms::BasicSurface::set_cursor_stream(std::shared_ptr const& stream, geom::Displacement const& hotspot) { cursor_stream_adapter->update(stream, hotspot); } void ms::BasicSurface::request_client_surface_close() { observers.client_surface_close_requested(); } int ms::BasicSurface::dpi() const { std::unique_lock lock(guard); return dpi_; } int ms::BasicSurface::set_dpi(int new_dpi) { if (new_dpi < 0) { BOOST_THROW_EXCEPTION(std::logic_error("Invalid DPI value")); } std::unique_lock lg(guard); if (dpi_ != new_dpi) { dpi_ = new_dpi; lg.unlock(); observers.attrib_changed(mir_surface_attrib_dpi, new_dpi); } return new_dpi; } MirSurfaceVisibility ms::BasicSurface::set_visibility(MirSurfaceVisibility new_visibility) { if (new_visibility != mir_surface_visibility_occluded && new_visibility != mir_surface_visibility_exposed) { BOOST_THROW_EXCEPTION(std::logic_error("Invalid visibility value")); } std::unique_lock lg(guard); if (visibility_ != new_visibility) { visibility_ = new_visibility; lg.unlock(); if (new_visibility == mir_surface_visibility_exposed) { for (auto& info : layers) info.stream->drop_old_buffers(); } observers.attrib_changed(mir_surface_attrib_visibility, visibility_); } return new_visibility; } void ms::BasicSurface::add_observer(std::shared_ptr const& observer) { observers.add(observer); for (auto& info : layers) info.stream->add_observer(observer); } void ms::BasicSurface::remove_observer(std::weak_ptr const& observer) { auto o = observer.lock(); if (!o) BOOST_THROW_EXCEPTION(std::runtime_error("Invalid observer (previously destroyed)")); observers.remove(o); for (auto& info : layers) info.stream->remove_observer(observer); } std::shared_ptr ms::BasicSurface::parent() const { std::lock_guard lg(guard); return parent_.lock(); } namespace { //This class avoids locking for long periods of time by copying (or lazy-copying) class SurfaceSnapshot : public mg::Renderable { public: SurfaceSnapshot( std::shared_ptr const& stream, void const* compositor_id, geom::Rectangle const& position, glm::mat4 const& transform, float alpha, bool shaped, mg::Renderable::ID id) : underlying_buffer_stream{stream}, compositor_buffer{nullptr}, compositor_id{compositor_id}, alpha_{alpha}, shaped_{shaped}, screen_position_(position), transformation_(transform), id_(id) { } ~SurfaceSnapshot() { } std::shared_ptr buffer() const override { if (!compositor_buffer) compositor_buffer = underlying_buffer_stream->lock_compositor_buffer(compositor_id); return compositor_buffer; } geom::Rectangle screen_position() const override { return screen_position_; } float alpha() const override { return alpha_; } glm::mat4 transformation() const override { return transformation_; } bool shaped() const override { return shaped_; } mg::Renderable::ID id() const override { return id_; } private: std::shared_ptr const underlying_buffer_stream; std::shared_ptr mutable compositor_buffer; void const*const compositor_id; float const alpha_; bool const shaped_; geom::Rectangle const screen_position_; glm::mat4 const transformation_; mg::Renderable::ID const id_; }; } int ms::BasicSurface::buffers_ready_for_compositor(void const* id) const { std::unique_lock lk(guard); auto max_buf = 0; for (auto const& info : layers) max_buf = std::max(max_buf, info.stream->buffers_ready_for_compositor(id)); return max_buf; } void ms::BasicSurface::consume(MirEvent const* event) { input_validator.validate_and_dispatch(*event); } void ms::BasicSurface::set_keymap(MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) { observers.keymap_changed(id, model, layout, variant, options); } void ms::BasicSurface::rename(std::string const& title) { if (surface_name != title) { surface_name = title; observers.renamed(surface_name.c_str()); } } void ms::BasicSurface::set_streams(std::list const& s) { { std::unique_lock lk(guard); for(auto& layer : layers) observers.for_each([&](std::shared_ptr const& observer) { layer.stream->remove_observer(observer); }); layers = s; for(auto& layer : layers) observers.for_each([&](std::shared_ptr const& observer) { layer.stream->add_observer(observer); }); } observers.moved_to(surface_rect.top_left); } mg::RenderableList ms::BasicSurface::generate_renderables(mc::CompositorID id) const { std::unique_lock lk(guard); mg::RenderableList list; for (auto const& info : layers) { if (info.stream->has_submitted_buffer()) { geom::Size size = info.stream->stream_size(); list.emplace_back(std::make_shared( info.stream, id, geom::Rectangle{surface_rect.top_left + info.displacement, std::move(size)}, transformation_matrix, surface_alpha, nonrectangular, info.stream.get())); } } return list; } ./src/server/scene/broadcasting_session_event_sink.h0000644000015600001650000000366312676616125023114 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #ifndef MIR_SCENE_BROADCASTING_SESSION_EVENT_SINK_H_ #define MIR_SCENE_BROADCASTING_SESSION_EVENT_SINK_H_ #include "session_event_sink.h" #include "session_event_handler_register.h" #include #include namespace mir { namespace scene { class BroadcastingSessionEventSink : public SessionEventSink, public SessionEventHandlerRegister { public: void handle_focus_change(std::shared_ptr const& session); void handle_no_focus(); void handle_session_stopping(std::shared_ptr const& session); void register_focus_change_handler( std::function const& session)> const& handler); void register_no_focus_handler( std::function const& handler); void register_session_stopping_handler( std::function const& session)> const& handler); private: std::mutex handler_mutex; std::vector const&)>> focus_change_handlers; std::vector> no_focus_handlers; std::vector const&)>> session_stopping_handlers; }; } } #endif /* MIR_SCENE_BROADCASTING_SESSION_EVENT_SINK_H_ */ ./src/server/scene/threaded_snapshot_strategy.cpp0000644000015600001650000000616512676616125022440 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #include "threaded_snapshot_strategy.h" #include "pixel_buffer.h" #include "mir/compositor/buffer_stream.h" #include "mir/thread_name.h" #include #include #include namespace geom = mir::geometry; namespace ms = mir::scene; namespace mir { namespace scene { struct WorkItem { std::shared_ptr const stream; ms::SnapshotCallback const snapshot_taken; }; class SnapshottingFunctor { public: SnapshottingFunctor(std::shared_ptr const& pixels) : running{true}, pixels{pixels} { } void operator()() { mir::set_thread_name("Mir/Snapshot"); std::unique_lock lock{work_mutex}; while (running) { while (running && work.empty()) work_cv.wait(lock); if (running) { auto wi = work.front(); work.pop_front(); lock.unlock(); take_snapshot(wi); lock.lock(); } } } void take_snapshot(WorkItem const& wi) { wi.stream->with_most_recent_buffer_do([this](mir::graphics::Buffer& buffer) { pixels->fill_from(buffer); }); wi.snapshot_taken( ms::Snapshot{pixels->size(), pixels->stride(), pixels->as_argb_8888()}); } void schedule_snapshot(WorkItem const& wi) { std::lock_guard lg{work_mutex}; work.push_back(wi); work_cv.notify_one(); } void stop() { std::lock_guard lg{work_mutex}; running = false; work_cv.notify_one(); } private: bool running; std::shared_ptr const pixels; std::mutex work_mutex; std::condition_variable work_cv; std::deque work; }; } } ms::ThreadedSnapshotStrategy::ThreadedSnapshotStrategy( std::shared_ptr const& pixels) : pixels{pixels}, functor{new SnapshottingFunctor{pixels}}, thread{std::ref(*functor)} { } ms::ThreadedSnapshotStrategy::~ThreadedSnapshotStrategy() noexcept { functor->stop(); thread.join(); } void ms::ThreadedSnapshotStrategy::take_snapshot_of( std::shared_ptr const& surface_buffer_access, SnapshotCallback const& snapshot_taken) { functor->schedule_snapshot(WorkItem{surface_buffer_access, snapshot_taken}); } ./src/server/scene/legacy_scene_change_notification.cpp0000644000015600001650000001220512676616125023503 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "legacy_surface_change_notification.h" #include "mir/scene/legacy_scene_change_notification.h" #include "mir/scene/surface.h" #include namespace ms = mir::scene; ms::LegacySceneChangeNotification::LegacySceneChangeNotification( std::function const& scene_notify_change, std::function const& buffer_notify_change) : scene_notify_change(scene_notify_change), buffer_notify_change(buffer_notify_change) { } ms::LegacySceneChangeNotification::LegacySceneChangeNotification( std::function const& scene_notify_change, std::function const& damage_notify_change) : scene_notify_change(scene_notify_change), damage_notify_change(damage_notify_change) { } ms::LegacySceneChangeNotification::~LegacySceneChangeNotification() { end_observation(); } namespace { class NonLegacySurfaceChangeNotification : public ms::LegacySurfaceChangeNotification { public: NonLegacySurfaceChangeNotification( std::function const& notify_scene_change, std::function const& damage_notify_change, ms::Surface* surface); void moved_to(mir::geometry::Point const& /*top_left*/) override; void frame_posted(int frames_available, mir::geometry::Size const& size) override; private: mir::geometry::Point top_left; std::function const damage_notify_change; }; NonLegacySurfaceChangeNotification::NonLegacySurfaceChangeNotification( std::function const& notify_scene_change, std::function const& damage_notify_change, ms::Surface* surface) : ms::LegacySurfaceChangeNotification(notify_scene_change, {}), damage_notify_change(damage_notify_change) { top_left = surface->top_left(); } void NonLegacySurfaceChangeNotification::moved_to(mir::geometry::Point const& top_left) { this->top_left = top_left; ms::LegacySurfaceChangeNotification::moved_to(top_left); } void NonLegacySurfaceChangeNotification::frame_posted(int frames_available, mir::geometry::Size const& size) { mir::geometry::Rectangle const update_region{top_left, size}; damage_notify_change(frames_available, update_region); } } void ms::LegacySceneChangeNotification::add_surface_observer(ms::Surface* surface) { auto notifier = [surface, this, was_visible = false] () mutable { if (surface->visible() || was_visible) scene_notify_change(); was_visible = surface->visible(); }; if (buffer_notify_change) { auto observer = std::make_shared(notifier, buffer_notify_change); surface->add_observer(observer); std::unique_lock lg(surface_observers_guard); surface_observers[surface] = observer; } else { auto observer = std::make_shared(notifier, damage_notify_change, surface); surface->add_observer(observer); std::unique_lock lg(surface_observers_guard); surface_observers[surface] = observer; } } void ms::LegacySceneChangeNotification::surface_added(ms::Surface* surface) { add_surface_observer(surface); } void ms::LegacySceneChangeNotification::surface_exists(ms::Surface* surface) { add_surface_observer(surface); } void ms::LegacySceneChangeNotification::surface_removed(ms::Surface* surface) { { std::unique_lock lg(surface_observers_guard); auto it = surface_observers.find(surface); if (it != surface_observers.end()) { surface->remove_observer(it->second); surface_observers.erase(it); } } if (surface->visible()) scene_notify_change(); } void ms::LegacySceneChangeNotification::surfaces_reordered() { scene_notify_change(); } void ms::LegacySceneChangeNotification::scene_changed() { scene_notify_change(); } void ms::LegacySceneChangeNotification::end_observation() { std::unique_lock lg(surface_observers_guard); for (auto &kv : surface_observers) { auto surface = kv.first; if (surface) surface->remove_observer(kv.second); } surface_observers.clear(); } ./src/server/scene/surface_stack.h0000644000015600001650000001011112676616125017263 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SCENE_SURFACE_STACK_H_ #define MIR_SCENE_SURFACE_STACK_H_ #include "mir/shell/surface_stack.h" #include "mir/compositor/scene.h" #include "mir/scene/observer.h" #include "mir/input/scene.h" #include "mir/recursive_read_write_mutex.h" #include "mir/basic_observers.h" #include #include #include #include #include #include namespace mir { namespace graphics { class Renderable; } /// Management of Surface objects. Includes the model (SurfaceStack and Surface /// classes) and controller (SurfaceController) elements of an MVC design. namespace scene { class InputRegistrar; class BasicSurface; class SceneReport; class RenderingTracker; class Observers : public Observer, BasicObservers { public: // ms::Observer void surface_added(Surface* surface) override; void surface_removed(Surface* surface) override; void surfaces_reordered() override; void scene_changed() override; void surface_exists(Surface* surface) override; void end_observation() override; using BasicObservers::add; using BasicObservers::remove; }; class SurfaceStack : public compositor::Scene, public input::Scene, public shell::SurfaceStack { public: explicit SurfaceStack( std::shared_ptr const& report); virtual ~SurfaceStack() noexcept(true) {} // From Scene compositor::SceneElementSequence scene_elements_for(compositor::CompositorID id) override; int frames_pending(compositor::CompositorID) const override; void register_compositor(compositor::CompositorID id) override; void unregister_compositor(compositor::CompositorID id) override; // From Scene void for_each(std::function const&)> const& callback) override; virtual void remove_surface(std::weak_ptr const& surface) override; virtual void raise(std::weak_ptr const& surface) override; void raise(SurfaceSet const& surfaces) override; void add_surface( std::shared_ptr const& surface, input::InputReceptionMode input_mode) override; auto surface_at(geometry::Point) const -> std::shared_ptr override; void add_observer(std::shared_ptr const& observer) override; void remove_observer(std::weak_ptr const& observer) override; // Intended for input overlays, as described in mir::input::Scene documentation. void add_input_visualization(std::shared_ptr const& overlay) override; void remove_input_visualization(std::weak_ptr const& overlay) override; void emit_scene_changed() override; private: SurfaceStack(const SurfaceStack&) = delete; SurfaceStack& operator=(const SurfaceStack&) = delete; void create_rendering_tracker_for(std::shared_ptr const&); void update_rendering_tracker_compositors(); RecursiveReadWriteMutex mutable guard; std::shared_ptr const input_registrar; std::shared_ptr const report; std::vector> surfaces; std::map> rendering_trackers; std::set registered_compositors; std::vector> overlays; Observers observers; std::atomic scene_changed; }; } } #endif /* MIR_SCENE_SURFACE_STACK_H_ */ ./src/server/scene/snapshot_strategy.h0000644000015600001650000000254512676616125020243 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #ifndef MIR_SCENE_SNAPSHOT_STRATEGY_H_ #define MIR_SCENE_SNAPSHOT_STRATEGY_H_ #include "mir/scene/snapshot.h" #include namespace mir { namespace compositor { class BufferStream; } namespace scene { class SnapshotStrategy { public: virtual ~SnapshotStrategy() = default; virtual void take_snapshot_of( std::shared_ptr const& surface_buffer_access, SnapshotCallback const& snapshot_taken) = 0; protected: SnapshotStrategy() = default; SnapshotStrategy(SnapshotStrategy const&) = delete; SnapshotStrategy& operator=(SnapshotStrategy const&) = delete; }; } } #endif /* MIR_SCENE_SNAPSHOT_STRATEGY_H_ */ ./src/server/scene/surface_event_source.cpp0000644000015600001650000000554412676616125021230 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/events/event_builders.h" #include "mir/scene/surface_event_source.h" #include "mir/scene/surface.h" #include "output_properties_cache.h" #include "mir/geometry/size.h" #include "mir/geometry/rectangle.h" #include #include namespace ms = mir::scene; namespace mev = mir::events; namespace geom = mir::geometry; ms::SurfaceEventSource::SurfaceEventSource( frontend::SurfaceId id, Surface const& surface, OutputPropertiesCache const& outputs, std::shared_ptr const& event_sink) : id(id), surface{surface}, outputs{outputs}, event_sink(event_sink) { } void ms::SurfaceEventSource::resized_to(geom::Size const& size) { event_sink->handle_event(*mev::make_event(id, size)); } void ms::SurfaceEventSource::moved_to(geom::Point const& top_left) { auto new_output_properties = outputs.properties_for(geom::Rectangle{top_left, surface.size()}); if (new_output_properties && (new_output_properties != last_output.lock())) { event_sink->handle_event(*mev::make_event( id, new_output_properties->dpi, new_output_properties->scale, new_output_properties->form_factor, static_cast(new_output_properties->id.as_value()) )); last_output = new_output_properties; } } void ms::SurfaceEventSource::attrib_changed(MirSurfaceAttrib attrib, int value) { event_sink->handle_event(*mev::make_event(id, attrib, value)); } void ms::SurfaceEventSource::orientation_set_to(MirOrientation orientation) { event_sink->handle_event(*mev::make_event(id, orientation)); } void ms::SurfaceEventSource::client_surface_close_requested() { event_sink->handle_event(*mev::make_event(id)); } void ms::SurfaceEventSource::keymap_changed(MirInputDeviceId device_id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) { event_sink->handle_event(*mev::make_event(id, device_id, model, layout, variant, options)); } ./src/server/scene/session_manager.cpp0000644000015600001650000001074612676616125020174 0ustar jenkinsjenkins/* * Copyright © 2012-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #include "session_manager.h" #include "application_session.h" #include "session_container.h" #include "mir/scene/surface.h" #include "mir/scene/session.h" #include "mir/scene/session_listener.h" #include "mir/scene/prompt_session.h" #include "mir/scene/application_not_responding_detector.h" #include "mir/shell/surface_stack.h" #include "session_event_sink.h" #include "mir/frontend/event_sink.h" #include "mir/graphics/display.h" #include "mir/graphics/display_configuration.h" #include #include #include #include #include namespace mf = mir::frontend; namespace ms = mir::scene; namespace mg = mir::graphics; namespace msh = mir::shell; ms::SessionManager::SessionManager( std::shared_ptr const& surface_stack, std::shared_ptr const& surface_factory, std::shared_ptr const& buffer_stream_factory, std::shared_ptr const& container, std::shared_ptr const& snapshot_strategy, std::shared_ptr const& session_event_sink, std::shared_ptr const& session_listener, std::shared_ptr const& display, std::shared_ptr const& anr_detector) : surface_stack(surface_stack), surface_factory(surface_factory), buffer_stream_factory(buffer_stream_factory), app_container(container), snapshot_strategy(snapshot_strategy), session_event_sink(session_event_sink), session_listener(session_listener), display{display}, anr_detector{anr_detector} { } ms::SessionManager::~SessionManager() noexcept { /* * Close all open sessions. We need to do this manually here * to break the cyclic dependency between msh::Session * and mi::*, since our implementations * of these interfaces keep strong references to each other. * TODO: Investigate other solutions (e.g. weak_ptr) */ std::vector> sessions; app_container->for_each([&](std::shared_ptr const& session) { sessions.push_back(session); }); for (auto& session : sessions) close_session(session); } std::shared_ptr ms::SessionManager::open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sender) { std::shared_ptr new_session = std::make_shared( surface_stack, surface_factory, buffer_stream_factory, client_pid, name, snapshot_strategy, session_listener, *display->configuration(), sender); app_container->insert_session(new_session); session_listener->starting(new_session); anr_detector->register_session(new_session.get(), [sender]() { sender->send_ping(0); }); return new_session; } void ms::SessionManager::set_focus_to(std::shared_ptr const& session) { session_event_sink->handle_focus_change(session); session_listener->focused(session); } void ms::SessionManager::unset_focus() { session_event_sink->handle_no_focus(); session_listener->unfocused(); } void ms::SessionManager::close_session(std::shared_ptr const& session) { auto scene_session = std::dynamic_pointer_cast(session); scene_session->force_requests_to_complete(); anr_detector->unregister_session(session.get()); session_event_sink->handle_session_stopping(scene_session); session_listener->stopping(scene_session); app_container->remove_session(scene_session); } std::shared_ptr ms::SessionManager::successor_of( std::shared_ptr const& session) const { return app_container->successor_of(session); } ./src/server/scene/global_event_sender.cpp0000644000015600001650000000373312676616125021016 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "global_event_sender.h" #include "session_container.h" #include "mir/scene/session.h" namespace mg=mir::graphics; namespace ms=mir::scene; namespace mi=mir::input; ms::GlobalEventSender::GlobalEventSender(std::shared_ptr const& session_container) : sessions(session_container) { } void ms::GlobalEventSender::handle_event(MirEvent const&) { //TODO, no driving test cases, although messages like 'server shutdown' could go here } void ms::GlobalEventSender::handle_lifecycle_event(MirLifecycleState) { // Lifecycle events are per application session, never global } void ms::GlobalEventSender::handle_display_config_change(mg::DisplayConfiguration const& config) { sessions->for_each([&config](std::shared_ptr const& session) { session->send_display_config(config); }); } void ms::GlobalEventSender::handle_input_device_change(std::vector> const& devices) { sessions->for_each([&devices](std::shared_ptr const& session) { session->send_input_device_change(devices); }); } void ms::GlobalEventSender::send_ping(int32_t) { // Ping events are per-application session. } void ms::GlobalEventSender::send_buffer(mir::frontend::BufferStreamId, mg::Buffer&, mg::BufferIpcMsgType) { } ./src/server/scene/pixel_buffer.h0000644000015600001650000000364712676616125017140 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #ifndef MIR_SCENE_PIXEL_BUFFER_H_ #define MIR_SCENE_PIXEL_BUFFER_H_ #include "mir/geometry/size.h" #include "mir/geometry/dimensions.h" namespace mir { namespace graphics { class Buffer; } namespace scene { /** * Interface for extracting the pixels from a graphics::Buffer. */ class PixelBuffer { public: virtual ~PixelBuffer() = default; /** * Fills the PixelBuffer with the contents of a graphics::Buffer. * * \param [in] buffer the buffer to get the pixels of */ virtual void fill_from(graphics::Buffer& buffer) = 0; /** * The pixels in 0xAARRGGBB format. * * The pixel data is owned by the PixelBuffer object and is only valid * until the next call to fill_from(). * * This method may involve transformation of the extracted data. */ virtual void const* as_argb_8888() = 0; /** * The size of the pixel buffer. */ virtual geometry::Size size() const = 0; /** * The stride of the pixel buffer. */ virtual geometry::Stride stride() const = 0; protected: PixelBuffer() = default; PixelBuffer(PixelBuffer const&) = delete; PixelBuffer& operator=(PixelBuffer const&) = delete; }; } } #endif /* MIR_SCENE_PIXEL_BUFFER_H_ */ ./src/server/scene/session_manager.h0000644000015600001650000000614612676616125017640 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #ifndef MIR_SCENE_APPLICATION_MANAGER_H_ #define MIR_SCENE_APPLICATION_MANAGER_H_ #include "mir/scene/session_coordinator.h" #include #include namespace mir { namespace graphics { class DisplayConfiguration; class Display; } namespace shell { class SurfaceStack; } namespace scene { class SessionContainer; class SessionEventSink; class SessionListener; class SnapshotStrategy; class SurfaceStack; class PromptSessionManager; class BufferStreamFactory; class SurfaceFactory; class ApplicationNotRespondingDetector; class SessionManager : public SessionCoordinator { public: SessionManager( std::shared_ptr const& surface_stack, std::shared_ptr const& surface_factory, std::shared_ptr const& buffer_stream_factory, std::shared_ptr const& app_container, std::shared_ptr const& snapshot_strategy, std::shared_ptr const& session_event_sink, std::shared_ptr const& session_listener, std::shared_ptr const& display, std::shared_ptr const& anr_detector); virtual ~SessionManager() noexcept; std::shared_ptr open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) override; void close_session(std::shared_ptr const& session) override; std::shared_ptr successor_of(std::shared_ptr const&) const override; void set_focus_to(std::shared_ptr const& focus) override; void unset_focus() override; protected: SessionManager(const SessionManager&) = delete; SessionManager& operator=(const SessionManager&) = delete; private: std::shared_ptr const surface_stack; std::shared_ptr const surface_factory; std::shared_ptr const buffer_stream_factory; std::shared_ptr const app_container; std::shared_ptr const snapshot_strategy; std::shared_ptr const session_event_sink; std::shared_ptr const session_listener; std::shared_ptr const display; std::shared_ptr const anr_detector; }; } } #endif // MIR_SCENE_APPLICATION_MANAGER_H_ ./src/server/scene/session_container.h0000644000015600001650000000316512676616125020206 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_SCENE_SESSION_CONTAINER_H_ #define MIR_SCENE_SESSION_CONTAINER_H_ #include #include #include namespace mir { namespace scene { class Session; class SessionContainer { public: virtual void insert_session(std::shared_ptr const& session) = 0; virtual void remove_session(std::shared_ptr const& session) = 0; virtual void for_each(std::function const&)> f) const = 0; // For convenience the successor of the null session is defined as the last session // which would be passed to the for_each callback virtual std::shared_ptr successor_of(std::shared_ptr const&) const = 0; protected: SessionContainer() = default; virtual ~SessionContainer() = default; SessionContainer(const SessionContainer&) = delete; SessionContainer& operator=(const SessionContainer&) = delete; }; } } #endif // MIR_SCENE_SESSION_CONTAINER_H_ ./src/server/scene/mediating_display_changer.cpp0000644000015600001650000002233512676616125022171 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include #include "mediating_display_changer.h" #include "session_container.h" #include "mir/scene/session.h" #include "session_event_handler_register.h" #include "mir/graphics/display.h" #include "mir/compositor/compositor.h" #include "mir/geometry/rectangles.h" #include "mir/graphics/display_configuration_policy.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/display_configuration_report.h" #include "mir/server_action_queue.h" namespace mf = mir::frontend; namespace ms = mir::scene; namespace mg = mir::graphics; namespace mc = mir::compositor; namespace mi = mir::input; namespace { class ApplyNowAndRevertOnScopeExit { public: ApplyNowAndRevertOnScopeExit(std::function const& apply, std::function const& revert) : revert{revert} { apply(); } ~ApplyNowAndRevertOnScopeExit() { revert(); } private: ApplyNowAndRevertOnScopeExit(ApplyNowAndRevertOnScopeExit const&) = delete; ApplyNowAndRevertOnScopeExit& operator=(ApplyNowAndRevertOnScopeExit const&) = delete; std::function const revert; }; } ms::MediatingDisplayChanger::MediatingDisplayChanger( std::shared_ptr const& display, std::shared_ptr const& compositor, std::shared_ptr const& display_configuration_policy, std::shared_ptr const& session_container, std::shared_ptr const& session_event_handler_register, std::shared_ptr const& server_action_queue, std::shared_ptr const& report, std::shared_ptr const& region) : display{display}, compositor{compositor}, display_configuration_policy{display_configuration_policy}, session_container{session_container}, session_event_handler_register{session_event_handler_register}, server_action_queue{server_action_queue}, report{report}, base_configuration_{display->configuration()}, base_configuration_applied{true}, region{region} { session_event_handler_register->register_focus_change_handler( [this](std::shared_ptr const& session) { auto const weak_session = std::weak_ptr(session); this->server_action_queue->enqueue( this, [this,weak_session] { if (auto const session = weak_session.lock()) focus_change_handler(session); }); }); session_event_handler_register->register_no_focus_handler( [this] { this->server_action_queue->enqueue( this, [this] { no_focus_handler(); }); }); session_event_handler_register->register_session_stopping_handler( [this](std::shared_ptr const& session) { auto const weak_session = std::weak_ptr(session); this->server_action_queue->enqueue( this, [this,weak_session] { if (auto const session = weak_session.lock()) session_stopping_handler(session); }); }); report->initial_configuration(*base_configuration_); update_input_rectangles(*base_configuration_); } void ms::MediatingDisplayChanger::configure( std::shared_ptr const& session, std::shared_ptr const& conf) { { std::lock_guard lg{configuration_mutex}; config_map[session] = conf; if (session != focused_session.lock()) return; } std::weak_ptr const weak_session{session}; server_action_queue->enqueue( this, [this, weak_session, conf] { if (auto const session = weak_session.lock()) { std::lock_guard lg{configuration_mutex}; /* If the session is focused, apply the configuration */ if (focused_session.lock() == session) apply_config(conf, PauseResumeSystem); } }); } std::shared_ptr ms::MediatingDisplayChanger::base_configuration() { std::lock_guard lg{configuration_mutex}; return base_configuration_->clone(); } void ms::MediatingDisplayChanger::configure_for_hardware_change( std::shared_ptr const& conf, SystemStateHandling pause_resume_system) { server_action_queue->enqueue( this, [this, conf, pause_resume_system] { std::lock_guard lg{configuration_mutex}; display_configuration_policy->apply_to(*conf); base_configuration_ = conf; if (base_configuration_applied) apply_base_config(pause_resume_system); /* * Clear all the per-session configurations, since they may have become * invalid due to the hardware change. */ config_map.clear(); /* Send the new configuration to all the sessions */ send_config_to_all_sessions(conf); }); } void ms::MediatingDisplayChanger::pause_display_config_processing() { server_action_queue->pause_processing_for(this); } void ms::MediatingDisplayChanger::resume_display_config_processing() { server_action_queue->resume_processing_for(this); } void ms::MediatingDisplayChanger::apply_config( std::shared_ptr const& conf, SystemStateHandling pause_resume_system) { report->new_configuration(*conf); if (pause_resume_system) { ApplyNowAndRevertOnScopeExit comp{ [this] { compositor->stop(); }, [this] { compositor->start(); }}; display->configure(*conf); } else { display->configure(*conf); } update_input_rectangles(*conf); base_configuration_applied = false; } void ms::MediatingDisplayChanger::apply_base_config( SystemStateHandling pause_resume_system) { apply_config(base_configuration_, pause_resume_system); base_configuration_applied = true; } void ms::MediatingDisplayChanger::send_config_to_all_sessions( std::shared_ptr const& conf) { session_container->for_each( [&conf](std::shared_ptr const& session) { session->send_display_config(*conf); }); } void ms::MediatingDisplayChanger::focus_change_handler( std::shared_ptr const& session) { std::lock_guard lg{configuration_mutex}; focused_session = session; /* * If the newly focused session has a display configuration, apply it. * Otherwise if we aren't currently using the base configuration, * apply that. */ auto const it = config_map.find(session); if (it != config_map.end()) { apply_config(it->second, PauseResumeSystem); } else if (!base_configuration_applied) { apply_base_config(PauseResumeSystem); } } void ms::MediatingDisplayChanger::no_focus_handler() { std::lock_guard lg{configuration_mutex}; focused_session.reset(); if (!base_configuration_applied) { apply_base_config(PauseResumeSystem); } } void ms::MediatingDisplayChanger::session_stopping_handler( std::shared_ptr const& session) { std::lock_guard lg{configuration_mutex}; config_map.erase(session); } void ms::MediatingDisplayChanger::set_base_configuration(std::shared_ptr const &conf) { server_action_queue->enqueue( this, [this, conf] { std::lock_guard lg{configuration_mutex}; base_configuration_ = conf; if (base_configuration_applied) apply_base_config(PauseResumeSystem); send_config_to_all_sessions(conf); }); } void ms::MediatingDisplayChanger::update_input_rectangles(mg::DisplayConfiguration const& config) { geometry::Rectangles rectangles; config.for_each_output( [&rectangles](mg::DisplayConfigurationOutput const& output) { if (output.power_mode == mir_power_mode_on && output.current_mode_index < output.modes.size()) rectangles.add(geometry::Rectangle(output.top_left, output.modes[output.current_mode_index].size)); }); region->set_input_rectangles(rectangles); } ./src/server/scene/application_session.h0000644000015600001650000001135612676616157020535 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Robert Carr */ #ifndef MIR_SCENE_APPLICATION_SESSION_H_ #define MIR_SCENE_APPLICATION_SESSION_H_ #include "mir/scene/session.h" #include "output_properties_cache.h" #include #include #include namespace mir { namespace frontend { class EventSink; } namespace compositor { class BufferStream; } namespace graphics { class DisplayConfiguration; } namespace shell { class SurfaceStack; } namespace scene { class SessionListener; class Surface; class SnapshotStrategy; class BufferStreamFactory; class SurfaceFactory; class ApplicationSession : public Session { public: ApplicationSession( std::shared_ptr const& surface_stack, std::shared_ptr const& surface_factory, std::shared_ptr const& buffer_stream_factory, pid_t pid, std::string const& session_name, std::shared_ptr const& snapshot_strategy, std::shared_ptr const& session_listener, graphics::DisplayConfiguration const& initial_config, std::shared_ptr const& sink); ~ApplicationSession(); frontend::SurfaceId create_surface( SurfaceCreationParameters const& params, std::shared_ptr const& surface_sink) override; void destroy_surface(frontend::SurfaceId surface) override; std::shared_ptr get_surface(frontend::SurfaceId surface) const override; std::shared_ptr surface(frontend::SurfaceId surface) const override; std::shared_ptr surface_after(std::shared_ptr const&) const override; void take_snapshot(SnapshotCallback const& snapshot_taken) override; std::shared_ptr default_surface() const override; std::string name() const override; pid_t process_id() const override; void force_requests_to_complete() override; void hide() override; void show() override; void send_display_config(graphics::DisplayConfiguration const& info) override; void send_input_device_change(std::vector> const& devices) override; void set_lifecycle_state(MirLifecycleState state) override; void start_prompt_session() override; void stop_prompt_session() override; void suspend_prompt_session() override; void resume_prompt_session() override; std::shared_ptr get_buffer_stream(frontend::BufferStreamId stream) const override; frontend::BufferStreamId create_buffer_stream(graphics::BufferProperties const& params) override; void destroy_buffer_stream(frontend::BufferStreamId stream) override; void configure_streams(Surface& surface, std::vector const& config) override; void destroy_surface(std::weak_ptr const& surface) override; protected: ApplicationSession(ApplicationSession const&) = delete; ApplicationSession& operator=(ApplicationSession const&) = delete; private: std::shared_ptr const surface_stack; std::shared_ptr const surface_factory; std::shared_ptr const buffer_stream_factory; pid_t const pid; std::string const session_name; std::shared_ptr const snapshot_strategy; std::shared_ptr const session_listener; std::shared_ptr const event_sink; frontend::SurfaceId next_id(); std::atomic next_surface_id; OutputPropertiesCache output_cache; typedef std::map> Surfaces; typedef std::map> Streams; Surfaces::const_iterator checked_find(frontend::SurfaceId id) const; Streams::const_iterator checked_find(frontend::BufferStreamId id) const; std::mutex mutable surfaces_and_streams_mutex; Surfaces surfaces; Streams streams; void destroy_surface(std::unique_lock& lock, Surfaces::const_iterator in_surfaces); }; } } // namespace mir #endif // MIR_SCENE_APPLICATION_SESSION_H_ ./src/server/scene/default_coordinate_translator.cpp0000644000015600001650000000235412676616125023117 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "default_coordinate_translator.h" #include "mir/scene/surface.h" #include "mir/geometry/displacement.h" namespace geom = mir::geometry; namespace mf = mir::frontend; namespace ms = mir::scene; geom::Point ms::DefaultCoordinateTranslator::surface_to_screen(std::shared_ptr surface, int32_t x, int32_t y) { auto const scene_surface = std::dynamic_pointer_cast(surface); return scene_surface->top_left() + geom::Displacement{x, y}; } ./src/server/scene/prompt_session_impl.h0000644000015600001650000000263212676616125020564 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Nick Dedekind */ #ifndef MIR_SCENE_PROMPT_SESSION_IMPL_H_ #define MIR_SCENE_PROMPT_SESSION_IMPL_H_ #include "mir/scene/prompt_session.h" #include namespace mir { namespace scene { class PromptSessionImpl : public scene::PromptSession { public: explicit PromptSessionImpl(); void start(std::shared_ptr const& helper_session) override; void stop(std::shared_ptr const& helper_session) override; void suspend(std::shared_ptr const& helper_session) override; void resume(std::shared_ptr const& helper_session) override; MirPromptSessionState state() const; private: std::mutex mutable guard; MirPromptSessionState current_state; }; } } #endif // MIR_SCENE_PROMPT_SESSION_IMPL_H_ ./src/server/scene/prompt_session_container.cpp0000644000015600001650000001130412676616125022134 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #include "prompt_session_container.h" #include "mir/scene/session.h" #include #include namespace ms = mir::scene; namespace mf = mir::frontend; namespace { std::atomic insertion_order{0}; } ms::PromptSessionContainer::PromptSessionContainer() : prompt_session_index(participant_map) , participant_index(get<1>(participant_map)) { } void ms::PromptSessionContainer::insert_prompt_session(std::shared_ptr const& prompt_session) { std::unique_lock lk(mutex); prompt_sessions[prompt_session.get()] = prompt_session; } void ms::PromptSessionContainer::remove_prompt_session(std::shared_ptr const& prompt_session) { std::unique_lock lk(mutex); { participant_by_prompt_session::iterator it, end; boost::tie(it, end) = prompt_session_index.equal_range(prompt_session.get()); prompt_session_index.erase(it, end); } prompt_sessions.erase(prompt_session.get()); } bool ms::PromptSessionContainer::insert_participant(PromptSession* prompt_session, std::weak_ptr const& session, ParticipantType participant_type) { std::unique_lock lk(mutex); // the prompt session must have first been added by insert_prompt_session. if (prompt_sessions.find(prompt_session) == prompt_sessions.end()) BOOST_THROW_EXCEPTION(std::runtime_error("Prompt Session does not exist")); if (auto locked_session = session.lock()) { participant_by_prompt_session::iterator it; bool valid = false; Participant participant{prompt_session, locked_session, participant_type, insertion_order++}; boost::tie(it,valid) = participant_map.insert(participant); return valid; } return false; } bool ms::PromptSessionContainer::remove_participant(PromptSession* prompt_session, std::weak_ptr const& session, ParticipantType participant_type) { std::unique_lock lk(mutex); participant_by_session::iterator it = participant_index.find(boost::make_tuple(session, participant_type, prompt_session)); if (it == participant_index.end()) return false; participant_index.erase(it); return true; } void ms::PromptSessionContainer::for_each_participant_in_prompt_session( PromptSession* prompt_session, std::function const&, ms::PromptSessionContainer::ParticipantType participant_type)> f) const { std::unique_lock lk(mutex); participant_by_prompt_session::iterator it,end; boost::tie(it,end) = prompt_session_index.equal_range(prompt_session); for (; it != end; ++it) { Participant const& participant = *it; f(participant.session, participant.participant_type); } } void ms::PromptSessionContainer::for_each_prompt_session_with_participant( std::weak_ptr const& participant, ParticipantType participant_type, std::function const&)> f) const { std::unique_lock lk(mutex); participant_by_session::iterator it,end; boost::tie(it,end) = participant_index.equal_range(boost::make_tuple(participant, participant_type)); for (; it != end; ++it) { Participant const& participant = *it; auto tsit = prompt_sessions.find(participant.prompt_session); if (tsit != prompt_sessions.end()) f(tsit->second); } } void ms::PromptSessionContainer::for_each_prompt_session_with_participant( std::weak_ptr const& participant, std::function const&, ParticipantType)> f) const { std::unique_lock lk(mutex); participant_by_session::iterator it,end; boost::tie(it,end) = participant_index.equal_range(participant); for (; it != end; ++it) { Participant const& participant = *it; auto tsit = prompt_sessions.find(participant.prompt_session); if (tsit != prompt_sessions.end()) f(tsit->second, participant.participant_type); } } ./src/server/scene/gl_pixel_buffer.h0000644000015600001650000000324712676616125017616 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #ifndef MIR_SCENE_GL_PIXEL_BUFFER_H_ #define MIR_SCENE_GL_PIXEL_BUFFER_H_ #include "pixel_buffer.h" #include #include #include namespace mir { namespace graphics { class Buffer; class GLContext; } namespace scene { /** Extracts the pixels from a graphics::Buffer using GL facilities. */ class GLPixelBuffer : public PixelBuffer { public: GLPixelBuffer(std::unique_ptr gl_context); ~GLPixelBuffer() noexcept; void fill_from(graphics::Buffer& buffer); void const* as_argb_8888(); geometry::Size size() const; geometry::Stride stride() const; private: void prepare(); void copy_and_convert_pixel_line(char* src, char* dst); std::unique_ptr const gl_context; GLuint tex; GLuint fbo; std::vector pixels; GLuint gl_pixel_format; bool pixels_need_y_flip; geometry::Size size_; geometry::Stride stride_; }; } } #endif /* MIR_SCENE_GL_PIXEL_BUFFER_H_ */ ./src/server/scene/default_configuration.cpp0000644000015600001650000001720412676616125021366 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/default_server_configuration.h" #include "mir/main_loop.h" #include "mir/graphics/display.h" #include "mir/graphics/gl_context.h" #include "mir/input/scene.h" #include "mir/abnormal_exit.h" #include "mir/scene/session.h" #include "mir/shell/display_configuration_controller.h" #include "broadcasting_session_event_sink.h" #include "default_session_container.h" #include "gl_pixel_buffer.h" #include "global_event_sender.h" #include "mediating_display_changer.h" #include "session_container.h" #include "session_manager.h" #include "surface_allocator.h" #include "surface_stack.h" #include "threaded_snapshot_strategy.h" #include "prompt_session_manager_impl.h" #include "default_coordinate_translator.h" #include "unsupported_coordinate_translator.h" #include "timeout_application_not_responding_detector.h" #include "mir/options/program_option.h" #include "mir/options/default_configuration.h" #include "mir/graphics/display_configuration.h" #include "mir/frontend/display_changer.h" namespace mc = mir::compositor; namespace mf = mir::frontend; namespace mi = mir::input; namespace ms = mir::scene; namespace mg = mir::graphics; namespace msh = mir::shell; std::shared_ptr mir::DefaultServerConfiguration::the_scene() { return scene_surface_stack([this]() { return std::make_shared(the_scene_report()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_input_scene() { return scene_surface_stack([this]() { return std::make_shared(the_scene_report()); }); } auto mir::DefaultServerConfiguration::the_surface_factory() -> std::shared_ptr { return surface_factory( [this]() { return std::make_shared( the_input_channel_factory(), the_input_sender(), the_default_cursor_image(), the_scene_report()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_surface_stack() { return surface_stack([this]() -> std::shared_ptr { auto const wrapped = scene_surface_stack([this]() { return std::make_shared(the_scene_report()); }); return wrap_surface_stack(wrapped); }); } auto mir::DefaultServerConfiguration::wrap_surface_stack(std::shared_ptr const& wrapped) -> std::shared_ptr { return wrapped; } std::shared_ptr mir::DefaultServerConfiguration::the_broadcasting_session_event_sink() { return broadcasting_session_event_sink( [] { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_session_event_sink() { return the_broadcasting_session_event_sink(); } std::shared_ptr mir::DefaultServerConfiguration::the_session_event_handler_register() { return the_broadcasting_session_event_sink(); } std::shared_ptr mir::DefaultServerConfiguration::the_session_container() { return session_container( []{ return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_mediating_display_changer() { return mediating_display_changer( [this]() { return std::make_shared( the_display(), the_compositor(), the_display_configuration_policy(), the_session_container(), the_session_event_handler_register(), the_server_action_queue(), the_display_configuration_report(), the_input_region()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_frontend_display_changer() { return the_mediating_display_changer(); } std::shared_ptr mir::DefaultServerConfiguration::the_display_changer() { return the_mediating_display_changer(); } std::shared_ptr mir::DefaultServerConfiguration::the_global_event_sink() { return global_event_sink( [this]() { return std::make_shared(the_session_container()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_session_coordinator() { return session_coordinator( [this]() { return std::make_shared( the_surface_stack(), the_surface_factory(), the_buffer_stream_factory(), the_session_container(), the_snapshot_strategy(), the_session_event_sink(), the_session_listener(), the_display(), the_application_not_responding_detector()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_pixel_buffer() { return pixel_buffer( [this]() { return std::make_shared( the_display()->create_gl_context()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_snapshot_strategy() { return snapshot_strategy( [this]() { return std::make_shared( the_pixel_buffer()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_prompt_session_manager() { return prompt_session_manager( [this]() { return std::make_shared( the_session_container(), the_prompt_session_listener()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_coordinate_translator() { return coordinate_translator( [this]() -> std::shared_ptr { if (the_options()->is_set(options::debug_opt)) return std::make_shared(); else return std::make_shared(); }); } auto mir::DefaultServerConfiguration::the_application_not_responding_detector() -> std::shared_ptr { return application_not_responding_detector( [this]() -> std::shared_ptr { using namespace std::literals::chrono_literals; return std::make_shared( *the_main_loop(), 1s); }); } std::shared_ptr mir::DefaultServerConfiguration::the_display_configuration_controller() { return the_mediating_display_changer(); } ./src/server/scene/surface_stack.cpp0000644000015600001650000002636112676616125017634 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Alan Griffiths * Thomas Voss */ #include "surface_stack.h" #include "rendering_tracker.h" #include "mir/scene/surface.h" #include "mir/scene/scene_report.h" #include "mir/compositor/scene_element.h" #include "mir/compositor/decoration.h" #include "mir/graphics/renderable.h" #include #include #include #include #include #include namespace ms = mir::scene; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace mi = mir::input; namespace geom = mir::geometry; namespace { class SurfaceSceneElement : public mc::SceneElement { public: SurfaceSceneElement( std::string name, std::shared_ptr const& renderable, std::shared_ptr const& tracker, mc::CompositorID id) : renderable_{renderable}, tracker{tracker}, cid{id}, surface_name(name) { } std::shared_ptr renderable() const override { return renderable_; } void rendered() override { tracker->rendered_in(cid); } void occluded() override { tracker->occluded_in(cid); } std::unique_ptr decoration() const override { return std::make_unique(mc::Decoration::Type::surface, surface_name); } private: std::shared_ptr const renderable_; std::shared_ptr const tracker; mc::CompositorID cid; std::string const surface_name; }; //note: something different than a 2D/HWC overlay class OverlaySceneElement : public mc::SceneElement { public: OverlaySceneElement( std::shared_ptr renderable) : renderable_{renderable} { } std::shared_ptr renderable() const override { return renderable_; } void rendered() override { } void occluded() override { } std::unique_ptr decoration() const override { return std::make_unique(); } private: std::shared_ptr const renderable_; }; } ms::SurfaceStack::SurfaceStack( std::shared_ptr const& report) : report{report}, scene_changed{false} { } mc::SceneElementSequence ms::SurfaceStack::scene_elements_for(mc::CompositorID id) { RecursiveReadLock lg(guard); scene_changed = false; mc::SceneElementSequence elements; for (auto const& surface : surfaces) { if (surface->visible()) { for (auto& renderable : surface->generate_renderables(id)) { elements.emplace_back( std::make_shared( surface->name(), renderable, rendering_trackers[surface.get()], id)); } } } for (auto const& renderable : overlays) { elements.emplace_back(std::make_shared(renderable)); } return elements; } int ms::SurfaceStack::frames_pending(mc::CompositorID id) const { RecursiveReadLock lg(guard); int result = scene_changed ? 1 : 0; for (auto const& surface : surfaces) { if (surface->visible()) { auto const tracker = rendering_trackers.find(surface.get()); if (tracker != rendering_trackers.end() && tracker->second->is_exposed_in(id)) { // Note that we ask the surface and not a Renderable. // This is because we don't want to waste time and resources // on a snapshot till we're sure we need it... int ready = surface->buffers_ready_for_compositor(id); if (ready > result) result = ready; } } } return result; } void ms::SurfaceStack::register_compositor(mc::CompositorID cid) { RecursiveWriteLock lg(guard); registered_compositors.insert(cid); update_rendering_tracker_compositors(); } void ms::SurfaceStack::unregister_compositor(mc::CompositorID cid) { RecursiveWriteLock lg(guard); registered_compositors.erase(cid); update_rendering_tracker_compositors(); } void ms::SurfaceStack::add_input_visualization( std::shared_ptr const& overlay) { { RecursiveWriteLock lg(guard); overlays.push_back(overlay); } emit_scene_changed(); } void ms::SurfaceStack::remove_input_visualization( std::weak_ptr const& weak_overlay) { auto overlay = weak_overlay.lock(); { RecursiveWriteLock lg(guard); auto const p = std::find(overlays.begin(), overlays.end(), overlay); if (p == overlays.end()) { BOOST_THROW_EXCEPTION(std::runtime_error("Attempt to remove an overlay which was never added or which has been previously removed")); } overlays.erase(p); } emit_scene_changed(); } void ms::SurfaceStack::emit_scene_changed() { { RecursiveWriteLock lg(guard); scene_changed = true; } observers.scene_changed(); } void ms::SurfaceStack::add_surface( std::shared_ptr const& surface, mi::InputReceptionMode input_mode) { { RecursiveWriteLock lg(guard); surfaces.push_back(surface); create_rendering_tracker_for(surface); } surface->set_reception_mode(input_mode); observers.surface_added(surface.get()); report->surface_added(surface.get(), surface.get()->name()); } void ms::SurfaceStack::remove_surface(std::weak_ptr const& surface) { auto const keep_alive = surface.lock(); bool found_surface = false; { RecursiveWriteLock lg(guard); auto const surface = std::find(surfaces.begin(), surfaces.end(), keep_alive); if (surface != surfaces.end()) { surfaces.erase(surface); rendering_trackers.erase(keep_alive.get()); found_surface = true; } } if (found_surface) { observers.surface_removed(keep_alive.get()); report->surface_removed(keep_alive.get(), keep_alive.get()->name()); } // TODO: error logging when surface not found } namespace { template struct InReverse { Container& container; auto begin() -> decltype(container.rbegin()) { return container.rbegin(); } auto end() -> decltype(container.rend()) { return container.rend(); } }; template InReverse in_reverse(Container& container) { return InReverse{container}; } } auto ms::SurfaceStack::surface_at(geometry::Point cursor) const -> std::shared_ptr { RecursiveReadLock lg(guard); for (auto const& surface : in_reverse(surfaces)) { // TODO There's a lack of clarity about how the input area will // TODO be maintained and whether this test will detect clicks on // TODO decorations (it should) as these may be outside the area // TODO known to the client. But it works for now. if (surface->input_area_contains(cursor)) return surface; } return {}; } void ms::SurfaceStack::for_each(std::function const&)> const& callback) { RecursiveReadLock lg(guard); for (auto &surface : surfaces) { if (surface->query(mir_surface_attrib_visibility) == MirSurfaceVisibility::mir_surface_visibility_exposed) { callback(surface); } } } void ms::SurfaceStack::raise(std::weak_ptr const& s) { bool surfaces_reordered{false}; { auto const surface = s.lock(); RecursiveWriteLock ul(guard); auto const p = std::find(surfaces.begin(), surfaces.end(), surface); if (p != surfaces.end()) { surfaces.erase(p); surfaces.push_back(surface); surfaces_reordered = true; } } if (!surfaces_reordered) BOOST_THROW_EXCEPTION(std::runtime_error("Invalid surface")); observers.surfaces_reordered(); return; } void ms::SurfaceStack::raise(SurfaceSet const& ss) { bool surfaces_reordered{false}; { RecursiveWriteLock ul(guard); auto const old_surfaces = surfaces; std::stable_partition( begin(surfaces), end(surfaces), [&](std::weak_ptr const& s) { return !ss.count(s); }); if (old_surfaces != surfaces) surfaces_reordered = true; } if (surfaces_reordered) observers.surfaces_reordered(); } void ms::SurfaceStack::create_rendering_tracker_for(std::shared_ptr const& surface) { auto const tracker = std::make_shared(surface); RecursiveWriteLock ul(guard); tracker->active_compositors(registered_compositors); rendering_trackers[surface.get()] = tracker; } void ms::SurfaceStack::update_rendering_tracker_compositors() { RecursiveReadLock ul(guard); for (auto const& pair : rendering_trackers) pair.second->active_compositors(registered_compositors); } void ms::SurfaceStack::add_observer(std::shared_ptr const& observer) { observers.add(observer); // Notify observer of existing surfaces RecursiveReadLock lk(guard); for (auto &surface : surfaces) { observer->surface_exists(surface.get()); } } void ms::SurfaceStack::remove_observer(std::weak_ptr const& observer) { auto o = observer.lock(); if (!o) BOOST_THROW_EXCEPTION(std::logic_error("Invalid observer (destroyed)")); o->end_observation(); observers.remove(o); } void ms::Observers::surface_added(ms::Surface* surface) { for_each([&](std::shared_ptr const& observer) { observer->surface_added(surface); }); } void ms::Observers::surface_removed(ms::Surface* surface) { for_each([&](std::shared_ptr const& observer) { observer->surface_removed(surface); }); } void ms::Observers::surfaces_reordered() { for_each([&](std::shared_ptr const& observer) { observer->surfaces_reordered(); }); } void ms::Observers::scene_changed() { for_each([&](std::shared_ptr const& observer) { observer->scene_changed(); }); } void ms::Observers::surface_exists(ms::Surface* surface) { for_each([&](std::shared_ptr const& observer) { observer->surface_exists(surface); }); } void ms::Observers::end_observation() { for_each([&](std::shared_ptr const& observer) { observer->end_observation(); }); } ./src/server/scene/surface_creation_parameters.cpp0000644000015600001650000001044112676616125022546 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/scene/surface_creation_parameters.h" namespace mg = mir::graphics; namespace ms = mir::scene; namespace mi = mir::input; namespace mf = mir::frontend; namespace geom = mir::geometry; ms::SurfaceCreationParameters::SurfaceCreationParameters() : buffer_usage{mg::BufferUsage::undefined}, pixel_format{mir_pixel_format_invalid}, input_mode{mi::InputReceptionMode::normal} { } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::of_name(std::string const& new_name) { name = new_name; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::of_size( geometry::Size new_size) { size = new_size; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::of_size( geometry::Width::ValueType width, geometry::Height::ValueType height) { return of_size({width, height}); } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::of_position(geometry::Point const& new_top_left) { top_left = new_top_left; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::of_buffer_usage( mg::BufferUsage new_buffer_usage) { buffer_usage = new_buffer_usage; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::of_pixel_format( MirPixelFormat new_pixel_format) { pixel_format = new_pixel_format; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::with_input_mode(input::InputReceptionMode const& new_mode) { input_mode = new_mode; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::with_output_id( graphics::DisplayConfigurationOutputId const& new_output_id) { output_id = new_output_id; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::of_type(MirSurfaceType the_type) { type = the_type; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::with_state(MirSurfaceState the_state) { state = the_state; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::with_preferred_orientation(MirOrientationMode mode) { preferred_orientation = mode; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::with_parent_id(mf::SurfaceId const& id) { parent_id = id; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::with_aux_rect(geometry::Rectangle const& rect) { aux_rect = rect; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::with_edge_attachment(MirEdgeAttachment edge) { edge_attachment = edge; return *this; } ms::SurfaceCreationParameters& ms::SurfaceCreationParameters::with_buffer_stream(mf::BufferStreamId const& id) { content_id = id; return *this; } bool ms::operator==( const SurfaceCreationParameters& lhs, const ms::SurfaceCreationParameters& rhs) { return lhs.name == rhs.name && lhs.size == rhs.size && lhs.top_left == rhs.top_left && lhs.buffer_usage == rhs.buffer_usage && lhs.pixel_format == rhs.pixel_format && lhs.input_mode == rhs.input_mode && lhs.output_id == rhs.output_id && lhs.state == rhs.state && lhs.type == rhs.type && lhs.preferred_orientation == rhs.preferred_orientation && lhs.parent_id == rhs.parent_id && lhs.content_id == rhs.content_id; } bool ms::operator!=( const SurfaceCreationParameters& lhs, const ms::SurfaceCreationParameters& rhs) { return !(lhs == rhs); } ms::SurfaceCreationParameters ms::a_surface() { return SurfaceCreationParameters(); } ./src/server/scene/prompt_session_manager_impl.cpp0000644000015600001650000001754312676616125022620 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "prompt_session_manager_impl.h" #include "mir/scene/prompt_session_creation_parameters.h" #include "mir/scene/prompt_session_listener.h" #include "mir/scene/session.h" #include "session_container.h" #include "prompt_session_container.h" #include "prompt_session_impl.h" namespace ms = mir::scene; ms::PromptSessionManagerImpl::PromptSessionManagerImpl( std::shared_ptr const& app_container, std::shared_ptr const& prompt_session_listener) : prompt_session_container(std::make_shared()), prompt_session_listener(prompt_session_listener), app_container(app_container) { } void ms::PromptSessionManagerImpl::stop_prompt_session_locked( std::lock_guard const&, std::shared_ptr const& prompt_session) const { std::vector> participants; prompt_session_container->for_each_participant_in_prompt_session(prompt_session.get(), [&](std::weak_ptr const& session, PromptSessionContainer::ParticipantType type) { if (type == PromptSessionContainer::ParticipantType::prompt_provider) { if (auto locked_session = session.lock()) participants.push_back(locked_session); } }); for (auto const& participant : participants) { if (prompt_session_container->remove_participant(prompt_session.get(), participant, PromptSessionContainer::ParticipantType::prompt_provider)) prompt_session_listener->prompt_provider_removed(*prompt_session, participant); } prompt_session->stop(helper_for(prompt_session)); prompt_session_container->remove_prompt_session(prompt_session); prompt_session_listener->stopping(prompt_session); } void ms::PromptSessionManagerImpl::remove_session(std::shared_ptr const& session) const { std::lock_guard lock(prompt_sessions_mutex); std::vector, PromptSessionContainer::ParticipantType>> prompt_sessions; prompt_session_container->for_each_prompt_session_with_participant(session, [&](std::shared_ptr const& prompt_session, PromptSessionContainer::ParticipantType participant_type) { prompt_sessions.push_back(std::make_pair(prompt_session, participant_type)); }); for(auto const& prompt_session : prompt_sessions) { if (prompt_session.second == PromptSessionContainer::ParticipantType::helper) { stop_prompt_session_locked(lock, prompt_session.first); } else { if (prompt_session_container->remove_participant(prompt_session.first.get(), session, prompt_session.second)) { if (prompt_session.second == PromptSessionContainer::ParticipantType::prompt_provider) prompt_session_listener->prompt_provider_removed(*prompt_session.first, session); } } } } void ms::PromptSessionManagerImpl::stop_prompt_session(std::shared_ptr const& prompt_session) const { std::lock_guard lock(prompt_sessions_mutex); stop_prompt_session_locked(lock, prompt_session); } void ms::PromptSessionManagerImpl::suspend_prompt_session(std::shared_ptr const& prompt_session) const { std::lock_guard lock(prompt_sessions_mutex); prompt_session->suspend(helper_for(prompt_session)); prompt_session_listener->suspending(prompt_session); } void ms::PromptSessionManagerImpl::resume_prompt_session(std::shared_ptr const& prompt_session) const { std::lock_guard lock(prompt_sessions_mutex); prompt_session->resume(helper_for(prompt_session)); prompt_session_listener->resuming(prompt_session); } std::shared_ptr ms::PromptSessionManagerImpl::start_prompt_session_for( std::shared_ptr const& session, PromptSessionCreationParameters const& params) const { auto prompt_session = std::make_shared(); std::shared_ptr application_session; app_container->for_each( [&](std::shared_ptr const& session) { if (session->process_id() == params.application_pid) { application_session = session; } }); if (!application_session) BOOST_THROW_EXCEPTION(std::runtime_error("Could not identify application session")); std::lock_guard lock(prompt_sessions_mutex); prompt_session_container->insert_prompt_session(prompt_session); if (!prompt_session_container->insert_participant(prompt_session.get(), session, PromptSessionContainer::ParticipantType::helper)) BOOST_THROW_EXCEPTION(std::runtime_error("Could not set prompt session helper")); prompt_session->start(session); prompt_session_listener->starting(prompt_session); prompt_session_container->insert_participant(prompt_session.get(), application_session, PromptSessionContainer::ParticipantType::application); return prompt_session; } void ms::PromptSessionManagerImpl::add_prompt_provider( std::shared_ptr const& prompt_session, std::shared_ptr const& prompt_provider) const { std::unique_lock lock(prompt_sessions_mutex); if (prompt_session_container->insert_participant(prompt_session.get(), prompt_provider, PromptSessionContainer::ParticipantType::prompt_provider)) prompt_session_listener->prompt_provider_added(*prompt_session, prompt_provider); } std::shared_ptr ms::PromptSessionManagerImpl::application_for( std::shared_ptr const& prompt_session) const { std::shared_ptr application_session; prompt_session_container->for_each_participant_in_prompt_session(prompt_session.get(), [&](std::weak_ptr const& session, PromptSessionContainer::ParticipantType type) { if (type == PromptSessionContainer::ParticipantType::application) application_session = session.lock(); }); return application_session; } std::shared_ptr ms::PromptSessionManagerImpl::helper_for( std::shared_ptr const& prompt_session) const { std::shared_ptr helper_session; prompt_session_container->for_each_participant_in_prompt_session(prompt_session.get(), [&](std::weak_ptr const& session, PromptSessionContainer::ParticipantType type) { if (type == PromptSessionContainer::ParticipantType::helper) helper_session = session.lock(); }); return helper_session; } void ms::PromptSessionManagerImpl::for_each_provider_in( std::shared_ptr const& prompt_session, std::function const& prompt_provider)> const& f) const { prompt_session_container->for_each_participant_in_prompt_session(prompt_session.get(), [&](std::weak_ptr const& session, PromptSessionContainer::ParticipantType type) { if (type == PromptSessionContainer::ParticipantType::prompt_provider) if (auto locked_session = session.lock()) f(locked_session); }); } ./src/server/scene/broadcasting_session_event_sink.cpp0000644000015600001650000000542512676616125023445 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #include "broadcasting_session_event_sink.h" namespace ms = mir::scene; /* * TODO: Use Boost.Signals2 for this when we default to Boost 1.54, see * https://svn.boost.org/trac/boost/ticket/8102 . For now, just use a custom * mechanism with coarse locking. When emitting events, we copy the handlers * to a temporary vector to avoid calling handlers while locked, while still * being thread-safe. */ void ms::BroadcastingSessionEventSink::handle_focus_change( std::shared_ptr const& session) { std::vector const&)>> handlers; { std::lock_guard lg{handler_mutex}; handlers = focus_change_handlers; } for (auto& handler : handlers) handler(session); } void ms::BroadcastingSessionEventSink::handle_no_focus() { std::vector> handlers; { std::lock_guard lg{handler_mutex}; handlers = no_focus_handlers; } for (auto& handler : handlers) handler(); } void ms::BroadcastingSessionEventSink::handle_session_stopping( std::shared_ptr const& session) { std::vector const&)>> handlers; { std::lock_guard lg{handler_mutex}; handlers = session_stopping_handlers; } for (auto& handler : handlers) handler(session); } void ms::BroadcastingSessionEventSink::register_focus_change_handler( std::function const& session)> const& handler) { std::lock_guard lg{handler_mutex}; focus_change_handlers.push_back(handler); } void ms::BroadcastingSessionEventSink::register_no_focus_handler( std::function const& handler) { std::lock_guard lg{handler_mutex}; no_focus_handlers.push_back(handler); } void ms::BroadcastingSessionEventSink::register_session_stopping_handler( std::function const& session)> const& handler) { std::lock_guard lg{handler_mutex}; session_stopping_handlers.push_back(handler); } ./src/server/scene/timeout_application_not_responding_detector.h0000644000015600001650000000545712676616125025541 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Christopher James Halse Rogers */ #ifndef MIR_SCENE_TIMOUT_APPLICATION_NOT_RESPONDING_DETECTOR_ #define MIR_SCENE_TIMOUT_APPLICATION_NOT_RESPONDING_DETECTOR_ #include "mir/scene/application_not_responding_detector.h" #include "mir/basic_observers.h" #include #include #include namespace mir { namespace time { class Alarm; class AlarmFactory; } namespace scene { class TimeoutApplicationNotRespondingDetector : public ApplicationNotRespondingDetector { public: TimeoutApplicationNotRespondingDetector( time::AlarmFactory& alarms, std::chrono::milliseconds period); template TimeoutApplicationNotRespondingDetector( time::AlarmFactory& alarms, std::chrono::duration period) : TimeoutApplicationNotRespondingDetector(alarms, std::chrono::duration_cast(period)) { } ~TimeoutApplicationNotRespondingDetector() override; void register_session(frontend::Session const* session, std::function const& pinger) override; void unregister_session(frontend::Session const* session) override; void pong_received(frontend::Session const* received_for) override; void register_observer(std::shared_ptr const& observer) override; void unregister_observer(std::shared_ptr const& observer) override; private: void handle_ping_cycle(); struct ANRContext; class ANRObservers : public Observer, private BasicObservers { public: using BasicObservers::add; using BasicObservers::remove; void session_unresponsive(Session const* session) override; void session_now_responsive(Session const* session) override; } observers; std::mutex session_mutex; std::unordered_map> sessions; std::vector unresponsive_sessions_temporary; std::chrono::milliseconds const period; std::unique_ptr const alarm; }; } } #endif // MIR_SCENE_TIMOUT_APPLICATION_NOT_RESPONDING_DETECTOR_ ./src/server/scene/rendering_tracker.cpp0000644000015600001650000000604012676616125020477 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "rendering_tracker.h" #include "mir/scene/surface.h" #include #include #include namespace ms = mir::scene; namespace mc = mir::compositor; ms::RenderingTracker::RenderingTracker( std::weak_ptr const& weak_surface) : weak_surface{weak_surface} { } void ms::RenderingTracker::rendered_in(mc::CompositorID cid) { std::lock_guard lock{guard}; ensure_is_active_compositor(cid); occlusions.erase(cid); configure_visibility(mir_surface_visibility_exposed); } void ms::RenderingTracker::occluded_in(mc::CompositorID cid) { std::lock_guard lock{guard}; ensure_is_active_compositor(cid); occlusions.insert(cid); if (occluded_in_all_active_compositors()) configure_visibility(mir_surface_visibility_occluded); } void ms::RenderingTracker::active_compositors(std::set const& cids) { std::lock_guard lock{guard}; active_compositors_ = cids; remove_occlusions_for_inactive_compositors(); if (occluded_in_all_active_compositors()) configure_visibility(mir_surface_visibility_occluded); } bool ms::RenderingTracker::is_exposed_in(mc::CompositorID cid) const { std::lock_guard lock{guard}; ensure_is_active_compositor(cid); if (occlusions.size() == 0) return true; return occlusions.find(cid) == occlusions.end(); } bool ms::RenderingTracker::occluded_in_all_active_compositors() { return occlusions == active_compositors_; } void ms::RenderingTracker::configure_visibility(MirSurfaceVisibility visibility) { if (auto const surface = weak_surface.lock()) surface->configure(mir_surface_attrib_visibility, visibility); } void ms::RenderingTracker::remove_occlusions_for_inactive_compositors() { std::set new_occlusions; std::set_intersection( active_compositors_.begin(), active_compositors_.end(), occlusions.begin(), occlusions.end(), std::inserter(new_occlusions, new_occlusions.begin())); occlusions = std::move(new_occlusions); } void ms::RenderingTracker::ensure_is_active_compositor(compositor::CompositorID cid) const { if (active_compositors_.find(cid) == active_compositors_.end()) BOOST_THROW_EXCEPTION(std::logic_error("No active compositor with supplied id")); } ./src/server/scene/timeout_application_not_responding_detector.cpp0000644000015600001650000001225012676616125026061 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Christopher James Halse Rogers */ #include "timeout_application_not_responding_detector.h" #include "mir/frontend/session.h" #include "mir/scene/session.h" #include "mir/time/alarm_factory.h" namespace ms = mir::scene; namespace mt = mir::time; struct ms::TimeoutApplicationNotRespondingDetector::ANRContext { ANRContext(std::function const& pinger) : pinger{pinger}, replied_since_last_ping{true}, flagged_as_unresponsive{false} { } std::function const pinger; bool replied_since_last_ping; bool flagged_as_unresponsive; }; void ms::TimeoutApplicationNotRespondingDetector::ANRObservers::session_unresponsive( Session const* session) { for_each([session](auto const& observer) { observer->session_unresponsive(session); }); } void ms::TimeoutApplicationNotRespondingDetector::ANRObservers::session_now_responsive( Session const* session) { for_each([session](auto const& observer) { observer->session_now_responsive(session); }); } ms::TimeoutApplicationNotRespondingDetector::TimeoutApplicationNotRespondingDetector( mt::AlarmFactory& alarms, std::chrono::milliseconds period) : period{period}, alarm{alarms.create_alarm(std::bind(&TimeoutApplicationNotRespondingDetector::handle_ping_cycle, this))} { } ms::TimeoutApplicationNotRespondingDetector::~TimeoutApplicationNotRespondingDetector() { } void ms::TimeoutApplicationNotRespondingDetector::register_session( frontend::Session const* session, std::function const& pinger) { bool alarm_needs_schedule; { std::lock_guard lock{session_mutex}; sessions[dynamic_cast(session)] = std::make_unique(pinger); alarm_needs_schedule = alarm->state() != mt::Alarm::State::pending; } if (alarm_needs_schedule) { alarm->reschedule_in(period); } } void ms::TimeoutApplicationNotRespondingDetector::unregister_session( frontend::Session const* session) { std::lock_guard lock{session_mutex}; sessions.erase(dynamic_cast(session)); } void ms::TimeoutApplicationNotRespondingDetector::pong_received( frontend::Session const* received_for) { bool needs_now_responsive_notification{false}; bool alarm_needs_rescheduling; { std::lock_guard lock{session_mutex}; auto& session_ctx = sessions.at(dynamic_cast(received_for)); if (session_ctx->flagged_as_unresponsive) { session_ctx->flagged_as_unresponsive = false; needs_now_responsive_notification = true; } session_ctx->replied_since_last_ping = true; alarm_needs_rescheduling = alarm->state() != mt::Alarm::State::pending; } if (needs_now_responsive_notification) { observers.session_now_responsive(dynamic_cast(received_for)); } if (alarm_needs_rescheduling) { alarm->reschedule_in(period); } } void ms::TimeoutApplicationNotRespondingDetector::register_observer( std::shared_ptr const& observer) { observers.add(observer); } void ms::TimeoutApplicationNotRespondingDetector::unregister_observer( std::shared_ptr const& observer) { observers.remove(observer); } void ms::TimeoutApplicationNotRespondingDetector::handle_ping_cycle() { bool needs_rearm{false}; { std::lock_guard lock{session_mutex}; for (auto const& session_pair : sessions) { bool const newly_unresponsive = !session_pair.second->replied_since_last_ping && !session_pair.second->flagged_as_unresponsive; bool const needs_ping = session_pair.second->replied_since_last_ping; if (newly_unresponsive) { session_pair.second->flagged_as_unresponsive = true; unresponsive_sessions_temporary.push_back(session_pair.first); } else if (needs_ping) { session_pair.second->pinger(); session_pair.second->replied_since_last_ping = false; needs_rearm = true; } } } // Dispatch notifications outside the lock. for (auto const& unresponsive_session : unresponsive_sessions_temporary) { observers.session_unresponsive(unresponsive_session); } unresponsive_sessions_temporary.clear(); if (needs_rearm) { this->alarm->reschedule_in(this->period); } } ./src/server/scene/session_event_handler_register.h0000644000015600001650000000320012676616125022734 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #ifndef MIR_SCENE_SESSION_EVENT_HANDLER_REGISTER_H_ #define MIR_SCENE_SESSION_EVENT_HANDLER_REGISTER_H_ #include #include namespace mir { namespace scene { class Session; class SessionEventHandlerRegister { public: virtual ~SessionEventHandlerRegister() = default; virtual void register_focus_change_handler( std::function const& session)> const& handler) = 0; virtual void register_no_focus_handler( std::function const& handler) = 0; virtual void register_session_stopping_handler( std::function const& session)> const& handler) = 0; protected: SessionEventHandlerRegister() = default; SessionEventHandlerRegister(SessionEventHandlerRegister const&) = delete; SessionEventHandlerRegister& operator=(SessionEventHandlerRegister const&) = delete; }; } } #endif /* MIR_SCENE_SESSION_EVENT_HANDLER_REGISTER_H_ */ ./src/server/scene/unsupported_coordinate_translator.h0000644000015600001650000000225612676616125023531 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_SCENE_UNSUPPORTED_COORDINATE_TRANSLATOR_H_ #define MIR_SCENE_UNSUPPORTED_COORDINATE_TRANSLATOR_H_ #include "mir/scene/coordinate_translator.h" namespace mir { namespace scene { class UnsupportedCoordinateTranslator : public CoordinateTranslator { geometry::Point surface_to_screen(std::shared_ptr surface, int32_t x, int32_t y) override; }; } } #endif // MIR_SCENE_UNSUPPORTED_COORDINATE_TRANSLATOR_H_ ./src/server/scene/gl_pixel_buffer.cpp0000644000015600001650000001267412676616125020155 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #include "gl_pixel_buffer.h" #include "mir/graphics/gl_context.h" #include "mir/graphics/buffer.h" #include "mir/renderer/gl/texture_source.h" #include #include #include namespace mg = mir::graphics; namespace ms = mir::scene; namespace geom = mir::geometry; namespace { bool is_big_endian() { uint32_t n = 1; return (*reinterpret_cast(&n) != 1); } inline uint32_t abgr_to_argb(uint32_t p) { return ((p << 16) & 0x00ff0000) | /* Move R to new position */ ((p) & 0x0000ff00) | /* G remains at same position */ ((p >> 16) & 0x000000ff) | /* Move B to new position */ ((p) & 0xff000000); /* A remains at same position */ } } ms::GLPixelBuffer::GLPixelBuffer(std::unique_ptr gl_context) : gl_context{std::move(gl_context)}, tex{0}, fbo{0}, gl_pixel_format{0}, pixels_need_y_flip{false} { /* * TODO: Handle systems that are big-endian, and therefore GL_BGRA doesn't * give the 0xAARRGGBB pixel format we need. */ if (is_big_endian()) { BOOST_THROW_EXCEPTION(std::runtime_error( "GLPixelBuffer doesn't support big endian architectures")); } } ms::GLPixelBuffer::~GLPixelBuffer() noexcept { /* * This may be called from a different thread * than the one that called prepare */ if (tex != 0 || fbo != 0) gl_context->make_current(); if (tex != 0) glDeleteTextures(1, &tex); if (fbo != 0) glDeleteFramebuffers(1, &fbo); } void ms::GLPixelBuffer::prepare() { gl_context->make_current(); if (tex == 0) glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glActiveTexture(GL_TEXTURE0); if (fbo == 0) glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); } void ms::GLPixelBuffer::fill_from(graphics::Buffer& buffer) { auto width = buffer.size().width.as_uint32_t(); auto height = buffer.size().height.as_uint32_t(); pixels.resize(width * height * 4); prepare(); auto const texture_source = dynamic_cast( buffer.native_buffer_base()); if (!texture_source) BOOST_THROW_EXCEPTION(std::logic_error("Buffer does not support GL rendering")); texture_source->gl_bind_to_texture(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); /* First try to get pixels as BGRA */ glGetError(); gl_pixel_format = GL_BGRA_EXT; glReadPixels(0, 0, width, height, gl_pixel_format, GL_UNSIGNED_BYTE, pixels.data()); /* If getting pixels as BGRA failed, fall back to RGBA */ if (glGetError() != GL_NO_ERROR) { gl_pixel_format = GL_RGBA; glReadPixels(0, 0, width, height, gl_pixel_format, GL_UNSIGNED_BYTE, pixels.data()); } size_ = buffer.size(); pixels_need_y_flip = true; } void const* ms::GLPixelBuffer::as_argb_8888() { if (pixels_need_y_flip) { auto const stride_val = stride().as_uint32_t(); auto const height = size_.height.as_uint32_t(); std::vector tmp(stride_val); for (unsigned int i = 0; i < height / 2; i++) { /* Store line i */ tmp.assign(&pixels[i * stride_val], &pixels[(i + 1) * stride_val]); /* Copy line height - i - 1 to line i */ copy_and_convert_pixel_line(&pixels[(height - i - 1) * stride_val], &pixels[i * stride_val]); /* Copy stored line (i) to height - i - 1 */ copy_and_convert_pixel_line(tmp.data(), &pixels[(height - i - 1) * stride_val]); } /* Process middle line if there is one */ if (height % 2 == 1) { copy_and_convert_pixel_line(&pixels[(height / 2) * stride_val], &pixels[(height / 2) * stride_val]); } pixels_need_y_flip = false; } return pixels.data(); } geom::Size ms::GLPixelBuffer::size() const { return size_; } geom::Stride ms::GLPixelBuffer::stride() const { return geom::Stride{size_.width.as_uint32_t() * sizeof(uint32_t)}; } void ms::GLPixelBuffer::copy_and_convert_pixel_line(char* src, char* dst) { if (gl_pixel_format == GL_RGBA) { /* Convert from abgr_8888 to argb_8888 while copying */ auto pixels_src = reinterpret_cast(src); auto pixels_dst = reinterpret_cast(dst); auto const width = size_.width.as_uint32_t(); for (uint32_t n = 0; n < width; n++) pixels_dst[n] = abgr_to_argb(pixels_src[n]); } else if (src != dst) { std::copy(src, src + stride().as_uint32_t(), dst); } } ./src/server/scene/mediating_display_changer.h0000644000015600001650000001062612676616125021636 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_SCENE_MEDIATING_DISPLAY_CHANGER_H_ #define MIR_SCENE_MEDIATING_DISPLAY_CHANGER_H_ #include "mir/frontend/display_changer.h" #include "mir/display_changer.h" #include "mir/shell/display_configuration_controller.h" #include "mir/input/input_region.h" #include #include namespace mir { class ServerActionQueue; namespace graphics { class Display; class DisplayConfigurationPolicy; class DisplayConfigurationReport; } namespace compositor { class Compositor; } namespace input { class InputRegion; } namespace scene { class SessionEventHandlerRegister; class SessionContainer; class Session; class MediatingDisplayChanger : public frontend::DisplayChanger, public mir::DisplayChanger, public shell::DisplayConfigurationController { public: MediatingDisplayChanger( std::shared_ptr const& display, std::shared_ptr const& compositor, std::shared_ptr const& display_configuration_policy, std::shared_ptr const& session_container, std::shared_ptr const& session_event_handler_register, std::shared_ptr const& server_action_queue, std::shared_ptr const& report, std::shared_ptr const& region); /* From mir::frontend::DisplayChanger */ std::shared_ptr base_configuration() override; void configure(std::shared_ptr const& session, std::shared_ptr const& conf) override; /* From mir::DisplayChanger */ void configure_for_hardware_change( std::shared_ptr const& conf, SystemStateHandling pause_resume_system) override; void pause_display_config_processing() override; void resume_display_config_processing() override; /* From shell::DisplayConfigurationController */ void set_base_configuration(std::shared_ptr const &conf) override; private: void focus_change_handler(std::shared_ptr const& session); void no_focus_handler(); void session_stopping_handler(std::shared_ptr const& session); void apply_config(std::shared_ptr const& conf, SystemStateHandling pause_resume_system); void apply_base_config(SystemStateHandling pause_resume_system); void send_config_to_all_sessions( std::shared_ptr const& conf); void update_input_rectangles(graphics::DisplayConfiguration const& conf); std::shared_ptr const display; std::shared_ptr const compositor; std::shared_ptr const display_configuration_policy; std::shared_ptr const session_container; std::shared_ptr const session_event_handler_register; std::shared_ptr const server_action_queue; std::shared_ptr const report; std::mutex configuration_mutex; std::map, std::shared_ptr, std::owner_less>> config_map; std::weak_ptr focused_session; std::shared_ptr base_configuration_; bool base_configuration_applied; std::shared_ptr const region; }; } } #endif /* MIR_SCENE_MEDIATING_DISPLAY_CHANGER_H_ */ ./src/server/scene/rendering_tracker.h0000644000015600001650000000333312676616125020146 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_SCENE_RENDERING_TRACKER_H_ #define MIR_SCENE_RENDERING_TRACKER_H_ #include "mir/compositor/compositor_id.h" #include #include #include #include "mir_toolkit/common.h" namespace mir { namespace scene { class Surface; class RenderingTracker { public: RenderingTracker(std::weak_ptr const& weak_surface); void rendered_in(compositor::CompositorID cid); void occluded_in(compositor::CompositorID cid); void active_compositors(std::set const& cids); bool is_exposed_in(compositor::CompositorID cid) const; private: bool occluded_in_all_active_compositors(); void configure_visibility(MirSurfaceVisibility visibility); void remove_occlusions_for_inactive_compositors(); void ensure_is_active_compositor(compositor::CompositorID cid) const; std::weak_ptr const weak_surface; std::set occlusions; std::set active_compositors_; std::mutex mutable guard; }; } } #endif ./src/server/scene/default_session_container.h0000644000015600001650000000265612676616125021716 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_SCENE_DEFAULT_SESSION_CONTAINER_H_ #define MIR_SCENE_DEFAULT_SESSION_CONTAINER_H_ #include #include #include #include "session_container.h" namespace mir { namespace scene { class DefaultSessionContainer : public SessionContainer { public: void insert_session(std::shared_ptr const& session) override; void remove_session(std::shared_ptr const& session) override; void for_each(std::function const&)> f) const override; std::shared_ptr successor_of(std::shared_ptr const& session) const override; private: std::vector> apps; mutable std::mutex guard; }; } } #endif // MIR_SCENE_DEFAULT_SESSION_CONTAINER_H_ ./src/server/scene/surface_allocator.cpp0000644000015600001650000000437412676616125020507 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "surface_allocator.h" #include "mir/scene/buffer_stream_factory.h" #include "mir/compositor/buffer_stream.h" #include "mir/input/input_channel_factory.h" #include "basic_surface.h" namespace geom=mir::geometry; namespace mc=mir::compositor; namespace mg=mir::graphics; namespace ms=mir::scene; namespace msh=mir::shell; namespace mi=mir::input; static inline bool has_alpha(MirPixelFormat fmt) { return (fmt == mir_pixel_format_abgr_8888) || (fmt == mir_pixel_format_argb_8888); } ms::SurfaceAllocator::SurfaceAllocator( std::shared_ptr const& input_factory, std::shared_ptr const& input_sender, std::shared_ptr const& default_cursor_image, std::shared_ptr const& report) : input_factory(input_factory), input_sender(input_sender), default_cursor_image(default_cursor_image), report(report) { } std::shared_ptr ms::SurfaceAllocator::create_surface( std::shared_ptr const& buffer_stream, SurfaceCreationParameters const& params) { auto actual_size = geom::Rectangle{params.top_left, buffer_stream->stream_size()}; bool nonrectangular = has_alpha(params.pixel_format); auto input_channel = input_factory->make_input_channel(); auto const surface = std::make_shared( params.name, actual_size, params.parent, nonrectangular, buffer_stream, input_channel, input_sender, default_cursor_image, report); return surface; } ./src/server/scene/null_observer.cpp0000644000015600001650000000203412676616125017667 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/scene/null_observer.h" namespace ms = mir::scene; void ms::NullObserver::surface_added(ms::Surface* /* surface */) {} void ms::NullObserver::surface_removed(ms::Surface* /* surface */) {} void ms::NullObserver::surfaces_reordered() {} void ms::NullObserver::surface_exists(ms::Surface* /* surface */) {} void ms::NullObserver::end_observation() {} ./src/server/scene/legacy_surface_change_notification.cpp0000644000015600001650000000600412676616125024036 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "legacy_surface_change_notification.h" namespace ms = mir::scene; namespace mg = mir::graphics; namespace mi = mir::input; namespace geom = mir::geometry; ms::LegacySurfaceChangeNotification::LegacySurfaceChangeNotification( std::function const& notify_scene_change, std::function const& notify_buffer_change) : notify_scene_change(notify_scene_change), notify_buffer_change(notify_buffer_change) { } void ms::LegacySurfaceChangeNotification::resized_to(geom::Size const& /*size*/) { notify_scene_change(); } void ms::LegacySurfaceChangeNotification::moved_to(geom::Point const& /*top_left*/) { notify_scene_change(); } void ms::LegacySurfaceChangeNotification::hidden_set_to(bool /*hide*/) { notify_scene_change(); } void ms::LegacySurfaceChangeNotification::frame_posted(int frames_available, geom::Size const& /* size */) { notify_buffer_change(frames_available); } void ms::LegacySurfaceChangeNotification::alpha_set_to(float /*alpha*/) { notify_scene_change(); } // An orientation change alone is not enough to trigger recomposition. void ms::LegacySurfaceChangeNotification::orientation_set_to(MirOrientation /*orientation*/) { } void ms::LegacySurfaceChangeNotification::transformation_set_to(glm::mat4 const& /*t*/) { notify_scene_change(); } // An attrib change alone is not enough to trigger recomposition. void ms::LegacySurfaceChangeNotification::attrib_changed(MirSurfaceAttrib /* attrib */, int /* value */) { } // Cursor image change request is not enough to trigger recomposition. void ms::LegacySurfaceChangeNotification::cursor_image_set_to(mg::CursorImage const& /* image */) { } void ms::LegacySurfaceChangeNotification::cursor_image_removed() { } void ms::LegacySurfaceChangeNotification::reception_mode_set_to(mi::InputReceptionMode /*mode*/) { notify_scene_change(); } // A client close request is not enough to trigger recomposition. void ms::LegacySurfaceChangeNotification::client_surface_close_requested() { } // A keymap change is not enough to trigger recomposition void ms::LegacySurfaceChangeNotification::keymap_changed(MirInputDeviceId, std::string const&, std::string const&, std::string const&, std::string const&) { } void ms::LegacySurfaceChangeNotification::renamed(char const*) { notify_scene_change(); } ./src/server/scene/prompt_session_container.h0000644000015600001650000001072012676616125021602 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #ifndef MIR_SCENE_PROMPT_SESSION_CONTAINER_H_ #define MIR_SCENE_PROMPT_SESSION_CONTAINER_H_ #include #include #include #include #include #include #include #include namespace mir { using boost::multi_index_container; using namespace boost::multi_index; namespace scene { class Session; class PromptSession; class PromptSessionContainer { public: PromptSessionContainer(); virtual ~PromptSessionContainer() = default; enum class ParticipantType { helper, application, prompt_provider, }; void insert_prompt_session(std::shared_ptr const& prompt_session); void remove_prompt_session(std::shared_ptr const& prompt_session); bool insert_participant(PromptSession* prompt_session, std::weak_ptr const& session, ParticipantType participant_type); bool remove_participant(PromptSession* prompt_session, std::weak_ptr const& session, ParticipantType participant_type); void for_each_participant_in_prompt_session(PromptSession* prompt_session, std::function const&, ParticipantType)> f) const; void for_each_prompt_session_with_participant(std::weak_ptr const& participant, ParticipantType participant_type, std::function const&)> f) const; void for_each_prompt_session_with_participant(std::weak_ptr const& participant, std::function const&, ParticipantType)> f) const; private: std::mutex mutable mutex; std::unordered_map> prompt_sessions; struct Participant { PromptSession* prompt_session; std::weak_ptr session; ParticipantType participant_type; uint insert_order; }; /** * A multi map for associating PromptSessions <-> Sessions. * indexed by insertion order for determining the Sessions participating in a PromptSession * and indexed for determining the Prompt Sessions in which a Session is participating. * A Session can be associated a number of times with a single PromptSession, providing it has a different type * eg A Session can be both a helper and a provider for a PromptSession. */ typedef multi_index_container< Participant, indexed_by< ordered_non_unique< composite_key< Participant, member, member > >, ordered_unique< composite_key< Participant, member, &Participant::session>, member, member >, composite_key_compare< std::owner_less>, std::less, std::less> > > > PromptSessionParticipants; typedef nth_index::type participant_by_prompt_session; typedef nth_index::type participant_by_session; PromptSessionParticipants participant_map; participant_by_prompt_session& prompt_session_index; participant_by_session& participant_index; }; } } #endif // MIR_SCENE_PROMPT_SESSION_CONTAINER_H_ ./src/server/scene/session_event_sink.h0000644000015600001650000000252412676616125020367 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #ifndef MIR_SCENE_SESSION_EVENT_SINK_H_ #define MIR_SCENE_SESSION_EVENT_SINK_H_ #include namespace mir { namespace scene { class Session; class SessionEventSink { public: virtual ~SessionEventSink() = default; virtual void handle_focus_change(std::shared_ptr const& session) = 0; virtual void handle_no_focus() = 0; virtual void handle_session_stopping(std::shared_ptr const& session) = 0; protected: SessionEventSink() = default; SessionEventSink(SessionEventSink const&) = delete; SessionEventSink& operator=(SessionEventSink const&) = delete; }; } } #endif /* MIR_SCENE_SESSION_EVENT_SINK_H_ */ ./src/server/scene/surface_allocator.h0000644000015600001650000000350112676616125020143 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_SCENE_SURFACE_ALLOCATOR_H_ #define MIR_SCENE_SURFACE_ALLOCATOR_H_ #include "mir/scene/surface_factory.h" namespace mir { namespace input { class InputChannelFactory; class InputSender; } namespace graphics { class CursorImage; } namespace compositor { class BufferStream; } namespace scene { class SceneReport; class SurfaceAllocator : public SurfaceFactory { public: SurfaceAllocator( std::shared_ptr const& input_factory, std::shared_ptr const& input_sender, std::shared_ptr const& default_cursor_image, std::shared_ptr const& report); std::shared_ptr create_surface( std::shared_ptr const& buffer_stream, SurfaceCreationParameters const& params) override; private: std::shared_ptr const input_factory; std::shared_ptr const input_sender; std::shared_ptr const default_cursor_image; std::shared_ptr const report; }; } } #endif /* MIR_SCENE_SURFACE_ALLOCATOR_H_ */ ./src/server/scene/basic_surface.h0000644000015600001650000001536712676616125017261 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #ifndef MIR_SCENE_BASIC_SURFACE_H_ #define MIR_SCENE_BASIC_SURFACE_H_ #include "mir/scene/surface.h" #include "mir/basic_observers.h" #include "mir/scene/surface_observers.h" #include "mir/input/validator.h" #include "mir/geometry/rectangle.h" #include "mir_toolkit/common.h" #include #include #include #include #include #include namespace mir { namespace compositor { struct BufferIPCPackage; class BufferStream; } namespace frontend { class EventSink; } namespace graphics { class Buffer; } namespace input { class InputChannel; class InputSender; class Surface; } namespace scene { class SceneReport; class CursorStreamImageAdapter; class BasicSurface : public Surface { public: BasicSurface( std::string const& name, geometry::Rectangle rect, bool nonrectangular, std::shared_ptr const& buffer_stream, std::shared_ptr const& input_channel, std::shared_ptr const& sender, std::shared_ptr const& cursor_image, std::shared_ptr const& report); BasicSurface( std::string const& name, geometry::Rectangle rect, std::weak_ptr const& parent, bool nonrectangular, std::shared_ptr const& buffer_stream, std::shared_ptr const& input_channel, std::shared_ptr const& sender, std::shared_ptr const& cursor_image, std::shared_ptr const& report); ~BasicSurface() noexcept; std::string name() const override; void move_to(geometry::Point const& top_left) override; float alpha() const override; void set_hidden(bool is_hidden); geometry::Size size() const override; geometry::Size client_size() const override; std::shared_ptr primary_buffer_stream() const override; void set_streams(std::list const& streams) override; bool supports_input() const override; int client_input_fd() const override; std::shared_ptr input_channel() const override; input::InputReceptionMode reception_mode() const override; void set_reception_mode(input::InputReceptionMode mode) override; void set_input_region(std::vector const& input_rectangles) override; std::shared_ptr buffer_stream() const; void resize(geometry::Size const& size) override; geometry::Point top_left() const override; geometry::Rectangle input_bounds() const override; bool input_area_contains(geometry::Point const& point) const override; void consume(MirEvent const* event) override; void set_alpha(float alpha) override; void set_orientation(MirOrientation orientation) override; void set_transformation(glm::mat4 const&) override; bool visible() const override; graphics::RenderableList generate_renderables(compositor::CompositorID id) const override; int buffers_ready_for_compositor(void const* compositor_id) const override; MirSurfaceType type() const override; MirSurfaceState state() const override; int configure(MirSurfaceAttrib attrib, int value) override; int query(MirSurfaceAttrib attrib) const override; void hide() override; void show() override; void set_cursor_image(std::shared_ptr const& image) override; std::shared_ptr cursor_image() const override; void set_cursor_stream(std::shared_ptr const& stream, geometry::Displacement const& hotspot) override; void set_cursor_from_buffer(graphics::Buffer& buffer, geometry::Displacement const& hotspot); void request_client_surface_close() override; std::shared_ptr parent() const override; void add_observer(std::shared_ptr const& observer) override; void remove_observer(std::weak_ptr const& observer) override; int dpi() const; void set_keymap(MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) override; void rename(std::string const& title) override; private: bool visible(std::unique_lock&) const; MirSurfaceType set_type(MirSurfaceType t); // Use configure() to make public changes MirSurfaceState set_state(MirSurfaceState s); int set_dpi(int); MirSurfaceVisibility set_visibility(MirSurfaceVisibility v); int set_swap_interval(int); MirSurfaceFocusState set_focus_state(MirSurfaceFocusState f); MirOrientationMode set_preferred_orientation(MirOrientationMode mode); SurfaceObservers observers; std::mutex mutable guard; std::string surface_name; geometry::Rectangle surface_rect; glm::mat4 transformation_matrix; float surface_alpha; bool hidden; input::InputReceptionMode input_mode; const bool nonrectangular; std::vector custom_input_rectangles; std::shared_ptr const surface_buffer_stream; std::shared_ptr const server_input_channel; std::shared_ptr const input_sender; std::shared_ptr cursor_image_; std::shared_ptr const report; std::weak_ptr const parent_; std::list layers; // Surface attributes: MirSurfaceType type_ = mir_surface_type_normal; MirSurfaceState state_ = mir_surface_state_restored; int swapinterval_ = 1; MirSurfaceFocusState focus_ = mir_surface_unfocused; int dpi_ = 0; MirSurfaceVisibility visibility_ = mir_surface_visibility_occluded; MirOrientationMode pref_orientation_mode = mir_orientation_mode_any; std::unique_ptr const cursor_stream_adapter; input::Validator input_validator; }; } } #endif // MIR_SCENE_BASIC_SURFACE_H_ ./src/server/scene/output_properties_cache.h0000644000015600001650000000313012676616125021410 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_SCENE_OUTPUT_PROPERTIES_CACHE_H_ #define MIR_SCENE_OUTPUT_PROPERTIES_CACHE_H_ #include "mir_toolkit/common.h" #include "mir/geometry/rectangle.h" #include "mir/graphics/display_configuration.h" #include #include #include namespace mir { namespace scene { struct OutputProperties { geometry::Rectangle extents; int dpi; float scale; MirFormFactor form_factor; graphics::DisplayConfigurationOutputId id; }; class OutputPropertiesCache { public: void update_from(graphics::DisplayConfiguration const& config); std::shared_ptr properties_for(geometry::Rectangle const& extents) const; private: std::shared_ptr> get_cache() const; std::mutex mutable mutex; std::shared_ptr> cache; }; } } #endif //MIR_SCENE_OUTPUT_PROPERTIES_CACHE_H_ ./src/server/scene/CMakeLists.txt0000644000015600001650000000162212676616125017044 0ustar jenkinsjenkinsinclude_directories( ${PROJECT_SOURCE_DIR}/include/renderers/gl ) ADD_LIBRARY( mirscene OBJECT application_session.cpp basic_surface.cpp broadcasting_session_event_sink.cpp default_configuration.cpp default_session_container.cpp gl_pixel_buffer.cpp global_event_sender.cpp mediating_display_changer.cpp session_manager.cpp surface_allocator.cpp surface_creation_parameters.cpp surface_stack.cpp surface_event_source.cpp null_surface_observer.cpp null_observer.cpp threaded_snapshot_strategy.cpp legacy_scene_change_notification.cpp legacy_surface_change_notification.cpp prompt_session_container.cpp prompt_session_impl.cpp prompt_session_manager_impl.cpp rendering_tracker.cpp default_coordinate_translator.cpp unsupported_coordinate_translator.cpp timeout_application_not_responding_detector.cpp output_properties_cache.cpp output_properties_cache.h ) ./src/server/scene/null_surface_observer.cpp0000644000015600001650000000402412676616125021400 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/scene/null_surface_observer.h" namespace ms = mir::scene; namespace mg = mir::graphics; void ms::NullSurfaceObserver::attrib_changed(MirSurfaceAttrib /*attrib*/, int /*value*/) {} void ms::NullSurfaceObserver::resized_to(geometry::Size const& /*size*/) {} void ms::NullSurfaceObserver::moved_to(geometry::Point const& /*top_left*/) {} void ms::NullSurfaceObserver::hidden_set_to(bool /*hide*/) {} void ms::NullSurfaceObserver::frame_posted(int /*frames_available*/, geometry::Size const& /* size */) {} void ms::NullSurfaceObserver::alpha_set_to(float /*alpha*/) {} void ms::NullSurfaceObserver::orientation_set_to(MirOrientation /*orientation*/) {} void ms::NullSurfaceObserver::transformation_set_to(glm::mat4 const& /*t*/) {} void ms::NullSurfaceObserver::reception_mode_set_to(input::InputReceptionMode /*mode*/) {} void ms::NullSurfaceObserver::cursor_image_set_to(mg::CursorImage const& /*image*/) {} void ms::NullSurfaceObserver::client_surface_close_requested() {} void ms::NullSurfaceObserver::keymap_changed(MirInputDeviceId /* id */, std::string const& /*model*/, std::string const& /*layout*/, std::string const& /*variant*/, std::string const& /**/) { } void ms::NullSurfaceObserver::renamed(char const*) {} void ms::NullSurfaceObserver::cursor_image_removed() {} ./src/server/scene/prompt_session_manager_impl.h0000644000015600001650000000573712676616125022267 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SCENE_PROMPT_SESSION_MANAGERIMPL_H_ #define MIR_SCENE_PROMPT_SESSION_MANAGERIMPL_H_ #include "mir/scene/prompt_session_manager.h" #include "mir_toolkit/common.h" #include #include namespace mir { namespace scene { class Session; class SessionContainer; class PromptSession; class PromptSessionContainer; class PromptSessionCreationParameters; class PromptSessionListener; class PromptSessionManagerImpl : public scene::PromptSessionManager { public: explicit PromptSessionManagerImpl( std::shared_ptr const& app_container, std::shared_ptr const& prompt_session_listener); std::shared_ptr start_prompt_session_for( std::shared_ptr const& session, PromptSessionCreationParameters const& params) const override; void stop_prompt_session( std::shared_ptr const& prompt_session) const override; void suspend_prompt_session( std::shared_ptr const& prompt_session) const override; void resume_prompt_session( std::shared_ptr const& prompt_session) const override; void add_prompt_provider( std::shared_ptr const& prompt_session, std::shared_ptr const& prompt_provider) const override; void remove_session( std::shared_ptr const& session) const override; std::shared_ptr application_for( std::shared_ptr const& prompt_session) const override; std::shared_ptr helper_for( std::shared_ptr const& prompt_session) const override; void for_each_provider_in( std::shared_ptr const& prompt_session, std::function const& prompt_provider)> const& f) const override; private: std::shared_ptr const prompt_session_container; std::shared_ptr const prompt_session_listener; std::shared_ptr const app_container; std::mutex mutable prompt_sessions_mutex; void stop_prompt_session_locked( std::lock_guard const&, std::shared_ptr const& prompt_session) const; }; } } #endif // MIR_SCENE_PROMPT_SESSION_MANAGERIMPL_H_ ./src/server/scene/application_session.cpp0000644000015600001650000003155012676616157021066 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "application_session.h" #include "snapshot_strategy.h" #include "default_session_container.h" #include "output_properties_cache.h" #include "mir/scene/surface.h" #include "mir/scene/surface_event_source.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/scene/session_listener.h" #include "mir/scene/surface_factory.h" #include "mir/scene/buffer_stream_factory.h" #include "mir/shell/surface_stack.h" #include "mir/compositor/buffer_stream.h" #include "mir/events/event_builders.h" #include "mir/frontend/event_sink.h" #include #include #include #include #include #include namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell; namespace mg = mir::graphics; namespace mev = mir::events; ms::ApplicationSession::ApplicationSession( std::shared_ptr const& surface_stack, std::shared_ptr const& surface_factory, std::shared_ptr const& buffer_stream_factory, pid_t pid, std::string const& session_name, std::shared_ptr const& snapshot_strategy, std::shared_ptr const& session_listener, mg::DisplayConfiguration const& initial_config, std::shared_ptr const& sink) : surface_stack(surface_stack), surface_factory(surface_factory), buffer_stream_factory(buffer_stream_factory), pid(pid), session_name(session_name), snapshot_strategy(snapshot_strategy), session_listener(session_listener), event_sink(sink), next_surface_id(0) { assert(surface_stack); output_cache.update_from(initial_config); } ms::ApplicationSession::~ApplicationSession() { std::unique_lock lock(surfaces_and_streams_mutex); for (auto const& pair_id_surface : surfaces) { session_listener->destroying_surface(*this, pair_id_surface.second); surface_stack->remove_surface(pair_id_surface.second); } } mf::SurfaceId ms::ApplicationSession::next_id() { return mf::SurfaceId(next_surface_id.fetch_add(1)); } mf::SurfaceId ms::ApplicationSession::create_surface( SurfaceCreationParameters const& the_params, std::shared_ptr const& surface_sink) { auto const id = next_id(); mf::BufferStreamId const stream_id{the_params.content_id.is_set() ? the_params.content_id.value().as_value() : id.as_value()}; auto params = the_params; if (params.parent_id.is_set()) params.parent = checked_find(the_params.parent_id.value())->second; std::shared_ptr buffer_stream; if (params.content_id.is_set()) { buffer_stream = checked_find(params.content_id.value())->second; } else { mg::BufferProperties buffer_properties{params.size, params.pixel_format, params.buffer_usage}; buffer_stream = buffer_stream_factory->create_buffer_stream( stream_id, surface_sink, buffer_properties); } auto surface = surface_factory->create_surface(buffer_stream, params); surface_stack->add_surface(surface, params.input_mode); if (params.state.is_set()) surface->configure(mir_surface_attrib_state, params.state.value()); if (params.type.is_set()) surface->configure(mir_surface_attrib_type, params.type.value()); if (params.preferred_orientation.is_set()) surface->configure(mir_surface_attrib_preferred_orientation, params.preferred_orientation.value()); if (params.input_shape.is_set()) surface->set_input_region(params.input_shape.value()); auto const observer = std::make_shared( id, *surface, output_cache, surface_sink); surface->add_observer(observer); { std::unique_lock lock(surfaces_and_streams_mutex); surfaces[id] = surface; streams[stream_id] = buffer_stream; } observer->moved_to(surface->top_left()); session_listener->surface_created(*this, surface); return id; } ms::ApplicationSession::Surfaces::const_iterator ms::ApplicationSession::checked_find(mf::SurfaceId id) const { auto p = surfaces.find(id); if (p == surfaces.end()) BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SurfaceId")); return p; } ms::ApplicationSession::Streams::const_iterator ms::ApplicationSession::checked_find(mf::BufferStreamId id) const { auto p = streams.find(id); if (p == streams.end()) BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SurfaceId")); return p; } std::shared_ptr ms::ApplicationSession::get_surface(mf::SurfaceId id) const { return surface(id); } std::shared_ptr ms::ApplicationSession::surface(mf::SurfaceId id) const { std::unique_lock lock(surfaces_and_streams_mutex); return checked_find(id)->second; } std::shared_ptr ms::ApplicationSession::surface_after(std::shared_ptr const& before) const { std::lock_guard lock(surfaces_and_streams_mutex); auto current = surfaces.begin(); for (; current != surfaces.end(); ++current) { if (current->second == before) break; } if (current == surfaces.end()) BOOST_THROW_EXCEPTION(std::runtime_error("surface_after: surface is not a member of this session")); auto const can_take_focus = [](Surfaces::value_type const &s) { switch (s.second->type()) { case mir_surface_type_normal: /**< AKA "regular" */ case mir_surface_type_utility: /**< AKA "floating" */ case mir_surface_type_dialog: case mir_surface_type_satellite: /**< AKA "toolbox"/"toolbar" */ case mir_surface_type_freestyle: case mir_surface_type_menu: case mir_surface_type_inputmethod: /**< AKA "OSK" or handwriting etc. */ return true; case mir_surface_type_gloss: case mir_surface_type_tip: /**< AKA "tooltip" */ default: // Cannot have input focus - skip it return false; } }; auto next = std::find_if(++current, end(surfaces), can_take_focus); if (next == surfaces.end()) next = std::find_if(begin(surfaces), current, can_take_focus); return next->second; } void ms::ApplicationSession::take_snapshot(SnapshotCallback const& snapshot_taken) { //TODO: taking a snapshot of a session doesn't make much sense. Snapshots can be on surfaces //or bufferstreams, as those represent some content. A multi-surface session doesn't have enough //info to cobble together a snapshot buffer without WM info. for(auto const& surface_it : surfaces) { if (default_surface() == surface_it.second) { auto id = mf::BufferStreamId(surface_it.first.as_value()); snapshot_strategy->take_snapshot_of(checked_find(id)->second, snapshot_taken); return; } } snapshot_taken(Snapshot()); } std::shared_ptr ms::ApplicationSession::default_surface() const { std::unique_lock lock(surfaces_and_streams_mutex); if (surfaces.size()) return surfaces.begin()->second; else return std::shared_ptr(); } void ms::ApplicationSession::destroy_surface(mf::SurfaceId id) { std::unique_lock lock(surfaces_and_streams_mutex); destroy_surface(lock, checked_find(id)); } std::string ms::ApplicationSession::name() const { return session_name; } pid_t ms::ApplicationSession::process_id() const { return pid; } void ms::ApplicationSession::force_requests_to_complete() { std::unique_lock lock(surfaces_and_streams_mutex); for (auto& stream : streams) { stream.second->force_requests_to_complete(); } } void ms::ApplicationSession::hide() { std::unique_lock lock(surfaces_and_streams_mutex); for (auto& id_s : surfaces) { id_s.second->hide(); } } void ms::ApplicationSession::show() { std::unique_lock lock(surfaces_and_streams_mutex); for (auto& id_s : surfaces) { id_s.second->show(); } } void ms::ApplicationSession::send_display_config(mg::DisplayConfiguration const& info) { output_cache.update_from(info); event_sink->handle_display_config_change(info); std::lock_guard lock{surfaces_and_streams_mutex}; for (auto& surface : surfaces) { auto output_properties = output_cache.properties_for(geometry::Rectangle{ surface.second->top_left(), surface.second->size()}); if (output_properties) { event_sink->handle_event( *mev::make_event( surface.first, output_properties->dpi, output_properties->scale, output_properties->form_factor, static_cast(output_properties->id.as_value()) )); } } } void ms::ApplicationSession::send_input_device_change(std::vector> const& devices) { event_sink->handle_input_device_change(devices); } void ms::ApplicationSession::set_lifecycle_state(MirLifecycleState state) { event_sink->handle_lifecycle_event(state); } void ms::ApplicationSession::start_prompt_session() { // All sessions which are part of the prompt session get this event. event_sink->handle_event(*mev::make_event(mir_prompt_session_state_started)); } void ms::ApplicationSession::stop_prompt_session() { event_sink->handle_event(*mev::make_event(mir_prompt_session_state_stopped)); } void ms::ApplicationSession::suspend_prompt_session() { event_sink->handle_event(*mev::make_event(mir_prompt_session_state_suspended)); } void ms::ApplicationSession::resume_prompt_session() { start_prompt_session(); } std::shared_ptr ms::ApplicationSession::get_buffer_stream(mf::BufferStreamId id) const { std::unique_lock lock(surfaces_and_streams_mutex); return checked_find(id)->second; } mf::BufferStreamId ms::ApplicationSession::create_buffer_stream(mg::BufferProperties const& props) { auto const id = static_cast(next_id().as_value()); auto stream = buffer_stream_factory->create_buffer_stream(id, event_sink, props); std::unique_lock lock(surfaces_and_streams_mutex); streams[id] = stream; return id; } void ms::ApplicationSession::destroy_buffer_stream(mf::BufferStreamId id) { std::unique_lock lock(surfaces_and_streams_mutex); streams.erase(checked_find(id)); } void ms::ApplicationSession::configure_streams( ms::Surface& surface, std::vector const& streams) { std::list list; for (auto& stream : streams) { auto s = checked_find(stream.stream_id)->second; list.emplace_back(ms::StreamInfo{s, stream.displacement}); } surface.set_streams(list); } void ms::ApplicationSession::destroy_surface(std::weak_ptr const& surface) { auto const ss = surface.lock(); std::unique_lock lock(surfaces_and_streams_mutex); auto p = find_if(begin(surfaces), end(surfaces), [&](Surfaces::value_type const& val) { return val.second == ss; }); if (p == surfaces.end()) BOOST_THROW_EXCEPTION(std::runtime_error("Invalid Surface")); destroy_surface(lock, p); } void ms::ApplicationSession::destroy_surface(std::unique_lock& lock, Surfaces::const_iterator in_surfaces) { auto const surface = in_surfaces->second; auto const id = in_surfaces->first; session_listener->destroying_surface(*this, surface); surfaces.erase(in_surfaces); auto stream_it = streams.find(mir::frontend::BufferStreamId(id.as_value())); if (stream_it != streams.end()) { stream_it->second->force_requests_to_complete(); streams.erase(stream_it); } lock.unlock(); surface_stack->remove_surface(surface); } ./src/server/scene/default_coordinate_translator.h0000644000015600001650000000220112676616125022553 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_FRONTEND_DEFAULT_COORDINATE_TRANSLATOR_H_ #define MIR_FRONTEND_DEFAULT_COORDINATE_TRANSLATOR_H_ #include "mir/scene/coordinate_translator.h" namespace mir { namespace scene { class DefaultCoordinateTranslator : public CoordinateTranslator { public: geometry::Point surface_to_screen(std::shared_ptr surface, int32_t x, int32_t y); }; } } #endif // MIR_FRONTEND_DEFAULT_COORDINATE_TRANSLATOR_H_ ./src/server/scene/legacy_surface_change_notification.h0000644000015600001650000000436012676616125023506 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SCENE_LEGACY_SURFACE_CHANGE_NOTIFICATION_H_ #define MIR_SCENE_LEGACY_SURFACE_CHANGE_NOTIFICATION_H_ #include "mir/scene/surface_observer.h" #include namespace mir { namespace scene { class LegacySurfaceChangeNotification : public mir::scene::SurfaceObserver { public: LegacySurfaceChangeNotification( std::function const& notify_scene_change, std::function const& notify_buffer_change); void resized_to(geometry::Size const& /*size*/) override; void moved_to(geometry::Point const& /*top_left*/) override; void hidden_set_to(bool /*hide*/) override; void frame_posted(int frames_available, geometry::Size const& size) override; void alpha_set_to(float /*alpha*/) override; void orientation_set_to(MirOrientation orientation) override; void transformation_set_to(glm::mat4 const& /*t*/) override; void attrib_changed(MirSurfaceAttrib, int) override; void reception_mode_set_to(input::InputReceptionMode mode) override; void cursor_image_set_to(graphics::CursorImage const& image) override; void client_surface_close_requested() override; void keymap_changed(MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) override; void renamed(char const*) override; void cursor_image_removed() override; private: std::function const notify_scene_change; std::function const notify_buffer_change; }; } } #endif // MIR_SCENE_LEGACY_SURFACE_CHANGE_NOTIFICATION_H_ ./src/server/scene/unsupported_coordinate_translator.cpp0000644000015600001650000000221012676616125024052 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "unsupported_coordinate_translator.h" #include "mir/frontend/unsupported_feature_exception.h" #include namespace mg = mir::geometry; namespace mf = mir::frontend; namespace ms = mir::scene; mg::Point ms::UnsupportedCoordinateTranslator::surface_to_screen( std::shared_ptr /*surface*/, int32_t /*x*/, int32_t /*y*/) { BOOST_THROW_EXCEPTION(mf::unsupported_feature()); } ./src/server/scene/global_event_sender.h0000644000015600001650000000312112676616125020452 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_SCENE_GLOBAL_EVENT_SENDER_H_ #define MIR_SCENE_GLOBAL_EVENT_SENDER_H_ #include "mir/frontend/event_sink.h" #include namespace mir { namespace scene { class SessionContainer; class GlobalEventSender : public frontend::EventSink { public: GlobalEventSender(std::shared_ptr const&); void handle_event(MirEvent const& e) override; void handle_lifecycle_event(MirLifecycleState state) override; void handle_display_config_change(graphics::DisplayConfiguration const& config) override; void handle_input_device_change(std::vector> const& devices) override; void send_ping(int32_t serial) override; void send_buffer(frontend::BufferStreamId id, graphics::Buffer& buffer, graphics::BufferIpcMsgType) override; private: std::shared_ptr const sessions; }; } } #endif /* MIR_SCENE_GLOBAL_EVENT_SENDER_H_ */ ./src/server/scene/prompt_session_impl.cpp0000644000015600001650000000445112676616125021120 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Nick Dedekind */ #include "prompt_session_impl.h" #include "mir/scene/session.h" namespace ms = mir::scene; ms::PromptSessionImpl::PromptSessionImpl() : current_state(mir_prompt_session_state_stopped) { } void ms::PromptSessionImpl::start(std::shared_ptr const& helper_session) { std::lock_guard lk(guard); if (current_state == mir_prompt_session_state_stopped) { current_state = mir_prompt_session_state_started; if (helper_session) helper_session->start_prompt_session(); } } void ms::PromptSessionImpl::stop(std::shared_ptr const& helper_session) { std::lock_guard lk(guard); if (current_state != mir_prompt_session_state_stopped) { current_state = mir_prompt_session_state_stopped; if (helper_session) helper_session->stop_prompt_session(); } } void ms::PromptSessionImpl::suspend(std::shared_ptr const& helper_session) { std::lock_guard lk(guard); if (current_state == mir_prompt_session_state_started) { current_state = mir_prompt_session_state_suspended; if (helper_session) helper_session->suspend_prompt_session(); } } void ms::PromptSessionImpl::resume(std::shared_ptr const& helper_session) { std::lock_guard lk(guard); if (current_state == mir_prompt_session_state_suspended) { current_state = mir_prompt_session_state_started; if (helper_session) helper_session->resume_prompt_session(); } } MirPromptSessionState ms::PromptSessionImpl::state() const { return current_state; } ./src/server/scene/threaded_snapshot_strategy.h0000644000015600001650000000274212676616125022102 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alexandros Frantzis */ #ifndef MIR_SCENE_THREADED_SNAPSHOT_STRATEGY_H_ #define MIR_SCENE_THREADED_SNAPSHOT_STRATEGY_H_ #include "snapshot_strategy.h" #include #include #include namespace mir { namespace scene { class PixelBuffer; class SnapshottingFunctor; class ThreadedSnapshotStrategy : public SnapshotStrategy { public: ThreadedSnapshotStrategy(std::shared_ptr const& pixels); ~ThreadedSnapshotStrategy() noexcept; void take_snapshot_of( std::shared_ptr const& surface_buffer_access, SnapshotCallback const& snapshot_taken); private: std::shared_ptr const pixels; std::unique_ptr functor; std::thread thread; }; } } #endif /* MIR_SCENE_THREADED_SNAPSHOT_STRATEGY_H_ */ ./src/server/default_emergency_cleanup.cpp0000644000015600001650000000320512676616125021103 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "default_emergency_cleanup.h" void mir::DefaultEmergencyCleanup::add(EmergencyCleanupHandler const& handler) { std::lock_guard lock{handlers_mutex}; last_item()->next.reset( new ListItem{std::make_shared(handler), nullptr}); ++num_handlers; } void mir::DefaultEmergencyCleanup::add(ModuleEmergencyCleanupHandler handler) { std::lock_guard lock{handlers_mutex}; last_item()->next.reset(new ListItem{std::move(handler), nullptr}); ++num_handlers; } void mir::DefaultEmergencyCleanup::operator()() const { int handlers_left = num_handlers; ListItem const* item = &head; while (handlers_left-- > 0) { item = item->next.get(); (*item->handler)(); } } mir::DefaultEmergencyCleanup::ListItem* mir::DefaultEmergencyCleanup::last_item() { ListItem* item = &head; while (item->next) item = item->next.get(); return item; } ./src/server/default_emergency_cleanup.h0000644000015600001650000000256312676616125020556 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_DEFAULT_EMERGENCY_CLEANUP_H_ #define MIR_DEFAULT_EMERGENCY_CLEANUP_H_ #include #include #include #include namespace mir { class DefaultEmergencyCleanup : public EmergencyCleanup { public: void add(EmergencyCleanupHandler const& handler) override; void add(ModuleEmergencyCleanupHandler handler) override; void operator()() const override; private: struct ListItem { std::shared_ptr handler; std::unique_ptr next; }; ListItem* last_item(); ListItem head; std::atomic num_handlers{0}; std::mutex handlers_mutex; }; } #endif ./src/server/basic_callback.cpp0000644000015600001650000000170612676616125016613 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #include "mir/basic_callback.h" mir::BasicCallback::BasicCallback(std::function const& callback) : callback{callback} { } void mir::BasicCallback::operator()() { callback(); } void mir::BasicCallback::lock() { } void mir::BasicCallback::unlock() { } ./src/server/shell/0000755000015600001650000000000012676616160014314 5ustar jenkinsjenkins./src/server/shell/shell_wrapper.cpp0000644000015600001650000001043012676616125017666 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/shell/shell_wrapper.h" #include "mir/geometry/point.h" namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell; msh::ShellWrapper::ShellWrapper(std::shared_ptr const& wrapped) : wrapped(wrapped) { } std::shared_ptr msh::ShellWrapper::open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) { return wrapped->open_session(client_pid, name, sink); } void msh::ShellWrapper::close_session(std::shared_ptr const& session) { wrapped->close_session(session); } void msh::ShellWrapper::focus_next_session() { wrapped->focus_next_session(); } std::shared_ptr msh::ShellWrapper::focused_session() const { return wrapped->focused_session(); } void msh::ShellWrapper::set_focus_to( std::shared_ptr const& focus_session, std::shared_ptr const& focus_surface) { wrapped->set_focus_to(focus_session, focus_surface); } std::shared_ptr msh::ShellWrapper::start_prompt_session_for( std::shared_ptr const& session, scene::PromptSessionCreationParameters const& params) { return wrapped->start_prompt_session_for(session, params); } void msh::ShellWrapper::add_prompt_provider_for( std::shared_ptr const& prompt_session, std::shared_ptr const& session) { wrapped->add_prompt_provider_for(prompt_session, session); } void msh::ShellWrapper::stop_prompt_session(std::shared_ptr const& prompt_session) { wrapped->stop_prompt_session(prompt_session); } mf::SurfaceId msh::ShellWrapper::create_surface( std::shared_ptr const& session, ms::SurfaceCreationParameters const& params, std::shared_ptr const& sink) { return wrapped->create_surface(session, params, sink); } void msh::ShellWrapper::modify_surface(std::shared_ptr const& session, std::shared_ptr const& surface, SurfaceSpecification const& modifications) { wrapped->modify_surface(session, surface, modifications); } void msh::ShellWrapper::destroy_surface(std::shared_ptr const& session, mf::SurfaceId surface) { wrapped->destroy_surface(session, surface); } int msh::ShellWrapper::set_surface_attribute( std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) { return wrapped->set_surface_attribute(session, surface, attrib, value); } int msh::ShellWrapper::get_surface_attribute( std::shared_ptr const& surface, MirSurfaceAttrib attrib) { return wrapped->get_surface_attribute(surface, attrib); } void msh::ShellWrapper::raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface, uint64_t timestamp) { wrapped->raise_surface(session, surface, timestamp); } void msh::ShellWrapper::add_display(geometry::Rectangle const& area) { wrapped->add_display(area); } void msh::ShellWrapper::remove_display(geometry::Rectangle const& area) { wrapped->remove_display(area); } bool msh::ShellWrapper::handle(MirEvent const& event) { return wrapped->handle(event); } auto msh::ShellWrapper::focused_surface() const -> std::shared_ptr { return wrapped->focused_surface(); } auto msh::ShellWrapper::surface_at(geometry::Point cursor) const -> std::shared_ptr { return wrapped->surface_at(cursor); } void msh::ShellWrapper::raise(SurfaceSet const& surfaces) { return wrapped->raise(surfaces); } ./src/server/shell/default_persistent_surface_store.cpp0000644000015600001650000000613012676616125023651 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "default_persistent_surface_store.h" #include #include #include #include namespace msh = mir::shell; namespace ms = mir::scene; class msh::DefaultPersistentSurfaceStore::SurfaceIdBimap { public: using Id = PersistentSurfaceStore::Id; Id const& insert_or_retrieve(std::shared_ptr const& surface); std::shared_ptr operator[](Id const& id) const; Id const& operator[](std::shared_ptr const& surface) const; private: std::unordered_map> id_to_surface; std::unordered_map surface_to_id; }; auto msh::DefaultPersistentSurfaceStore::SurfaceIdBimap::insert_or_retrieve(std::shared_ptr const& surface) -> Id const& { auto element = surface_to_id.insert(std::make_pair(surface.get(), nullptr)); if (!element.second) { // Surface already exists in our map, return it. return *element.first->second; } else { auto new_element = id_to_surface.insert(std::make_pair(Id{}, surface)); // Update the surface_to_id map element from nullptr to our new Id element.first->second = &new_element.first->first; return *element.first->second; } } auto msh::DefaultPersistentSurfaceStore::SurfaceIdBimap::operator[](std::shared_ptr const& surface) const -> Id const& { return *surface_to_id.at(surface.get()); } auto msh::DefaultPersistentSurfaceStore::SurfaceIdBimap::operator[](Id const& id) const -> std::shared_ptr { return id_to_surface.at(id).lock(); } msh::DefaultPersistentSurfaceStore::DefaultPersistentSurfaceStore() : store{std::make_unique()} { } msh::DefaultPersistentSurfaceStore::~DefaultPersistentSurfaceStore() { } auto msh::DefaultPersistentSurfaceStore::id_for_surface(std::shared_ptr const& surface) -> Id { return store->insert_or_retrieve(surface); } std::shared_ptr msh::DefaultPersistentSurfaceStore::surface_for_id(Id const& id) const { try { return (*store)[id]; } catch (std::out_of_range& err) { using namespace std::literals; BOOST_THROW_EXCEPTION(std::out_of_range( "Lookup for surface with ID: "s + id.serialize_to_string() + " failed.")); } } ./src/server/shell/basic_window_manager.cpp0000644000015600001650000002341112676616157021171 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/shell/basic_window_manager.h" #include "mir/scene/session.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" namespace msh = mir::shell; msh::BasicWindowManager::BasicWindowManager( FocusController* focus_controller, std::unique_ptr policy) : focus_controller(focus_controller), policy(std::move(policy)) { } void msh::BasicWindowManager::add_session(std::shared_ptr const& session) { std::lock_guard lock(mutex); session_info[session] = SessionInfo(); policy->handle_session_info_updated(session_info, displays); } void msh::BasicWindowManager::remove_session(std::shared_ptr const& session) { std::lock_guard lock(mutex); session_info.erase(session); policy->handle_session_info_updated(session_info, displays); } auto msh::BasicWindowManager::add_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::function const& session, scene::SurfaceCreationParameters const& params)> const& build) -> frontend::SurfaceId { std::lock_guard lock(mutex); scene::SurfaceCreationParameters const placed_params = policy->handle_place_new_surface(session, params); auto const result = build(session, placed_params); auto const surface = session->surface(result); surface_info.emplace(surface, SurfaceInfo{session, surface, placed_params}); policy->handle_new_surface(session, surface); return result; } void msh::BasicWindowManager::modify_surface( std::shared_ptr const& session, std::shared_ptr const& surface, shell::SurfaceSpecification const& modifications) { std::lock_guard lock(mutex); policy->handle_modify_surface(session, surface, modifications); } void msh::BasicWindowManager::remove_surface( std::shared_ptr const& session, std::weak_ptr const& surface) { std::lock_guard lock(mutex); policy->handle_delete_surface(session, surface); surface_info.erase(surface); } void msh::BasicWindowManager::forget(std::weak_ptr const& surface) { surface_info.erase(surface); } void msh::BasicWindowManager::add_display(geometry::Rectangle const& area) { std::lock_guard lock(mutex); displays.add(area); policy->handle_displays_updated(session_info, displays); } void msh::BasicWindowManager::remove_display(geometry::Rectangle const& area) { std::lock_guard lock(mutex); displays.remove(area); policy->handle_displays_updated(session_info, displays); } bool msh::BasicWindowManager::handle_keyboard_event(MirKeyboardEvent const* event) { std::lock_guard lock(mutex); update_event_timestamp(event); return policy->handle_keyboard_event(event); } bool msh::BasicWindowManager::handle_touch_event(MirTouchEvent const* event) { std::lock_guard lock(mutex); update_event_timestamp(event); return policy->handle_touch_event(event); } bool msh::BasicWindowManager::handle_pointer_event(MirPointerEvent const* event) { std::lock_guard lock(mutex); update_event_timestamp(event); cursor = { mir_pointer_event_axis_value(event, mir_pointer_axis_x), mir_pointer_event_axis_value(event, mir_pointer_axis_y)}; return policy->handle_pointer_event(event); } void msh::BasicWindowManager::handle_raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface, uint64_t timestamp) { std::lock_guard lock(mutex); if (timestamp >= last_input_event_timestamp) policy->handle_raise_surface(session, surface); } int msh::BasicWindowManager::set_surface_attribute( std::shared_ptr const& /*session*/, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) { std::lock_guard lock(mutex); switch (attrib) { case mir_surface_attrib_state: { auto const state = policy->handle_set_state(surface, MirSurfaceState(value)); return surface->configure(attrib, state); } default: return surface->configure(attrib, value); } } auto msh::BasicWindowManager::find_session(std::function const& predicate) -> std::shared_ptr { for(auto& info : session_info) { if (predicate(info.second)) { return info.first.lock(); } } return std::shared_ptr{}; } auto msh::BasicWindowManager::info_for(std::weak_ptr const& session) const -> SessionInfo& { return const_cast(session_info.at(session)); } auto msh::BasicWindowManager::info_for(std::weak_ptr const& surface) const -> SurfaceInfo& { return const_cast(surface_info.at(surface)); } auto msh::BasicWindowManager::focused_session() const -> std::shared_ptr { return focus_controller->focused_session(); } auto msh::BasicWindowManager::focused_surface() const ->std::shared_ptr { return focus_controller->focused_surface(); } void msh::BasicWindowManager::focus_next_session() { focus_controller->focus_next_session(); } void msh::BasicWindowManager::set_focus_to( std::shared_ptr const& focus, std::shared_ptr const& surface) { focus_controller->set_focus_to(focus, surface); } auto msh::BasicWindowManager::surface_at(geometry::Point cursor) const -> std::shared_ptr { return focus_controller->surface_at(cursor); } auto msh::BasicWindowManager::active_display() -> geometry::Rectangle const { geometry::Rectangle result; // 1. If a window has input focus, whichever display contains the largest // proportion of the area of that window. if (auto const surface = focused_surface()) { auto const surface_rect = surface->input_bounds(); int max_overlap_area = -1; for (auto const& display : displays) { auto const intersection = surface_rect.intersection_with(display).size; if (intersection.width.as_int()*intersection.height.as_int() > max_overlap_area) { max_overlap_area = intersection.width.as_int()*intersection.height.as_int(); result = display; } } return result; } // 2. Otherwise, if any window previously had input focus, for the window that had // it most recently, the display that contained the largest proportion of the // area of that window at the moment it closed, as long as that display is still // available. // 3. Otherwise, the display that contains the pointer, if there is one. for (auto const& display : displays) { if (display.contains(cursor)) { // Ignore the (unspecified) possiblity of overlapping displays return display; } } // 4. Otherwise, the primary display, if there is one (for example, the laptop display). // 5. Otherwise, the first display. if (displays.size()) result = *displays.begin(); return result; } void msh::BasicWindowManager::raise_tree(std::shared_ptr const& root) { SurfaceSet surfaces; std::function const& surface)> const add_children = [&,this](std::weak_ptr const& surface) { auto const& info = info_for(surface); surfaces.insert(begin(info.children), end(info.children)); for (auto const& child : info.children) add_children(child); }; surfaces.insert(root); add_children(root); focus_controller->raise(surfaces); } void msh::BasicWindowManager::update_event_timestamp(MirKeyboardEvent const* kev) { auto iev = mir_keyboard_event_input_event(kev); last_input_event_timestamp = mir_input_event_get_event_time(iev); } void msh::BasicWindowManager::update_event_timestamp(MirPointerEvent const* pev) { auto iev = mir_pointer_event_input_event(pev); auto pointer_action = mir_pointer_event_action(pev); if (pointer_action == mir_pointer_action_button_up || pointer_action == mir_pointer_action_button_down) { last_input_event_timestamp = mir_input_event_get_event_time(iev); } } void msh::BasicWindowManager::update_event_timestamp(MirTouchEvent const* tev) { auto iev = mir_touch_event_input_event(tev); auto touch_count = mir_touch_event_point_count(tev); for (unsigned i = 0; i < touch_count; i++) { auto touch_action = mir_touch_event_action(tev, i); if (touch_action == mir_touch_action_up || touch_action == mir_touch_action_down) { last_input_event_timestamp = mir_input_event_get_event_time(iev); break; } } } ./src/server/shell/abstract_shell.cpp0000644000015600001650000002336312676616125020022 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/shell/abstract_shell.h" #include "mir/shell/input_targeter.h" #include "mir/shell/shell_report.h" #include "mir/shell/surface_specification.h" #include "mir/shell/surface_stack.h" #include "mir/shell/window_manager.h" #include "mir/scene/prompt_session.h" #include "mir/scene/prompt_session_manager.h" #include "mir/scene/session_coordinator.h" #include "mir/scene/session.h" #include "mir/scene/surface.h" namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell; msh::AbstractShell::AbstractShell( std::shared_ptr const& input_targeter, std::shared_ptr const& surface_stack, std::shared_ptr const& session_coordinator, std::shared_ptr const& prompt_session_manager, std::shared_ptr const& report, std::function(FocusController* focus_controller)> const& wm_builder) : input_targeter(input_targeter), surface_stack(surface_stack), session_coordinator(session_coordinator), prompt_session_manager(prompt_session_manager), window_manager(wm_builder(this)), report(report) { } msh::AbstractShell::~AbstractShell() noexcept { } std::shared_ptr msh::AbstractShell::open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) { auto const result = session_coordinator->open_session(client_pid, name, sink); window_manager->add_session(result); report->opened_session(*result); return result; } void msh::AbstractShell::close_session( std::shared_ptr const& session) { report->closing_session(*session); prompt_session_manager->remove_session(session); // TODO revisit this when reworking the AbstractShell/WindowManager interactions // this is an ugly kludge to extract the list of surfaces owned by the session // We're likely to have this information in the WindowManager implementation std::set> surfaces; for (auto surface = session->default_surface(); surface; surface = session->surface_after(surface)) if (!surfaces.insert(surface).second) break; // this is an ugly kludge to remove the each of the surfaces owned by the session // We could likely do this better (and atomically) within the WindowManager for (auto const& surface : surfaces) window_manager->remove_surface(session, surface); session_coordinator->close_session(session); window_manager->remove_session(session); } mf::SurfaceId msh::AbstractShell::create_surface( std::shared_ptr const& session, ms::SurfaceCreationParameters const& params, std::shared_ptr const& sink) { auto const build = [this, sink](std::shared_ptr const& session, ms::SurfaceCreationParameters const& placed_params) { return session->create_surface(placed_params, sink); }; auto const result = window_manager->add_surface(session, params, build); report->created_surface(*session, result); return result; } void msh::AbstractShell::modify_surface(std::shared_ptr const& session, std::shared_ptr const& surface, SurfaceSpecification const& modifications) { report->update_surface(*session, *surface, modifications); auto wm_relevant_mods = modifications; if (wm_relevant_mods.streams.is_set()) { session->configure_streams(*surface, wm_relevant_mods.streams.consume()); } if (!wm_relevant_mods.is_empty()) { window_manager->modify_surface(session, surface, wm_relevant_mods); } } void msh::AbstractShell::destroy_surface( std::shared_ptr const& session, mf::SurfaceId surface) { report->destroying_surface(*session, surface); window_manager->remove_surface(session, session->surface(surface)); } std::shared_ptr msh::AbstractShell::start_prompt_session_for( std::shared_ptr const& session, scene::PromptSessionCreationParameters const& params) { auto const result = prompt_session_manager->start_prompt_session_for(session, params); report->started_prompt_session(*result, *session); return result; } void msh::AbstractShell::add_prompt_provider_for( std::shared_ptr const& prompt_session, std::shared_ptr const& session) { prompt_session_manager->add_prompt_provider(prompt_session, session); report->added_prompt_provider(*prompt_session, *session); } void msh::AbstractShell::stop_prompt_session( std::shared_ptr const& prompt_session) { report->stopping_prompt_session(*prompt_session); prompt_session_manager->stop_prompt_session(prompt_session); } int msh::AbstractShell::set_surface_attribute( std::shared_ptr const& session, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) { report->update_surface(*session, *surface, attrib, value); return window_manager->set_surface_attribute(session, surface, attrib, value); } int msh::AbstractShell::get_surface_attribute( std::shared_ptr const& surface, MirSurfaceAttrib attrib) { return surface->query(attrib); } void msh::AbstractShell::raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface, uint64_t timestamp) { window_manager->handle_raise_surface(session, surface, timestamp); } void msh::AbstractShell::focus_next_session() { std::unique_lock lock(focus_mutex); auto const focused_session = focus_session.lock(); auto successor = session_coordinator->successor_of(focused_session); while (successor != nullptr && successor != focused_session && successor->default_surface() == nullptr) { successor = session_coordinator->successor_of(successor); } auto const surface = successor ? successor->default_surface() : nullptr; if (!surface) successor = nullptr; set_focus_to_locked(lock, successor, surface); } std::shared_ptr msh::AbstractShell::focused_session() const { std::unique_lock lg(focus_mutex); return focus_session.lock(); } std::shared_ptr msh::AbstractShell::focused_surface() const { std::unique_lock lock(focus_mutex); return focus_surface.lock(); } void msh::AbstractShell::set_focus_to( std::shared_ptr const& focus_session, std::shared_ptr const& focus_surface) { std::unique_lock lock(focus_mutex); set_focus_to_locked(lock, focus_session, focus_surface); } void msh::AbstractShell::set_focus_to_locked( std::unique_lock const& /*lock*/, std::shared_ptr const& session, std::shared_ptr const& surface) { auto const current_focus = focus_surface.lock(); if (surface != current_focus) { focus_surface = surface; if (current_focus) current_focus->configure(mir_surface_attrib_focus, mir_surface_unfocused); if (surface) { // Ensure the surface has really taken the focus before notifying it that it is focused input_targeter->set_focus(surface); surface->configure(mir_surface_attrib_focus, mir_surface_focused); } else { input_targeter->clear_focus(); } } auto const current_session = focus_session.lock(); if (session != current_session) { focus_session = session; if (session) { session_coordinator->set_focus_to(session); } else { session_coordinator->unset_focus(); } } report->input_focus_set_to(session.get(), surface.get()); } void msh::AbstractShell::add_display(geometry::Rectangle const& area) { report->adding_display(area); window_manager->add_display(area); } void msh::AbstractShell::remove_display(geometry::Rectangle const& area) { report->removing_display(area); window_manager->remove_display(area); } bool msh::AbstractShell::handle(MirEvent const& event) { if (mir_event_get_type(&event) != mir_event_type_input) return false; auto const input_event = mir_event_get_input_event(&event); switch (mir_input_event_get_type(input_event)) { case mir_input_event_type_key: return window_manager->handle_keyboard_event(mir_input_event_get_keyboard_event(input_event)); case mir_input_event_type_touch: return window_manager->handle_touch_event(mir_input_event_get_touch_event(input_event)); case mir_input_event_type_pointer: return window_manager->handle_pointer_event(mir_input_event_get_pointer_event(input_event)); } return false; } auto msh::AbstractShell::surface_at(geometry::Point cursor) const -> std::shared_ptr { return surface_stack->surface_at(cursor); } void msh::AbstractShell::raise(SurfaceSet const& surfaces) { surface_stack->raise(surfaces); report->surfaces_raised(surfaces); } ./src/server/shell/graphics_display_layout.cpp0000644000015600001650000000644112676616125021750 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "graphics_display_layout.h" #include "mir/graphics/display.h" #include "mir/graphics/display_buffer.h" #include "mir/geometry/rectangle.h" #include "mir/geometry/rectangles.h" #include "mir/geometry/displacement.h" namespace msh = mir::shell; namespace mg = mir::graphics; namespace geom = mir::geometry; msh::GraphicsDisplayLayout::GraphicsDisplayLayout( std::shared_ptr const& display) : display{display} { } void msh::GraphicsDisplayLayout::clip_to_output(geometry::Rectangle& rect) { auto output = get_output_for(rect); if (output.size.width > geom::Width{0} && output.size.height > geom::Height{0} && rect.size.width > geom::Width{0} && rect.size.height > geom::Height{0}) { auto tl_closed = rect.top_left; auto br_closed = rect.bottom_right() - geom::Displacement{1,1}; geom::Rectangles rectangles; rectangles.add(output); rectangles.confine(tl_closed); rectangles.confine(br_closed); rect.top_left = tl_closed; rect.size = as_size(br_closed - tl_closed + geom::Displacement{1, 1}); } else { rect.size = geom::Size{0,0}; } } void msh::GraphicsDisplayLayout::size_to_output(geometry::Rectangle& rect) { auto output = get_output_for(rect); rect = output; } bool msh::GraphicsDisplayLayout::place_in_output( graphics::DisplayConfigurationOutputId id, geometry::Rectangle& rect) { auto config = display->configuration(); bool placed = false; /* Accept only fullscreen placements for now */ config->for_each_output([&](mg::DisplayConfigurationOutput const& output) { if (output.id == id && output.current_mode_index < output.modes.size()) { rect.top_left = output.top_left; rect.size = output.extents().size; placed = true; } }); return placed; } geom::Rectangle msh::GraphicsDisplayLayout::get_output_for(geometry::Rectangle& rect) { int max_area = -1; geometry::Rectangle best = rect; display->for_each_display_sync_group([&](mg::DisplaySyncGroup& group) { group.for_each_display_buffer([&](mg::DisplayBuffer const& db) { auto const& screen = db.view_area(); auto const& overlap = rect.intersection_with(screen); int area = overlap.size.width.as_int() * overlap.size.height.as_int(); if (area > max_area) { best = screen; max_area = area; } }); }); return best; } ./src/server/shell/window_management_info.cpp0000644000015600001650000002106512676616157021550 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/shell/window_management_info.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/graphics/buffer.h" #include namespace msh = mir::shell; namespace ms = mir::scene; namespace mg = mir::graphics; using namespace mir::geometry; msh::SurfaceInfo::SurfaceInfo( std::shared_ptr const& session, std::shared_ptr const& surface, ms::SurfaceCreationParameters const& params) : type{surface->type()}, state{surface->state()}, restore_rect{surface->top_left(), surface->size()}, session{session}, parent{params.parent}, min_width{params.min_width.is_set() ? params.min_width.value() : Width{}}, min_height{params.min_height.is_set() ? params.min_height.value() : Height{}}, max_width{params.max_width.is_set() ? params.max_width.value() : Width{std::numeric_limits::max()}}, max_height{params.max_height.is_set() ? params.max_height.value() : Height{std::numeric_limits::max()}}, width_inc{params.width_inc}, height_inc{params.height_inc}, min_aspect{params.min_aspect}, max_aspect{params.max_aspect} { if (params.output_id != mir::graphics::DisplayConfigurationOutputId{0}) output_id = params.output_id; } bool msh::SurfaceInfo::can_be_active() const { switch (type) { case mir_surface_type_normal: /**< AKA "regular" */ case mir_surface_type_utility: /**< AKA "floating" */ case mir_surface_type_dialog: case mir_surface_type_satellite: /**< AKA "toolbox"/"toolbar" */ case mir_surface_type_freestyle: case mir_surface_type_menu: case mir_surface_type_inputmethod: /**< AKA "OSK" or handwriting etc. */ return true; case mir_surface_type_gloss: case mir_surface_type_tip: /**< AKA "tooltip" */ default: // Cannot have input focus return false; } } bool msh::SurfaceInfo::must_have_parent() const { switch (type) { case mir_surface_type_overlay:; case mir_surface_type_inputmethod: case mir_surface_type_satellite: case mir_surface_type_tip: return true; default: return false; } } bool msh::SurfaceInfo::can_morph_to(MirSurfaceType new_type) const { switch (new_type) { case mir_surface_type_normal: case mir_surface_type_utility: case mir_surface_type_satellite: switch (type) { case mir_surface_type_normal: case mir_surface_type_utility: case mir_surface_type_dialog: case mir_surface_type_satellite: return true; default: break; } break; case mir_surface_type_dialog: switch (type) { case mir_surface_type_normal: case mir_surface_type_utility: case mir_surface_type_dialog: case mir_surface_type_popover: case mir_surface_type_satellite: return true; default: break; } break; default: break; } return false; } bool msh::SurfaceInfo::must_not_have_parent() const { switch (type) { case mir_surface_type_normal: case mir_surface_type_utility: return true; default: return false; } } bool msh::SurfaceInfo::is_visible() const { switch (state) { case mir_surface_state_hidden: case mir_surface_state_minimized: return false; default: break; } return true; } void msh::SurfaceInfo::constrain_resize( std::shared_ptr const& surface, Point& requested_pos, Size& requested_size, bool const left_resize, bool const top_resize, Rectangle const& /*bounds*/) const { Point new_pos = requested_pos; Size new_size = requested_size; if (min_aspect.is_set()) { auto const ar = min_aspect.value(); auto const error = new_size.height.as_int()*long(ar.width) - new_size.width.as_int()*long(ar.height); if (error > 0) { // Add (denominator-1) to numerator to ensure rounding up auto const width_correction = (error+(ar.height-1))/ar.height; auto const height_correction = (error+(ar.width-1))/ar.width; if (width_correction < height_correction) { new_size.width = new_size.width + DeltaX(width_correction); } else { new_size.height = new_size.height - DeltaY(height_correction); } } } if (max_aspect.is_set()) { auto const ar = max_aspect.value(); auto const error = new_size.width.as_int()*long(ar.height) - new_size.height.as_int()*long(ar.width); if (error > 0) { // Add (denominator-1) to numerator to ensure rounding up auto const height_correction = (error+(ar.width-1))/ar.width; auto const width_correction = (error+(ar.height-1))/ar.height; if (width_correction < height_correction) { new_size.width = new_size.width - DeltaX(width_correction); } else { new_size.height = new_size.height + DeltaY(height_correction); } } } if (min_width > new_size.width) new_size.width = min_width; if (min_height > new_size.height) new_size.height = min_height; if (max_width < new_size.width) new_size.width = max_width; if (max_height < new_size.height) new_size.height = max_height; if (width_inc.is_set()) { auto const width = new_size.width.as_int() - min_width.as_int(); auto inc = width_inc.value().as_int(); if (width % inc) new_size.width = min_width + DeltaX{inc*(((2L*width + inc)/2)/inc)}; } if (height_inc.is_set()) { auto const height = new_size.height.as_int() - min_height.as_int(); auto inc = height_inc.value().as_int(); if (height % inc) new_size.height = min_height + DeltaY{inc*(((2L*height + inc)/2)/inc)}; } if (left_resize) new_pos.x += new_size.width - requested_size.width; if (top_resize) new_pos.y += new_size.height - requested_size.height; // placeholder - constrain onscreen switch (state) { case mir_surface_state_restored: break; // "A vertically maximised surface is anchored to the top and bottom of // the available workspace and can have any width." case mir_surface_state_vertmaximized: new_pos.y = surface->top_left().y; new_size.height = surface->size().height; break; // "A horizontally maximised surface is anchored to the left and right of // the available workspace and can have any height" case mir_surface_state_horizmaximized: new_pos.x = surface->top_left().x; new_size.width = surface->size().width; break; // "A maximised surface is anchored to the top, bottom, left and right of the // available workspace. For example, if the launcher is always-visible then // the left-edge of the surface is anchored to the right-edge of the launcher." case mir_surface_state_maximized: default: new_pos.x = surface->top_left().x; new_pos.y = surface->top_left().y; new_size.width = surface->size().width; new_size.height = surface->size().height; break; } requested_pos = new_pos; requested_size = new_size; } bool msh::SurfaceInfo::needs_titlebar(MirSurfaceType type) { switch (type) { case mir_surface_type_freestyle: case mir_surface_type_menu: case mir_surface_type_inputmethod: case mir_surface_type_gloss: case mir_surface_type_tip: // No decorations for these surface types return false; default: return true; } } ./src/server/shell/default_configuration.cpp0000644000015600001650000000635412676616125021404 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/default_server_configuration.h" #include "null_host_lifecycle_event_listener.h" #include "mir/shell/canonical_window_manager.h" #include "mir/input/composite_event_filter.h" #include "mir/shell/abstract_shell.h" #include "default_persistent_surface_store.h" #include "frontend_shell.h" #include "graphics_display_layout.h" namespace ms = mir::scene; namespace msh = mir::shell; namespace mf = mir::frontend; auto mir::DefaultServerConfiguration::the_shell() -> std::shared_ptr { return shell([this] { auto const result = wrap_shell(std::make_shared( the_input_targeter(), the_surface_stack(), the_session_coordinator(), the_prompt_session_manager(), the_shell_report(), the_window_manager_builder())); the_composite_event_filter()->prepend(result); return result; }); } auto mir::DefaultServerConfiguration::the_window_manager_builder() -> shell::WindowManagerBuilder { return [this](msh::FocusController* focus_controller) { return std::make_shared( focus_controller, the_shell_display_layout()); }; } auto mir::DefaultServerConfiguration::wrap_shell(std::shared_ptr const& wrapped) -> std::shared_ptr { return wrapped; } std::shared_ptr mir::DefaultServerConfiguration::the_persistent_surface_store() { return surface_store([]() { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_frontend_shell() { return frontend_shell([this] { return std::make_shared(the_shell(), the_persistent_surface_store()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_focus_controller() { return the_shell(); } std::shared_ptr mir::DefaultServerConfiguration::the_shell_display_layout() { return shell_display_layout( [this]() { return std::make_shared(the_display()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_host_lifecycle_event_listener() { return host_lifecycle_event_listener( []() { return std::make_shared(); }); } ./src/server/shell/frontend_shell.h0000644000015600001650000000636312676616125017504 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SHELL_FRONTEND_SHELL_H_ #define MIR_SHELL_FRONTEND_SHELL_H_ #include "mir/frontend/shell.h" namespace ms = mir::scene; namespace mf = mir::frontend; namespace mir { namespace shell { class Shell; class PersistentSurfaceStore; namespace detail { // Adapter class to translate types between frontend and shell struct FrontendShell : mf::Shell { std::shared_ptr const wrapped; std::shared_ptr const surface_store; explicit FrontendShell(std::shared_ptr const& wrapped, std::shared_ptr const& surface_store) : wrapped{wrapped}, surface_store{surface_store} { } std::shared_ptr open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) override; void close_session(std::shared_ptr const& session) override; std::shared_ptr start_prompt_session_for( std::shared_ptr const& session, ms::PromptSessionCreationParameters const& params) override; void add_prompt_provider_for( std::shared_ptr const& prompt_session, std::shared_ptr const& session) override; void stop_prompt_session(std::shared_ptr const& prompt_session) override; mf::SurfaceId create_surface( std::shared_ptr const& session, ms::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override; void modify_surface(std::shared_ptr const& session, mf::SurfaceId surface, SurfaceSpecification const& modifications) override; void destroy_surface(std::shared_ptr const& session, mf::SurfaceId surface) override; std::string persistent_id_for(std::shared_ptr const& session, mf::SurfaceId surface) override; std::shared_ptr surface_for_id(std::string const& serialized_id) override; int set_surface_attribute( std::shared_ptr const& session, mf::SurfaceId surface_id, MirSurfaceAttrib attrib, int value) override; int get_surface_attribute( std::shared_ptr const& session, mf::SurfaceId surface_id, MirSurfaceAttrib attrib) override; void raise_surface( std::shared_ptr const& session, mf::SurfaceId surface_id, uint64_t timestamp) override; }; } } } #endif /* MIR_SHELL_FRONTEND_SHELL_H_ */ ./src/server/shell/surface_ready_observer.cpp0000644000015600001650000000252312676616125021546 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/shell/surface_ready_observer.h" #include "mir/scene/surface.h" namespace msh = mir::shell; namespace ms = mir::scene; msh::SurfaceReadyObserver::SurfaceReadyObserver( ActivateFunction const& activate, std::shared_ptr const& session, std::shared_ptr const& surface) : activate{activate}, session{session}, surface{surface} { } msh::SurfaceReadyObserver::~SurfaceReadyObserver() = default; void msh::SurfaceReadyObserver::frame_posted(int, mir::geometry::Size const&) { if (auto const s = surface.lock()) { activate(session.lock(), s); s->remove_observer(shared_from_this()); } } ./src/server/shell/graphics_display_layout.h0000644000015600001650000000275412676616125021420 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_SHELL_GRAPHICS_DISPLAY_LAYOUT_H_ #define MIR_SHELL_GRAPHICS_DISPLAY_LAYOUT_H_ #include "mir/shell/display_layout.h" #include namespace mir { namespace graphics { class Display; } namespace shell { class GraphicsDisplayLayout : public DisplayLayout { public: GraphicsDisplayLayout(std::shared_ptr const& display); void clip_to_output(geometry::Rectangle& rect) override; void size_to_output(geometry::Rectangle& rect) override; bool place_in_output(graphics::DisplayConfigurationOutputId output_id, geometry::Rectangle& rect) override; private: geometry::Rectangle get_output_for(geometry::Rectangle& rect); std::shared_ptr const display; }; } } #endif /* MIR_SHELL_GRAPHICS_DISPLAY_LAYOUT_H_ */ ./src/server/shell/canonical_window_manager.cpp0000644000015600001650000006401112676616157022040 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/shell/canonical_window_manager.h" #include "mir/scene/session.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/shell/surface_ready_observer.h" #include "mir/shell/display_layout.h" #include #include #include namespace msh = mir::shell; namespace ms = mir::scene; using namespace mir::geometry; namespace { int const title_bar_height = 10; // TODO this really should belong on CanonicalSurfaceInfo // but is currently used when placing the surface before construction. // Which implies we need some rework so that we can construct metadata // before the surface. bool must_not_have_parent(MirSurfaceType type) { switch (type) { case mir_surface_type_normal: case mir_surface_type_utility: return true; default: return false; } } // TODO this really should belong on CanonicalSurfaceInfo // but is currently used when placing the surface before construction. // Which implies we need some rework so that we can construct metadata // before the surface. bool must_have_parent(MirSurfaceType type) { switch (type) { case mir_surface_type_overlay:; case mir_surface_type_satellite: case mir_surface_type_tip: return true; default: return false; } } } msh::CanonicalWindowManagerPolicy::CanonicalWindowManagerPolicy( WindowManagerTools* const tools, std::shared_ptr const& display_layout) : tools{tools}, display_layout{display_layout} { } void msh::CanonicalWindowManagerPolicy::click(Point cursor) { if (auto const surface = tools->surface_at(cursor)) select_active_surface(surface); old_cursor = cursor; } void msh::CanonicalWindowManagerPolicy::handle_session_info_updated(SessionInfoMap& /*session_info*/, Rectangles const& /*displays*/) { } void msh::CanonicalWindowManagerPolicy::handle_displays_updated(SessionInfoMap& /*session_info*/, Rectangles const& displays) { display_area = displays.bounding_rectangle(); for (auto const weak_surface : fullscreen_surfaces) { if (auto const surface = weak_surface.lock()) { auto const& info = tools->info_for(weak_surface); Rectangle rect{surface->top_left(), surface->size()}; display_layout->place_in_output(info.output_id.value(), rect); surface->move_to(rect.top_left); surface->resize(rect.size); } } } void msh::CanonicalWindowManagerPolicy::resize(Point cursor) { select_active_surface(tools->surface_at(old_cursor)); resize(active_surface(), cursor, old_cursor, display_area); old_cursor = cursor; } auto msh::CanonicalWindowManagerPolicy::handle_place_new_surface( std::shared_ptr const& session, ms::SurfaceCreationParameters const& request_parameters) -> ms::SurfaceCreationParameters { auto parameters = request_parameters; if (!parameters.type.is_set()) throw std::runtime_error("Surface type must be provided"); if (!parameters.state.is_set()) parameters.state = mir_surface_state_restored; auto const active_display = tools->active_display(); auto const width = parameters.size.width.as_int(); auto const height = parameters.size.height.as_int(); bool positioned = false; auto const parent = parameters.parent.lock(); if (must_not_have_parent(parameters.type.value()) && parent) throw std::runtime_error("Surface type cannot have parent"); if (must_have_parent(parameters.type.value()) && !parent) throw std::runtime_error("Surface type must have parent"); if (parameters.output_id != mir::graphics::DisplayConfigurationOutputId{0}) { Rectangle rect{parameters.top_left, parameters.size}; display_layout->place_in_output(parameters.output_id, rect); parameters.top_left = rect.top_left; parameters.size = rect.size; parameters.state = mir_surface_state_fullscreen; positioned = true; } else if (!parent) // No parent => client can't suggest positioning { if (auto const default_surface = session->default_surface()) { static Displacement const offset{title_bar_height, title_bar_height}; parameters.top_left = default_surface->top_left() + offset; geometry::Rectangle display_for_app{default_surface->top_left(), default_surface->size()}; display_layout->size_to_output(display_for_app); // // TODO This is what is currently in the spec, but I think it's wrong // if (!display_for_app.contains(parameters.top_left + as_displacement(parameters.size))) // { // parameters.size = as_size(display_for_app.bottom_right() - parameters.top_left); // } // // positioned = display_for_app.contains(Rectangle{parameters.top_left, parameters.size}); positioned = display_for_app.overlaps(Rectangle{parameters.top_left, parameters.size}); } } if (parent && parameters.aux_rect.is_set() && parameters.edge_attachment.is_set()) { auto const edge_attachment = parameters.edge_attachment.value(); auto const aux_rect = parameters.aux_rect.value(); auto const parent_top_left = parent->top_left(); auto const top_left = aux_rect.top_left -Point{} + parent_top_left; auto const top_right= aux_rect.top_right() -Point{} + parent_top_left; auto const bot_left = aux_rect.bottom_left()-Point{} + parent_top_left; if (edge_attachment & mir_edge_attachment_vertical) { if (active_display.contains(top_right + Displacement{width, height})) { parameters.top_left = top_right; positioned = true; } else if (active_display.contains(top_left + Displacement{-width, height})) { parameters.top_left = top_left + Displacement{-width, 0}; positioned = true; } } if (edge_attachment & mir_edge_attachment_horizontal) { if (active_display.contains(bot_left + Displacement{width, height})) { parameters.top_left = bot_left; positioned = true; } else if (active_display.contains(top_left + Displacement{width, -height})) { parameters.top_left = top_left + Displacement{0, -height}; positioned = true; } } } else if (parent) { // o Otherwise, if the dialog is not the same as any previous dialog for the // same parent window, and/or it does not have user-customized position: // o It should be optically centered relative to its parent, unless this // would overlap or cover the title bar of the parent. // o Otherwise, it should be cascaded vertically (but not horizontally) // relative to its parent, unless, this would cause at least part of // it to extend into shell space. auto const parent_top_left = parent->top_left(); auto const centred = parent_top_left + 0.5*(as_displacement(parent->size()) - as_displacement(parameters.size)) - DeltaY{(parent->size().height.as_int()-height)/6}; parameters.top_left = centred; positioned = true; } if (!positioned) { auto const centred = active_display.top_left + 0.5*(as_displacement(active_display.size) - as_displacement(parameters.size)) - DeltaY{(active_display.size.height.as_int()-height)/6}; switch (parameters.state.value()) { case mir_surface_state_fullscreen: case mir_surface_state_maximized: parameters.top_left = active_display.top_left; parameters.size = active_display.size; break; case mir_surface_state_vertmaximized: parameters.top_left = centred; parameters.top_left.y = active_display.top_left.y; parameters.size.height = active_display.size.height; break; case mir_surface_state_horizmaximized: parameters.top_left = centred; parameters.top_left.x = active_display.top_left.x; parameters.size.width = active_display.size.width; break; default: parameters.top_left = centred; } if (parameters.top_left.y < display_area.top_left.y) parameters.top_left.y = display_area.top_left.y; } return parameters; } void msh::CanonicalWindowManagerPolicy::handle_new_surface(std::shared_ptr const& session, std::shared_ptr const& surface) { auto& surface_info = tools->info_for(surface); if (auto const parent = surface_info.parent.lock()) { tools->info_for(parent).children.push_back(surface); } tools->info_for(session).surfaces.push_back(surface); if (surface_info.can_be_active()) { surface->add_observer(std::make_shared( [this](std::shared_ptr const& /*session*/, std::shared_ptr const& surface) { select_active_surface(surface); }, session, surface)); } if (surface_info.state == mir_surface_state_fullscreen) fullscreen_surfaces.insert(surface); } void msh::CanonicalWindowManagerPolicy::handle_modify_surface( std::shared_ptr const& session, std::shared_ptr const& surface, SurfaceSpecification const& modifications) { auto& surface_info_old = tools->info_for(surface); auto surface_info = surface_info_old; if (modifications.parent.is_set()) surface_info.parent = modifications.parent.value(); if (modifications.type.is_set() && surface_info.type != modifications.type.value()) { auto const new_type = modifications.type.value(); if (!surface_info.can_morph_to(new_type)) { throw std::runtime_error("Unsupported surface type change"); } surface_info.type = new_type; if (surface_info.must_not_have_parent()) { if (modifications.parent.is_set()) throw std::runtime_error("Target surface type does not support parent"); surface_info.parent.reset(); } else if (surface_info.must_have_parent()) { if (!surface_info.parent.lock()) throw std::runtime_error("Target surface type requires parent"); } surface->configure(mir_surface_attrib_type, new_type); } #define COPY_IF_SET(field)\ if (modifications.field.is_set())\ surface_info.field = modifications.field.value() COPY_IF_SET(min_width); COPY_IF_SET(min_height); COPY_IF_SET(max_width); COPY_IF_SET(max_height); COPY_IF_SET(min_width); COPY_IF_SET(width_inc); COPY_IF_SET(height_inc); COPY_IF_SET(min_aspect); COPY_IF_SET(max_aspect); COPY_IF_SET(output_id); #undef COPY_IF_SET std::swap(surface_info, surface_info_old); if (modifications.name.is_set()) surface->rename(modifications.name.value()); if (modifications.streams.is_set()) { auto v = modifications.streams.value(); std::vector l (v.begin(), v.end()); session->configure_streams(*surface, l); } if (modifications.input_shape.is_set()) { surface->set_input_region(modifications.input_shape.value()); } if (modifications.width.is_set() || modifications.height.is_set()) { auto new_size = surface->size(); if (modifications.width.is_set()) new_size.width = modifications.width.value(); if (modifications.height.is_set()) new_size.height = modifications.height.value(); auto top_left = surface->top_left(); surface_info.constrain_resize( surface, top_left, new_size, false, false, display_area); apply_resize(surface, top_left, new_size); } if (modifications.state.is_set()) { auto const state = handle_set_state(surface, modifications.state.value()); surface->configure(mir_surface_attrib_state, state); } } void msh::CanonicalWindowManagerPolicy::handle_delete_surface(std::shared_ptr const& session, std::weak_ptr const& surface) { fullscreen_surfaces.erase(surface); auto& info = tools->info_for(surface); if (auto const parent = info.parent.lock()) { auto& siblings = tools->info_for(parent).children; for (auto i = begin(siblings); i != end(siblings); ++i) { if (surface.lock() == i->lock()) { siblings.erase(i); break; } } } session->destroy_surface(surface); auto& surfaces = tools->info_for(session).surfaces; for (auto i = begin(surfaces); i != end(surfaces); ++i) { if (surface.lock() == i->lock()) { surfaces.erase(i); break; } } if (surfaces.empty() && session == tools->focused_session()) { active_surface_.reset(); tools->focus_next_session(); select_active_surface(tools->focused_surface()); } } int msh::CanonicalWindowManagerPolicy::handle_set_state(std::shared_ptr const& surface, MirSurfaceState value) { auto& info = tools->info_for(surface); switch (value) { case mir_surface_state_restored: case mir_surface_state_maximized: case mir_surface_state_vertmaximized: case mir_surface_state_horizmaximized: case mir_surface_state_fullscreen: case mir_surface_state_hidden: case mir_surface_state_minimized: break; default: return info.state; } if (info.state == mir_surface_state_restored) { info.restore_rect = {surface->top_left(), surface->size()}; } if (info.state != mir_surface_state_fullscreen) { info.output_id = decltype(info.output_id){}; fullscreen_surfaces.erase(surface); } else { fullscreen_surfaces.insert(surface); } if (info.state == value) { return info.state; } auto const old_pos = surface->top_left(); Displacement movement; switch (value) { case mir_surface_state_restored: movement = info.restore_rect.top_left - old_pos; surface->resize(info.restore_rect.size); break; case mir_surface_state_maximized: movement = display_area.top_left - old_pos; surface->resize(display_area.size); break; case mir_surface_state_horizmaximized: movement = Point{display_area.top_left.x, info.restore_rect.top_left.y} - old_pos; surface->resize({display_area.size.width, info.restore_rect.size.height}); break; case mir_surface_state_vertmaximized: movement = Point{info.restore_rect.top_left.x, display_area.top_left.y} - old_pos; surface->resize({info.restore_rect.size.width, display_area.size.height}); break; case mir_surface_state_fullscreen: { Rectangle rect{old_pos, surface->size()}; if (info.output_id.is_set()) { display_layout->place_in_output(info.output_id.value(), rect); } else { display_layout->size_to_output(rect); } movement = rect.top_left - old_pos; surface->resize(rect.size); break; } case mir_surface_state_hidden: case mir_surface_state_minimized: surface->hide(); return info.state = value; default: break; } // TODO It is rather simplistic to move a tree WRT the top_left of the root // TODO when resizing. But for more sophistication we would need to encode // TODO some sensible layout rules. move_tree(surface, movement); info.state = value; if (info.is_visible()) surface->show(); return info.state; } void msh::CanonicalWindowManagerPolicy::drag(Point cursor) { select_active_surface(tools->surface_at(old_cursor)); drag(active_surface(), cursor, old_cursor, display_area); old_cursor = cursor; } void msh::CanonicalWindowManagerPolicy::handle_raise_surface( std::shared_ptr const& /*session*/, std::shared_ptr const& surface) { select_active_surface(surface); } bool msh::CanonicalWindowManagerPolicy::handle_keyboard_event(MirKeyboardEvent const* event) { auto const action = mir_keyboard_event_action(event); auto const scan_code = mir_keyboard_event_scan_code(event); auto const modifiers = mir_keyboard_event_modifiers(event) & modifier_mask; if (action == mir_keyboard_action_down && scan_code == KEY_F11) { switch (modifiers) { case mir_input_event_modifier_alt: toggle(mir_surface_state_maximized); return true; case mir_input_event_modifier_shift: toggle(mir_surface_state_vertmaximized); return true; case mir_input_event_modifier_ctrl: toggle(mir_surface_state_horizmaximized); return true; default: break; } } else if (action == mir_keyboard_action_down && scan_code == KEY_F4) { if (auto const session = tools->focused_session()) { switch (modifiers) { case mir_input_event_modifier_alt: kill(session->process_id(), SIGTERM); return true; case mir_input_event_modifier_ctrl: if (auto const surf = session->default_surface()) { surf->request_client_surface_close(); return true; } default: break; } } } else if (action == mir_keyboard_action_down && modifiers == mir_input_event_modifier_alt && scan_code == KEY_TAB) { tools->focus_next_session(); if (auto const surface = tools->focused_surface()) select_active_surface(surface); return true; } else if (action == mir_keyboard_action_down && modifiers == mir_input_event_modifier_alt && scan_code == KEY_GRAVE) { if (auto const prev = tools->focused_surface()) { if (auto const app = tools->focused_session()) select_active_surface(app->surface_after(prev)); } return true; } return false; } bool msh::CanonicalWindowManagerPolicy::handle_touch_event(MirTouchEvent const* event) { auto const count = mir_touch_event_point_count(event); long total_x = 0; long total_y = 0; for (auto i = 0U; i != count; ++i) { total_x += mir_touch_event_axis_value(event, i, mir_touch_axis_x); total_y += mir_touch_event_axis_value(event, i, mir_touch_axis_y); } Point const cursor{total_x/count, total_y/count}; bool is_drag = true; for (auto i = 0U; i != count; ++i) { switch (mir_touch_event_action(event, i)) { case mir_touch_action_up: return false; case mir_touch_action_down: is_drag = false; case mir_touch_action_change: continue; } } bool consumes_event = false; if (is_drag) { switch (count) { case 2: resize(cursor); consumes_event = true; break; case 3: drag(cursor); consumes_event = true; break; } } old_cursor = cursor; return consumes_event; } bool msh::CanonicalWindowManagerPolicy::handle_pointer_event(MirPointerEvent const* event) { auto const action = mir_pointer_event_action(event); auto const modifiers = mir_pointer_event_modifiers(event) & modifier_mask; Point const cursor{ mir_pointer_event_axis_value(event, mir_pointer_axis_x), mir_pointer_event_axis_value(event, mir_pointer_axis_y)}; bool consumes_event = false; if (action == mir_pointer_action_button_down) { click(cursor); } else if (action == mir_pointer_action_motion && modifiers == mir_input_event_modifier_alt) { if (mir_pointer_event_button_state(event, mir_pointer_button_primary)) { drag(cursor); consumes_event = true; } if (mir_pointer_event_button_state(event, mir_pointer_button_tertiary)) { resize(cursor); consumes_event = true; } } old_cursor = cursor; return consumes_event; } void msh::CanonicalWindowManagerPolicy::toggle(MirSurfaceState state) { if (auto const surface = active_surface()) { auto& info = tools->info_for(surface); if (info.state == state) state = mir_surface_state_restored; auto const value = handle_set_state(surface, MirSurfaceState(state)); surface->configure(mir_surface_attrib_state, value); } } void msh::CanonicalWindowManagerPolicy::select_active_surface(std::shared_ptr const& surface) { if (!surface) { if (active_surface_.lock()) tools->set_focus_to({}, {}); active_surface_.reset(); return; } auto const& info_for = tools->info_for(surface); if (info_for.can_be_active()) { tools->set_focus_to(info_for.session.lock(), surface); tools->raise_tree(surface); active_surface_ = surface; } else { // Cannot have input focus - try the parent if (auto const parent = info_for.parent.lock()) select_active_surface(parent); } } auto msh::CanonicalWindowManagerPolicy::active_surface() const -> std::shared_ptr { if (auto const surface = active_surface_.lock()) return surface; if (auto const session = tools->focused_session()) { if (auto const surface = session->default_surface()) return surface; } return std::shared_ptr{}; } bool msh::CanonicalWindowManagerPolicy::resize(std::shared_ptr const& surface, Point cursor, Point old_cursor, Rectangle bounds) { if (!surface || !surface->input_area_contains(old_cursor)) return false; auto const top_left = surface->top_left(); Rectangle const old_pos{top_left, surface->size()}; auto anchor = top_left; for (auto const& corner : { old_pos.top_right(), old_pos.bottom_left(), old_pos.bottom_right()}) { if ((old_cursor - anchor).length_squared() < (old_cursor - corner).length_squared()) { anchor = corner; } } bool const left_resize = anchor.x != top_left.x; bool const top_resize = anchor.y != top_left.y; int const x_sign = left_resize? -1 : 1; int const y_sign = top_resize? -1 : 1; auto const delta = cursor-old_cursor; Size new_size{old_pos.size.width + x_sign*delta.dx, old_pos.size.height + y_sign*delta.dy}; Point new_pos = top_left + left_resize*delta.dx + top_resize*delta.dy; auto const& surface_info = tools->info_for(surface); surface_info.constrain_resize(surface, new_pos, new_size, left_resize, top_resize, bounds); apply_resize(surface, new_pos, new_size); return true; } void msh::CanonicalWindowManagerPolicy::apply_resize( std::shared_ptr const& surface, Point const& new_pos, Size const& new_size) const { surface->resize(new_size); // TODO It is rather simplistic to move a tree WRT the top_left of the root // TODO when resizing. But for more sophistication we would need to encode // TODO some sensible layout rules. move_tree(surface, new_pos-surface->top_left()); } bool msh::CanonicalWindowManagerPolicy::drag(std::shared_ptr surface, Point to, Point from, Rectangle /*bounds*/) { if (!surface) return false; if (!surface->input_area_contains(from)) return false; auto movement = to - from; // placeholder - constrain onscreen switch (tools->info_for(surface).state) { case mir_surface_state_restored: break; // "A vertically maximised surface is anchored to the top and bottom of // the available workspace and can have any width." case mir_surface_state_vertmaximized: movement.dy = DeltaY(0); break; // "A horizontally maximised surface is anchored to the left and right of // the available workspace and can have any height" case mir_surface_state_horizmaximized: movement.dx = DeltaX(0); break; // "A maximised surface is anchored to the top, bottom, left and right of the // available workspace. For example, if the launcher is always-visible then // the left-edge of the surface is anchored to the right-edge of the launcher." case mir_surface_state_maximized: case mir_surface_state_fullscreen: default: return true; } move_tree(surface, movement); return true; } void msh::CanonicalWindowManagerPolicy::move_tree(std::shared_ptr const& root, Displacement movement) const { root->move_to(root->top_left() + movement); for (auto const& child: tools->info_for(root).children) { move_tree(child.lock(), movement); } } ./src/server/shell/surface_stack_wrapper.cpp0000644000015600001650000000310212676616125021372 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/shell/surface_stack_wrapper.h" #include "mir/geometry/point.h" namespace msh = mir::shell; msh::SurfaceStackWrapper::SurfaceStackWrapper(std::shared_ptr const& wrapped) : wrapped(wrapped) { } void msh::SurfaceStackWrapper::add_surface( std::shared_ptr const& surface, input::InputReceptionMode new_mode) { wrapped->add_surface(surface, new_mode); } void msh::SurfaceStackWrapper::raise(std::weak_ptr const& surface) { wrapped->raise(surface); } void msh::SurfaceStackWrapper::raise(SurfaceSet const& surfaces) { wrapped->raise(surfaces); } void msh::SurfaceStackWrapper::remove_surface(std::weak_ptr const& surface) { wrapped->remove_surface(surface); } auto msh::SurfaceStackWrapper::surface_at(geometry::Point point) const -> std::shared_ptr { return wrapped->surface_at(point); } ./src/server/shell/default_persistent_surface_store.h0000644000015600001650000000255012676616125023320 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_SHELL_DEFAULT_PERSISTENT_SURFACE_STORE_H_ #define MIR_SHELL_DEFAULT_PERSISTENT_SURFACE_STORE_H_ #include "mir/shell/persistent_surface_store.h" namespace mir { namespace shell { class DefaultPersistentSurfaceStore : public PersistentSurfaceStore { public: DefaultPersistentSurfaceStore(); ~DefaultPersistentSurfaceStore() override; Id id_for_surface(std::shared_ptr const& surface) override; std::shared_ptr surface_for_id(Id const& id) const override; private: class SurfaceIdBimap; std::unique_ptr const store; }; } } #endif // MIR_SHELL_DEFAULT_PERSISTENT_SURFACE_STORE_H_ ./src/server/shell/frontend_shell.cpp0000644000015600001650000001366312676616125020040 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "frontend_shell.h" #include "mir/shell/persistent_surface_store.h" #include "mir/shell/shell.h" #include "mir/scene/session.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/scene/prompt_session.h" #include namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell::detail; std::shared_ptr msh::FrontendShell::open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) { return wrapped->open_session(client_pid, name, sink); } void msh::FrontendShell::close_session(std::shared_ptr const& session) { auto const scene_session = std::dynamic_pointer_cast(session); wrapped->close_session(scene_session); } std::shared_ptr msh::FrontendShell::start_prompt_session_for( std::shared_ptr const& session, ms::PromptSessionCreationParameters const& params) { auto const scene_session = std::dynamic_pointer_cast(session); return wrapped->start_prompt_session_for(scene_session, params); } void msh::FrontendShell::add_prompt_provider_for( std::shared_ptr const& prompt_session, std::shared_ptr const& session) { auto const scene_prompt_session = std::dynamic_pointer_cast(prompt_session); auto const scene_session = std::dynamic_pointer_cast(session); wrapped->add_prompt_provider_for(scene_prompt_session, scene_session); } void msh::FrontendShell::stop_prompt_session(std::shared_ptr const& prompt_session) { auto const scene_prompt_session = std::dynamic_pointer_cast(prompt_session); wrapped->stop_prompt_session(scene_prompt_session); } mf::SurfaceId msh::FrontendShell::create_surface( std::shared_ptr const& session, ms::SurfaceCreationParameters const& params, std::shared_ptr const& sink) { auto const scene_session = std::dynamic_pointer_cast(session); auto populated_params = params; // TODO: Fish out a policy verification object that enforces the various invariants // in the surface spec requirements (eg: regular surface has no parent, // dialog may have a parent, gloss must have a parent). if (populated_params.parent.lock() && populated_params.type.value() != mir_surface_type_inputmethod) { BOOST_THROW_EXCEPTION(std::invalid_argument("Foreign parents may only be set on surfaces of type mir_surface_type_inputmethod")); } if (populated_params.parent_id.is_set()) populated_params.parent = scene_session->surface(populated_params.parent_id.value()); return wrapped->create_surface(scene_session, populated_params, sink); } void msh::FrontendShell::modify_surface(std::shared_ptr const& session, mf::SurfaceId surface_id, SurfaceSpecification const& modifications) { auto const scene_session = std::dynamic_pointer_cast(session); auto const surface = scene_session->surface(surface_id); auto populated_modifications = modifications; if (populated_modifications.parent_id.is_set()) populated_modifications.parent = scene_session->surface(populated_modifications.parent_id.value()); wrapped->modify_surface(scene_session, surface, populated_modifications); } void msh::FrontendShell::destroy_surface(std::shared_ptr const& session, mf::SurfaceId surface) { auto const scene_session = std::dynamic_pointer_cast(session); wrapped->destroy_surface(scene_session, surface); } std::string msh::FrontendShell::persistent_id_for(std::shared_ptr const& session, mf::SurfaceId surface_id) { auto const scene_session = std::dynamic_pointer_cast(session); auto const surface = scene_session->surface(surface_id); return surface_store->id_for_surface(surface).serialize_to_string(); } std::shared_ptr msh::FrontendShell::surface_for_id(std::string const& serialized_id) { PersistentSurfaceStore::Id const id{serialized_id}; return surface_store->surface_for_id(id); } int msh::FrontendShell::set_surface_attribute( std::shared_ptr const& session, mf::SurfaceId surface_id, MirSurfaceAttrib attrib, int value) { auto const scene_session = std::dynamic_pointer_cast(session); auto const surface = scene_session->surface(surface_id); return wrapped->set_surface_attribute(scene_session, surface, attrib, value); } int msh::FrontendShell::get_surface_attribute( std::shared_ptr const& session, mf::SurfaceId surface_id, MirSurfaceAttrib attrib) { auto const scene_session = std::dynamic_pointer_cast(session); auto const surface = scene_session->surface(surface_id); return wrapped->get_surface_attribute(surface, attrib); } void msh::FrontendShell::raise_surface( std::shared_ptr const& session, mf::SurfaceId surface_id, uint64_t timestamp) { auto const scene_session = std::dynamic_pointer_cast(session); auto const surface = scene_session->surface(surface_id); wrapped->raise_surface(scene_session, surface, timestamp); } ./src/server/shell/CMakeLists.txt0000644000015600001650000000163312676616157017065 0ustar jenkinsjenkinsset( SHELL_SOURCES abstract_shell.cpp basic_window_manager.cpp ${CMAKE_SOURCE_DIR}/src/include/server/mir/shell/basic_window_manager.h canonical_window_manager.cpp ${CMAKE_SOURCE_DIR}/src/include/server/mir/shell/canonical_window_manager.h frontend_shell.cpp graphics_display_layout.cpp graphics_display_layout.h default_configuration.cpp null_host_lifecycle_event_listener.h shell_wrapper.cpp surface_ready_observer.cpp system_compositor_window_manager.cpp default_persistent_surface_store.cpp persistent_surface_store.cpp ${CMAKE_SOURCE_DIR}/include/server/mir/shell/display_configuration_controller.h surface_specification.cpp surface_stack_wrapper.cpp ${CMAKE_SOURCE_DIR}/include/server/mir/shell/surface_stack_wrapper.h window_management_info.cpp ${CMAKE_SOURCE_DIR}/src/include/server/mir/shell/window_management_info.h ) add_library( mirshell OBJECT ${SHELL_SOURCES} ) ./src/server/shell/surface_specification.cpp0000644000015600001650000000311412676616125021350 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Christopher James Halse Rogers . * * Authored by: Christopher James Halse Rogers */ #include "mir/shell/persistent_surface_store.h" #include namespace msh = mir::shell; using Id = mir::shell::PersistentSurfaceStore::Id; Id::Id() { uuid_generate(uuid); } Id::Id(std::string const& serialized_form) { using namespace std::literals::string_literals; if (serialized_form.size() != 36) { BOOST_THROW_EXCEPTION((std::invalid_argument{"Failed to parse: "s + serialized_form + " (has invalid length: "s + std::to_string(serialized_form.size()) + " expected 36)"})); } if (uuid_parse(serialized_form.c_str(), uuid) != 0) { BOOST_THROW_EXCEPTION((std::invalid_argument{"Failed to parse: "s + serialized_form})); } } Id::Id(Id const& rhs) { std::copy(rhs.uuid, rhs.uuid + sizeof(rhs.uuid), uuid); } Id& Id::operator=(Id const& rhs) { std::copy(rhs.uuid, rhs.uuid + sizeof(rhs.uuid), uuid); return *this; } bool Id::operator==(Id const& rhs) const { return uuid_compare(uuid, rhs.uuid) == 0; } std::string Id::serialize_to_string() const { // uuid_unparse adds a trailing null; allocate enough memory for it... char buffer[37]; uuid_unparse(uuid, buffer); return buffer; } auto std::hash::operator()(argument_type const &uuid) const -> result_type { // uuid_t is defined in the header to be a char[16]; hash this as two 64-byte integers // and mix the result with XOR. return std::hash()(*reinterpret_cast(uuid.uuid)) ^ std::hash()(*reinterpret_cast(uuid.uuid + sizeof(uint64_t))); } ./src/server/shell/null_host_lifecycle_event_listener.h0000644000015600001650000000213212676616125023620 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Cemil Azizoglu */ #ifndef MIR_NULL_HOST_LIFECYCLE_EVENT_LISTENER_H_ #define MIR_NULL_HOST_LIFECYCLE_EVENT_LISTENER_H_ #include "mir/shell/host_lifecycle_event_listener.h" namespace mir { namespace shell { class NullHostLifecycleEventListener : public HostLifecycleEventListener { public: virtual void lifecycle_event_occurred(MirLifecycleState /*state*/) override {} }; } } #endif /* MIR_NULL_HOST_LIFECYCLE_EVENT_LISTENER_H_ */ ./src/server/shell/system_compositor_window_manager.cpp0000644000015600001650000001442012676616125023705 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/shell/system_compositor_window_manager.h" #include "mir/shell/display_layout.h" #include "mir/shell/focus_controller.h" #include "mir/shell/surface_ready_observer.h" #include "mir/shell/surface_specification.h" #include "mir/scene/session.h" #include "mir/scene/session_coordinator.h" #include "mir/scene/surface.h" #include "mir/scene/surface_creation_parameters.h" #include namespace mf = mir::frontend; namespace ms = mir::scene; namespace msh = mir::shell; msh::SystemCompositorWindowManager::SystemCompositorWindowManager( FocusController* focus_controller, std::shared_ptr const& display_layout, std::shared_ptr const& session_coordinator) : focus_controller{focus_controller}, display_layout{display_layout}, session_coordinator{session_coordinator} { } void msh::SystemCompositorWindowManager::add_session(std::shared_ptr const& session) { on_session_added(session); } void msh::SystemCompositorWindowManager::remove_session(std::shared_ptr const& session) { on_session_removed(session); } auto msh::SystemCompositorWindowManager::add_surface( std::shared_ptr const& session, ms::SurfaceCreationParameters const& params, std::function const& session, ms::SurfaceCreationParameters const& params)> const& build) -> mf::SurfaceId { mir::geometry::Rectangle rect{params.top_left, params.size}; if (!params.output_id.as_value()) BOOST_THROW_EXCEPTION(std::runtime_error("An output ID must be specified")); display_layout->place_in_output(params.output_id, rect); auto placed_parameters = params; placed_parameters.top_left = rect.top_left; placed_parameters.size = rect.size; auto const result = build(session, placed_parameters); auto const surface = session->surface(result); auto const session_ready_observer = std::make_shared( [this](std::shared_ptr const& session, std::shared_ptr const& /*surface*/) { on_session_ready(session); }, session, surface); surface->add_observer(session_ready_observer); std::lock_guard lock{mutex}; output_map[surface] = params.output_id; return result; } void msh::SystemCompositorWindowManager::modify_surface( std::shared_ptr const& /*session*/, std::shared_ptr const& surface, SurfaceSpecification const& modifications) { if (modifications.name.is_set()) surface->rename(modifications.name.value()); if (modifications.output_id.is_set()) { auto const output_id = modifications.output_id.value(); mir::geometry::Rectangle rect{surface->top_left(), surface->size()}; if (display_layout->place_in_output(output_id, rect)) { surface->move_to(rect.top_left); surface->resize(rect.size); } std::lock_guard lock{mutex}; output_map[surface] = output_id; } } void msh::SystemCompositorWindowManager::remove_surface( std::shared_ptr const& session, std::weak_ptr const& surface) { session->destroy_surface(surface); std::lock_guard lock{mutex}; output_map.erase(surface); } void msh::SystemCompositorWindowManager::add_display(mir::geometry::Rectangle const& /*area*/) { std::lock_guard lock{mutex}; for (auto const& so : output_map) { if (auto surface = so.first.lock()) { auto const output_id = so.second; mir::geometry::Rectangle rect{surface->top_left(), surface->size()}; if (display_layout->place_in_output(output_id, rect)) { surface->move_to(rect.top_left); surface->resize(rect.size); } } } } void msh::SystemCompositorWindowManager::remove_display(mir::geometry::Rectangle const& /*area*/) { } bool msh::SystemCompositorWindowManager::handle_keyboard_event(MirKeyboardEvent const* /*event*/) { return false; } bool msh::SystemCompositorWindowManager::handle_touch_event(MirTouchEvent const* /*event*/) { return false; } bool msh::SystemCompositorWindowManager::handle_pointer_event(MirPointerEvent const* /*event*/) { return false; } int msh::SystemCompositorWindowManager::set_surface_attribute( std::shared_ptr const& /*session*/, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) { return surface->configure(attrib, value); } void msh::SystemCompositorWindowManager::on_session_added(std::shared_ptr const& /*session*/) const { } void msh::SystemCompositorWindowManager::on_session_removed(std::shared_ptr const& session) const { if (focus_controller->focused_session() == session) { auto const next_session = session_coordinator->successor_of({}); if (next_session) focus_controller->set_focus_to(next_session, next_session->default_surface()); else focus_controller->set_focus_to(next_session, {}); } } void msh::SystemCompositorWindowManager::on_session_ready(std::shared_ptr const& session) const { focus_controller->set_focus_to(session, session->default_surface()); } void msh::SystemCompositorWindowManager::handle_raise_surface( std::shared_ptr const& /*session*/, std::shared_ptr const& /*surface*/, uint64_t /*timestamp*/) { } ./src/server/compositor/0000755000015600001650000000000012676616160015403 5ustar jenkinsjenkins./src/server/compositor/buffer_map.cpp0000644000015600001650000000603312676616125020220 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/frontend/buffer_sink.h" #include "buffer_map.h" #include #include namespace mc = mir::compositor; namespace mf = mir::frontend; namespace mg = mir::graphics; namespace mir { namespace compositor { enum class BufferMap::Owner { server, client }; } } mc::BufferMap::BufferMap( mf::BufferStreamId id, std::shared_ptr const& sink, std::shared_ptr const& allocator) : stream_id(id), sink(sink), allocator(allocator) { } mg::BufferID mc::BufferMap::add_buffer(mg::BufferProperties const& properties) { std::unique_lock lk(mutex); auto buffer = allocator->alloc_buffer(properties); buffers[buffer->id()] = {buffer, Owner::client}; sink->send_buffer(stream_id, *buffer, mg::BufferIpcMsgType::full_msg); return buffer->id(); } void mc::BufferMap::remove_buffer(mg::BufferID id) { std::unique_lock lk(mutex); buffers.erase(checked_buffers_find(id, lk)); } void mc::BufferMap::send_buffer(mg::BufferID id) { std::unique_lock lk(mutex); auto it = buffers.find(id); if (it != buffers.end()) { auto buffer = it->second.buffer; it->second.owner = Owner::client; lk.unlock(); sink->send_buffer(stream_id, *buffer, mg::BufferIpcMsgType::update_msg); } } void mc::BufferMap::receive_buffer(graphics::BufferID id) { std::unique_lock lk(mutex); auto it = buffers.find(id); if (it != buffers.end()) it->second.owner = Owner::server; } std::shared_ptr& mc::BufferMap::operator[](mg::BufferID id) { std::unique_lock lk(mutex); return checked_buffers_find(id, lk)->second.buffer; } mc::BufferMap::Map::iterator mc::BufferMap::checked_buffers_find( mg::BufferID id, std::unique_lock const&) { auto it = buffers.find(id); if (it == buffers.end()) BOOST_THROW_EXCEPTION(std::logic_error("cannot find buffer by id")); return it; } size_t mc::BufferMap::client_owned_buffer_count() const { std::unique_lock lk(mutex); return std::count_if(buffers.begin(), buffers.end(), [](std::pair const& entry) { return entry.second.owner == Owner::client; }); } ./src/server/compositor/compositing_screencast.cpp0000644000015600001650000001600412676616125022656 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "compositing_screencast.h" #include "screencast_display_buffer.h" #include "mir/graphics/buffer.h" #include "mir/graphics/buffer_properties.h" #include "mir/graphics/display.h" #include "mir/graphics/virtual_output.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/gl_context.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/compositor/display_buffer_compositor_factory.h" #include "mir/compositor/display_buffer_compositor.h" #include "mir/geometry/rectangles.h" #include "mir/raii.h" #include namespace mc = mir::compositor; namespace mf = mir::frontend; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { uint32_t const max_screencast_sessions{100}; bool needs_virtual_output(mg::DisplayConfiguration const& conf, geom::Rectangle const& region) { geom::Rectangles disp_rects; conf.for_each_output([&disp_rects](mg::DisplayConfigurationOutput const& disp_conf) { if (disp_conf.connected) disp_rects.add(disp_conf.extents()); }); geom::Rectangle empty{}; return empty == disp_rects.bounding_rectangle().intersection_with(region); } std::unique_ptr make_virtual_output(mg::Display& display, geom::Rectangle const& rect) { if (needs_virtual_output(*display.configuration(), rect)) { return display.create_virtual_output(rect.size.width.as_int(), rect.size.height.as_int()); } return nullptr; } } struct mc::detail::ScreencastSessionContext { ScreencastSessionContext( std::shared_ptr const& scene, std::shared_ptr const& buffer, std::unique_ptr gl_context, std::unique_ptr display_buffer, std::unique_ptr display_buffer_compositor, std::unique_ptr a_virtual_output) : scene{scene}, buffer{buffer}, gl_context{std::move(gl_context)}, display_buffer{std::move(display_buffer)}, display_buffer_compositor{std::move(display_buffer_compositor)}, virtual_output{std::move(a_virtual_output)} { scene->register_compositor(this); if (virtual_output) virtual_output->enable(); } ~ScreencastSessionContext() { scene->unregister_compositor(this); } std::shared_ptr const scene; std::shared_ptr buffer; std::unique_ptr gl_context; std::unique_ptr display_buffer; std::unique_ptr display_buffer_compositor; std::unique_ptr virtual_output; }; mc::CompositingScreencast::CompositingScreencast( std::shared_ptr const& scene, std::shared_ptr const& display, std::shared_ptr const& buffer_allocator, std::shared_ptr const& db_compositor_factory) : scene{scene}, display{display}, buffer_allocator{buffer_allocator}, db_compositor_factory{db_compositor_factory} { } mf::ScreencastSessionId mc::CompositingScreencast::create_session( geom::Rectangle const& region, geom::Size const& size, MirPixelFormat const pixel_format) { if (size.width.as_int() == 0 || size.height.as_int() == 0 || region.size.width.as_int() == 0 || region.size.height.as_int() == 0 || pixel_format == mir_pixel_format_invalid) { BOOST_THROW_EXCEPTION(std::runtime_error("Invalid parameters")); } std::lock_guard lock{session_mutex}; auto const id = next_available_session_id(); session_contexts[id] = create_session_context(region, size, pixel_format); return id; } void mc::CompositingScreencast::destroy_session(mf::ScreencastSessionId id) { std::lock_guard lock{session_mutex}; auto gl_context = std::move(session_contexts.at(id)->gl_context); auto using_gl_context = mir::raii::paired_calls( [&] { gl_context->make_current(); }, [&] { gl_context->release_current(); }); session_contexts.erase(id); } std::shared_ptr mc::CompositingScreencast::capture(mf::ScreencastSessionId id) { std::shared_ptr session_context; { std::lock_guard lock{session_mutex}; session_context = session_contexts.at(id); } auto using_gl_context = mir::raii::paired_calls( [&] { session_context->gl_context->make_current(); }, [&] { session_context->gl_context->release_current(); }); session_context->display_buffer_compositor->composite( session_context->scene->scene_elements_for(session_context.get())); return session_context->buffer; } mf::ScreencastSessionId mc::CompositingScreencast::next_available_session_id() { for (uint32_t i = 1; i <= max_screencast_sessions; ++i) { mf::ScreencastSessionId const id{i}; if (session_contexts.find(id) == session_contexts.end()) return id; } BOOST_THROW_EXCEPTION(std::runtime_error("Too many screencast sessions!")); } std::shared_ptr mc::CompositingScreencast::create_session_context( geometry::Rectangle const& rect, geometry::Size const& size, MirPixelFormat pixel_format) { mg::BufferProperties buffer_properties{ size, pixel_format, mg::BufferUsage::hardware}; auto gl_context = display->create_gl_context(); auto gl_context_raw = gl_context.get(); auto using_gl_context = mir::raii::paired_calls( [&] { gl_context_raw->make_current(); }, [&] { gl_context_raw->release_current(); }); auto buffer = buffer_allocator->alloc_buffer(buffer_properties); auto display_buffer = std::make_unique(rect, *buffer); auto db_compositor = db_compositor_factory->create_compositor_for(*display_buffer); auto virtual_output = make_virtual_output(*display, rect); return std::shared_ptr( new detail::ScreencastSessionContext{ scene, buffer, std::move(gl_context), std::move(display_buffer), std::move(db_compositor), std::move(virtual_output)}); } ./src/server/compositor/buffer_bundle.h0000644000015600001650000000640612676616125020365 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #ifndef MIR_COMPOSITOR_BUFFER_BUNDLE_H_ #define MIR_COMPOSITOR_BUFFER_BUNDLE_H_ #include "mir/graphics/buffer_properties.h" #include namespace mir { namespace graphics { class Buffer; struct BufferProperties; } namespace compositor { class BufferAcquisition { public: /** * Acquire the next buffer that's ready to display/composite. * * \param [in] user_id A unique identifier of who is going to use the * buffer, to ensure that separate users representing * separate monitors who need the same frame will get * the same buffer. However consecutive calls for the * same user will get different buffers. To avoid * collisions, all callers should determine user_id * in the same way (e.g. always use "this" pointer). */ virtual std::shared_ptr compositor_acquire(void const* user_id) = 0; virtual void compositor_release(std::shared_ptr const&) = 0; virtual std::shared_ptr snapshot_acquire() = 0; virtual void snapshot_release(std::shared_ptr const&) = 0; virtual ~BufferAcquisition() = default; protected: BufferAcquisition() = default; BufferAcquisition(BufferAcquisition const&) = delete; BufferAcquisition& operator=(BufferAcquisition const&) = delete; }; class BufferBundle : public BufferAcquisition { public: virtual ~BufferBundle() noexcept {} virtual void client_acquire(std::function complete) = 0; virtual void client_release(graphics::Buffer*) = 0; virtual graphics::BufferProperties properties() const = 0; virtual void allow_framedropping(bool dropping_allowed) = 0; virtual void force_requests_to_complete() = 0; virtual void resize(const geometry::Size &newsize) = 0; virtual int buffers_ready_for_compositor(void const* user_id) const = 0; /** * Return the number of client acquisitions that can be completed * synchronously without blocking, before a compositor consumes one. This * is used for pre-filling the queue in tests. Don't assume it's always * nbuffers-1 as it might be less. */ virtual int buffers_free_for_client() const = 0; virtual void drop_old_buffers() = 0; virtual void drop_client_requests() = 0; protected: BufferBundle() = default; BufferBundle(BufferBundle const&) = delete; BufferBundle& operator=(BufferBundle const&) = delete; }; } } #endif /*MIR_COMPOSITOR_BUFFER_BUNDLE_H_*/ ./src/server/compositor/default_display_buffer_compositor.h0000644000015600001650000000311112676616125024531 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_COMPOSITOR_DEFAULT_DISPLAY_BUFFER_COMPOSITOR_H_ #define MIR_COMPOSITOR_DEFAULT_DISPLAY_BUFFER_COMPOSITOR_H_ #include "mir/compositor/display_buffer_compositor.h" #include "mir/compositor/compositor_report.h" #include namespace mir { namespace graphics { class DisplayBuffer; } namespace compositor { class Scene; class Renderer; class DefaultDisplayBufferCompositor : public DisplayBufferCompositor { public: DefaultDisplayBufferCompositor( graphics::DisplayBuffer& display_buffer, std::shared_ptr const& renderer, std::shared_ptr const& report); void composite(SceneElementSequence&& scene_sequence) override; private: graphics::DisplayBuffer& display_buffer; std::shared_ptr const renderer; std::shared_ptr const report; }; } } #endif /* MIR_COMPOSITOR_DEFAULT_DISPLAY_BUFFER_COMPOSITOR_H_ */ ./src/server/compositor/buffer_stream_factory.cpp0000644000015600001650000000511212676616125022462 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Alan Griffiths * Thomas Voss */ #include "buffer_stream_factory.h" #include "buffer_stream_surfaces.h" #include "mir/graphics/buffer_properties.h" #include "buffer_queue.h" #include "stream.h" #include "buffer_map.h" #include "mir/graphics/buffer.h" #include "mir/graphics/buffer_id.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/display.h" #include #include namespace mc = mir::compositor; namespace mg = mir::graphics; namespace ms = mir::scene; namespace mf = mir::frontend; mc::BufferStreamFactory::BufferStreamFactory( std::shared_ptr const& gralloc, std::shared_ptr const& policy_factory, unsigned int nbuffers) : gralloc(gralloc), policy_factory{policy_factory}, nbuffers(nbuffers) { assert(gralloc); assert(policy_factory); if (nbuffers == 1) throw std::logic_error("nbuffers cannot be 1"); } std::shared_ptr mc::BufferStreamFactory::create_buffer_stream( mf::BufferStreamId id, std::shared_ptr const& sink, mg::BufferProperties const& buffer_properties) { return create_buffer_stream(id, sink, nbuffers, buffer_properties); } std::shared_ptr mc::BufferStreamFactory::create_buffer_stream( mf::BufferStreamId id, std::shared_ptr const& sink, int nbuffers, mg::BufferProperties const& buffer_properties) { if (nbuffers == 0) { return std::make_shared( *policy_factory, std::make_unique(id, sink, gralloc), buffer_properties.size, buffer_properties.format); } else { auto switching_bundle = std::make_shared( nbuffers, gralloc, buffer_properties, *policy_factory); return std::make_shared(switching_bundle); } } ./src/server/compositor/timeout_frame_dropping_policy_factory.h0000644000015600001650000000354212676616125025431 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Alberto Aguirre */ #ifndef MIR_COMPOSITOR_TIMEOUT_FRAME_DROPPING_POLICY_FACTORY_H_ #define MIR_COMPOSITOR_TIMEOUT_FRAME_DROPPING_POLICY_FACTORY_H_ #include "mir/compositor/frame_dropping_policy_factory.h" #include "mir/time/alarm_factory.h" #include #include namespace mir { namespace compositor { /** * \brief Creator of timeout-based FrameDroppingPolicies */ class TimeoutFrameDroppingPolicyFactory : public FrameDroppingPolicyFactory { public: /** * \param factory Factory which can create alarms used to schedule frame drops. * \param timeout Number of milliseconds to wait before dropping a frame */ TimeoutFrameDroppingPolicyFactory( std::shared_ptr const& factory, std::chrono::milliseconds timeout); std::unique_ptr create_policy( std::shared_ptr const& drop_frame) const override; private: std::shared_ptr const factory; std::chrono::milliseconds timeout; }; } } #endif // MIR_COMPOSITOR_TIMEOUT_FRAME_DROPPING_POLICY_FACTORY_FACTORY_H_ ./src/server/compositor/timeout_frame_dropping_policy_factory.cpp0000644000015600001650000000613612676616125025766 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Alberto Aguirre */ #include "mir/compositor/frame_dropping_policy.h" #include "mir/lockable_callback_wrapper.h" #include "timeout_frame_dropping_policy_factory.h" #include #include #include #include namespace mc = mir::compositor; namespace { class TimeoutFrameDroppingPolicy : public mc::FrameDroppingPolicy { public: TimeoutFrameDroppingPolicy(std::shared_ptr const& factory, std::chrono::milliseconds timeout, std::shared_ptr const& drop_frame); void swap_now_blocking() override; void swap_unblocked() override; private: std::chrono::milliseconds const timeout; std::atomic pending_swaps; // Ensure alarm gets destroyed first so its handler does not access dead // objects. std::unique_ptr const alarm; }; TimeoutFrameDroppingPolicy::TimeoutFrameDroppingPolicy(std::shared_ptr const& factory, std::chrono::milliseconds timeout, std::shared_ptr const& callback) : timeout{timeout}, pending_swaps{0}, alarm{factory->create_alarm( std::make_shared(callback, [this] { assert(pending_swaps.load() > 0); }, [this] { if (--pending_swaps > 0) alarm->reschedule_in(this->timeout);} ))} { } void TimeoutFrameDroppingPolicy::swap_now_blocking() { if (pending_swaps++ == 0) alarm->reschedule_in(timeout); } void TimeoutFrameDroppingPolicy::swap_unblocked() { if (alarm->state() != mir::time::Alarm::cancelled && alarm->cancel()) { if (--pending_swaps > 0) { alarm->reschedule_in(timeout); } } } } mc::TimeoutFrameDroppingPolicyFactory::TimeoutFrameDroppingPolicyFactory( std::shared_ptr const& timer, std::chrono::milliseconds timeout) : factory{timer}, timeout{timeout} { } std::unique_ptr mc::TimeoutFrameDroppingPolicyFactory::create_policy(std::shared_ptr const& drop_frame) const { return std::make_unique(factory, timeout, drop_frame); } ./src/server/compositor/queueing_schedule.h0000644000015600001650000000241012676616125021250 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_COMPOSITOR_QUEUEING_SCHEDULE_H_ #define MIR_COMPOSITOR_QUEUEING_SCHEDULE_H_ #include "schedule.h" #include #include #include namespace mir { namespace graphics { class Buffer; } namespace compositor { class QueueingSchedule : public Schedule { public: void schedule(std::shared_ptr const& buffer); unsigned int num_scheduled(); std::shared_ptr next_buffer(); private: std::mutex mutable mutex; std::deque> queue; }; } } #endif /* MIR_COMPOSITOR_QUEUEING_SCHEDULE_H_ */ ./src/server/compositor/buffer_queue.cpp0000644000015600001650000005233412676616125020574 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "buffer_queue.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/buffer_id.h" #include "mir/lockable_callback.h" #include #include #include namespace mc = mir::compositor; namespace mg = mir::graphics; namespace { mg::Buffer* pop(std::deque& q) { auto const buffer = q.front(); q.pop_front(); return buffer; } bool remove(mg::Buffer const* item, std::vector& list) { int const size = list.size(); for (int i = 0; i < size; ++i) { if (list[i] == item) { list.erase(list.begin() + i); return true; } } /* nothing removed*/ return false; } bool contains(mg::Buffer const* item, std::vector const& list) { int const size = list.size(); for (int i = 0; i < size; ++i) { if (list[i] == item) return true; } return false; } std::shared_ptr const& buffer_for(mg::Buffer const* item, std::vector> const& list) { int const size = list.size(); for (int i = 0; i < size; ++i) { if (list[i].get() == item) return list[i]; } BOOST_THROW_EXCEPTION(std::logic_error("buffer for pointer not found in list")); } void replace(mg::Buffer const* item, std::shared_ptr const& new_buffer, std::vector>& list) { int size = list.size(); for (int i = 0; i < size; ++i) { if (list[i].get() == item) { list[i] = new_buffer; return; } } BOOST_THROW_EXCEPTION(std::logic_error("item to replace not found in list")); } } namespace mir { namespace compositor { class BufferQueue::LockableCallback : public mir::LockableCallback { public: LockableCallback(BufferQueue* const q) : q{q} { } void operator()() override { // We ignore any ongoing snapshotting as it could lead to deadlock. // In order to wait the guard_lock needs to be released; a BufferQueue::release // call can sneak in at that time from a different thread which // can invoke framedrop_policy methods q->drop_frame(guard_lock, BufferQueue::ignore_snapshot); } void lock() override { // This lock is aquired before the framedrop policy acquires any locks of its own. guard_lock = std::unique_lock{q->guard}; } void unlock() override { if (guard_lock.owns_lock()) guard_lock.unlock(); } private: BufferQueue* const q; std::unique_lock guard_lock; }; } } mc::BufferQueue::BufferQueue( int nbuffers, std::shared_ptr const& gralloc, graphics::BufferProperties const& props, mc::FrameDroppingPolicyFactory const& policy_provider) : nbuffers{nbuffers}, frame_deadlines_threshold{-1}, // Disable scaling by default frame_deadlines_met{0}, scheduled_extra_frames{0}, frame_dropping_enabled{false}, current_compositor_buffer_valid{false}, the_properties{props}, force_new_compositor_buffer{false}, callbacks_allowed{true}, single_compositor{false}, // When true we can optimize performance gralloc{gralloc} { if (nbuffers < 1) { BOOST_THROW_EXCEPTION( std::logic_error("invalid number of buffers for BufferQueue")); } /* By default not all buffers are allocated. * If there is increased pressure by the client to acquire * more buffers, more will be allocated at that time (up to nbuffers) */ auto buf = gralloc->alloc_buffer(the_properties); buffers.push_back(buf); current_compositor_buffer = buf.get(); /* Special case: with one buffer both clients and compositors * need to share the same buffer */ if (nbuffers == 1) free_buffers.push_back(current_compositor_buffer); framedrop_policy = policy_provider.create_policy( std::make_shared(this)); } void mc::BufferQueue::set_scaling_delay(int nframes) { std::unique_lock lock(guard); frame_deadlines_threshold = nframes; } int mc::BufferQueue::scaling_delay() const { std::unique_lock lock(guard); return frame_deadlines_threshold; } bool mc::BufferQueue::client_ahead_of_compositor() const { return nbuffers > 1 && !frame_dropping_enabled && // Never throttle frame droppers frame_deadlines_threshold >= 0 && // Queue scaling enabled !ready_to_composite_queue.empty() && // At least one frame is ready frame_deadlines_met >= frame_deadlines_threshold; // Is smooth } mg::Buffer* mc::BufferQueue::get_a_free_buffer() { mg::Buffer* buf = nullptr; if (!free_buffers.empty()) { buf = free_buffers.back(); free_buffers.pop_back(); } else if (static_cast(buffers.size()) < nbuffers) { auto const& buffer = gralloc->alloc_buffer(the_properties); buffers.push_back(buffer); buf = buffer.get(); } return buf; } void mc::BufferQueue::client_acquire(mc::BufferQueue::Callback complete) { std::unique_lock lock(guard); pending_client_notifications.push_back(std::move(complete)); /* * Fast clients that can be safely throttled should be. This keeps the * number of prefilled buffers limited to one (equivalent to double- * buffering) for minimal latency. */ if (client_ahead_of_compositor()) { framedrop_policy->swap_now_blocking(); return; } if (auto buf = get_a_free_buffer()) { give_buffer_to_client(buf, lock); return; } /* Last resort, drop oldest buffer from the ready queue */ if (frame_dropping_enabled) { drop_frame(lock, wait_for_snapshot); return; } /* Can't give the client a buffer yet; they'll just have to wait * until the compositor is done with an old frame, or the policy * says they've waited long enough. */ framedrop_policy->swap_now_blocking(); } void mc::BufferQueue::client_release(graphics::Buffer* released_buffer) { std::lock_guard lock(guard); if (buffers_owned_by_client.empty()) { BOOST_THROW_EXCEPTION( std::logic_error("unexpected release: no buffers were given to client")); } if (buffers_owned_by_client.front() != released_buffer) { BOOST_THROW_EXCEPTION( std::logic_error("client released out of sequence")); } auto const buffer = pop(buffers_owned_by_client); ready_to_composite_queue.push_back(buffer); /* * Timerless client performance detection: * Over-schedule the compositor so that it can detect if the client * is falling behind. Otherwise the compositor itself goes to sleep * and wouldn't be able to tell a slow client from a fast one. */ scheduled_extra_frames = frame_deadlines_threshold - 1; } std::shared_ptr mc::BufferQueue::compositor_acquire(void const* user_id) { std::unique_lock lock(guard); bool use_current_buffer = false; if (is_a_current_buffer_user(user_id)) // Primary/fastest display { /* * Yes I know different compositor user_ids will get different * results with this but that's OK, and actually more efficient. * We only need to overschedule one display at most for the * slow client detection to work. */ if (scheduled_extra_frames > 0) --scheduled_extra_frames; if (ready_to_composite_queue.empty()) frame_deadlines_met = 0; else if (frame_deadlines_met < frame_deadlines_threshold) ++frame_deadlines_met; } else // Second and subsequent displays sync to the primary one { use_current_buffer = true; current_buffer_users.push_back(user_id); } single_compositor = current_compositor_buffer_valid && current_buffer_users.size() <= 1; // might be zero if (ready_to_composite_queue.empty()) { use_current_buffer = true; } else if (force_new_compositor_buffer) { use_current_buffer = false; force_new_compositor_buffer = false; } mg::Buffer* buffer_to_release = nullptr; if (!use_current_buffer) { /* No other compositors currently reference this * buffer so release it */ if (!contains(current_compositor_buffer, buffers_sent_to_compositor)) buffer_to_release = current_compositor_buffer; /* The current compositor buffer is * being changed, the new one has no users yet */ current_buffer_users.clear(); current_buffer_users.push_back(user_id); current_compositor_buffer = pop(ready_to_composite_queue); current_compositor_buffer_valid = true; /* * If we just emptied the ready queue above and hold this compositor * buffer for very long (e.g. bypass) then there's a chance the client * would starve and miss a frame. Make sure that can't happen, by * using more than double buffers... */ if (!buffer_to_release && !client_ahead_of_compositor()) buffer_to_release = get_a_free_buffer(); } else if (current_buffer_users.empty()) { // current_buffer_users and ready_to_composite_queue both empty current_buffer_users.push_back(user_id); } buffers_sent_to_compositor.push_back(current_compositor_buffer); std::shared_ptr const acquired_buffer = buffer_for(current_compositor_buffer, buffers); if (buffer_to_release) release(buffer_to_release, std::move(lock)); return acquired_buffer; } void mc::BufferQueue::compositor_release(std::shared_ptr const& buffer) { std::unique_lock lock(guard); if (!remove(buffer.get(), buffers_sent_to_compositor)) { BOOST_THROW_EXCEPTION( std::logic_error("unexpected release: buffer was not given to compositor")); } /* Not ready to release it yet, other compositors still reference this buffer */ if (contains(buffer.get(), buffers_sent_to_compositor)) return; if (nbuffers <= 1) return; if (current_compositor_buffer != buffer.get()) release(buffer.get(), std::move(lock)); else if (!ready_to_composite_queue.empty() && single_compositor) { /* * The "early release" optimisation: Note "single_compositor" above * is because this path will break (actually just overclock) the * multi-monitor frame sync algorithm. For the moment we prefer * /perfect/ multi-monitor frame sync all the time. But if you so * choose that overclocking with multi-monitors is acceptable then * you could remove the above "single_compositor" check and get this * optimised code path with multi-monitors too... */ current_compositor_buffer = pop(ready_to_composite_queue); current_buffer_users.clear(); /* * As we have now caused the next compositor_acquire() to skip the * frame_deadlines_met update, we need to do it here: */ ++frame_deadlines_met; release(buffer.get(), std::move(lock)); } } std::shared_ptr mc::BufferQueue::snapshot_acquire() { std::unique_lock lock(guard); pending_snapshots.push_back(current_compositor_buffer); return buffer_for(current_compositor_buffer, buffers); } void mc::BufferQueue::snapshot_release(std::shared_ptr const& buffer) { std::unique_lock lock(guard); if (!remove(buffer.get(), pending_snapshots)) { BOOST_THROW_EXCEPTION( std::logic_error("unexpected release: no buffers were given to snapshotter")); } snapshot_released.notify_all(); } mg::BufferProperties mc::BufferQueue::properties() const { std::lock_guard lock(guard); return the_properties; } void mc::BufferQueue::allow_framedropping(bool flag) { std::lock_guard lock(guard); frame_dropping_enabled = flag; } bool mc::BufferQueue::framedropping_allowed() const { std::lock_guard lock(guard); return frame_dropping_enabled; } void mc::BufferQueue::force_requests_to_complete() { std::unique_lock lock(guard); if (!pending_client_notifications.empty() && !ready_to_composite_queue.empty()) { auto const buffer = pop(ready_to_composite_queue); while (!ready_to_composite_queue.empty()) { free_buffers.push_back(pop(ready_to_composite_queue)); } give_buffer_to_client(buffer, lock, ignore_snapshot); } } void mc::BufferQueue::resize(geometry::Size const& new_size) { std::lock_guard lock(guard); the_properties.size = new_size; } int mc::BufferQueue::buffers_ready_for_compositor(void const* user_id) const { std::lock_guard lock(guard); int count = ready_to_composite_queue.size(); if (!is_a_current_buffer_user(user_id)) { // The virtual front of the ready queue isn't actually in the ready // queue, but is the current_compositor_buffer, so count that too: ++count; } /* * Intentionally schedule more frames than we need, and for good * reason... We can only accurately detect frame_deadlines_met in * compositor_acquire if compositor_acquire is still waking up at full * frame rate even with a slow client. This is crucial to scaling the * queue performance dynamically in "client_ahead_of_compositor". * But don't be concerned; very little is lost by over-scheduling. Under * normal smooth rendering conditions all frames are used (not wasted). * And under sluggish client rendering conditions the extra frames have a * critical role in providing a sample point in which we detect if the * client is keeping up. Only when the compositor changes from active to * idle is the extra frame wasted. Sounds like a reasonable price to pay * for dynamic performance monitoring. */ if (frame_deadlines_threshold >= 0 && scheduled_extra_frames > 0) count += scheduled_extra_frames; return count; } /* * This function is a kludge used in tests only. It attempts to predict how * many calls to client_acquire you can make before it blocks. * Future tests should not use it. */ int mc::BufferQueue::buffers_free_for_client() const { std::lock_guard lock(guard); int ret = 1; if (nbuffers == 1) ret = 1; else if (client_ahead_of_compositor()) // client_acquire will block ret = 0; else { int nfree = free_buffers.size(); int future_growth = nbuffers - buffers.size(); ret = nfree + future_growth; } return ret; } void mc::BufferQueue::give_buffer_to_client( mg::Buffer* buffer, std::unique_lock& lock) { give_buffer_to_client(buffer, lock, wait_for_snapshot); } void mc::BufferQueue::give_buffer_to_client( mg::Buffer* buffer, std::unique_lock& lock, SnapshotWait wait_type) { /* Clears callback */ auto give_to_client_cb = std::move(pending_client_notifications.front()); pending_client_notifications.pop_front(); bool const resize_buffer = buffer->size() != the_properties.size; if (resize_buffer) { auto const& resized_buffer = gralloc->alloc_buffer(the_properties); replace(buffer, resized_buffer, buffers); buffer = resized_buffer.get(); /* Special case: the current compositor buffer also needs to be * replaced as it's shared with the client */ if (nbuffers == 1) current_compositor_buffer = buffer; } /* Don't give to the client just yet if there's a pending snapshot */ if (wait_type == wait_for_snapshot && !resize_buffer && contains(buffer, pending_snapshots)) { snapshot_released.wait(lock, [&]{ return !contains(buffer, pending_snapshots); }); } if (!callbacks_allowed) // We're shutting down return; buffers_owned_by_client.push_back(buffer); lock.unlock(); try { give_to_client_cb(buffer); } catch (...) { /* comms errors should not propagate to compositing threads */ } } bool mc::BufferQueue::is_a_current_buffer_user(void const* user_id) const { if (!current_compositor_buffer_valid) return true; int const size = current_buffer_users.size(); int i = 0; while (i < size && current_buffer_users[i] != user_id) ++i; return i < size; } void mc::BufferQueue::release( mg::Buffer* buffer, std::unique_lock lock) { if (!pending_client_notifications.empty() && !client_ahead_of_compositor()) { framedrop_policy->swap_unblocked(); give_buffer_to_client(buffer, lock); } else if (!frame_dropping_enabled && buffers.size() > size_t(nbuffers)) { /* * We're overallocated. * If frame_dropping_enabled, keep it that way to avoid having * to repeatedly reallocate. We must need the overallocation due to a * greedy compositor and insufficient nbuffers (LP: #1379685). * If not framedropping then we only overallocated to briefly * guarantee the framedropping policy and poke the client. Safe * to free it then because that's a rare occurrence. */ for (auto i = buffers.begin(); i != buffers.end(); ++i) { if (i->get() == buffer) { buffers.erase(i); break; } } } else free_buffers.push_back(buffer); } void mc::BufferQueue::drop_frame(std::unique_lock& lock, SnapshotWait wait_type) { // Make sure there is a client waiting for the frame before we drop it. // If not, then there's nothing to do. if (pending_client_notifications.empty()) return; mg::Buffer* buffer_to_give = nullptr; if (!free_buffers.empty()) { // We expect this to usually be empty, but always check free list first buffer_to_give = free_buffers.back(); free_buffers.pop_back(); } else if (!ready_to_composite_queue.empty() && !contains(current_compositor_buffer, buffers_sent_to_compositor)) { // Remember current_compositor_buffer is implicitly the front // of the ready queue. buffer_to_give = current_compositor_buffer; current_compositor_buffer = pop(ready_to_composite_queue); current_buffer_users.clear(); current_compositor_buffer_valid = true; } else if (ready_to_composite_queue.size() > 1) { buffer_to_give = pop(ready_to_composite_queue); } else { /* * Insufficient nbuffers for frame dropping? This means you're either * trying to use frame dropping with bypass/overlays/multimonitor or * have chosen nbuffers too low, or some combination thereof. So * consider the options... * 1. Crash. No, that's really unhelpful. * 2. Drop the visible frame. Probably not; that looks pretty awful. * Not just tearing but you'll see very ugly polygon rendering * artifacts. * 3. Drop the newest ready frame. Absolutely not; that will cause * indefinite freezes or at least stuttering. * 4. Just give a warning and carry on at regular frame rate * as if framedropping was disabled. That's pretty nice, but we * can do better still... * 5. Overallocate; more buffers! Yes, see below. */ auto const& buffer = gralloc->alloc_buffer(the_properties); buffers.push_back(buffer); buffer_to_give = buffer.get(); } give_buffer_to_client(buffer_to_give, lock, wait_type); } void mc::BufferQueue::drop_old_buffers() { std::vector to_release; { std::lock_guard lock{guard}; force_new_compositor_buffer = true; while (ready_to_composite_queue.size() > 1) to_release.push_back(pop(ready_to_composite_queue)); } for (auto buffer : to_release) { std::unique_lock lock{guard}; release(buffer, std::move(lock)); } std::lock_guard lock(guard); scheduled_extra_frames = 0; } void mc::BufferQueue::drop_client_requests() { std::unique_lock lock(guard); callbacks_allowed = false; pending_client_notifications.clear(); } ./src/server/compositor/compositing_screencast.h0000644000015600001650000000503712676616125022327 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_COMPOSITOR_COMPOSITING_SCREENCAST_H_ #define MIR_COMPOSITOR_COMPOSITING_SCREENCAST_H_ #include "mir/frontend/screencast.h" #include #include namespace mir { namespace geometry { struct Rectangle; } namespace graphics { class Display; class DisplayBuffer; class GraphicBufferAllocator; } namespace compositor { class Scene; namespace detail { struct ScreencastSessionContext; } class DisplayBufferCompositorFactory; class CompositingScreencast : public frontend::Screencast { public: CompositingScreencast( std::shared_ptr const& scene, std::shared_ptr const& display, std::shared_ptr const& buffer_allocator, std::shared_ptr const& db_compositor_factory); frontend::ScreencastSessionId create_session( geometry::Rectangle const& region, geometry::Size const& size, MirPixelFormat pixel_format); void destroy_session(frontend::ScreencastSessionId id); std::shared_ptr capture(frontend::ScreencastSessionId id); private: frontend::ScreencastSessionId next_available_session_id(); std::shared_ptr create_session_context(geometry::Rectangle const& rect, geometry::Size const& size, MirPixelFormat pixel_format); std::mutex session_mutex; std::shared_ptr const scene; std::shared_ptr const display; std::shared_ptr const buffer_allocator; std::shared_ptr const db_compositor_factory; std::unordered_map> session_contexts; }; } } #endif /* MIR_FRONTEND_SCREENCAST_H_ */ ./src/server/compositor/default_display_buffer_compositor_factory.h0000644000015600001650000000314012676616125026262 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_COMPOSITOR_DEFAULT_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ #define MIR_COMPOSITOR_DEFAULT_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ #include "mir/compositor/display_buffer_compositor_factory.h" #include "mir/compositor/compositor_report.h" namespace mir { /// Compositing. Combining renderables into a display image. namespace compositor { class RendererFactory; class DefaultDisplayBufferCompositorFactory : public DisplayBufferCompositorFactory { public: DefaultDisplayBufferCompositorFactory( std::shared_ptr const& renderer_factory, std::shared_ptr const& report); std::unique_ptr create_compositor_for(graphics::DisplayBuffer& display_buffer); private: std::shared_ptr const renderer_factory; std::shared_ptr const report; }; } } #endif /* MIR_COMPOSITOR_DEFAULT_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ */ ./src/server/compositor/dropping_schedule.h0000644000015600001650000000264312676616125021260 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_COMPOSITOR_DROPPING_SCHEDULE_H_ #define MIR_COMPOSITOR_DROPPING_SCHEDULE_H_ #include "schedule.h" #include #include namespace mir { namespace graphics { class Buffer; } namespace frontend { class ClientBuffers; } namespace compositor { class DroppingSchedule : public Schedule { public: DroppingSchedule(std::shared_ptr const&); void schedule(std::shared_ptr const& buffer); unsigned int num_scheduled(); std::shared_ptr next_buffer(); private: std::mutex mutable mutex; std::shared_ptr const sender; std::shared_ptr the_only_buffer; }; } } #endif /* MIR_COMPOSITOR_DROPPING_SCHEDULE_H_ */ ./src/server/compositor/default_display_buffer_compositor.cpp0000644000015600001650000000635112676616125025075 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths * Daniel van Vugt */ #include "default_display_buffer_compositor.h" #include "mir/compositor/scene.h" #include "mir/compositor/scene_element.h" #include "mir/compositor/renderer.h" #include "mir/graphics/renderable.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/buffer.h" #include "mir/compositor/buffer_stream.h" #include "occlusion.h" #include #include #include namespace mc = mir::compositor; namespace mg = mir::graphics; mc::DefaultDisplayBufferCompositor::DefaultDisplayBufferCompositor( mg::DisplayBuffer& display_buffer, std::shared_ptr const& renderer, std::shared_ptr const& report) : display_buffer(display_buffer), renderer(renderer), report(report) { } void mc::DefaultDisplayBufferCompositor::composite(mc::SceneElementSequence&& scene_elements) { report->began_frame(this); auto const& view_area = display_buffer.view_area(); auto const& occlusions = mc::filter_occlusions_from(scene_elements, view_area); for (auto const& element : occlusions) element->occluded(); mg::RenderableList renderable_list; renderable_list.reserve(scene_elements.size()); for (auto const& element : scene_elements) { element->rendered(); renderable_list.push_back(element->renderable()); } /* * Note: Buffer lifetimes are ensured by the two objects holding * references to them; scene_elements and renderable_list. * So no buffer is going to be released back to the client till * both of those containers get destroyed (end of the function). * Actually, there's a third reference held by the texture cache * in GLRenderer, but that gets released earlier in render(). */ scene_elements.clear(); // Those in use are still in renderable_list if (display_buffer.post_renderables_if_optimizable(renderable_list)) { report->renderables_in_frame(this, renderable_list); renderer->suspend(); } else { renderer->set_rotation(display_buffer.orientation()); renderer->render(renderable_list); report->renderables_in_frame(this, renderable_list); report->rendered_frame(this); // Release the buffers we did use back to the clients, before starting // on the potentially slow flip(). // FIXME: This clear() call is blocking a little because we drive IPC here (LP: #1395421) renderable_list.clear(); } report->finished_frame(this); } ./src/server/compositor/stream.h0000644000015600001650000000662212676616125017056 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #ifndef MIR_COMPOSITOR_STREAM_H_ #define MIR_COMPOSITOR_STREAM_H_ #include "mir/compositor/buffer_stream.h" #include "mir/scene/surface_observers.h" #include "mir/frontend/buffer_stream_id.h" #include "mir/lockable_callback.h" #include "mir/geometry/size.h" #include "multi_monitor_arbiter.h" #include #include namespace mir { namespace frontend { class ClientBuffers; } namespace compositor { class Schedule; class FrameDroppingPolicyFactory; class FrameDroppingPolicy; class Stream : public BufferStream { public: Stream( FrameDroppingPolicyFactory const& policy_factory, std::unique_ptr, geometry::Size sz, MirPixelFormat format); void swap_buffers( graphics::Buffer* old_buffer, std::function complete) override; void with_most_recent_buffer_do(std::function const& exec) override; MirPixelFormat pixel_format() const override; void add_observer(std::shared_ptr const& observer) override; void remove_observer(std::weak_ptr const& observer) override; std::shared_ptr lock_compositor_buffer(void const* user_id) override; geometry::Size stream_size() override; void resize(geometry::Size const& size) override; void allow_framedropping(bool) override; void force_requests_to_complete() override; int buffers_ready_for_compositor(void const* user_id) const override; void drop_old_buffers() override; bool has_submitted_buffer() const override; graphics::BufferID allocate_buffer(graphics::BufferProperties const&) override; void remove_buffer(graphics::BufferID) override; void with_buffer(graphics::BufferID id, std::function const& fn) override; void set_scale(float scale) override; private: enum class ScheduleMode; struct DroppingCallback : mir::LockableCallback { DroppingCallback(Stream* stream); void operator()() override; void lock() override; void unlock() override; Stream* stream; std::unique_lock guard_lock; }; void transition_schedule(std::shared_ptr&& new_schedule, std::lock_guard const&); void drop_frame(); std::mutex mutable mutex; std::unique_ptr drop_policy; ScheduleMode schedule_mode; std::shared_ptr schedule; std::shared_ptr buffers; std::shared_ptr arbiter; geometry::Size size; MirPixelFormat const pf; bool first_frame_posted; scene::SurfaceObservers observers; }; } } #endif /* MIR_COMPOSITOR_STREAM_H_ */ ./src/server/compositor/buffer_queue.h0000644000015600001650000001061612676616125020236 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef MIR_BUFFER_QUEUE_H_ #define MIR_BUFFER_QUEUE_H_ #include "mir/compositor/frame_dropping_policy_factory.h" #include "mir/compositor/frame_dropping_policy.h" #include "buffer_bundle.h" #include #include #include #include namespace mir { namespace graphics { class Buffer; class GraphicBufferAllocator; } namespace compositor { class BufferQueue : public BufferBundle { public: typedef std::function Callback; BufferQueue(int nbuffers, std::shared_ptr const& alloc, graphics::BufferProperties const& props, FrameDroppingPolicyFactory const& policy_provider); void client_acquire(Callback complete) override; void client_release(graphics::Buffer* buffer) override; std::shared_ptr compositor_acquire(void const* user_id) override; void compositor_release(std::shared_ptr const& buffer) override; std::shared_ptr snapshot_acquire() override; void snapshot_release(std::shared_ptr const& buffer) override; graphics::BufferProperties properties() const override; void allow_framedropping(bool dropping_allowed) override; void force_requests_to_complete() override; void resize(const geometry::Size &newsize) override; int buffers_ready_for_compositor(void const* user_id) const override; int buffers_free_for_client() const override; bool framedropping_allowed() const; bool is_a_current_buffer_user(void const* user_id) const; void drop_old_buffers() override; void drop_client_requests() override; /** * Set the minimum number of smooth frames the client must keep up with * the compositor for in order to qualify for queue scaling (dynamic * double buffering for reduced latency). A negative value means never * but it's recommended that you never change this. */ void set_scaling_delay(int nframes); int scaling_delay() const; private: class LockableCallback; enum SnapshotWait { wait_for_snapshot, ignore_snapshot }; void give_buffer_to_client(graphics::Buffer* buffer, std::unique_lock& lock); void give_buffer_to_client(graphics::Buffer* buffer, std::unique_lock& lock, SnapshotWait wait_type); void release(graphics::Buffer* buffer, std::unique_lock lock); void drop_frame(std::unique_lock& lock, SnapshotWait wait_type); mutable std::mutex guard; std::vector> buffers; std::deque ready_to_composite_queue; std::deque buffers_owned_by_client; std::vector free_buffers; std::vector buffers_sent_to_compositor; std::vector pending_snapshots; std::vector current_buffer_users; graphics::Buffer* current_compositor_buffer; std::deque pending_client_notifications; bool client_ahead_of_compositor() const; graphics::Buffer* get_a_free_buffer(); int nbuffers; int frame_deadlines_threshold; int frame_deadlines_met; int scheduled_extra_frames; bool frame_dropping_enabled; bool current_compositor_buffer_valid; graphics::BufferProperties the_properties; bool force_new_compositor_buffer; bool callbacks_allowed; bool single_compositor; std::condition_variable snapshot_released; std::shared_ptr gralloc; // Ensure framedrop_policy gets destroyed first so the callback installed // does not access dead objects. std::unique_ptr framedrop_policy; }; } } #endif ./src/server/compositor/occlusion.h0000644000015600001650000000201312676616125017547 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_COMPOSITOR_OCCLUSION_H_ #define MIR_COMPOSITOR_OCCLUSION_H_ #include "mir/compositor/scene.h" namespace mir { namespace compositor { SceneElementSequence filter_occlusions_from(SceneElementSequence& list, geometry::Rectangle const& area); } // namespace compositor } // namespace mir #endif // MIR_COMPOSITOR_OCCLUSION_H_ ./src/server/compositor/default_display_buffer_compositor_factory.cpp0000644000015600001650000000311212676616125026614 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "default_display_buffer_compositor_factory.h" #include "mir/compositor/renderer_factory.h" #include "mir/compositor/renderer.h" #include "mir/graphics/display_buffer.h" #include "default_display_buffer_compositor.h" namespace mc = mir::compositor; namespace mg = mir::graphics; mc::DefaultDisplayBufferCompositorFactory::DefaultDisplayBufferCompositorFactory( std::shared_ptr const& renderer_factory, std::shared_ptr const& report) : renderer_factory{renderer_factory}, report{report} { } std::unique_ptr mc::DefaultDisplayBufferCompositorFactory::create_compositor_for( mg::DisplayBuffer& display_buffer) { auto renderer = renderer_factory->create_renderer_for(display_buffer); return std::make_unique( display_buffer, std::move(renderer), report); } ./src/server/compositor/temporary_buffers.cpp0000644000015600001650000000500612676616125021647 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "buffer_bundle.h" #include "temporary_buffers.h" #include #include namespace mc=mir::compositor; namespace mg=mir::graphics; namespace geom=mir::geometry; mc::TemporaryBuffer::TemporaryBuffer(std::shared_ptr const& real_buffer) : buffer(real_buffer) { } mc::TemporaryCompositorBuffer::TemporaryCompositorBuffer( std::shared_ptr const& acquisition, void const* user_id) : TemporaryBuffer(acquisition->compositor_acquire(user_id)), acquisition(acquisition) { } mc::TemporaryCompositorBuffer::~TemporaryCompositorBuffer() { acquisition->compositor_release(buffer); } mc::TemporarySnapshotBuffer::TemporarySnapshotBuffer( std::shared_ptr const& acquisition) : TemporaryBuffer(acquisition->snapshot_acquire()), acquisition(acquisition) { } mc::TemporarySnapshotBuffer::~TemporarySnapshotBuffer() { acquisition->snapshot_release(buffer); } geom::Size mc::TemporaryBuffer::size() const { return buffer->size(); } geom::Stride mc::TemporaryBuffer::stride() const { return buffer->stride(); } MirPixelFormat mc::TemporaryBuffer::pixel_format() const { return buffer->pixel_format(); } mg::BufferID mc::TemporaryBuffer::id() const { return buffer->id(); } std::shared_ptr mc::TemporaryBuffer::native_buffer_handle() const { return buffer->native_buffer_handle(); } void mc::TemporaryBuffer::write(unsigned char const*, size_t) { BOOST_THROW_EXCEPTION( std::runtime_error("Write to temporary buffer snapshot is ill advised and indicates programmer error")); } void mc::TemporaryBuffer::read(std::function const& exec) { buffer->read(exec); } mg::NativeBufferBase* mc::TemporaryBuffer::native_buffer_base() { return buffer->native_buffer_base(); } ./src/server/compositor/screencast_display_buffer.h0000644000015600001650000000504212676616125022766 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_COMPOSITOR_SCREENCAST_DISPLAY_BUFFER_H_ #define MIR_COMPOSITOR_SCREENCAST_DISPLAY_BUFFER_H_ #include "mir/graphics/display_buffer.h" #include "mir/renderer/gl/render_target.h" #include namespace mir { namespace renderer { namespace gl { class TextureSource; }} namespace compositor { namespace detail { template class GLResource { public: GLResource() { Generate(1, &resource); } ~GLResource() { Delete(1, &resource); } operator GLuint() const { return resource; } private: GLResource(GLResource const&) = delete; GLResource& operator=(GLResource const&) = delete; GLuint resource = 0; }; } class ScreencastDisplayBuffer : public graphics::DisplayBuffer, public graphics::NativeDisplayBuffer, public renderer::gl::RenderTarget { public: ScreencastDisplayBuffer( geometry::Rectangle const& rect, graphics::Buffer& buffer); ~ScreencastDisplayBuffer(); geometry::Rectangle view_area() const override; void make_current() override; void release_current() override; bool post_renderables_if_optimizable(graphics::RenderableList const&) override; void swap_buffers() override; MirOrientation orientation() const override; NativeDisplayBuffer* native_display_buffer() override; private: geometry::Rectangle const rect; graphics::Buffer& buffer; renderer::gl::TextureSource* texture_source; GLint old_fbo; GLint old_viewport[4]; detail::GLResource const color_tex; detail::GLResource const depth_rbo; detail::GLResource const fbo; }; } } #endif /* MIR_COMPOSITOR_SCREENCAST_DISPLAY_BUFFER_H_ */ ./src/server/compositor/multi_monitor_arbiter.h0000644000015600001650000000517412676616125022175 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef MIR_COMPOSITOR_MULTI_MONITOR_ARBITER_H_ #define MIR_COMPOSITOR_MULTI_MONITOR_ARBITER_H_ #include "mir/compositor/compositor_id.h" #include "mir/graphics/buffer_id.h" #include "buffer_bundle.h" #include #include #include #include namespace mir { namespace graphics { class Buffer; } namespace frontend { class ClientBuffers; } namespace compositor { class Schedule; enum class MultiMonitorMode { multi_monitor_sync, single_monitor_fast }; class MultiMonitorArbiter : public BufferAcquisition { public: MultiMonitorArbiter( MultiMonitorMode mode, std::shared_ptr const& map, std::shared_ptr const& schedule); std::shared_ptr compositor_acquire(compositor::CompositorID id) override; void compositor_release(std::shared_ptr const&) override; std::shared_ptr snapshot_acquire() override; void snapshot_release(std::shared_ptr const&) override; void set_schedule(std::shared_ptr const& schedule); void set_mode(MultiMonitorMode mode); bool buffer_ready_for(compositor::CompositorID id); bool has_buffer(); void advance_schedule(); private: void decrease_refcount_for(graphics::BufferID id, std::lock_guard const&); void clean_onscreen_buffers(std::lock_guard const&); std::mutex mutable mutex; MultiMonitorMode mode; std::shared_ptr const map; struct ScheduleEntry { ScheduleEntry(std::shared_ptr const& buffer, unsigned int use_count) : buffer(buffer), use_count(use_count) { } std::shared_ptr buffer; unsigned int use_count; }; std::deque onscreen_buffers; std::set current_buffer_users; std::shared_ptr schedule; }; } } #endif /* MIR_COMPOSITOR_MULTI_MONITOR_ARBITER_H_ */ ./src/server/compositor/default_configuration.cpp0000644000015600001650000000753512676616125022475 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/default_server_configuration.h" #include "mir/shell/shell.h" #include "buffer_stream_factory.h" #include "default_display_buffer_compositor_factory.h" #include "multi_threaded_compositor.h" #include "gl/renderer_factory.h" #include "compositing_screencast.h" #include "timeout_frame_dropping_policy_factory.h" #include "mir/main_loop.h" #include "mir/frontend/screencast.h" #include "mir/options/configuration.h" #include namespace mc = mir::compositor; namespace ms = mir::scene; namespace mf = mir::frontend; std::shared_ptr mir::DefaultServerConfiguration::the_buffer_stream_factory() { return buffer_stream_factory( [this]() { return std::make_shared( the_buffer_allocator(), the_frame_dropping_policy_factory(), the_options()->get(options::nbuffers_opt)); }); } std::shared_ptr mir::DefaultServerConfiguration::the_frame_dropping_policy_factory() { return frame_dropping_policy_factory( [this]() { return std::make_shared(the_main_loop(), std::chrono::milliseconds{100}); }); } std::shared_ptr mir::DefaultServerConfiguration::the_display_buffer_compositor_factory() { return display_buffer_compositor_factory( [this]() { return wrap_display_buffer_compositor_factory(std::make_shared( the_renderer_factory(), the_compositor_report())); }); } std::shared_ptr mir::DefaultServerConfiguration::wrap_display_buffer_compositor_factory( std::shared_ptr const& wrapped) { return wrapped; } std::shared_ptr mir::DefaultServerConfiguration::the_compositor() { return compositor( [this]() { std::chrono::milliseconds const composite_delay( the_options()->get(options::composite_delay_opt)); return std::make_shared( the_display(), the_scene(), the_display_buffer_compositor_factory(), the_shell(), the_compositor_report(), composite_delay, !the_options()->is_set(options::host_socket_opt)); }); } std::shared_ptr mir::DefaultServerConfiguration::the_renderer_factory() { return renderer_factory( [this]() { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_screencast() { return screencast( [this]() { return std::make_shared( the_scene(), the_display(), the_buffer_allocator(), the_display_buffer_compositor_factory() ); }); } ./src/server/compositor/queueing_schedule.cpp0000644000015600001650000000300512676616125021604 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "queueing_schedule.h" #include #include namespace mc = mir::compositor; namespace mg = mir::graphics; void mc::QueueingSchedule::schedule(std::shared_ptr const& buffer) { std::lock_guard lk(mutex); auto it = std::find(queue.begin(), queue.end(), buffer); if (it != queue.end()) queue.erase(it); queue.emplace_back(buffer); } unsigned int mc::QueueingSchedule::num_scheduled() { std::lock_guard lk(mutex); return queue.size(); } std::shared_ptr mc::QueueingSchedule::next_buffer() { std::lock_guard lk(mutex); if (queue.empty()) BOOST_THROW_EXCEPTION(std::logic_error("no buffer scheduled")); auto buffer = queue.front(); queue.pop_front(); return buffer; } ./src/server/compositor/buffer_stream_surfaces.h0000644000015600001650000000573312676616125022304 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #ifndef MIR_COMPOSITOR_BUFFER_STREAM_SCENE_H_ #define MIR_COMPOSITOR_BUFFER_STREAM_SCENE_H_ #include "mir/compositor/buffer_stream.h" #include "mir/scene/surface_observers.h" #include namespace mir { namespace compositor { class BufferIDUniqueGenerator; class BufferBundle; class BackBufferStrategy; class BufferStreamSurfaces : public BufferStream { public: BufferStreamSurfaces(std::shared_ptr const& swapper); ~BufferStreamSurfaces(); //from mf::BufferStream void swap_buffers( graphics::Buffer* old_buffer, std::function complete) override; void with_most_recent_buffer_do(std::function const& exec) override; MirPixelFormat pixel_format() const override; void add_observer(std::shared_ptr const& observer) override; void remove_observer(std::weak_ptr const& observer) override; graphics::BufferID allocate_buffer(graphics::BufferProperties const&) override; void remove_buffer(graphics::BufferID) override; void with_buffer(graphics::BufferID id, std::function const& fn) override; //from mc::BufferStream void acquire_client_buffer(std::function complete); void release_client_buffer(graphics::Buffer* buf); std::shared_ptr lock_compositor_buffer(void const* user_id) override; geometry::Size stream_size() override; void resize(geometry::Size const& size) override; void allow_framedropping(bool) override; void force_requests_to_complete() override; int buffers_ready_for_compositor(void const* user_id) const override; void drop_old_buffers() override; bool has_submitted_buffer() const override; void set_scale(float scale) override; protected: BufferStreamSurfaces(const BufferStreamSurfaces&) = delete; BufferStreamSurfaces& operator=(const BufferStreamSurfaces&) = delete; private: std::mutex mutable mutex; std::shared_ptr const buffer_bundle; scene::SurfaceObservers observers; bool first_frame_posted; geometry::Size logical_size; //physical size is maintained in the buffer_bundle float scale; }; } } #endif /* MIR_COMPOSITOR_BUFFER_STREAM_SCENE_H_ */ ./src/server/compositor/multi_threaded_compositor.h0000644000015600001650000000551712676616125023035 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_COMPOSITOR_MULTI_THREADED_COMPOSITOR_H_ #define MIR_COMPOSITOR_MULTI_THREADED_COMPOSITOR_H_ #include "mir/compositor/compositor.h" #include "mir/thread/basic_thread_pool.h" #include #include #include #include #include #include namespace mir { namespace geometry { class Rectangle; } namespace graphics { class Display; } namespace scene { class Observer; } namespace compositor { class DisplayBufferCompositorFactory; class DisplayListener; class CompositingFunctor; class Scene; class CompositorReport; enum class CompositorState { started, stopped, starting, stopping }; class MultiThreadedCompositor : public Compositor { public: MultiThreadedCompositor( std::shared_ptr const& display, std::shared_ptr const& scene, std::shared_ptr const& db_compositor_factory, std::shared_ptr const& display_listener, std::shared_ptr const& compositor_report, std::chrono::milliseconds fixed_composite_delay, // -1 = automatic bool compose_on_start); ~MultiThreadedCompositor(); void start(); void stop(); private: void create_compositing_threads(); void destroy_compositing_threads(); std::shared_ptr const display; std::shared_ptr const scene; std::shared_ptr const display_buffer_compositor_factory; std::shared_ptr const display_listener; std::shared_ptr const report; std::vector> thread_functors; std::vector> futures; std::atomic state; std::chrono::milliseconds fixed_composite_delay; bool compose_on_start; void schedule_compositing(int number_composites); void schedule_compositing(int number_composites, geometry::Rectangle const& damage) const; std::shared_ptr observer; mir::thread::BasicThreadPool thread_pool; }; } } #endif /* MIR_COMPOSITOR_MULTI_THREADED_COMPOSITOR_H_ */ ./src/server/compositor/dropping_schedule.cpp0000644000015600001650000000341012676616125021604 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "dropping_schedule.h" #include "mir/frontend/client_buffers.h" #include "mir/graphics/buffer.h" #include namespace mf = mir::frontend; namespace mg = mir::graphics; namespace mc = mir::compositor; mc::DroppingSchedule::DroppingSchedule(std::shared_ptr const& client_buffers) : sender(client_buffers) { } void mc::DroppingSchedule::schedule(std::shared_ptr const& buffer) { std::lock_guard lk(mutex); if ((the_only_buffer != buffer) && the_only_buffer) sender->send_buffer(the_only_buffer->id()); the_only_buffer = buffer; } unsigned int mc::DroppingSchedule::num_scheduled() { std::lock_guard lk(mutex); if (the_only_buffer) return 1; else return 0; } std::shared_ptr mc::DroppingSchedule::next_buffer() { std::lock_guard lk(mutex); if (!the_only_buffer) BOOST_THROW_EXCEPTION(std::logic_error("no buffer scheduled")); auto buffer = the_only_buffer; the_only_buffer = nullptr; return buffer; } ./src/server/compositor/multi_threaded_compositor.cpp0000644000015600001650000003236312676616125023367 0ustar jenkinsjenkins/* * Copyright © 2013-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "multi_threaded_compositor.h" #include "mir/graphics/display.h" #include "mir/graphics/display_buffer.h" #include "mir/compositor/display_buffer_compositor.h" #include "mir/compositor/display_buffer_compositor_factory.h" #include "mir/compositor/display_listener.h" #include "mir/compositor/scene.h" #include "mir/compositor/compositor_report.h" #include "mir/scene/legacy_scene_change_notification.h" #include "mir/scene/surface_observer.h" #include "mir/scene/surface.h" #include "mir/terminate_with_current_exception.h" #include "mir/raii.h" #include "mir/unwind_helpers.h" #include "mir/thread_name.h" #include #include #include #include using namespace std::literals::chrono_literals; namespace mc = mir::compositor; namespace mg = mir::graphics; namespace ms = mir::scene; namespace mir { namespace compositor { class CompositingFunctor { public: CompositingFunctor( std::shared_ptr const& db_compositor_factory, mg::DisplaySyncGroup& group, std::shared_ptr const& scene, std::shared_ptr const& display_listener, std::chrono::milliseconds fixed_composite_delay, std::shared_ptr const& report) : compositor_factory{db_compositor_factory}, group(group), scene(scene), running{true}, frames_scheduled{0}, force_sleep{fixed_composite_delay}, display_listener{display_listener}, report{report}, started_future{started.get_future()} { } void operator()() noexcept // noexcept is important! (LP: #1237332) try { mir::set_thread_name("Mir/Comp"); std::vector>> compositors; group.for_each_display_buffer( [this, &compositors](mg::DisplayBuffer& buffer) { compositors.emplace_back( std::make_tuple(&buffer, compositor_factory->create_compositor_for(buffer))); auto const& r = buffer.view_area(); auto const comp_id = std::get<1>(compositors.back()).get(); report->added_display(r.size.width.as_int(), r.size.height.as_int(), r.top_left.x.as_int(), r.top_left.y.as_int(), CompositorReport::SubCompositorId{comp_id}); }); //Appease TSan, avoid destructor and this thread accessing the same shared_ptr instance auto const disp_listener = display_listener; auto display_registration = mir::raii::paired_calls( [this, &disp_listener]{group.for_each_display_buffer([&disp_listener](mg::DisplayBuffer& buffer) { disp_listener->add_display(buffer.view_area()); });}, [this, &disp_listener]{group.for_each_display_buffer([&disp_listener](mg::DisplayBuffer& buffer) { disp_listener->remove_display(buffer.view_area()); });}); auto compositor_registration = mir::raii::paired_calls( [this,&compositors] { for (auto& compositor : compositors) scene->register_compositor(std::get<1>(compositor).get()); }, [this,&compositors]{ for (auto& compositor : compositors) scene->unregister_compositor(std::get<1>(compositor).get()); }); started.set_value(); try { std::unique_lock lock{run_mutex}; while (running) { /* Wait until compositing has been scheduled or we are stopped */ run_cv.wait(lock, [&]{ return (frames_scheduled > 0) || !running; }); /* * Check if we are running before compositing, since we may have * been stopped while waiting for the run_cv above. */ if (running) { /* * Each surface could have a number of frames ready in its buffer * queue. And we need to ensure that we render all of them so that * none linger in the queue indefinitely (seen as input lag). * frames_scheduled indicates the number of frames that are scheduled * to ensure all surfaces' queues are fully drained. */ frames_scheduled--; not_posted_yet = false; lock.unlock(); for (auto& tuple : compositors) { auto& compositor = std::get<1>(tuple); compositor->composite(scene->scene_elements_for(compositor.get())); } group.post(); /* * "Predictive bypass" optimization: If the last frame was * bypassed/overlayed or you simply have a fast GPU, it is * beneficial to sleep for most of the next frame. This reduces * the latency between snapshotting the scene and post() * completing by almost a whole frame. */ auto delay = force_sleep >= std::chrono::milliseconds::zero() ? force_sleep : group.recommended_sleep(); std::this_thread::sleep_for(delay); lock.lock(); /* * Note the compositor may have chosen to ignore any number * of renderables and not consumed buffers from them. So it's * important to re-count number of frames pending, separately * to the initial scene_elements_for()... */ int pending = 0; for (auto& compositor : compositors) { auto const comp_id = std::get<1>(compositor).get(); int pend = scene->frames_pending(comp_id); if (pend > pending) pending = pend; } if (pending > frames_scheduled) frames_scheduled = pending; } } } catch (...) { mir::terminate_with_current_exception(); } } catch(...) { started.set_exception(std::current_exception()); //Move the promise so that the promise destructor occurs here rather than in the thread //destroying CompositingFunctor, mostly to appease TSan auto promise = std::move(started); } void schedule_compositing(int num_frames) { std::lock_guard lock{run_mutex}; if (num_frames > frames_scheduled) { frames_scheduled = num_frames; run_cv.notify_one(); } } void schedule_compositing(int num_frames, geometry::Rectangle const& damage) { std::lock_guard lock{run_mutex}; bool took_damage = not_posted_yet; group.for_each_display_buffer([&](mg::DisplayBuffer& buffer) { if (damage.overlaps(buffer.view_area())) took_damage = true; }); if (took_damage && num_frames > frames_scheduled) { frames_scheduled = num_frames; run_cv.notify_one(); } } void stop() { std::lock_guard lock{run_mutex}; running = false; run_cv.notify_one(); } void wait_until_started() { if (started_future.wait_for(10s) != std::future_status::ready) BOOST_THROW_EXCEPTION(std::runtime_error("Compositor thread failed to start")); started_future.get(); } private: std::shared_ptr const compositor_factory; mg::DisplaySyncGroup& group; std::shared_ptr const scene; bool running; int frames_scheduled; std::chrono::milliseconds force_sleep{-1}; std::mutex run_mutex; std::condition_variable run_cv; std::shared_ptr const display_listener; std::shared_ptr const report; std::promise started; std::future started_future; bool not_posted_yet = true; }; } } mc::MultiThreadedCompositor::MultiThreadedCompositor( std::shared_ptr const& display, std::shared_ptr const& scene, std::shared_ptr const& db_compositor_factory, std::shared_ptr const& display_listener, std::shared_ptr const& compositor_report, std::chrono::milliseconds fixed_composite_delay, bool compose_on_start) : display{display}, scene{scene}, display_buffer_compositor_factory{db_compositor_factory}, display_listener{display_listener}, report{compositor_report}, state{CompositorState::stopped}, fixed_composite_delay{fixed_composite_delay}, compose_on_start{compose_on_start}, thread_pool{1} { observer = std::make_shared( [this]() { schedule_compositing(1); }, [this](int num, geometry::Rectangle const& damage) { schedule_compositing(num, damage); }); } mc::MultiThreadedCompositor::~MultiThreadedCompositor() { stop(); } void mc::MultiThreadedCompositor::schedule_compositing(int num) { report->scheduled(); for (auto& f : thread_functors) f->schedule_compositing(num); } void mc::MultiThreadedCompositor::schedule_compositing(int num, geometry::Rectangle const& damage) const { report->scheduled(); for (auto& f : thread_functors) f->schedule_compositing(num, damage); } void mc::MultiThreadedCompositor::start() { auto stopped = CompositorState::stopped; if (!state.compare_exchange_strong(stopped, CompositorState::starting)) return; report->started(); /* To cleanup state if any code below throws */ auto cleanup_if_unwinding = on_unwind([this] { // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62258 // After using rethrow_exception() (and catching the exception), // all subsequent calls to uncaught_exception() return `true'. if (state == CompositorState::started) return; destroy_compositing_threads(); state = CompositorState::stopped; }); create_compositing_threads(); /* Add the observer after we have created the compositing threads */ scene->add_observer(observer); /* Optional first render */ if (compose_on_start) schedule_compositing(1); state = CompositorState::started; } void mc::MultiThreadedCompositor::stop() { auto started = CompositorState::started; if (!state.compare_exchange_strong(started, CompositorState::stopping)) return; /* To cleanup state if any code below throws */ auto cleanup_if_unwinding = on_unwind([this] { // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62258 // After using rethrow_exception() (and catching the exception), // all subsequent calls to uncaught_exception() return `true'. if (state == CompositorState::stopped) return; state = CompositorState::started; }); /* Remove the observer before destroying the compositing threads */ scene->remove_observer(observer); destroy_compositing_threads(); // If the compositor is restarted we've likely got clients blocked // so we will need to schedule compositing immediately compose_on_start = true; report->stopped(); state = CompositorState::stopped; } void mc::MultiThreadedCompositor::create_compositing_threads() { /* Start the display buffer compositing threads */ display->for_each_display_sync_group([this](mg::DisplaySyncGroup& group) { auto thread_functor = std::make_unique( display_buffer_compositor_factory, group, scene, display_listener, fixed_composite_delay, report); futures.push_back(thread_pool.run(std::ref(*thread_functor), &group)); thread_functors.push_back(std::move(thread_functor)); }); thread_pool.shrink(); for (auto& functor : thread_functors) functor->wait_until_started(); } void mc::MultiThreadedCompositor::destroy_compositing_threads() { for (auto& f : thread_functors) f->stop(); for (auto& f : futures) f.wait(); thread_functors.clear(); futures.clear(); } ./src/server/compositor/schedule.h0000644000015600001650000000230012676616125017344 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef MIR_COMPOSITOR_SCHEDULE_H_ #define MIR_COMPOSITOR_SCHEDULE_H_ #include namespace mir { namespace graphics { class Buffer; } namespace compositor { class Schedule { public: virtual void schedule(std::shared_ptr const& buffer) = 0; virtual unsigned int num_scheduled() = 0; virtual std::shared_ptr next_buffer() = 0; virtual ~Schedule() = default; Schedule() = default; Schedule(Schedule const&) = delete; Schedule& operator=(Schedule const&) = delete; }; } } #endif /* MIR_COMPOSITOR_SCHEDULE_H_ */ ./src/server/compositor/occlusion.cpp0000644000015600001650000000464712676616125020121 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "mir/geometry/rectangle.h" #include "mir/compositor/scene_element.h" #include "mir/graphics/renderable.h" #include "occlusion.h" #include using namespace mir::geometry; using namespace mir::graphics; using namespace mir::compositor; namespace { bool renderable_is_occluded( Renderable const& renderable, Rectangle const& area, std::vector& coverage) { static glm::mat4 const identity; static Rectangle const empty{}; if (renderable.transformation() != identity) return false; // Weirdly transformed. Assume never occluded. auto const& window = renderable.screen_position(); auto const& clipped_window = window.intersection_with(area); if (clipped_window == empty) return true; // Not in the area; definitely occluded. bool occluded = false; for (auto const& r : coverage) { if (r.contains(clipped_window)) { occluded = true; break; } } if (!occluded && renderable.alpha() == 1.0f && !renderable.shaped()) coverage.push_back(clipped_window); return occluded; } } SceneElementSequence mir::compositor::filter_occlusions_from( SceneElementSequence& elements, Rectangle const& area) { SceneElementSequence occluded; std::vector coverage; auto it = elements.rbegin(); while (it != elements.rend()) { auto const renderable = (*it)->renderable(); if (renderable_is_occluded(*renderable, area, coverage)) { occluded.insert(occluded.begin(), *it); it = SceneElementSequence::reverse_iterator(elements.erase(std::prev(it.base()))); } else { it++; } } return occluded; } ./src/server/compositor/buffer_stream_factory.h0000644000015600001650000000403212676616125022127 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Alan Griffiths * Thomas Voss */ #ifndef MIR_COMPOSITOR_BUFFER_STREAM_FACTORY_H_ #define MIR_COMPOSITOR_BUFFER_STREAM_FACTORY_H_ #include "mir/scene/buffer_stream_factory.h" #include "mir/compositor/frame_dropping_policy_factory.h" #include namespace mir { namespace graphics { class GraphicBufferAllocator; } namespace compositor { class BufferStreamFactory : public scene::BufferStreamFactory { public: BufferStreamFactory(std::shared_ptr const& gralloc, std::shared_ptr const& policy_factory, unsigned int nbuffers); virtual ~BufferStreamFactory() {} virtual std::shared_ptr create_buffer_stream( frontend::BufferStreamId, std::shared_ptr const& sink, int nbuffers, graphics::BufferProperties const& buffer_properties) override; virtual std::shared_ptr create_buffer_stream( frontend::BufferStreamId, std::shared_ptr const& sink, graphics::BufferProperties const&) override; private: std::shared_ptr gralloc; std::shared_ptr const policy_factory; unsigned int const nbuffers; }; } } #endif /* MIR_COMPOSITOR_BUFFER_STREAM_FACTORY_H_ */ ./src/server/compositor/stream.cpp0000644000015600001650000001427412676616157017420 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #include "stream.h" #include "queueing_schedule.h" #include "dropping_schedule.h" #include "temporary_buffers.h" #include "mir/frontend/client_buffers.h" #include "mir/graphics/buffer.h" #include "mir/compositor/frame_dropping_policy_factory.h" #include "mir/compositor/frame_dropping_policy.h" namespace mc = mir::compositor; namespace geom = mir::geometry; namespace mg = mir::graphics; namespace ms = mir::scene; namespace geom = mir::geometry; enum class mc::Stream::ScheduleMode { Queueing, Dropping }; mc::Stream::DroppingCallback::DroppingCallback(Stream* stream) : stream(stream) { } void mc::Stream::DroppingCallback::operator()() { stream->drop_frame(); } void mc::Stream::DroppingCallback::lock() { guard_lock = std::unique_lock{stream->mutex}; } void mc::Stream::DroppingCallback::unlock() { if (guard_lock.owns_lock()) guard_lock.unlock(); } mc::Stream::Stream( mc::FrameDroppingPolicyFactory const& policy_factory, std::unique_ptr map, geom::Size size, MirPixelFormat pf) : drop_policy(policy_factory.create_policy(std::make_shared(this))), schedule_mode(ScheduleMode::Queueing), schedule(std::make_shared()), buffers(std::move(map)), arbiter(std::make_shared( mc::MultiMonitorMode::multi_monitor_sync, buffers, schedule)), size(size), pf(pf), first_frame_posted(false) { } void mc::Stream::swap_buffers(mg::Buffer* buffer, std::function fn) { if (buffer) { { std::lock_guard lk(mutex); first_frame_posted = true; size = buffer->size(); buffers->receive_buffer(buffer->id()); schedule->schedule((*buffers)[buffer->id()]); if (buffers->client_owned_buffer_count() == 0) drop_policy->swap_now_blocking(); } observers.frame_posted(1, buffer->size()); } fn(nullptr); //legacy support } void mc::Stream::with_most_recent_buffer_do(std::function const& fn) { std::lock_guard lk(mutex); TemporarySnapshotBuffer buffer(arbiter); fn(buffer); } MirPixelFormat mc::Stream::pixel_format() const { return pf; } void mc::Stream::add_observer(std::shared_ptr const& observer) { observers.add(observer); } void mc::Stream::remove_observer(std::weak_ptr const& observer) { if (auto o = observer.lock()) observers.remove(o); } std::shared_ptr mc::Stream::lock_compositor_buffer(void const* id) { std::lock_guard lk(mutex); drop_policy->swap_unblocked(); return std::make_shared(arbiter, id); } geom::Size mc::Stream::stream_size() { std::lock_guard lk(mutex); return size; } void mc::Stream::resize(geom::Size const& new_size) { //TODO: the client should be resizing itself via the buffer creation/destruction rpc calls std::lock_guard lk(mutex); size = new_size; } void mc::Stream::allow_framedropping(bool dropping) { std::lock_guard lk(mutex); if (dropping && schedule_mode == ScheduleMode::Queueing) { transition_schedule(std::make_shared(buffers), lk); schedule_mode = ScheduleMode::Dropping; } else if (!dropping && schedule_mode == ScheduleMode::Dropping) { transition_schedule(std::make_shared(), lk); schedule_mode = ScheduleMode::Queueing; } } void mc::Stream::transition_schedule( std::shared_ptr&& new_schedule, std::lock_guard const&) { std::vector> transferred_buffers; while(schedule->num_scheduled()) transferred_buffers.emplace_back(schedule->next_buffer()); for(auto& buffer : transferred_buffers) new_schedule->schedule(buffer); schedule = new_schedule; arbiter->set_schedule(schedule); } void mc::Stream::force_requests_to_complete() { //we dont block any requests in this system, nothing to force } int mc::Stream::buffers_ready_for_compositor(void const* id) const { std::lock_guard lk(mutex); if (arbiter->buffer_ready_for(id)) return 1; return 0; } void mc::Stream::drop_old_buffers() { std::lock_guard lk(mutex); std::vector> transferred_buffers; while(schedule->num_scheduled()) transferred_buffers.emplace_back(schedule->next_buffer()); if (!transferred_buffers.empty()) { schedule->schedule(transferred_buffers.back()); transferred_buffers.pop_back(); } for (auto &buffer : transferred_buffers) buffers->send_buffer(buffer->id()); arbiter->advance_schedule(); } bool mc::Stream::has_submitted_buffer() const { std::lock_guard lk(mutex); return first_frame_posted; } mg::BufferID mc::Stream::allocate_buffer(mg::BufferProperties const& properties) { return buffers->add_buffer(properties); } void mc::Stream::remove_buffer(mg::BufferID id) { buffers->remove_buffer(id); } void mc::Stream::with_buffer(mg::BufferID id, std::function const& fn) { auto buffer = (*buffers)[id]; fn(*buffer); } void mc::Stream::set_scale(float) { } void mc::Stream::drop_frame() { if (schedule->num_scheduled() > 1) buffers->send_buffer(schedule->next_buffer()->id()); } ./src/server/compositor/CMakeLists.txt0000644000015600001650000000137012676616125020145 0ustar jenkinsjenkinsinclude_directories( ${PROJECT_SOURCE_DIR}/include/renderers/gl/ # TODO: This is a temporary dependency until renderers become proper plugins ${PROJECT_SOURCE_DIR}/src/renderers/ ) set( MIR_COMPOSITOR_SRCS default_display_buffer_compositor.cpp default_display_buffer_compositor_factory.cpp temporary_buffers.cpp buffer_stream_factory.cpp buffer_stream_surfaces.cpp multi_threaded_compositor.cpp occlusion.cpp default_configuration.cpp screencast_display_buffer.cpp compositing_screencast.cpp timeout_frame_dropping_policy_factory.cpp buffer_queue.cpp stream.cpp multi_monitor_arbiter.cpp buffer_map.cpp dropping_schedule.cpp queueing_schedule.cpp ) ADD_LIBRARY( mircompositor OBJECT ${MIR_COMPOSITOR_SRCS} ) ./src/server/compositor/buffer_map.h0000644000015600001650000000431612676616125017667 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_COMPOSITOR_BUFFER_MAP_H_ #define MIR_COMPOSITOR_BUFFER_MAP_H_ #include "mir/frontend/buffer_stream_id.h" #include "mir/frontend/client_buffers.h" #include #include namespace mir { namespace graphics { class GraphicBufferAllocator; } namespace frontend { class BufferSink; } namespace compositor { class BufferMap : public frontend::ClientBuffers { public: BufferMap( frontend::BufferStreamId id, std::shared_ptr const& sink, std::shared_ptr const& allocator); graphics::BufferID add_buffer(graphics::BufferProperties const& properties) override; void remove_buffer(graphics::BufferID id) override; void receive_buffer(graphics::BufferID id) override; void send_buffer(graphics::BufferID id) override; size_t client_owned_buffer_count() const override; std::shared_ptr& operator[](graphics::BufferID) override; private: std::mutex mutable mutex; enum class Owner; struct MapEntry { std::shared_ptr buffer; Owner owner; }; typedef std::map Map; //used to keep strong reference Map buffers; Map::iterator checked_buffers_find(graphics::BufferID, std::unique_lock const&); frontend::BufferStreamId const stream_id; std::shared_ptr const sink; std::shared_ptr const allocator; }; } } #endif /* MIR_COMPOSITOR_BUFFER_MAP_H_ */ ./src/server/compositor/temporary_buffers.h0000644000015600001650000000432312676616125021315 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_COMPOSITOR_TEMPORARY_BUFFERS_H_ #define MIR_COMPOSITOR_TEMPORARY_BUFFERS_H_ #include "mir/graphics/buffer.h" #include "mir/graphics/buffer_id.h" namespace mg = mir::graphics; namespace mir { namespace compositor { class BufferAcquisition; class BackBufferStrategy; class TemporaryBuffer : public mg::Buffer { public: geometry::Size size() const override; geometry::Stride stride() const override; MirPixelFormat pixel_format() const override; mg::BufferID id() const override; std::shared_ptr native_buffer_handle() const override; void write (unsigned char const* data, size_t size) override; void read (std::function const& do_with_pixels) override; graphics::NativeBufferBase* native_buffer_base() override; protected: explicit TemporaryBuffer(std::shared_ptr const& real_buffer); std::shared_ptr const buffer; }; class TemporaryCompositorBuffer : public TemporaryBuffer { public: explicit TemporaryCompositorBuffer( std::shared_ptr const& acquisition, void const* user_id); ~TemporaryCompositorBuffer(); private: std::shared_ptr const acquisition; }; class TemporarySnapshotBuffer : public TemporaryBuffer { public: explicit TemporarySnapshotBuffer( std::shared_ptr const& acquisition); ~TemporarySnapshotBuffer(); private: std::shared_ptr const acquisition; }; } } #endif /* MIR_COMPOSITOR_TEMPORARY_BUFFERS_H_ */ ./src/server/compositor/buffer_stream_surfaces.cpp0000644000015600001650000001176212676616125022636 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #include "buffer_stream_surfaces.h" #include "buffer_bundle.h" #include "mir/graphics/buffer_properties.h" #include "temporary_buffers.h" #include namespace mc = mir::compositor; namespace mg = mir::graphics; namespace geom = mir::geometry; mc::BufferStreamSurfaces::BufferStreamSurfaces(std::shared_ptr const& buffer_bundle) : buffer_bundle(buffer_bundle), first_frame_posted{false}, logical_size(buffer_bundle->properties().size), scale{1.0f} { } mc::BufferStreamSurfaces::~BufferStreamSurfaces() { buffer_bundle->drop_client_requests(); force_requests_to_complete(); } std::shared_ptr mc::BufferStreamSurfaces::lock_compositor_buffer( void const* user_id) { return std::make_shared( buffer_bundle, user_id); } void mc::BufferStreamSurfaces::acquire_client_buffer( std::function complete) { buffer_bundle->client_acquire(complete); } void mc::BufferStreamSurfaces::release_client_buffer(graphics::Buffer* buf) { buffer_bundle->client_release(buf); { std::lock_guard lk(mutex); first_frame_posted = true; } } geom::Size mc::BufferStreamSurfaces::stream_size() { std::lock_guard lk(mutex); return logical_size; } void mc::BufferStreamSurfaces::resize(geom::Size const& size) { std::lock_guard lk(mutex); logical_size = size; buffer_bundle->resize(logical_size * scale); } void mc::BufferStreamSurfaces::force_requests_to_complete() { buffer_bundle->force_requests_to_complete(); } void mc::BufferStreamSurfaces::allow_framedropping(bool allow) { buffer_bundle->allow_framedropping(allow); } int mc::BufferStreamSurfaces::buffers_ready_for_compositor(void const* user_id) const { return buffer_bundle->buffers_ready_for_compositor(user_id); } void mc::BufferStreamSurfaces::drop_old_buffers() { buffer_bundle->drop_old_buffers(); } void mc::BufferStreamSurfaces::swap_buffers( mg::Buffer* old_buffer, std::function complete) { if (old_buffer) { release_client_buffer(old_buffer); /* * TODO: In future frame_posted() could be made parameterless. * The new method of catching up on buffer backlogs is to * query buffers_ready_for_compositor() or Scene::frames_pending */ observers.frame_posted(1, old_buffer->size()); } acquire_client_buffer(complete); } bool mc::BufferStreamSurfaces::has_submitted_buffer() const { std::lock_guard lk(mutex); return first_frame_posted; } void mc::BufferStreamSurfaces::with_most_recent_buffer_do(std::function const& exec) { { std::lock_guard lk(mutex); if (!first_frame_posted) BOOST_THROW_EXCEPTION(std::runtime_error("No frame posted yet")); } exec(*std::make_shared(buffer_bundle)); } MirPixelFormat mc::BufferStreamSurfaces::pixel_format() const { return buffer_bundle->properties().format; } void mc::BufferStreamSurfaces::add_observer(std::shared_ptr const& observer) { observers.add(observer); } void mc::BufferStreamSurfaces::remove_observer(std::weak_ptr const& observer) { if (auto o = observer.lock()) observers.remove(o); } mg::BufferID mc::BufferStreamSurfaces::allocate_buffer(graphics::BufferProperties const&) { BOOST_THROW_EXCEPTION(std::logic_error("buffer allocation cannot happen with an exchange-based buffer client")); } void mc::BufferStreamSurfaces::remove_buffer(graphics::BufferID) { BOOST_THROW_EXCEPTION(std::logic_error("buffer removal cannot happen with an exchange-based buffer client")); } void mc::BufferStreamSurfaces::with_buffer(mg::BufferID, std::function const&) { BOOST_THROW_EXCEPTION(std::logic_error("buffer lookup cannot happen with an exchange-based buffer client")); } void mc::BufferStreamSurfaces::set_scale(float new_scale) { if (new_scale <= 0.0f) BOOST_THROW_EXCEPTION(std::logic_error("invalid scale (must be greater than zero)")); std::lock_guard lk(mutex); scale = new_scale; buffer_bundle->resize(logical_size * scale); } ./src/server/compositor/screencast_display_buffer.cpp0000644000015600001650000000713612676616125023327 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "screencast_display_buffer.h" #include "mir/graphics/buffer.h" #include "mir/renderer/gl/texture_source.h" #include namespace mc = mir::compositor; namespace mg = mir::graphics; namespace geom = mir::geometry; mc::ScreencastDisplayBuffer::ScreencastDisplayBuffer( geom::Rectangle const& rect, mg::Buffer& buffer) : rect(rect), buffer(buffer), texture_source( dynamic_cast( buffer.native_buffer_base())), old_fbo(), old_viewport() { if (!texture_source) BOOST_THROW_EXCEPTION(std::logic_error("Buffer does not support GL rendering")); glGetIntegerv(GL_VIEWPORT, old_viewport); glGetIntegerv(GL_FRAMEBUFFER_BINDING, &old_fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); /* Set up the color buffer... */ glBindTexture(GL_TEXTURE_2D, color_tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* and the depth buffer */ auto const buf_size = buffer.size(); glBindRenderbuffer(GL_RENDERBUFFER, depth_rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, buf_size.width.as_uint32_t(), buf_size.height.as_uint32_t()); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_rbo); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create FBO for buffer")); } mc::ScreencastDisplayBuffer::~ScreencastDisplayBuffer() { release_current(); } geom::Rectangle mc::ScreencastDisplayBuffer::view_area() const { return rect; } void mc::ScreencastDisplayBuffer::make_current() { glBindTexture(GL_TEXTURE_2D, color_tex); texture_source->gl_bind_to_texture(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex, 0); auto const buf_size = buffer.size(); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glViewport(0, 0, buf_size.width.as_uint32_t(), buf_size.height.as_uint32_t()); } void mc::ScreencastDisplayBuffer::release_current() { glBindFramebuffer(GL_FRAMEBUFFER, old_fbo); glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]); } bool mc::ScreencastDisplayBuffer::post_renderables_if_optimizable(mg::RenderableList const&) { return false; } void mc::ScreencastDisplayBuffer::swap_buffers() { glFinish(); } MirOrientation mc::ScreencastDisplayBuffer::orientation() const { return mir_orientation_normal; } mg::NativeDisplayBuffer* mc::ScreencastDisplayBuffer::native_display_buffer() { return this; } ./src/server/compositor/multi_monitor_arbiter.cpp0000644000015600001650000001167012676616157022533 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "multi_monitor_arbiter.h" #include "mir/graphics/buffer.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/frontend/event_sink.h" #include "mir/frontend/client_buffers.h" #include "schedule.h" #include #include namespace mg = mir::graphics; namespace mc = mir::compositor; namespace mf = mir::frontend; mc::MultiMonitorArbiter::MultiMonitorArbiter( MultiMonitorMode mode, std::shared_ptr const& map, std::shared_ptr const& schedule) : mode(mode), map(map), schedule(schedule) { } std::shared_ptr mc::MultiMonitorArbiter::compositor_acquire(compositor::CompositorID id) { std::lock_guard lk(mutex); if (onscreen_buffers.empty() && !schedule->num_scheduled()) BOOST_THROW_EXCEPTION(std::logic_error("no buffer to give to compositor")); if (current_buffer_users.find(id) != current_buffer_users.end() || onscreen_buffers.empty()) { if (schedule->num_scheduled()) onscreen_buffers.emplace_front(schedule->next_buffer(), 0); current_buffer_users.clear(); } current_buffer_users.insert(id); auto& last_entry = onscreen_buffers.front(); last_entry.use_count++; if (mode == mc::MultiMonitorMode::multi_monitor_sync) clean_onscreen_buffers(lk); return last_entry.buffer; } void mc::MultiMonitorArbiter::compositor_release(std::shared_ptr const& buffer) { std::lock_guard lk(mutex); decrease_refcount_for(buffer->id(), lk); if ((mode == mc::MultiMonitorMode::single_monitor_fast) || (onscreen_buffers.begin()->buffer != buffer)) clean_onscreen_buffers(lk); } void mc::MultiMonitorArbiter::decrease_refcount_for(mg::BufferID id, std::lock_guard const&) { auto it = std::find_if(onscreen_buffers.begin(), onscreen_buffers.end(), [&id](ScheduleEntry const& s) { return s.buffer->id() == id; }); if (it == onscreen_buffers.end()) BOOST_THROW_EXCEPTION(std::logic_error("buffer not scheduled")); it->use_count--; } void mc::MultiMonitorArbiter::clean_onscreen_buffers(std::lock_guard const&) { for(auto it = onscreen_buffers.begin(); it != onscreen_buffers.end();) { if ((it->use_count == 0) && (it != onscreen_buffers.begin() || schedule->num_scheduled())) //ensure monitors always have a buffer { map->send_buffer(it->buffer->id()); it = onscreen_buffers.erase(it); } else { it++; } } } std::shared_ptr mc::MultiMonitorArbiter::snapshot_acquire() { std::lock_guard lk(mutex); if (onscreen_buffers.empty() && !schedule->num_scheduled()) BOOST_THROW_EXCEPTION(std::logic_error("no buffer to give to snapshotter")); if (onscreen_buffers.empty()) { if (schedule->num_scheduled()) onscreen_buffers.emplace_front(schedule->next_buffer(), 0); } auto& last_entry = onscreen_buffers.front(); last_entry.use_count++; return last_entry.buffer; } void mc::MultiMonitorArbiter::snapshot_release(std::shared_ptr const& buffer) { std::lock_guard lk(mutex); decrease_refcount_for(buffer->id(), lk); clean_onscreen_buffers(lk); } void mc::MultiMonitorArbiter::set_schedule(std::shared_ptr const& new_schedule) { std::lock_guard lk(mutex); schedule = new_schedule; } void mc::MultiMonitorArbiter::set_mode(MultiMonitorMode new_mode) { std::lock_guard lk(mutex); mode = new_mode; } bool mc::MultiMonitorArbiter::buffer_ready_for(mc::CompositorID id) { std::lock_guard lk(mutex); return schedule->num_scheduled() || ((current_buffer_users.find(id) == current_buffer_users.end()) && !onscreen_buffers.empty()); } bool mc::MultiMonitorArbiter::has_buffer() { std::lock_guard lk(mutex); return !onscreen_buffers.empty(); } void mc::MultiMonitorArbiter::advance_schedule() { std::lock_guard lk(mutex); if (schedule->num_scheduled()) { onscreen_buffers.emplace_front(schedule->next_buffer(), 0); current_buffer_users.clear(); } } ./src/server/glib_main_loop.cpp0000644000015600001650000002355312676616125016674 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis * Alberto Aguirre */ #include "mir/glib_main_loop.h" #include "mir/lockable_callback_wrapper.h" #include "mir/basic_callback.h" #include #include #include #include namespace { class AlarmImpl : public mir::time::Alarm { public: AlarmImpl( GMainContext* main_context, std::shared_ptr const& clock, std::shared_ptr const& callback, std::function const& exception_handler) : main_context{g_main_context_ref(main_context)}, clock{clock}, state_{State::cancelled}, exception_handler{exception_handler}, wrapped_callback{std::make_shared( callback, [this] { state_ = State::triggered; })} { } ~AlarmImpl() override { gsource.ensure_no_further_dispatch(); g_main_context_unref(main_context); } bool cancel() override { std::lock_guard lock{alarm_mutex}; gsource.ensure_no_further_dispatch(); if (state_ == State::pending) { gsource = mir::detail::GSourceHandle{}; state_ = State::cancelled; } return state_ == State::cancelled; } State state() const override { std::lock_guard lock{alarm_mutex}; return state_; } bool reschedule_in(std::chrono::milliseconds delay) override { return reschedule_for(clock->now() + delay); } bool reschedule_for(mir::time::Timestamp time_point) override { std::lock_guard lock{alarm_mutex}; auto old_state = state_; state_ = State::pending; gsource = mir::detail::add_timer_gsource( main_context, clock, wrapped_callback, exception_handler, time_point); return old_state == State::pending; } private: mutable std::mutex alarm_mutex; GMainContext* main_context; std::shared_ptr const clock; State state_; std::function exception_handler; std::shared_ptr wrapped_callback; mir::detail::GSourceHandle gsource; }; } mir::detail::GMainContextHandle::GMainContextHandle() : main_context{g_main_context_new()} { if (!main_context) BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create GMainContext")); } mir::detail::GMainContextHandle::~GMainContextHandle() { if (main_context) g_main_context_unref(main_context); } mir::detail::GMainContextHandle::operator GMainContext*() const { return main_context; } mir::GLibMainLoop::GLibMainLoop( std::shared_ptr const& clock) : clock{clock}, running{false}, fd_sources{main_context}, signal_sources{fd_sources}, before_iteration_hook{[]{}} { } void mir::GLibMainLoop::run() { main_loop_exception = nullptr; running = true; while (running) { before_iteration_hook(); g_main_context_iteration(main_context, TRUE); } if (main_loop_exception) std::rethrow_exception(main_loop_exception); } void mir::GLibMainLoop::stop() { detail::add_idle_gsource(main_context, G_PRIORITY_HIGH, [this] { running = false; g_main_context_wakeup(main_context); }); } void mir::GLibMainLoop::register_signal_handler( std::initializer_list sigs, std::function const& handler) { auto const handler_with_exception_handling = [this, handler] (int sig) { try { handler(sig); } catch (...) { handle_exception(std::current_exception()); } }; signal_sources.add(sigs, handler_with_exception_handling); } void mir::GLibMainLoop::register_signal_handler( std::initializer_list sigs, mir::UniqueModulePtr> handler) { std::shared_ptr> const shared_handler{std::move(handler)}; auto const handler_with_exception_handling = [this, shared_handler] (int sig) { try { (*shared_handler)(sig); } catch (...) { handle_exception(std::current_exception()); } }; signal_sources.add(sigs, handler_with_exception_handling); } void mir::GLibMainLoop::register_fd_handler( std::initializer_list fds, void const* owner, std::function const& handler) { auto const handler_with_exception_handling = [this, handler] (int fd) { try { handler(fd); } catch (...) { handle_exception(std::current_exception()); } }; for (auto fd : fds) fd_sources.add(fd, owner, handler_with_exception_handling); } void mir::GLibMainLoop::register_fd_handler( std::initializer_list fds, void const* owner, mir::UniqueModulePtr> handler) { std::shared_ptr> const shared_handler{std::move(handler)}; auto const handler_with_exception_handling = [this, shared_handler] (int fd) { try { (*shared_handler)(fd); } catch (...) { handle_exception(std::current_exception()); } }; for (auto fd : fds) fd_sources.add(fd, owner, handler_with_exception_handling); } void mir::GLibMainLoop::unregister_fd_handler( void const* owner) { fd_sources.remove_all_owned_by(owner); } void mir::GLibMainLoop::enqueue(void const* owner, ServerAction const& action) { auto const action_with_exception_handling = [this, action] { try { action(); } catch (...) { handle_exception(std::current_exception()); } }; detail::add_server_action_gsource(main_context, owner, action_with_exception_handling, [this] (void const* owner) { return should_process_actions_for(owner); }); } void mir::GLibMainLoop::pause_processing_for(void const* owner) { std::lock_guard lock{do_not_process_mutex}; auto const iter = std::find(do_not_process.begin(), do_not_process.end(), owner); if (iter == do_not_process.end()) do_not_process.push_back(owner); } void mir::GLibMainLoop::resume_processing_for(void const* owner) { std::lock_guard lock{do_not_process_mutex}; auto const new_end = std::remove(do_not_process.begin(), do_not_process.end(), owner); do_not_process.erase(new_end, do_not_process.end()); // Wake up the context to reprocess all sources g_main_context_wakeup(main_context); } bool mir::GLibMainLoop::should_process_actions_for(void const* owner) { std::lock_guard lock{do_not_process_mutex}; auto const iter = std::find(do_not_process.begin(), do_not_process.end(), owner); return iter == do_not_process.end(); } std::unique_ptr mir::GLibMainLoop::create_alarm( std::function const& callback) { return create_alarm(std::make_shared(callback)); } std::unique_ptr mir::GLibMainLoop::create_alarm( std::shared_ptr const& callback) { auto const exception_hander = [this] { handle_exception(std::current_exception()); }; return std::make_unique( main_context, clock, callback, exception_hander); } void mir::GLibMainLoop::reprocess_all_sources() { std::condition_variable reprocessed_cv; std::mutex reprocessed_mutex; bool reprocessed = false; // Schedule setting the before_iteration_hook as an // idle source to ensure there is no concurrent access // to it. detail::add_idle_gsource(main_context, G_PRIORITY_HIGH, [&] { // GMainContexts process sources in order of decreasing priority. // Since all of our sources have priority higher than // G_PRIORITY_LOW, by adding a G_PRIORITY_LOW source, we can be // sure that when this source is processed all other sources will // have been processed before it. We add the source in the // before_iteration_hook to avoid premature notifications. before_iteration_hook = [&] { detail::add_idle_gsource(main_context, G_PRIORITY_LOW, [&] { std::lock_guard lock{reprocessed_mutex}; reprocessed = true; reprocessed_cv.notify_all(); }); before_iteration_hook = []{}; }; // Wake up the main loop to ensure that we eventually leave // g_main_context_iteration() and reprocess all sources after // having called the newly set before_iteration_hook. g_main_context_wakeup(main_context); }); std::unique_lock reprocessed_lock{reprocessed_mutex}; reprocessed_cv.wait(reprocessed_lock, [&] { return reprocessed == true; }); } void mir::GLibMainLoop::handle_exception(std::exception_ptr const& e) { main_loop_exception = e; stop(); } ./src/server/lockable_callback_wrapper.cpp0000644000015600001650000000301612676616125021042 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #include "mir/lockable_callback_wrapper.h" mir::LockableCallbackWrapper::LockableCallbackWrapper( std::shared_ptr const& wrapped, std::function const& precall_hook, std::function const& postcall_hook) : wrapped_callback{wrapped}, precall_hook{precall_hook}, postcall_hook{postcall_hook} { } mir::LockableCallbackWrapper::LockableCallbackWrapper( std::shared_ptr const& wrapped, std::function const& precall_hook) : LockableCallbackWrapper(wrapped, precall_hook, []{}) { } void mir::LockableCallbackWrapper::operator()() { precall_hook(); (*wrapped_callback)(); postcall_hook(); } void mir::LockableCallbackWrapper::lock() { wrapped_callback->lock(); } void mir::LockableCallbackWrapper::unlock() { wrapped_callback->unlock(); } ./src/server/frontend/0000755000015600001650000000000012676616160015024 5ustar jenkinsjenkins./src/server/frontend/unauthorized_screencast.cpp0000644000015600001650000000273012676616125022466 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "unauthorized_screencast.h" #include #include namespace mf = mir::frontend; mf::ScreencastSessionId mf::UnauthorizedScreencast::create_session( geometry::Rectangle const&, geometry::Size const&, MirPixelFormat) { BOOST_THROW_EXCEPTION( std::runtime_error("Process is not authorized to capture screencasts")); } void mf::UnauthorizedScreencast::destroy_session(mf::ScreencastSessionId) { BOOST_THROW_EXCEPTION( std::runtime_error("Process is not authorized to capture screencasts")); } std::shared_ptr mf::UnauthorizedScreencast::capture( mf::ScreencastSessionId) { BOOST_THROW_EXCEPTION( std::runtime_error("Process is not authorized to capture screencasts")); } ./src/server/frontend/session_mediator.h0000644000015600001650000002412712676616157020560 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_SESSION_MEDIATOR_H_ #define MIR_FRONTEND_SESSION_MEDIATOR_H_ #include "display_server.h" #include "buffer_stream_tracker.h" #include "protobuf_ipc_factory.h" #include "mir/frontend/connection_context.h" #include "mir/frontend/surface_id.h" #include "mir/frontend/buffer_stream_id.h" #include "mir/graphics/platform_ipc_operations.h" #include "mir/protobuf/display_server_debug.h" #include "mir_toolkit/common.h" #include #include #include #include namespace mir { namespace cookie { class Authority; } namespace graphics { class Buffer; class Display; class DisplayConfiguration; class GraphicBufferAllocator; } namespace input { class CursorImages; class InputDeviceHub; } namespace scene { class CoordinateTranslator; class ApplicationNotRespondingDetector; } /// Frontend interface. Mediates the interaction between client /// processes and the core of the mir system. namespace frontend { class ClientBufferTracker; class Shell; class Session; class Surface; class MessageResourceCache; class SessionMediatorReport; class EventSink; class EventSinkFactory; class MessageSender; class DisplayChanger; class Screencast; class PromptSession; class BufferStream; /** * SessionMediator relays requests from the client process into the server. * * Each SessionMediator is associated with exactly one client socket connection, and * visa versa. * * \note SessionMediator is *not* reentrant. If two threads want to process events on a client * socket at the same time they must perform their own locking. */ class SessionMediator : public detail::DisplayServer, public mir::protobuf::DisplayServerDebug { public: SessionMediator( std::shared_ptr const& shell, std::shared_ptr const& ipc_operations, std::shared_ptr const& display_changer, std::vector const& surface_pixel_formats, std::shared_ptr const& report, std::shared_ptr const& sink_factory, std::shared_ptr const& message_sender, std::shared_ptr const& resource_cache, std::shared_ptr const& screencast, ConnectionContext const& connection_context, std::shared_ptr const& cursor_images, std::shared_ptr const& translator, std::shared_ptr const& anr_detector, std::shared_ptr const& cookie_authority, std::shared_ptr const& hub ); ~SessionMediator() noexcept; void client_pid(int pid) override; void connect( mir::protobuf::ConnectParameters const* request, mir::protobuf::Connection* response, google::protobuf::Closure* done) override; void disconnect( mir::protobuf::Void const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void create_surface( mir::protobuf::SurfaceParameters const* request, mir::protobuf::Surface* response, google::protobuf::Closure* done) override; void modify_surface( mir::protobuf::SurfaceModifications const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void next_buffer( mir::protobuf::SurfaceId const* request, mir::protobuf::Buffer* response, google::protobuf::Closure* done) override; void release_surface( mir::protobuf::SurfaceId const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void platform_operation( mir::protobuf::PlatformOperationMessage const* request, mir::protobuf::PlatformOperationMessage* response, google::protobuf::Closure* done) override; void configure_surface( mir::protobuf::SurfaceSetting const* request, mir::protobuf::SurfaceSetting* response, google::protobuf::Closure* done) override; void configure_display( mir::protobuf::DisplayConfiguration const* request, mir::protobuf::DisplayConfiguration* response, google::protobuf::Closure* done) override; void set_base_display_configuration( mir::protobuf::DisplayConfiguration const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void create_screencast( mir::protobuf::ScreencastParameters const* request, mir::protobuf::Screencast* response, google::protobuf::Closure* done) override; void screencast_buffer( mir::protobuf::ScreencastId const* request, mir::protobuf::Buffer* response, google::protobuf::Closure* done) override; void release_screencast( mir::protobuf::ScreencastId const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void create_buffer_stream( mir::protobuf::BufferStreamParameters const* request, mir::protobuf::BufferStream* response, google::protobuf::Closure* done) override; void release_buffer_stream( mir::protobuf::BufferStreamId const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void configure_cursor( mir::protobuf::CursorSetting const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void new_fds_for_prompt_providers( mir::protobuf::SocketFDRequest const* request, mir::protobuf::SocketFD* response, google::protobuf::Closure* done) override; void start_prompt_session( mir::protobuf::PromptSessionParameters const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void stop_prompt_session( mir::protobuf::Void const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void exchange_buffer( mir::protobuf::BufferRequest const* request, mir::protobuf::Buffer* response, google::protobuf::Closure* done) override; void submit_buffer( mir::protobuf::BufferRequest const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void allocate_buffers( mir::protobuf::BufferAllocation const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void release_buffers( mir::protobuf::BufferRelease const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void request_persistent_surface_id( mir::protobuf::SurfaceId const* request, mir::protobuf::PersistentSurfaceId* response, google::protobuf::Closure* done) override; void pong( mir::protobuf::PingEvent const* request, mir::protobuf::Void* response, google::protobuf::Closure* done) override; void configure_buffer_stream( mir::protobuf::StreamConfiguration const* request, mir::protobuf::Void*, google::protobuf::Closure* done) override; void raise_surface( mir::protobuf::RaiseRequest const* request, mir::protobuf::Void*, google::protobuf::Closure* done) override; // TODO: Split this into a separate thing void translate_surface_to_screen( mir::protobuf::CoordinateTranslationRequest const* request, mir::protobuf::CoordinateTranslationResponse* response, google::protobuf::Closure* done) override; private: void pack_protobuf_buffer(protobuf::Buffer& protobuf_buffer, graphics::Buffer* graphics_buffer, graphics::BufferIpcMsgType msg_type); void advance_buffer( BufferStreamId surf_id, BufferStream& buffer_stream, graphics::Buffer* old_buffer, std::function complete); std::shared_ptr unpack_and_sanitize_display_configuration( protobuf::DisplayConfiguration const*); virtual std::function const&)> prompt_session_connect_handler() const; pid_t client_pid_; std::shared_ptr const shell; std::shared_ptr const ipc_operations; std::vector const surface_pixel_formats; std::shared_ptr const display_changer; std::shared_ptr const report; std::shared_ptr const sink_factory; std::shared_ptr const event_sink; std::shared_ptr const message_sender; std::shared_ptr const resource_cache; std::shared_ptr const screencast; ConnectionContext const connection_context; std::shared_ptr const cursor_images; std::shared_ptr const translator; std::shared_ptr const anr_detector; std::shared_ptr const cookie_authority; std::shared_ptr const hub; BufferStreamTracker buffer_stream_tracker; std::weak_ptr weak_session; std::weak_ptr weak_prompt_session; }; } } #endif /* MIR_FRONTEND_SESSION_MEDIATOR_H_ */ ./src/server/frontend/reordering_message_sender.cpp0000644000015600001650000000315512676616125022741 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include "reordering_message_sender.h" namespace mf = mir::frontend; mf::ReorderingMessageSender::ReorderingMessageSender(std::shared_ptr const& sink) : corked{true}, sink{sink} { } void mf::ReorderingMessageSender::send( char const* data, size_t length, mf::FdSets const& fds) { { std::lock_guard lock{message_lock}; if (corked) { buffered_messages.emplace_back(Message {std::vector(data, data + length), FdSets(fds)}); return; } } sink->send(data, length, fds); } void mf::ReorderingMessageSender::uncork() { { std::lock_guard lock{message_lock}; corked = false; } for (auto const& message : buffered_messages) { sink->send(message.data.data(), message.data.size(), message.fds); } buffered_messages.clear(); } ./src/server/frontend/shell_wrapper.cpp0000644000015600001650000000642012676616125020402 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "shell_wrapper.h" namespace mf = mir::frontend; namespace ms = mir::scene; std::shared_ptr mf::ShellWrapper::open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) { return wrapped->open_session(client_pid, name,sink); } void mf::ShellWrapper::close_session(std::shared_ptr const& session) { wrapped->close_session(session); } std::shared_ptr mf::ShellWrapper::start_prompt_session_for( std::shared_ptr const& session, scene::PromptSessionCreationParameters const& params) { return wrapped->start_prompt_session_for(session, params); } void mf::ShellWrapper::add_prompt_provider_for( std::shared_ptr const& prompt_session, std::shared_ptr const& session) { wrapped->add_prompt_provider_for(prompt_session, session); } void mf::ShellWrapper::stop_prompt_session( std::shared_ptr const& prompt_session) { wrapped->stop_prompt_session(prompt_session); } mf::SurfaceId mf::ShellWrapper::create_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) { return wrapped->create_surface(session, params, sink); } void mf::ShellWrapper::modify_surface(std::shared_ptr const& session, SurfaceId surface, shell::SurfaceSpecification const& modifications) { wrapped->modify_surface(session, surface, modifications); } void mf::ShellWrapper::destroy_surface(std::shared_ptr const& session, SurfaceId surface) { wrapped->destroy_surface(session, surface); } std::string mf::ShellWrapper::persistent_id_for(std::shared_ptr const& session, mf::SurfaceId surface) { return wrapped->persistent_id_for(session, surface); } std::shared_ptr mf::ShellWrapper::surface_for_id(std::string const& serialised_id) { return wrapped->surface_for_id(serialised_id); } int mf::ShellWrapper::set_surface_attribute( std::shared_ptr const& session, SurfaceId surface_id, MirSurfaceAttrib attrib, int value) { return wrapped->set_surface_attribute(session, surface_id, attrib, value); } int mf::ShellWrapper::get_surface_attribute( std::shared_ptr const& session, SurfaceId surface_id, MirSurfaceAttrib attrib) { return wrapped->get_surface_attribute(session, surface_id, attrib); } void mf::ShellWrapper::raise_surface( std::shared_ptr const& session, SurfaceId surface_id, uint64_t timestamp) { wrapped->raise_surface(session, surface_id, timestamp); } ./src/server/frontend/default_ipc_factory.h0000644000015600001650000001011612676616157021210 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_FRONTEND_DEFAULT_IPC_FACTORY_H_ #define MIR_FRONTEND_DEFAULT_IPC_FACTORY_H_ #include "protobuf_ipc_factory.h" namespace mir { namespace cookie { class Authority; } namespace graphics { class PlatformIpcOperations; class GraphicBufferAllocator; } namespace input { class CursorImages; class InputDeviceHub; } namespace scene { class ApplicationNotRespondingDetector; class CoordinateTranslator; } namespace frontend { class Shell; class SessionMediatorReport; class DisplayChanger; class Screencast; class SessionAuthorizer; class EventSinkFactory; class DefaultIpcFactory : public ProtobufIpcFactory { public: explicit DefaultIpcFactory( std::shared_ptr const& shell, std::shared_ptr const& sm_report, std::shared_ptr const& platform_ipc_operations, std::shared_ptr const& display_changer, std::shared_ptr const& buffer_allocator, std::shared_ptr const& screencast, std::shared_ptr const& session_authorizer, std::shared_ptr const& cursor_images, std::shared_ptr const& translator, std::shared_ptr const& anr_detector, std::shared_ptr const& cookie_authority, std::shared_ptr const& seat); std::shared_ptr make_ipc_server( SessionCredentials const &creds, std::shared_ptr const& sink_factory, std::shared_ptr const& message_sender, ConnectionContext const &connection_context) override; virtual std::shared_ptr resource_cache() override; virtual std::shared_ptr make_mediator( std::shared_ptr const& shell, std::shared_ptr const& platform_ipc_operations, std::shared_ptr const& changer, std::shared_ptr const& buffer_allocator, std::shared_ptr const& sm_report, std::shared_ptr const& sink_factory, std::shared_ptr const& message_sender, std::shared_ptr const& effective_screencast, ConnectionContext const& connection_context, std::shared_ptr const& cursor_images); private: std::shared_ptr const shell; std::shared_ptr const no_prompt_shell; std::shared_ptr const sm_report; std::shared_ptr const cache; std::shared_ptr const platform_ipc_operations; std::shared_ptr const display_changer; std::shared_ptr const buffer_allocator; std::shared_ptr const screencast; std::shared_ptr const session_authorizer; std::shared_ptr const cursor_images; std::shared_ptr const translator; std::shared_ptr const anr_detector; std::shared_ptr const cookie_authority; std::shared_ptr const hub; }; } } #endif /* MIR_FRONTEND_DEFAULT_IPC_FACTORY_H_ */ ./src/server/frontend/message_sender.h0000644000015600001650000000227612676616125020171 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_MESSAGE_SENDER_H_ #define MIR_FRONTEND_MESSAGE_SENDER_H_ #include "mir/frontend/fd_sets.h" #include namespace mir { namespace frontend { class MessageSender { public: virtual void send(char const* data, size_t length, FdSets const& fds) = 0; protected: MessageSender() = default; virtual ~MessageSender() = default; MessageSender(MessageSender const&) = delete; MessageSender& operator=(MessageSender const&) = delete; }; } } #endif /* MIR_FRONTEND_MESSAGE_SENDER_H_ */ ./src/server/frontend/client_buffer_tracker.cpp0000644000015600001650000000376212676616125022063 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #include #include "client_buffer_tracker.h" #include "mir/graphics/buffer_id.h" #include "mir/graphics/buffer.h" namespace mf = mir::frontend; namespace mg = mir::graphics; mf::ClientBufferTracker::ClientBufferTracker(unsigned int client_cache_size) : buffers{}, cache_size{client_cache_size} { } void mf::ClientBufferTracker::add(mg::Buffer* buffer) { if (!buffer) return; IdBufferAssociation buf_id{buffer->id(), buffer}; auto existing_buffer = std::find(buffers.begin(), buffers.end(), buf_id); if (existing_buffer != buffers.end()) { buffers.push_front(*existing_buffer); buffers.erase(existing_buffer); } else { buffers.push_front(buf_id); } if (buffers.size() > cache_size) { buffers.pop_back(); } } mg::Buffer* mf::ClientBufferTracker::buffer_from(mg::BufferID const& buffer_id) const { auto it = std::find_if(buffers.begin(), buffers.end(), [&buffer_id](IdBufferAssociation b) { return (std::get<0>(b) == buffer_id); }); if (it == buffers.end()) return nullptr; else return std::get<1>(*it); } bool mf::ClientBufferTracker::client_has(mg::BufferID const& buffer_id) const { return (nullptr != buffer_from(buffer_id)); } ./src/server/frontend/resource_cache.cpp0000644000015600001650000000272612676616125020512 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "resource_cache.h" void mir::frontend::ResourceCache::save_resource( google::protobuf::MessageLite* key, std::shared_ptr const& value) { std::lock_guard lock(guard); resources[key] = value; } void mir::frontend::ResourceCache::save_fd( google::protobuf::MessageLite* key, mir::Fd const& fd) { std::lock_guard lock(guard); fd_resources.emplace(key, fd); } void mir::frontend::ResourceCache::free_resource(google::protobuf::MessageLite* key) { std::shared_ptr value; { std::lock_guard lock(guard); auto const& p = resources.find(key); if (p != resources.end()) { value = p->second; } resources.erase(key); fd_resources.erase(key); } } ./src/server/frontend/protobuf_ipc_factory.h0000644000015600001650000000324012676616125021417 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_PROTOBUF_IPC_FACTORY_H_ #define MIR_FRONTEND_PROTOBUF_IPC_FACTORY_H_ #include #include namespace mir { namespace frontend { namespace detail { class DisplayServer; } class ConnectionContext; class EventSink; class ResourceCache; class SessionCredentials; class MessageSender; class EventSinkFactory; class ProtobufIpcFactory { public: virtual std::shared_ptr make_ipc_server( SessionCredentials const &creds, std::shared_ptr const& sink_factory, std::shared_ptr const& message_sender, ConnectionContext const &connection_context) = 0; virtual std::shared_ptr resource_cache() = 0; protected: ProtobufIpcFactory() {} virtual ~ProtobufIpcFactory() {} ProtobufIpcFactory(ProtobufIpcFactory const&) = delete; ProtobufIpcFactory& operator =(ProtobufIpcFactory const&) = delete; }; } } #endif // MIR_FRONTEND_PROTOBUF_IPC_FACTORY_H_ ./src/server/frontend/session_credentials.cpp0000644000015600001650000000202612676616125021571 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "mir/frontend/session_credentials.h" namespace mf = mir::frontend; mf::SessionCredentials::SessionCredentials(pid_t pid, uid_t uid, gid_t gid) : the_pid{pid}, the_uid{uid}, the_gid{gid} { } pid_t mf::SessionCredentials::pid() const { return the_pid; } uid_t mf::SessionCredentials::uid() const { return the_uid; } gid_t mf::SessionCredentials::gid() const { return the_gid; } ./src/server/frontend/buffer_stream_tracker.h0000644000015600001650000000470612676616125021544 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_FRONTEND_BUFFER_STREAM_TRACKER_H_ #define MIR_FRONTEND_BUFFER_STREAM_TRACKER_H_ #include "mir/frontend/buffer_stream_id.h" #include "mir/graphics/buffer_id.h" #include #include #include namespace mir { namespace graphics { class Buffer; } namespace frontend { class ClientBufferTracker; class BufferStreamTracker { public: BufferStreamTracker(size_t client_cache_size); BufferStreamTracker(BufferStreamTracker const&) = delete; BufferStreamTracker& operator=(BufferStreamTracker const&) = delete; /* track a buffer as associated with a buffer stream * \warning the buffer must correspond to a single buffer stream id * \param buffer_stream_id id that the the buffer is associated with * \param buffer buffer to be tracked * \returns true if the buffer is already tracked * false if the buffer is not tracked */ bool track_buffer(BufferStreamId buffer_stream_id, graphics::Buffer* buffer); /* removes the buffer stream id from all tracking */ void remove_buffer_stream(BufferStreamId); /* Access the buffer resource that the id corresponds to. */ graphics::Buffer* buffer_from(graphics::BufferID) const; private: size_t const client_cache_size; std::unordered_map> client_buffer_tracker; public: /* accesses the last buffer given to track_buffer() for the given BufferStreamId */ //TODO: once next_buffer rpc call is fully deprecated, this can be removed. graphics::Buffer* last_buffer(BufferStreamId) const; private: mutable std::mutex mutex; std::unordered_map client_buffer_resource; }; } } #endif // MIR_FRONTEND_BUFFER_STREAM_TRACKER_H_ ./src/server/frontend/client_buffer_tracker.h0000644000015600001650000000401312676616125021516 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_FRONTEND_CLIENT_BUFFER_TRACKER_H_ #define MIR_FRONTEND_CLIENT_BUFFER_TRACKER_H_ #include "mir/graphics/buffer_id.h" #include #include #include namespace mir { namespace graphics { class Buffer; } namespace frontend { /// Responsible for tracking what buffers the client library knows about for a surface. /// \sa mir::client::ClientBufferDepository for the client-side of this tracking /// \note Changes to the tracking algorithm of mir::client::ClientBufferDepository will need to be mirrored here class ClientBufferTracker { public: ClientBufferTracker(unsigned int client_cache_size); ClientBufferTracker(ClientBufferTracker const&) = delete; ClientBufferTracker& operator=(ClientBufferTracker const&) = delete; /// Add a Buffer to the list of buffers known by the client. /// /// Typically this should be done just prior to or just after sending the buffer information void add(graphics::Buffer* buffer); bool client_has(graphics::BufferID const& id) const; graphics::Buffer* buffer_from(graphics::BufferID const& id) const; private: typedef std::tuple IdBufferAssociation; std::list buffers; unsigned int const cache_size; }; } } #endif // MIR_FRONTEND_CLIENT_BUFFER_TRACKER_H_ ./src/server/frontend/session_mediator.cpp0000644000015600001650000011077312676616157021116 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "session_mediator.h" #include "reordering_message_sender.h" #include "event_sink_factory.h" #include "mir/frontend/session_mediator_report.h" #include "mir/frontend/shell.h" #include "mir/frontend/session.h" #include "mir/frontend/surface.h" #include "mir/shell/surface_specification.h" #include "mir/scene/surface_creation_parameters.h" #include "mir/scene/coordinate_translator.h" #include "mir/scene/application_not_responding_detector.h" #include "mir/frontend/display_changer.h" #include "resource_cache.h" #include "mir_toolkit/common.h" #include "mir/graphics/buffer_id.h" #include "mir/graphics/buffer.h" #include "mir/input/cursor_images.h" #include "mir/compositor/buffer_stream.h" #include "mir/geometry/dimensions.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/pixel_format_utils.h" #include "mir/graphics/platform_ipc_operations.h" #include "mir/graphics/platform_ipc_package.h" #include "mir/graphics/platform_operation_message.h" #include "mir/frontend/client_constants.h" #include "mir/frontend/event_sink.h" #include "mir/frontend/screencast.h" #include "mir/frontend/prompt_session.h" #include "mir/frontend/buffer_stream.h" #include "mir/input/input_device_hub.h" #include "mir/input/device.h" #include "mir/scene/prompt_session_creation_parameters.h" #include "mir/fd.h" #include "mir/cookie/authority.h" #include "mir/module_properties.h" #include "mir/geometry/rectangles.h" #include "buffer_stream_tracker.h" #include "client_buffer_tracker.h" #include "protobuf_buffer_packer.h" #include "mir_toolkit/client_types.h" #include #include #include #include #include #include #include namespace ms = mir::scene; namespace msh = mir::shell; namespace mf = mir::frontend; namespace mfd=mir::frontend::detail; namespace mg = mir::graphics; namespace mi = mir::input; namespace geom = mir::geometry; mf::SessionMediator::SessionMediator( std::shared_ptr const& shell, std::shared_ptr const& ipc_operations, std::shared_ptr const& display_changer, std::vector const& surface_pixel_formats, std::shared_ptr const& report, std::shared_ptr const& sink_factory, std::shared_ptr const& message_sender, std::shared_ptr const& resource_cache, std::shared_ptr const& screencast, ConnectionContext const& connection_context, std::shared_ptr const& cursor_images, std::shared_ptr const& translator, std::shared_ptr const& anr_detector, std::shared_ptr const& cookie_authority, std::shared_ptr const& hub) : client_pid_(0), shell(shell), ipc_operations(ipc_operations), surface_pixel_formats(surface_pixel_formats), display_changer(display_changer), report(report), sink_factory{sink_factory}, event_sink{sink_factory->create_sink(message_sender)}, message_sender{message_sender}, resource_cache(resource_cache), screencast(screencast), connection_context(connection_context), cursor_images(cursor_images), translator{translator}, anr_detector{anr_detector}, cookie_authority(cookie_authority), hub(hub), buffer_stream_tracker{static_cast(client_buffer_cache_size)} { } mf::SessionMediator::~SessionMediator() noexcept { if (auto session = weak_session.lock()) { report->session_error(session->name(), __PRETTY_FUNCTION__, "connection dropped without disconnect"); shell->close_session(session); } } void mf::SessionMediator::client_pid(int pid) { client_pid_ = pid; } void mf::SessionMediator::connect( const ::mir::protobuf::ConnectParameters* request, ::mir::protobuf::Connection* response, ::google::protobuf::Closure* done) { report->session_connect_called(request->application_name()); auto const session = shell->open_session(client_pid_, request->application_name(), event_sink); weak_session = session; connection_context.handle_client_connect(session); auto ipc_package = ipc_operations->connection_ipc_package(); auto platform = response->mutable_platform(); for (auto& data : ipc_package->ipc_data) platform->add_data(data); for (auto& ipc_fds : ipc_package->ipc_fds) platform->add_fd(ipc_fds); if (auto const graphics_module = ipc_package->graphics_module) { auto const module = platform->mutable_graphics_module(); module->set_name(graphics_module->name); module->set_major_version(graphics_module->major_version); module->set_minor_version(graphics_module->minor_version); module->set_micro_version(graphics_module->micro_version); module->set_file(graphics_module->file); } auto display_config = display_changer->base_configuration(); auto protobuf_config = response->mutable_display_configuration(); mfd::pack_protobuf_display_configuration(*protobuf_config, *display_config); hub->for_each_input_device( [response](auto const& dev) { auto dev_info = response->add_input_devices(); dev_info->set_name(dev.name()); dev_info->set_id(dev.id()); dev_info->set_unique_id(dev.unique_id()); dev_info->set_capabilities(dev.capabilities().value()); }); for (auto pf : surface_pixel_formats) response->add_surface_pixel_format(static_cast<::google::protobuf::uint32>(pf)); resource_cache->save_resource(response, ipc_package); done->Run(); } void mf::SessionMediator::advance_buffer( BufferStreamId stream_id, BufferStream& stream, graphics::Buffer* old_buffer, std::function complete) { stream.swap_buffers( old_buffer, [this, stream_id, complete](mg::Buffer* new_buffer) { if (!new_buffer || buffer_stream_tracker.track_buffer(stream_id, new_buffer)) complete(new_buffer, mg::BufferIpcMsgType::update_msg); else complete(new_buffer, mg::BufferIpcMsgType::full_msg); }); } namespace { template std::vector extract_input_shape_from(T const& params) { std::vector shapes; if (params->input_shape_size() > 0) { for (auto& rect : params->input_shape()) { shapes.push_back(geom::Rectangle( geom::Point{rect.left(), rect.top()}, geom::Size{rect.width(), rect.height()}) ); } } return shapes; } } void mf::SessionMediator::create_surface( const mir::protobuf::SurfaceParameters* request, mir::protobuf::Surface* response, google::protobuf::Closure* done) { auto const session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_create_surface_called(session->name()); auto params = ms::SurfaceCreationParameters() .of_size(request->width(), request->height()) .of_buffer_usage(static_cast(request->buffer_usage())) .of_pixel_format(static_cast(request->pixel_format())); if (request->has_surface_name()) params.of_name(request->surface_name()); if (request->has_output_id()) params.with_output_id(graphics::DisplayConfigurationOutputId(request->output_id())); if (request->has_type()) params.of_type(static_cast(request->type())); if (request->has_state()) params.with_state(static_cast(request->state())); if (request->has_pref_orientation()) params.with_preferred_orientation(static_cast(request->pref_orientation())); if (request->has_parent_id()) params.with_parent_id(SurfaceId{request->parent_id()}); if (request->has_parent_persistent_id()) { auto persistent_id = request->parent_persistent_id().value(); params.parent = shell->surface_for_id(persistent_id); } if (request->has_aux_rect()) { params.with_aux_rect(geom::Rectangle{ {request->aux_rect().left(), request->aux_rect().top()}, {request->aux_rect().width(), request->aux_rect().height()} }); } if (request->has_edge_attachment()) params.with_edge_attachment(static_cast(request->edge_attachment())); #define COPY_IF_SET(field)\ if (request->has_##field())\ params.field = decltype(params.field.value())(request->field()) COPY_IF_SET(min_width); COPY_IF_SET(min_height); COPY_IF_SET(max_width); COPY_IF_SET(max_height); COPY_IF_SET(width_inc); COPY_IF_SET(height_inc); COPY_IF_SET(shell_chrome); #undef COPY_IF_SET if (request->has_min_aspect()) params.min_aspect = { request->min_aspect().width(), request->min_aspect().height()}; if (request->has_max_aspect()) params.max_aspect = { request->max_aspect().width(), request->max_aspect().height()}; params.input_shape = extract_input_shape_from(request); auto buffering_sender = std::make_shared(message_sender); std::shared_ptr sink = sink_factory->create_sink(buffering_sender); auto const surf_id = shell->create_surface(session, params, sink); auto stream_id = mf::BufferStreamId(surf_id.as_value()); auto surface = session->get_surface(surf_id); auto stream = session->get_buffer_stream(stream_id); auto const& client_size = surface->client_size(); response->mutable_id()->set_value(surf_id.as_value()); response->set_width(client_size.width.as_uint32_t()); response->set_height(client_size.height.as_uint32_t()); // TODO: Deprecate response->set_pixel_format(stream->pixel_format()); response->set_buffer_usage(request->buffer_usage()); response->mutable_buffer_stream()->set_pixel_format(stream->pixel_format()); response->mutable_buffer_stream()->set_buffer_usage(request->buffer_usage()); if (surface->supports_input()) response->add_fd(surface->client_input_fd()); for (unsigned int i = 0; i < mir_surface_attribs; i++) { auto setting = response->add_attributes(); setting->mutable_surfaceid()->set_value(surf_id.as_value()); setting->set_attrib(i); setting->set_ivalue(shell->get_surface_attribute(session, surf_id, static_cast(i))); } advance_buffer(stream_id, *stream, buffer_stream_tracker.last_buffer(stream_id), [this, buffering_sender, surf_id, response, done, session] (graphics::Buffer* client_buffer, graphics::BufferIpcMsgType msg_type) { response->mutable_buffer_stream()->mutable_id()->set_value(surf_id.as_value()); if (client_buffer) pack_protobuf_buffer(*response->mutable_buffer_stream()->mutable_buffer(), client_buffer, msg_type); // Send the create_surface reply first... done->Run(); // ...then uncork the message sender, sending all buffered surface events. buffering_sender->uncork(); }); } void mf::SessionMediator::next_buffer( ::mir::protobuf::SurfaceId const* request, ::mir::protobuf::Buffer* response, ::google::protobuf::Closure* done) { SurfaceId const surf_id{request->value()}; auto const session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_next_buffer_called(session->name()); auto surface = session->get_surface(surf_id); auto stream = surface->primary_buffer_stream(); auto stream_id = mf::BufferStreamId{surf_id.as_value()}; advance_buffer(stream_id, *stream, buffer_stream_tracker.last_buffer(stream_id), [this, response, done] (graphics::Buffer* client_buffer, graphics::BufferIpcMsgType msg_type) { pack_protobuf_buffer(*response, client_buffer, msg_type); done->Run(); }); } void mf::SessionMediator::exchange_buffer( mir::protobuf::BufferRequest const* request, mir::protobuf::Buffer* response, google::protobuf::Closure* done) { mf::BufferStreamId const stream_id{request->id().value()}; mg::BufferID const buffer_id{static_cast(request->buffer().buffer_id())}; mfd::ProtobufBufferPacker request_msg{const_cast(&request->buffer())}; auto buffer = buffer_stream_tracker.last_buffer(stream_id); if (!buffer) BOOST_THROW_EXCEPTION(std::logic_error("No buffer found for given stream id")); ipc_operations->unpack_buffer(request_msg, *buffer); auto const session = weak_session.lock(); if (!session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_exchange_buffer_called(session->name()); auto const& surface = session->get_buffer_stream(stream_id); advance_buffer(stream_id, *surface, buffer_stream_tracker.buffer_from(buffer_id), [this, response, done] (graphics::Buffer* new_buffer, graphics::BufferIpcMsgType msg_type) { pack_protobuf_buffer(*response, new_buffer, msg_type); done->Run(); }); } void mf::SessionMediator::submit_buffer( mir::protobuf::BufferRequest const* request, mir::protobuf::Void*, google::protobuf::Closure* done) { auto const session = weak_session.lock(); if (!session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_submit_buffer_called(session->name()); mf::BufferStreamId const stream_id{request->id().value()}; mg::BufferID const buffer_id{static_cast(request->buffer().buffer_id())}; auto stream = session->get_buffer_stream(stream_id); mfd::ProtobufBufferPacker request_msg{const_cast(&request->buffer())}; if (auto* buffer = buffer_stream_tracker.last_buffer(stream_id)) { ipc_operations->unpack_buffer(request_msg, *buffer); stream->swap_buffers(buffer, [this, stream_id](mg::Buffer* new_buffer) { if (buffer_stream_tracker.track_buffer(stream_id, new_buffer)) event_sink->send_buffer(stream_id, *new_buffer, mg::BufferIpcMsgType::update_msg); else event_sink->send_buffer(stream_id, *new_buffer, mg::BufferIpcMsgType::full_msg); }); } else { stream->with_buffer(buffer_id, [&, this](mg::Buffer& buffer) { ipc_operations->unpack_buffer(request_msg, buffer); stream->swap_buffers(&buffer, [](mg::Buffer*) {}); }); } done->Run(); } void mf::SessionMediator::allocate_buffers( mir::protobuf::BufferAllocation const* request, mir::protobuf::Void*, google::protobuf::Closure* done) { auto session = weak_session.lock(); if (!session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_allocate_buffers_called(session->name()); mf::BufferStreamId stream_id{request->id().value()}; auto stream = session->get_buffer_stream(stream_id); for (auto i = 0; i < request->buffer_requests().size(); i++) { auto const& req = request->buffer_requests(i); mg::BufferProperties properties( geom::Size{req.width(), req.height()}, static_cast(req.pixel_format()), static_cast(req.buffer_usage())); stream->allocate_buffer(properties); } done->Run(); } void mf::SessionMediator::release_buffers( mir::protobuf::BufferRelease const* request, mir::protobuf::Void*, google::protobuf::Closure* done) { auto session = weak_session.lock(); if (!session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_release_buffers_called(session->name()); mf::BufferStreamId stream_id{request->id().value()}; auto stream = session->get_buffer_stream(stream_id); for (auto i = 0; i < request->buffers().size(); i++) stream->remove_buffer(mg::BufferID{static_cast(request->buffers(i).buffer_id())}); done->Run(); } void mf::SessionMediator::release_surface( const mir::protobuf::SurfaceId* request, mir::protobuf::Void*, google::protobuf::Closure* done) { auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_release_surface_called(session->name()); auto const id = SurfaceId(request->value()); shell->destroy_surface(session, id); buffer_stream_tracker.remove_buffer_stream(BufferStreamId(request->value())); // TODO: We rely on this sending responses synchronously. done->Run(); } void mf::SessionMediator::disconnect( const mir::protobuf::Void* /*request*/, mir::protobuf::Void* /*response*/, google::protobuf::Closure* done) { auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_disconnect_called(session->name()); shell->close_session(session); weak_session.reset(); done->Run(); } void mf::SessionMediator::configure_surface( const mir::protobuf::SurfaceSetting* request, mir::protobuf::SurfaceSetting* response, google::protobuf::Closure* done) { MirSurfaceAttrib attrib = static_cast(request->attrib()); // Required response fields: response->mutable_surfaceid()->CopyFrom(request->surfaceid()); response->set_attrib(attrib); auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_configure_surface_called(session->name()); auto const id = mf::SurfaceId(request->surfaceid().value()); int value = request->ivalue(); int newvalue = shell->set_surface_attribute(session, id, attrib, value); response->set_ivalue(newvalue); done->Run(); } void mf::SessionMediator::modify_surface( const mir::protobuf::SurfaceModifications* request, mir::protobuf::Void* /*response*/, google::protobuf::Closure* done) { auto const& surface_specification = request->surface_specification(); auto const session = weak_session.lock(); if (!session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); msh::SurfaceSpecification mods; #define COPY_IF_SET(name)\ if (surface_specification.has_##name())\ mods.name = decltype(mods.name.value())(surface_specification.name()) COPY_IF_SET(width); COPY_IF_SET(height); COPY_IF_SET(pixel_format); COPY_IF_SET(buffer_usage); COPY_IF_SET(name); COPY_IF_SET(output_id); COPY_IF_SET(type); COPY_IF_SET(state); COPY_IF_SET(preferred_orientation); COPY_IF_SET(parent_id); // aux_rect is a special case (below) COPY_IF_SET(edge_attachment); COPY_IF_SET(min_width); COPY_IF_SET(min_height); COPY_IF_SET(max_width); COPY_IF_SET(max_height); COPY_IF_SET(width_inc); COPY_IF_SET(height_inc); COPY_IF_SET(shell_chrome); // min_aspect is a special case (below) // max_aspect is a special case (below) #undef COPY_IF_SET if (surface_specification.stream_size() > 0) { std::vector stream_spec; for (auto& stream : surface_specification.stream()) { stream_spec.emplace_back( msh::StreamSpecification{ mf::BufferStreamId{stream.id().value()}, geom::Displacement{stream.displacement_x(), stream.displacement_y()}}); } mods.streams = std::move(stream_spec); } if (surface_specification.has_aux_rect()) { auto const& rect = surface_specification.aux_rect(); mods.aux_rect = {{rect.left(), rect.top()}, {rect.width(), rect.height()}}; } if (surface_specification.has_min_aspect()) mods.min_aspect = { surface_specification.min_aspect().width(), surface_specification.min_aspect().height() }; if (surface_specification.has_max_aspect()) mods.max_aspect = { surface_specification.max_aspect().width(), surface_specification.max_aspect().height() }; mods.input_shape = extract_input_shape_from(&surface_specification); auto const id = mf::SurfaceId(request->surface_id().value()); shell->modify_surface(session, id, mods); done->Run(); } void mf::SessionMediator::configure_display( const ::mir::protobuf::DisplayConfiguration* request, ::mir::protobuf::DisplayConfiguration* response, ::google::protobuf::Closure* done) { auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_configure_display_called(session->name()); auto const config = unpack_and_sanitize_display_configuration(request); display_changer->configure(session, config); auto display_config = display_changer->base_configuration(); mfd::pack_protobuf_display_configuration(*response, *display_config); done->Run(); } void mf::SessionMediator::set_base_display_configuration( mir::protobuf::DisplayConfiguration const* request, mir::protobuf::Void* /*response*/, google::protobuf::Closure* done) { auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_set_base_display_configuration_called(session->name()); auto const config = unpack_and_sanitize_display_configuration(request); display_changer->set_base_configuration(config); done->Run(); } void mf::SessionMediator::create_screencast( const mir::protobuf::ScreencastParameters* parameters, mir::protobuf::Screencast* protobuf_screencast, google::protobuf::Closure* done) { static auto const msg_type = mg::BufferIpcMsgType::full_msg; geom::Rectangle const region{ {parameters->region().left(), parameters->region().top()}, {parameters->region().width(), parameters->region().height()} }; geom::Size const size{parameters->width(), parameters->height()}; MirPixelFormat const pixel_format = static_cast(parameters->pixel_format()); auto screencast_session_id = screencast->create_session(region, size, pixel_format); auto buffer = screencast->capture(screencast_session_id); protobuf_screencast->mutable_screencast_id()->set_value( screencast_session_id.as_value()); protobuf_screencast->mutable_buffer_stream()->mutable_id()->set_value( screencast_session_id.as_value()); pack_protobuf_buffer(*protobuf_screencast->mutable_buffer_stream()->mutable_buffer(), buffer.get(), msg_type); done->Run(); } void mf::SessionMediator::release_screencast( const mir::protobuf::ScreencastId* protobuf_screencast_id, mir::protobuf::Void*, google::protobuf::Closure* done) { ScreencastSessionId const screencast_session_id{ protobuf_screencast_id->value()}; screencast->destroy_session(screencast_session_id); done->Run(); } void mf::SessionMediator::screencast_buffer( const mir::protobuf::ScreencastId* protobuf_screencast_id, mir::protobuf::Buffer* protobuf_buffer, google::protobuf::Closure* done) { static auto const msg_type = mg::BufferIpcMsgType::update_msg; ScreencastSessionId const screencast_session_id{ protobuf_screencast_id->value()}; auto buffer = screencast->capture(screencast_session_id); pack_protobuf_buffer(*protobuf_buffer, buffer.get(), msg_type); done->Run(); } void mf::SessionMediator::create_buffer_stream( mir::protobuf::BufferStreamParameters const* request, mir::protobuf::BufferStream* response, google::protobuf::Closure* done) { auto const session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_create_buffer_stream_called(session->name()); auto const usage = (request->buffer_usage() == mir_buffer_usage_hardware) ? mg::BufferUsage::hardware : mg::BufferUsage::software; auto stream_size = geom::Size{geom::Width{request->width()}, geom::Height{request->height()}}; mg::BufferProperties props(stream_size, static_cast(request->pixel_format()), usage); auto const buffer_stream_id = session->create_buffer_stream(props); auto stream = session->get_buffer_stream(buffer_stream_id); response->mutable_id()->set_value(buffer_stream_id.as_value()); response->set_pixel_format(stream->pixel_format()); // TODO: Is it guaranteed we get the buffer usage we want? response->set_buffer_usage(request->buffer_usage()); advance_buffer(buffer_stream_id, *stream, buffer_stream_tracker.last_buffer(buffer_stream_id), [this, response, done, session] (graphics::Buffer* client_buffer, graphics::BufferIpcMsgType msg_type) { if (client_buffer) pack_protobuf_buffer(*response->mutable_buffer(), client_buffer, msg_type); done->Run(); }); } void mf::SessionMediator::release_buffer_stream( const mir::protobuf::BufferStreamId* request, mir::protobuf::Void*, google::protobuf::Closure* done) { auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_release_buffer_stream_called(session->name()); auto const id = BufferStreamId(request->value()); session->destroy_buffer_stream(id); buffer_stream_tracker.remove_buffer_stream(id); done->Run(); } std::function const&)> mf::SessionMediator::prompt_session_connect_handler() const { return [this](std::shared_ptr const& session) { auto prompt_session = weak_prompt_session.lock(); if (prompt_session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid prompt session")); shell->add_prompt_provider_for(prompt_session, session); }; } namespace { void throw_if_unsuitable_for_cursor(mf::BufferStream& stream) { if (stream.pixel_format() != mir_pixel_format_argb_8888) BOOST_THROW_EXCEPTION(std::logic_error("Only argb8888 buffer streams may currently be attached to the cursor")); } } void mf::SessionMediator::configure_cursor( mir::protobuf::CursorSetting const* cursor_request, mir::protobuf::Void* /* void_response */, google::protobuf::Closure* done) { auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); report->session_configure_surface_cursor_called(session->name()); auto const id = mf::SurfaceId(cursor_request->surfaceid().value()); auto const surface = session->get_surface(id); if (cursor_request->has_name()) { auto const& image = cursor_images->image(cursor_request->name(), mi::default_cursor_size); surface->set_cursor_image(image); } else if (cursor_request->has_buffer_stream()) { auto const& stream_id = mf::BufferStreamId(cursor_request->buffer_stream().value()); auto hotspot = geom::Displacement{cursor_request->hotspot_x(), cursor_request->hotspot_y()}; auto stream = session->get_buffer_stream(stream_id); throw_if_unsuitable_for_cursor(*stream); surface->set_cursor_stream(stream, hotspot); } else { surface->set_cursor_image({}); } done->Run(); } void mf::SessionMediator::new_fds_for_prompt_providers( ::mir::protobuf::SocketFDRequest const* parameters, ::mir::protobuf::SocketFD* response, ::google::protobuf::Closure* done) { auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); auto const connect_handler = prompt_session_connect_handler(); auto const fds_requested = parameters->number(); // < 1 is illogical, > 42 is unreasonable if (fds_requested < 1 || fds_requested > 42) BOOST_THROW_EXCEPTION(std::runtime_error("number of fds requested out of range")); for (auto i = 0; i != fds_requested; ++i) { auto const fd = connection_context.fd_for_new_client(connect_handler); response->add_fd(fd); resource_cache->save_fd(response, mir::Fd{fd}); } done->Run(); } void mf::SessionMediator::pong( mir::protobuf::PingEvent const* /*request*/, mir::protobuf::Void* /* response */, google::protobuf::Closure* done) { auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); anr_detector->pong_received(session.get()); done->Run(); } void mf::SessionMediator::translate_surface_to_screen( ::mir::protobuf::CoordinateTranslationRequest const* request, ::mir::protobuf::CoordinateTranslationResponse* response, ::google::protobuf::Closure *done) { auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); auto const id = mf::SurfaceId(request->surfaceid().value()); auto const coords = translator->surface_to_screen(session->get_surface(id), request->x(), request->y()); response->set_x(coords.x.as_uint32_t()); response->set_y(coords.y.as_uint32_t()); done->Run(); } void mf::SessionMediator::platform_operation( mir::protobuf::PlatformOperationMessage const* request, mir::protobuf::PlatformOperationMessage* response, google::protobuf::Closure* done) { auto session = weak_session.lock(); if (session.get() == nullptr) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); mg::PlatformOperationMessage platform_request; unsigned int const opcode = request->opcode(); platform_request.data.assign(request->data().begin(), request->data().end()); platform_request.fds.assign(request->fd().begin(), request->fd().end()); auto const& platform_response = ipc_operations->platform_operation(opcode, platform_request); response->set_opcode(opcode); response->set_data(platform_response.data.data(), platform_response.data.size()); for (auto fd : platform_response.fds) { response->add_fd(fd); resource_cache->save_fd(response, mir::Fd{fd}); } done->Run(); } void mf::SessionMediator::start_prompt_session( const ::mir::protobuf::PromptSessionParameters* request, ::mir::protobuf::Void* /*response*/, ::google::protobuf::Closure* done) { auto const session = weak_session.lock(); if (!session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); if (weak_prompt_session.lock()) BOOST_THROW_EXCEPTION(std::runtime_error("Cannot start another prompt session")); ms::PromptSessionCreationParameters parameters; parameters.application_pid = request->application_pid(); report->session_start_prompt_session_called(session->name(), parameters.application_pid); weak_prompt_session = shell->start_prompt_session_for(session, parameters); done->Run(); } void mf::SessionMediator::stop_prompt_session( const ::mir::protobuf::Void*, ::mir::protobuf::Void*, ::google::protobuf::Closure* done) { auto const session = weak_session.lock(); if (!session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); auto const prompt_session = weak_prompt_session.lock(); if (!prompt_session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid prompt session")); weak_prompt_session.reset(); report->session_stop_prompt_session_called(session->name()); shell->stop_prompt_session(prompt_session); done->Run(); } void mf::SessionMediator::request_persistent_surface_id( mir::protobuf::SurfaceId const* request, mir::protobuf::PersistentSurfaceId* response, google::protobuf::Closure* done) { auto const session = weak_session.lock(); if (!session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); auto buffer = shell->persistent_id_for(session, mf::SurfaceId{request->value()}); *response->mutable_value() = std::string{buffer.begin(), buffer.end()}; done->Run(); } void mf::SessionMediator::pack_protobuf_buffer( protobuf::Buffer& protobuf_buffer, graphics::Buffer* graphics_buffer, mg::BufferIpcMsgType buffer_msg_type) { protobuf_buffer.set_buffer_id(graphics_buffer->id().as_value()); mfd::ProtobufBufferPacker packer{&protobuf_buffer}; ipc_operations->pack_buffer(packer, *graphics_buffer, buffer_msg_type); for(auto const& fd : packer.fds()) resource_cache->save_fd(&protobuf_buffer, fd); } void mf::SessionMediator::configure_buffer_stream( mir::protobuf::StreamConfiguration const* request, mir::protobuf::Void*, google::protobuf::Closure* done) { auto const session = weak_session.lock(); if (!session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); auto stream = session->get_buffer_stream(mf::BufferStreamId(request->id().value())); if (request->has_swapinterval()) stream->allow_framedropping(request->swapinterval() == 0); if (request->has_scale()) stream->set_scale(request->scale()); done->Run(); } void mf::SessionMediator::raise_surface( mir::protobuf::RaiseRequest const* request, mir::protobuf::Void*, google::protobuf::Closure* done) { auto const session = weak_session.lock(); if (!session) BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session")); auto const cookie = request->cookie(); auto const surface_id = request->surface_id(); auto cookie_string = cookie.cookie(); std::vector cookie_bytes(cookie_string.begin(), cookie_string.end()); auto const cookie_ptr = cookie_authority->make_cookie(cookie_bytes); shell->raise_surface(session, mf::SurfaceId{surface_id.value()}, cookie_ptr->timestamp()); done->Run(); } std::shared_ptr mf::SessionMediator::unpack_and_sanitize_display_configuration( mir::protobuf::DisplayConfiguration const* protobuf_config) { auto config = display_changer->base_configuration(); config->for_each_output([&](mg::UserDisplayConfigurationOutput& dest){ unsigned id = dest.id.as_value(); int n = 0; for (; n < protobuf_config->display_output_size(); ++n) { if (protobuf_config->display_output(n).output_id() == id) break; } if (n >= protobuf_config->display_output_size()) return; auto& src = protobuf_config->display_output(n); dest.used = src.used(); dest.top_left = geom::Point{src.position_x(), src.position_y()}; dest.current_mode_index = src.current_mode(); dest.current_format = static_cast(src.current_format()); dest.power_mode = static_cast(src.power_mode()); dest.orientation = static_cast(src.orientation()); }); return config; } ./src/server/frontend/socket_messenger.cpp0000644000015600001650000001311412676616125021071 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "socket_messenger.h" #include "mir/frontend/client_constants.h" #include "mir/variable_length_array.h" #include "mir/fd_socket_transmission.h" #include "mir/raii.h" #include #include #include #include namespace mf = mir::frontend; namespace mfd = mf::detail; namespace bs = boost::system; namespace ba = boost::asio; mfd::SocketMessenger::SocketMessenger(std::shared_ptr const& socket) : socket(socket), socket_fd{IntOwnedFd{socket->native_handle()}} { // Make the socket non-blocking to avoid hanging the server when a client // is unresponsive. Also increase the send buffer size to 64KiB to allow // more leeway for transient client freezes. // See https://bugs.launchpad.net/mir/+bug/1350207 // TODO: Rework the messenger to support asynchronous sends socket->non_blocking(true); boost::asio::socket_base::send_buffer_size option(64*1024); socket->set_option(option); } mf::SessionCredentials mfd::SocketMessenger::creator_creds() const { struct ucred cr; socklen_t cl = sizeof(cr); auto status = getsockopt(socket_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl); if (status) BOOST_THROW_EXCEPTION(std::runtime_error("Failed to query client socket credentials")); return {cr.pid, cr.uid, cr.gid}; } mf::SessionCredentials mfd::SocketMessenger::client_creds() { if (session_creds.pid() != 0) return session_creds; // We've not got the credentials from client yet. // Return the credentials that created the socket instead. return creator_creds(); } void mfd::SocketMessenger::send(char const* data, size_t length, FdSets const& fd_set) { static size_t const header_size{2}; mir::VariableLengthArray whole_message{header_size + length}; whole_message.data()[0] = static_cast((length >> 8) & 0xff); whole_message.data()[1] = static_cast((length >> 0) & 0xff); std::copy(data, data + length, whole_message.data() + header_size); std::unique_lock lg(message_lock); // TODO: This should be asynchronous, but we are not making sure // that a potential call to send_fds is executed _after_ this // function has completed (if it would be executed asynchronously. // NOTE: we rely on this synchronous behavior as per the comment in // mf::SessionMediator::create_surface ba::write(*socket, ba::buffer(whole_message.data(), whole_message.size())); for (auto const& fds : fd_set) mir::send_fds(socket_fd, fds); } void mfd::SocketMessenger::async_receive_msg( MirReadHandler const& handler, ba::mutable_buffers_1 const& buffer) { boost::asio::async_read( *socket, buffer, boost::asio::transfer_exactly(ba::buffer_size(buffer)), handler); } bs::error_code mfd::SocketMessenger::receive_msg( ba::mutable_buffers_1 const& buffer) { bs::error_code e; size_t nread = 0; while (nread < ba::buffer_size(buffer)) { nread += boost::asio::read( *socket, ba::mutable_buffers_1{buffer + nread}, e); if (e && e != ba::error::would_block) break; } return e; } void mfd::SocketMessenger::receive_fds(std::vector& fds) { static char buffer; mir::receive_data(socket_fd, &buffer, 1, fds); } size_t mfd::SocketMessenger::available_bytes() { // We call available_bytes() once the client is talking to us // so this is a pragmatic place to grab the session credentials if (session_creds.pid() == 0) update_session_creds(); boost::asio::socket_base::bytes_readable command{true}; socket->io_control(command); return command.get(); } void mfd::SocketMessenger::set_passcred(int opt) { if (setsockopt(socket_fd, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)) == -1) BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set SO_PASSCRED")); } void mfd::SocketMessenger::update_session_creds() { union { struct cmsghdr cmh; char control[CMSG_SPACE(sizeof(ucred))]; } control_un; control_un.cmh.cmsg_len = CMSG_LEN(sizeof(ucred)); control_un.cmh.cmsg_level = SOL_SOCKET; control_un.cmh.cmsg_type = SCM_CREDENTIALS; msghdr msgh; msgh.msg_name = nullptr; msgh.msg_namelen = 0; msgh.msg_iov = nullptr; msgh.msg_iovlen = 0; msgh.msg_control = control_un.control; msgh.msg_controllen = sizeof(control_un.control); /* We set the SO_PASSCRED socket option in order to receive credentials */ auto const so_passcred_option = raii::paired_calls( [this] { set_passcred(1); }, [this] { set_passcred(0); }); if (recvmsg(socket_fd, &msgh, MSG_PEEK) != -1) { auto const ucredp = reinterpret_cast(CMSG_DATA(CMSG_FIRSTHDR(&msgh))); session_creds = {ucredp->pid, ucredp->uid, ucredp->gid}; } } ./src/server/frontend/event_sender.h0000644000015600001650000000372312676616125017664 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #ifndef MIR_FRONTEND_EVENT_SENDER_H_ #define MIR_FRONTEND_EVENT_SENDER_H_ #include "mir/frontend/event_sink.h" #include "mir/frontend/fd_sets.h" #include namespace mir { namespace graphics { class PlatformIpcOperations; } namespace protobuf { class EventSequence; } namespace frontend { class MessageSender; namespace detail { class EventSender : public mir::frontend::EventSink { public: explicit EventSender( std::shared_ptr const& socket_sender, std::shared_ptr const& buffer_packer); void handle_event(MirEvent const& e) override; void handle_lifecycle_event(MirLifecycleState state) override; void handle_display_config_change(graphics::DisplayConfiguration const& config) override; void handle_input_device_change(std::vector> const& devices) override; void send_ping(int32_t serial) override; void send_buffer(frontend::BufferStreamId id, graphics::Buffer& buffer, graphics::BufferIpcMsgType) override; private: void send_event_sequence(protobuf::EventSequence&, FdSets const&); std::shared_ptr const sender; std::shared_ptr const buffer_packer; }; } } } #endif /* MIR_FRONTEND_EVENT_SENDER_H_ */ ./src/server/frontend/socket_connection.h0000644000015600001650000000365212676616125020713 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_DETAIL_SOCKET_CONNECTION_H_ #define MIR_FRONTEND_DETAIL_SOCKET_CONNECTION_H_ #include "mir/frontend/connections.h" #include #include namespace mir { namespace frontend { namespace detail { class MessageProcessor; class MessageReceiver; class SocketConnection { public: SocketConnection( std::shared_ptr const& message_receiver, int id_, std::shared_ptr> const& connections, std::shared_ptr const& processor); ~SocketConnection() noexcept; int id() const { return id_; } void read_next_message(); private: void on_response_sent(boost::system::error_code const& error, std::size_t); void on_new_message(const boost::system::error_code& ec); void on_read_size(const boost::system::error_code& ec); std::shared_ptr const message_receiver; int const id_; std::shared_ptr> const connections; std::shared_ptr processor; static size_t const header_size = 2; char header[header_size]; std::vector body; int client_pid = 0; }; } } } #endif /* MIR_FRONTEND_DETAIL_SOCKET_CONNECTION_H_ */ ./src/server/frontend/message_receiver.h0000644000015600001650000000340112676616125020504 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_FRONTEND_MESSAGE_RECEIVER_H_ #define MIR_FRONTEND_MESSAGE_RECEIVER_H_ #include #include #include namespace mir { namespace frontend { class SessionCredentials; namespace detail { class MessageReceiver { public: //receive message from the socket. 'handler' will be called when 'buffer' has been filled with exactly 'size' typedef std::function MirReadHandler; virtual void async_receive_msg(MirReadHandler const& handler, boost::asio::mutable_buffers_1 const& buffer) = 0; virtual boost::system::error_code receive_msg(boost::asio::mutable_buffers_1 const& buffer) = 0; virtual size_t available_bytes() = 0; virtual SessionCredentials client_creds() = 0; virtual void receive_fds(std::vector& fds) = 0; protected: MessageReceiver() = default; virtual ~MessageReceiver() = default; MessageReceiver(MessageReceiver const&) = delete; MessageReceiver& operator=(MessageReceiver const&) = delete; }; } } } #endif /* MIR_FRONTEND_MESSAGE_RECEIVER_H_ */ ./src/server/frontend/connection_context.cpp0000644000015600001650000000231412676616125021434 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "mir/frontend/connection_context.h" #include "mir/frontend/connector.h" namespace mf = mir::frontend; mf::ConnectionContext::ConnectionContext( std::function const& session)> const connect_handler, Connector const* connector) : connect_handler(connect_handler), connector(connector) { } int mf::ConnectionContext::fd_for_new_client(std::function const& session)> const& connect_handler) const { return connector->client_socket_fd(connect_handler); } ./src/server/frontend/protobuf_responder.h0000644000015600001650000000312312676616125021116 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_PROTOBUF_RESPONDER_H_ #define MIR_FRONTEND_PROTOBUF_RESPONDER_H_ #include "mir/frontend/protobuf_message_sender.h" #include "mir_protobuf_wire.pb.h" #include #include namespace mir { namespace frontend { class ResourceCache; class MessageSender; namespace detail { class ProtobufResponder : public ProtobufMessageSender { public: ProtobufResponder( std::shared_ptr const& sender, std::shared_ptr const& resource_cache); void send_response( ::google::protobuf::uint32 id, ::google::protobuf::MessageLite* response, FdSets const& fd_sets) override; private: std::shared_ptr const sender; std::shared_ptr const resource_cache; std::mutex result_guard; mir::protobuf::wire::Result send_response_result; }; } } } #endif /* PROTOBUF_MESSAGE_PROCESSOR_H_ */ ./src/server/frontend/published_socket_connector.h0000644000015600001650000000550012676616125022577 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Guest */ #ifndef MIR_FRONTEND_PROTOBUF_ASIO_COMMUNICATOR_H_ #define MIR_FRONTEND_PROTOBUF_ASIO_COMMUNICATOR_H_ #include "mir/frontend/connector.h" #include #include #include #include namespace google { namespace protobuf { class Message; } } namespace mir { class EmergencyCleanupRegistry; namespace frontend { class ConnectionCreator; class ConnectorReport; /// provides a client-side socket fd for each connection class BasicConnector : public Connector { public: explicit BasicConnector( std::shared_ptr const& connection_creator, std::shared_ptr const& report); ~BasicConnector() noexcept; void start() override; void stop() override; int client_socket_fd() const override; int client_socket_fd(std::function const& session)> const& connect_handler) const override; protected: void create_session_for( std::shared_ptr const& server_socket, std::function const& session)> const& connect_handler) const; boost::asio::io_service mutable io_service; boost::asio::io_service::work work; std::shared_ptr const report; private: std::thread io_service_thread; std::shared_ptr const connection_creator; }; /// Accept connections over a published socket class PublishedSocketConnector : public BasicConnector { public: explicit PublishedSocketConnector( const std::string& socket_file, std::shared_ptr const& connection_creator, EmergencyCleanupRegistry& emergency_cleanup_registry, std::shared_ptr const& report); ~PublishedSocketConnector() noexcept; private: void start_accept(); void on_new_connection(std::shared_ptr const& socket, boost::system::error_code const& ec); const std::string socket_file; boost::asio::local::stream_protocol::acceptor acceptor; }; } } #endif // MIR_FRONTEND_PROTOBUF_ASIO_COMMUNICATOR_H_ ./src/server/frontend/authorizing_display_changer.h0000644000015600001650000000410312676616157022760 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_FRONTEND_UNAUTHORIZED_DISPLAY_CHANGER_H_ #define MIR_FRONTEND_UNAUTHORIZED_DISPLAY_CHANGER_H_ #include "mir/frontend/display_changer.h" namespace mir { namespace frontend { /** * Adaptor to selectively permit display configuration calls. * * Wraps an authorization layer around an existing frontend::DisplayChanger. * * Authorization is set at construction time, and is then immutable. * * Authorisation for client-specific "session" display changes is * separate from authorization for system-wide default display changes. * Neither imply the other. */ class AuthorizingDisplayChanger : public frontend::DisplayChanger { public: AuthorizingDisplayChanger( std::shared_ptr const& changer, bool configuration_is_authorized, bool base_configuration_modification_is_authorized); std::shared_ptr base_configuration() override; void configure( std::shared_ptr const&, std::shared_ptr const&) override; void set_base_configuration( std::shared_ptr const&) override; private: std::shared_ptr const changer; bool const configure_display_is_allowed; bool const set_base_configuration_is_allowed; }; } } #endif /* MIR_FRONTEND_UNAUTHORIZED_DISPLAY_CHANGER_H_ */ ./src/server/frontend/default_configuration.cpp0000644000015600001650000001253512676616157022117 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/default_server_configuration.h" #include "mir/emergency_cleanup.h" #include "default_ipc_factory.h" #include "published_socket_connector.h" #include "mir/graphics/platform.h" #include "mir/graphics/platform_ipc_operations.h" #include "mir/frontend/protobuf_connection_creator.h" #include "mir/frontend/session_authorizer.h" #include "mir/options/configuration.h" #include "mir/options/option.h" namespace mf = mir::frontend; namespace mg = mir::graphics; namespace ms = mir::scene; std::shared_ptr mir::DefaultServerConfiguration::the_connection_creator() { return connection_creator([this] { auto const session_authorizer = the_session_authorizer(); return std::make_shared( new_ipc_factory(session_authorizer), session_authorizer, the_graphics_platform()->make_ipc_operations(), the_message_processor_report()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_connector() { return connector( [&,this]() -> std::shared_ptr { if (the_options()->is_set(options::no_server_socket_opt)) { return std::make_shared( the_connection_creator(), the_connector_report()); } else { auto const result = std::make_shared( the_socket_file(), the_connection_creator(), *the_emergency_cleanup(), the_connector_report()); if (the_options()->is_set(options::arw_server_socket_opt)) chmod(the_socket_file().c_str(), S_IRUSR|S_IWUSR| S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH); return result; } }); } std::shared_ptr mir::DefaultServerConfiguration::the_prompt_connection_creator() { struct PromptSessionAuthorizer : public mf::SessionAuthorizer { bool connection_is_allowed(mf::SessionCredentials const& /* creds */) override { return true; } bool configure_display_is_allowed(mf::SessionCredentials const& /* creds */) override { return true; } bool set_base_display_configuration_is_allowed(mf::SessionCredentials const& /* creds */) override { return true; } bool screencast_is_allowed(mf::SessionCredentials const& /* creds */) override { return true; } bool prompt_session_is_allowed(mf::SessionCredentials const& /* creds */) override { return true; } }; return prompt_connection_creator([this] { auto const session_authorizer = std::make_shared(); return std::make_shared( new_ipc_factory(session_authorizer), session_authorizer, the_graphics_platform()->make_ipc_operations(), the_message_processor_report()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_prompt_connector() { return prompt_connector( [&,this]() -> std::shared_ptr { if (the_options()->is_set(options::prompt_socket_opt)) { return std::make_shared( the_socket_file() + "_trusted", the_prompt_connection_creator(), *the_emergency_cleanup(), the_connector_report()); } else { return std::make_shared( the_prompt_connection_creator(), the_connector_report()); } }); } std::shared_ptr mir::DefaultServerConfiguration::new_ipc_factory( std::shared_ptr const& session_authorizer) { return std::make_shared( the_frontend_shell(), the_session_mediator_report(), the_graphics_platform()->make_ipc_operations(), the_frontend_display_changer(), the_buffer_allocator(), the_screencast(), session_authorizer, the_cursor_images(), the_coordinate_translator(), the_application_not_responding_detector(), the_cookie_authority(), the_input_device_hub()); } ./src/server/frontend/buffer_stream_tracker.cpp0000644000015600001650000000606312676616125022075 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "buffer_stream_tracker.h" #include "client_buffer_tracker.h" #include "mir/graphics/buffer.h" #include "mir/graphics/buffer_id.h" #include #include namespace mf = mir::frontend; namespace mg = mir::graphics; mf::BufferStreamTracker::BufferStreamTracker(size_t client_cache_size) : client_cache_size{client_cache_size} { } bool mf::BufferStreamTracker::track_buffer(BufferStreamId buffer_stream_id, mg::Buffer* buffer) { std::lock_guard lock{mutex}; auto& tracker = client_buffer_tracker[buffer_stream_id]; if (!tracker) tracker = std::make_shared(client_cache_size); for (auto it = client_buffer_tracker.begin(); it != client_buffer_tracker.end(); it++) { if (it->first == buffer_stream_id) continue; if (it->second->client_has(buffer->id())) BOOST_THROW_EXCEPTION(std::logic_error("buffer already associated with another surface")); } auto already_tracked = tracker->client_has(buffer->id()); tracker->add(buffer); client_buffer_resource[buffer_stream_id] = buffer; return already_tracked; } void mf::BufferStreamTracker::remove_buffer_stream(BufferStreamId buffer_stream_id) { std::lock_guard lock{mutex}; auto it = client_buffer_tracker.find(buffer_stream_id); if (it != client_buffer_tracker.end()) client_buffer_tracker.erase(it); auto last_buffer_it = client_buffer_resource.find(buffer_stream_id); if (last_buffer_it != client_buffer_resource.end()) client_buffer_resource.erase(last_buffer_it); } mg::Buffer* mf::BufferStreamTracker::last_buffer(BufferStreamId buffer_stream_id) const { std::lock_guard lock{mutex}; auto it = client_buffer_resource.find(buffer_stream_id); if (it != client_buffer_resource.end()) return it->second; else //should really throw, but that is difficult with the way the code currently works return nullptr; } mg::Buffer* mf::BufferStreamTracker::buffer_from(mg::BufferID buffer_id) const { std::lock_guard lock{mutex}; for (auto const& tracker : client_buffer_tracker) { auto buffer = tracker.second->buffer_from(buffer_id); if (buffer != nullptr) return buffer; } BOOST_THROW_EXCEPTION(std::logic_error("Buffer is not tracked")); } ./src/server/frontend/no_prompt_shell.h0000644000015600001650000000261112676616125020402 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_FRONTEND_NO_PROMPT_SHELL_H_ #define MIR_FRONTEND_NO_PROMPT_SHELL_H_ #include "shell_wrapper.h" namespace mir { namespace frontend { class NoPromptShell : public ShellWrapper { public: using ShellWrapper::ShellWrapper; std::shared_ptr start_prompt_session_for( std::shared_ptr const& session, scene::PromptSessionCreationParameters const& params) override; void add_prompt_provider_for( std::shared_ptr const& prompt_session, std::shared_ptr const& session) override; void stop_prompt_session( std::shared_ptr const& prompt_session) override; }; } } #endif /* MIR_FRONTEND_NO_PROMPT_SHELL_H_ */ ./src/server/frontend/shell_wrapper.h0000644000015600001650000000552412676616125020053 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_FRONTEND_SHELL_WRAPPER_H_ #define MIR_FRONTEND_SHELL_WRAPPER_H_ #include "mir/frontend/shell.h" namespace mir { namespace frontend { class ShellWrapper : public Shell { public: explicit ShellWrapper(std::shared_ptr const& wrapped) : wrapped(wrapped) {} virtual ~ShellWrapper() = default; std::shared_ptr open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) override; void close_session(std::shared_ptr const& session) override; std::shared_ptr start_prompt_session_for( std::shared_ptr const& session, scene::PromptSessionCreationParameters const& params) override; void add_prompt_provider_for( std::shared_ptr const& prompt_session, std::shared_ptr const& session) override; void stop_prompt_session( std::shared_ptr const& prompt_session) override; SurfaceId create_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) override; void modify_surface(std::shared_ptr const& session, SurfaceId surface, shell::SurfaceSpecification const& modifications) override; void destroy_surface(std::shared_ptr const& session, SurfaceId surface) override; std::string persistent_id_for(std::shared_ptr const& session, SurfaceId surface) override; std::shared_ptr surface_for_id(std::string const& serialised_id) override; int set_surface_attribute( std::shared_ptr const& session, SurfaceId surface_id, MirSurfaceAttrib attrib, int value) override; int get_surface_attribute( std::shared_ptr const& session, SurfaceId surface_id, MirSurfaceAttrib attrib) override; void raise_surface( std::shared_ptr const& session, SurfaceId surface_id, uint64_t timestamp) override; protected: std::shared_ptr const wrapped; }; } } #endif /* MIR_FRONTEND_SHELL_WRAPPER_H_ */ ./src/server/frontend/display_server.h0000644000015600001650000000202712676616125020232 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_FRONTEND_DETAIL_DISPLAY_SERVER_H_ #define MIR_FRONTEND_DETAIL_DISPLAY_SERVER_H_ #include namespace mir { namespace frontend { namespace detail { class DisplayServer : public mir::protobuf::DisplayServer { public: virtual void client_pid(int pid) = 0; }; } } } #endif /* MIR_FRONTEND_DETAIL_DISPLAY_SERVER_H_ */ ./src/server/frontend/protobuf_message_processor.cpp0000644000015600001650000003643412676616125023206 0ustar jenkinsjenkins/* * Copyright © 2012, 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "display_server.h" #include "protobuf_message_processor.h" #include "mir/cookie/authority.h" #include "mir/frontend/message_processor_report.h" #include "mir/frontend/protobuf_message_sender.h" #include "mir/frontend/template_protobuf_message_processor.h" #include "mir/frontend/unsupported_feature_exception.h" #include #include "mir_protobuf_wire.pb.h" namespace mfd = mir::frontend::detail; namespace { template std::vector extract_fds_from(Response* response) { std::vector fd; for (auto i = 0; i < response->fd().size(); ++i) fd.emplace_back(mir::Fd(dup(response->fd().data()[i]))); response->clear_fd(); response->set_fds_on_side_channel(fd.size()); return fd; } } mfd::ProtobufMessageProcessor::ProtobufMessageProcessor( std::shared_ptr const& sender, std::shared_ptr const& display_server, std::shared_ptr const& report) : sender(sender), display_server(display_server), report(report) { } namespace mir { namespace frontend { namespace detail { template<> struct result_ptr_t<::mir::protobuf::Buffer> { typedef ::mir::protobuf::Buffer* type; }; template<> struct result_ptr_t<::mir::protobuf::BufferStream> { typedef ::mir::protobuf::BufferStream* type; }; template<> struct result_ptr_t<::mir::protobuf::Connection> { typedef ::mir::protobuf::Connection* type; }; template<> struct result_ptr_t<::mir::protobuf::Surface> { typedef ::mir::protobuf::Surface* type; }; template<> struct result_ptr_t<::mir::protobuf::Screencast> { typedef ::mir::protobuf::Screencast* type; }; template<> struct result_ptr_t { typedef ::mir::protobuf::SocketFD* type; }; template<> struct result_ptr_t { typedef ::mir::protobuf::PlatformOperationMessage* type; }; //The exchange_buffer and next_buffer calls can complete on a different thread than the //one the invocation was called on. Make sure to preserve the result resource. template ParameterMessage parse_parameter(Invocation const& invocation) { ParameterMessage request; if (!request.ParseFromString(invocation.parameters())) BOOST_THROW_EXCEPTION(std::runtime_error("Failed to parse message parameters!")); return request; } class SelfDeletingCallback : public google::protobuf::Closure { public: SelfDeletingCallback(std::function const& callback) : callback(callback) { } void Run() override { struct Deleter { ~Deleter() { delete obj; } SelfDeletingCallback* obj; }; Deleter deleter{this}; callback(); } private: ~SelfDeletingCallback() = default; SelfDeletingCallback(SelfDeletingCallback&) = delete; void operator=(const SelfDeletingCallback&) = delete; std::function callback; }; template void invoke( std::shared_ptr const& mp, DisplayServer* server, void (mir::protobuf::DisplayServer::*function)( const RequestType* request, ResponseType* response, ::google::protobuf::Closure* done), unsigned int invocation_id, RequestType* request) { auto const result_message = std::make_shared(); std::weak_ptr weak_mp = mp; auto const response_callback = [weak_mp, invocation_id, result_message] { auto message_processor = weak_mp.lock(); if (message_processor) { message_processor->send_response(invocation_id, result_message); } }; auto callback = new SelfDeletingCallback{response_callback}; try { (server->*function)( request, result_message.get(), callback); } catch (mir::cookie::SecurityCheckError const& /*err*/) { throw; } catch (std::exception const& x) { using namespace std::literals; result_message->set_error("Error processing request: "s + x.what() + "\nInternal error details: " + boost::diagnostic_information(x)); callback->Run(); } } // A partial-specialisation to handle error cases. template void invoke( Self* self, std::string* error, void (ServerX::*/*function*/)( ParameterMessage const* request, ResultMessage* response, ::google::protobuf::Closure* done), Invocation const& invocation) { ResultMessage result_message; result_message.set_error(error->c_str()); self->send_response(invocation.id(), &result_message); } } } } const std::string& mfd::Invocation::method_name() const { return invocation.method_name(); } const std::string& mfd::Invocation::parameters() const { return invocation.parameters(); } google::protobuf::uint32 mfd::Invocation::id() const { return invocation.id(); } void mfd::ProtobufMessageProcessor::client_pid(int pid) { display_server->client_pid(pid); } bool mfd::ProtobufMessageProcessor::dispatch( Invocation const& invocation, std::vector const& side_channel_fds) { report->received_invocation(display_server.get(), invocation.id(), invocation.method_name()); bool result = true; try { // TODO comparing strings in an if-else chain isn't efficient. // It is probably possible to generate a Trie at compile time. if ("connect" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::connect, invocation); } else if ("create_surface" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::create_surface, invocation); } else if ("next_buffer" == invocation.method_name()) { auto request = parse_parameter(invocation); invoke(shared_from_this(), display_server.get(), &DisplayServer::next_buffer, invocation.id(), &request); } else if ("exchange_buffer" == invocation.method_name()) { auto request = parse_parameter(invocation); request.mutable_buffer()->clear_fd(); for (auto& fd : side_channel_fds) request.mutable_buffer()->add_fd(fd); invoke(shared_from_this(), display_server.get(), &DisplayServer::exchange_buffer, invocation.id(), &request); } else if ("submit_buffer" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::submit_buffer, invocation); } else if ("allocate_buffers" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::allocate_buffers, invocation); } else if ("release_buffers" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::release_buffers, invocation); } else if ("release_surface" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::release_surface, invocation); } else if ("platform_operation" == invocation.method_name()) { auto request = parse_parameter(invocation); request.clear_fd(); for (auto& fd : side_channel_fds) request.add_fd(fd); invoke(shared_from_this(), display_server.get(), &DisplayServer::platform_operation, invocation.id(), &request); } else if ("configure_display" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::configure_display, invocation); } else if ("set_base_display_configuration" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::set_base_display_configuration, invocation); } else if ("configure_surface" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::configure_surface, invocation); } else if ("modify_surface" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::modify_surface, invocation); } else if ("create_screencast" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::create_screencast, invocation); } else if ("screencast_buffer" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::screencast_buffer, invocation); } else if ("release_screencast" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::release_screencast, invocation); } else if ("create_buffer_stream" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::create_buffer_stream, invocation); } else if ("release_buffer_stream" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::release_buffer_stream, invocation); } else if ("configure_cursor" == invocation.method_name()) { invoke(this, display_server.get(), &protobuf::DisplayServer::configure_cursor, invocation); } else if ("new_fds_for_prompt_providers" == invocation.method_name()) { invoke(this, display_server.get(), &protobuf::DisplayServer::new_fds_for_prompt_providers, invocation); } else if ("start_prompt_session" == invocation.method_name()) { invoke(this, display_server.get(), &protobuf::DisplayServer::start_prompt_session, invocation); } else if ("stop_prompt_session" == invocation.method_name()) { invoke(this, display_server.get(), &protobuf::DisplayServer::stop_prompt_session, invocation); } else if ("disconnect" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::disconnect, invocation); result = false; } else if ("pong" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::pong, invocation); } else if ("configure_buffer_stream" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::configure_buffer_stream, invocation); } else if ("raise_surface" == invocation.method_name()) { invoke(this, display_server.get(), &DisplayServer::raise_surface, invocation); } else if ("translate_surface_to_screen" == invocation.method_name()) { try { auto debug_interface = dynamic_cast(display_server.get()); invoke(this, debug_interface, &mir::protobuf::DisplayServerDebug::translate_surface_to_screen, invocation); } catch (mir::frontend::unsupported_feature const&) { std::string message{"Server does not support the client debugging interface"}; invoke(this, &message, &mir::protobuf::DisplayServerDebug::translate_surface_to_screen, invocation); std::runtime_error err{"Client attempted to use unavailable debug interface"}; report->exception_handled(display_server.get(), invocation.id(), err); } } else if ("request_persistent_surface_id" == invocation.method_name()) { invoke(this, display_server.get(), &protobuf::DisplayServer::request_persistent_surface_id, invocation); } else { report->unknown_method(display_server.get(), invocation.id(), invocation.method_name()); result = false; } } catch (std::exception const& error) { report->exception_handled(display_server.get(), invocation.id(), error); result = false; } report->completed_invocation(display_server.get(), invocation.id(), result); return result; } void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, ::google::protobuf::MessageLite* response) { sender->send_response(id, response, {}); } void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, mir::protobuf::Buffer* response) { sender->send_response(id, response, {extract_fds_from(response)}); } void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, std::shared_ptr response) { send_response(id, response.get()); } void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, mir::protobuf::Connection* response) { if (response->has_platform()) sender->send_response(id, response, {extract_fds_from(response->mutable_platform())}); else sender->send_response(id, response, {}); } void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, mir::protobuf::Surface* response) { if (response->has_buffer_stream() && response->buffer_stream().has_buffer()) sender->send_response(id, response, {extract_fds_from(response), extract_fds_from(response->mutable_buffer_stream()->mutable_buffer())}); else sender->send_response(id, response, {extract_fds_from(response)}); } void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, mir::protobuf::BufferStream* response) { if (response->has_buffer()) sender->send_response(id, response, {extract_fds_from(response->mutable_buffer())}); else sender->send_response(id, response, {}); } void mfd::ProtobufMessageProcessor::send_response( ::google::protobuf::uint32 id, mir::protobuf::Screencast* response) { if (response->has_buffer_stream()) sender->send_response(id, response, {extract_fds_from(response->mutable_buffer_stream()->mutable_buffer())}); else sender->send_response(id, response, {}); } void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, mir::protobuf::SocketFD* response) { sender->send_response(id, response, {extract_fds_from(response)}); } void mfd::ProtobufMessageProcessor::send_response( ::google::protobuf::uint32 id, std::shared_ptr response) { sender->send_response(id, response.get(), {extract_fds_from(response.get())}); } ./src/server/frontend/protobuf_buffer_packer.cpp0000644000015600001650000001130212676616157022251 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "protobuf_buffer_packer.h" #include "mir/graphics/display_configuration.h" #include "mir_protobuf.pb.h" namespace mfd = mir::frontend::detail; namespace mg = mir::graphics; namespace mp = mir::protobuf; namespace { void pack_protobuf_display_card(mp::DisplayCard& protobuf_card, mg::DisplayConfigurationCard const& display_card) { protobuf_card.set_card_id(display_card.id.as_value()); protobuf_card.set_max_simultaneous_outputs(display_card.max_simultaneous_outputs); } void pack_protobuf_display_output(mp::DisplayOutput& protobuf_output, mg::DisplayConfigurationOutput const& display_output) { protobuf_output.set_output_id(display_output.id.as_value()); protobuf_output.set_card_id(display_output.card_id.as_value()); protobuf_output.set_type(static_cast(display_output.type)); for (auto const& pf : display_output.pixel_formats) { protobuf_output.add_pixel_format(static_cast(pf)); } for (auto const& mode : display_output.modes) { auto protobuf_output_mode = protobuf_output.add_mode(); protobuf_output_mode->set_horizontal_resolution(mode.size.width.as_uint32_t()); protobuf_output_mode->set_vertical_resolution(mode.size.height.as_uint32_t()); protobuf_output_mode->set_refresh_rate(mode.vrefresh_hz); } protobuf_output.set_preferred_mode(display_output.preferred_mode_index); protobuf_output.set_physical_width_mm(display_output.physical_size_mm.width.as_uint32_t()); protobuf_output.set_physical_height_mm(display_output.physical_size_mm.height.as_uint32_t()); protobuf_output.set_connected(display_output.connected); protobuf_output.set_used(display_output.used); protobuf_output.set_position_x(display_output.top_left.x.as_uint32_t()); protobuf_output.set_position_y(display_output.top_left.y.as_uint32_t()); protobuf_output.set_current_mode(display_output.current_mode_index); protobuf_output.set_current_format(static_cast(display_output.current_format)); protobuf_output.set_power_mode(static_cast(display_output.power_mode)); protobuf_output.set_orientation(display_output.orientation); protobuf_output.set_scale_factor(display_output.scale); protobuf_output.set_form_factor(display_output.form_factor); } } void mfd::pack_protobuf_display_configuration(mp::DisplayConfiguration& protobuf_config, mg::DisplayConfiguration const& display_config) { display_config.for_each_card( [&protobuf_config](mg::DisplayConfigurationCard const& card) { auto protobuf_card = protobuf_config.add_display_card(); pack_protobuf_display_card(*protobuf_card, card); }); display_config.for_each_output( [&protobuf_config](mg::DisplayConfigurationOutput const& output) { auto protobuf_output = protobuf_config.add_display_output(); pack_protobuf_display_output(*protobuf_output, output); }); } mfd::ProtobufBufferPacker::ProtobufBufferPacker(protobuf::Buffer* response) : fds_(response->fd().begin(), response->fd().end()), buffer_response(response) { } void mfd::ProtobufBufferPacker::pack_fd(Fd const& fd) { fds_.emplace_back(fd); buffer_response->add_fd(fd); } void mfd::ProtobufBufferPacker::pack_data(int data) { buffer_response->add_data(data); } void mfd::ProtobufBufferPacker::pack_stride(geometry::Stride stride) { buffer_response->set_stride(stride.as_uint32_t()); } void mfd::ProtobufBufferPacker::pack_flags(unsigned int flags) { buffer_response->set_flags(flags); } void mfd::ProtobufBufferPacker::pack_size(geometry::Size const& size) { buffer_response->set_width(size.width.as_int()); buffer_response->set_height(size.height.as_int()); } std::vector mfd::ProtobufBufferPacker::fds() { return fds_; } std::vector mfd::ProtobufBufferPacker::data() { return {buffer_response->data().begin(), buffer_response->data().end()}; } ./src/server/frontend/protobuf_connection_creator.cpp0000644000015600001650000000740612676616125023336 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/frontend/protobuf_connection_creator.h" #include "mir/frontend/session_credentials.h" #include "event_sender.h" #include "event_sink_factory.h" #include "protobuf_message_processor.h" #include "protobuf_responder.h" #include "socket_messenger.h" #include "socket_connection.h" #include "protobuf_ipc_factory.h" #include "mir/frontend/session_authorizer.h" namespace mf = mir::frontend; namespace mfd = mir::frontend::detail; namespace ba = boost::asio; mf::ProtobufConnectionCreator::ProtobufConnectionCreator( std::shared_ptr const& ipc_factory, std::shared_ptr const& session_authorizer, std::shared_ptr const& operations, std::shared_ptr const& report) : ipc_factory(ipc_factory), session_authorizer(session_authorizer), operations(operations), report(report), next_session_id(0), connections(std::make_shared>()) { } mf::ProtobufConnectionCreator::~ProtobufConnectionCreator() noexcept { connections->clear(); } int mf::ProtobufConnectionCreator::next_id() { return next_session_id.fetch_add(1); } namespace { class ProtobufEventFactory : public mf::EventSinkFactory { public: ProtobufEventFactory(std::shared_ptr const& operations) : ops{operations} { } std::unique_ptr create_sink(std::shared_ptr const& messenger) { return std::make_unique(messenger, ops); }; private: std::shared_ptr const ops; }; } void mf::ProtobufConnectionCreator::create_connection_for( std::shared_ptr const& socket, ConnectionContext const& connection_context) { auto const messenger = std::make_shared(socket); auto const creds = messenger->client_creds(); if (session_authorizer->connection_is_allowed(creds)) { auto const message_sender = std::make_shared( messenger, ipc_factory->resource_cache()); auto const msg_processor = create_processor( message_sender, ipc_factory->make_ipc_server( creds, std::make_shared(operations), messenger, connection_context), report); auto const& connection = std::make_shared(messenger, next_id(), connections, msg_processor); connections->add(connection); connection->read_next_message(); } } std::shared_ptr mf::ProtobufConnectionCreator::create_processor( std::shared_ptr const& sender, std::shared_ptr const& display_server, std::shared_ptr const& report) const { return std::make_shared( sender, display_server, report); } ./src/server/frontend/protobuf_message_processor.h0000644000015600001650000000531212676616125022642 0ustar jenkinsjenkins/* * Copyright © 2012. 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_PROTOBUF_MESSAGE_PROCESSOR_H_ #define MIR_FRONTEND_PROTOBUF_MESSAGE_PROCESSOR_H_ #include "mir/frontend/message_processor.h" #include "mir_protobuf.pb.h" #include #include namespace google { namespace protobuf { class MessageLite; } } namespace mir { namespace frontend { class MessageProcessorReport; namespace detail { class DisplayServer; class ProtobufMessageSender; class ProtobufMessageProcessor : public MessageProcessor, public std::enable_shared_from_this { public: ProtobufMessageProcessor( std::shared_ptr const& sender, std::shared_ptr const& display_server, std::shared_ptr const& report); ~ProtobufMessageProcessor() noexcept {} void client_pid(int pid) override; void send_response(google::protobuf::uint32 id, google::protobuf::MessageLite* response); void send_response(google::protobuf::uint32 id, protobuf::Buffer* response); void send_response(google::protobuf::uint32 id, protobuf::Connection* response); void send_response(google::protobuf::uint32 id, protobuf::Surface* response); void send_response(google::protobuf::uint32 id, std::shared_ptr response); void send_response(google::protobuf::uint32 id, mir::protobuf::Screencast* response); void send_response(google::protobuf::uint32 id, mir::protobuf::BufferStream* response); void send_response(google::protobuf::uint32 id, mir::protobuf::SocketFD* response); void send_response(google::protobuf::uint32 id, std::shared_ptr response); private: bool dispatch(Invocation const& invocation, std::vector const& side_channel_fds) override; std::shared_ptr const sender; std::shared_ptr const display_server; std::shared_ptr const report; }; } } } #endif /* PROTOBUF_MESSAGE_PROCESSOR_H_ */ ./src/server/frontend/socket_connection.cpp0000644000015600001650000001004512676616125021240 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "socket_connection.h" #include "message_sender.h" #include "message_receiver.h" #include "mir/frontend/message_processor.h" #include "mir/frontend/session_credentials.h" #include "mir/protobuf/protocol_version.h" #include "mir/log.h" #include "mir_protobuf_wire.pb.h" #include #include #include #include #include namespace ba = boost::asio; namespace bs = boost::system; namespace mfd = mir::frontend::detail; mfd::SocketConnection::SocketConnection( std::shared_ptr const& message_receiver, int id_, std::shared_ptr> const& connections, std::shared_ptr const& processor) : message_receiver(message_receiver), id_(id_), connections(connections), processor(processor) { } mfd::SocketConnection::~SocketConnection() noexcept { } void mfd::SocketConnection::read_next_message() { auto callback = std::bind(&mfd::SocketConnection::on_read_size, this, std::placeholders::_1); message_receiver->async_receive_msg(callback, ba::buffer(header, header_size)); } void mfd::SocketConnection::on_read_size(const boost::system::error_code& error) { if (error) { connections->remove(id()); BOOST_THROW_EXCEPTION(std::runtime_error(error.message())); } unsigned char const high_byte = header[0]; unsigned char const low_byte = header[1]; size_t const body_size = (high_byte << 8) + low_byte; body.resize(body_size); if (message_receiver->available_bytes() >= body_size) { on_new_message(message_receiver->receive_msg(ba::buffer(body))); } else { auto callback = std::bind(&mfd::SocketConnection::on_new_message, this, std::placeholders::_1); message_receiver->async_receive_msg(callback, ba::buffer(body)); } } void mfd::SocketConnection::on_new_message(const boost::system::error_code& error) try { if (error) { BOOST_THROW_EXCEPTION(std::runtime_error(error.message())); } mir::protobuf::wire::Invocation invocation; invocation.ParseFromArray(body.data(), body.size()); int const v = invocation.has_protocol_version() ? invocation.protocol_version() : -1; if (v < mir::protobuf::oldest_compatible_protocol_version() || v >= mir::protobuf::next_incompatible_protocol_version()) BOOST_THROW_EXCEPTION(std::runtime_error("Unsupported protocol version")); std::vector fds; if (invocation.side_channel_fds() > 0) { fds.resize(invocation.side_channel_fds()); message_receiver->receive_fds(fds); } if (!client_pid) { client_pid = message_receiver->client_creds().pid(); processor->client_pid(client_pid); } if (processor->dispatch(invocation, fds)) { read_next_message(); } else { connections->remove(id()); } } catch (std::exception& e) { connections->remove(id()); mir::log_warning("Rejected and disconnected a client (%s)", e.what()); throw; } void mfd::SocketConnection::on_response_sent(bs::error_code const& error, std::size_t) { if (error) { connections->remove(id()); BOOST_THROW_EXCEPTION(std::runtime_error(error.message())); } } ./src/server/frontend/protobuf_buffer_packer.h0000644000015600001650000000325112676616125021715 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_FRONTEND_PROTOBUF_BUFFER_PACKER_H_ #define MIR_FRONTEND_PROTOBUF_BUFFER_PACKER_H_ #include "mir/graphics/buffer_ipc_message.h" #include namespace mir { namespace protobuf { class Buffer; class DisplayConfiguration; } namespace graphics { class DisplayConfiguration; } namespace frontend { namespace detail { void pack_protobuf_display_configuration(protobuf::DisplayConfiguration& protobuf_config, graphics::DisplayConfiguration const& display_config); class ProtobufBufferPacker : public graphics::BufferIpcMessage { public: ProtobufBufferPacker(protobuf::Buffer*); void pack_fd(Fd const&); void pack_data(int); void pack_stride(geometry::Stride); void pack_flags(unsigned int); void pack_size(geometry::Size const& size); std::vector fds(); std::vector data(); private: std::vector fds_; protobuf::Buffer* buffer_response; }; } } } #endif /* MIR_FRONTEND_PROTOBUF_BUFFER_PACKER_H_ */ ./src/server/frontend/unauthorized_screencast.h0000644000015600001650000000245412676616125022136 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_FRONTEND_UNAUTHORIZED_SCREENCAST_H_ #define MIR_FRONTEND_UNAUTHORIZED_SCREENCAST_H_ #include "mir/frontend/screencast.h" namespace mir { namespace frontend { class UnauthorizedScreencast : public Screencast { public: ScreencastSessionId create_session( geometry::Rectangle const& region, geometry::Size const& size, MirPixelFormat pixel_format) override; void destroy_session(frontend::ScreencastSessionId id) override; std::shared_ptr capture(frontend::ScreencastSessionId id) override; }; } } #endif /* MIR_FRONTEND_UNAUTHORIZED_SCREENCAST_H_ */ ./src/server/frontend/authorizing_display_changer.cpp0000644000015600001650000000401112676616157023311 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "authorizing_display_changer.h" #include namespace mf = mir::frontend; namespace mg = mir::graphics; mf::AuthorizingDisplayChanger::AuthorizingDisplayChanger( std::shared_ptr const& changer, bool configuration_is_authorized, bool base_configuration_modification_is_authorized) : changer(changer), configure_display_is_allowed{configuration_is_authorized}, set_base_configuration_is_allowed{base_configuration_modification_is_authorized} { } std::shared_ptr mf::AuthorizingDisplayChanger::base_configuration() { return changer->base_configuration(); } void mf::AuthorizingDisplayChanger::configure( std::shared_ptr const& session, std::shared_ptr const& config) { if (configure_display_is_allowed) changer->configure(session, config); else BOOST_THROW_EXCEPTION(std::runtime_error("not authorized to apply display configurations")); } void mf::AuthorizingDisplayChanger::set_base_configuration( std::shared_ptr const& config) { if (set_base_configuration_is_allowed) changer->set_base_configuration(config); else BOOST_THROW_EXCEPTION(std::runtime_error("not authorized to set base display configurations")); } ./src/server/frontend/resource_cache.h0000644000015600001650000000412012676616125020145 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_RESOURCE_CACHE_H_ #define MIR_FRONTEND_RESOURCE_CACHE_H_ #include "mir/fd.h" #include #include #include namespace google { namespace protobuf { class MessageLite; } } namespace mir { namespace frontend { class MessageResourceCache { public: virtual void save_resource(google::protobuf::MessageLite* key, std::shared_ptr const& value) = 0; virtual void free_resource(google::protobuf::MessageLite* key) = 0; virtual void save_fd(google::protobuf::MessageLite* key, Fd const& fd) = 0; virtual ~MessageResourceCache() = default; MessageResourceCache() = default; MessageResourceCache(MessageResourceCache const&) = delete; MessageResourceCache& operator=(MessageResourceCache const&) = delete; }; // Used to save resources that must be retained until a call completes. class ResourceCache : public MessageResourceCache { public: void save_resource(google::protobuf::MessageLite* key, std::shared_ptr const& value); void save_fd(google::protobuf::MessageLite* key, Fd const& fd); void free_resource(google::protobuf::MessageLite* key); private: typedef std::map> Resources; typedef std::multimap FdResources; std::mutex guard; Resources resources; FdResources fd_resources; }; } } #endif /* MIR_FRONTEND_RESOURCE_CACHE_H_ */ ./src/server/frontend/no_prompt_shell.cpp0000644000015600001650000000306012676616125020734 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "no_prompt_shell.h" #include #include namespace mf = mir::frontend; namespace { char const* const prompt_sessions_disabled = "Prompt sessions disabled"; } std::shared_ptr mf::NoPromptShell::start_prompt_session_for( std::shared_ptr const& /*session*/, scene::PromptSessionCreationParameters const& /*params*/) { BOOST_THROW_EXCEPTION(std::runtime_error(prompt_sessions_disabled)); } void mf::NoPromptShell::add_prompt_provider_for( std::shared_ptr const& /*prompt_session*/, std::shared_ptr const& /*session*/) { BOOST_THROW_EXCEPTION(std::runtime_error(prompt_sessions_disabled)); } void mf::NoPromptShell::stop_prompt_session( std::shared_ptr const& /*prompt_session*/) { BOOST_THROW_EXCEPTION(std::runtime_error(prompt_sessions_disabled)); } ./src/server/frontend/published_socket_connector.cpp0000644000015600001650000001515712676616125023143 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Guest */ #include "published_socket_connector.h" #include "mir/frontend/protobuf_connection_creator.h" #include "mir/frontend/connection_context.h" #include "mir/frontend/connector_report.h" #include "mir/emergency_cleanup_registry.h" #include "mir/thread_name.h" #include #include #include #include #include #include namespace mf = mir::frontend; namespace mfd = mir::frontend::detail; namespace ba = boost::asio; namespace { bool socket_file_exists(std::string const& filename) { struct stat statbuf; bool exists = (0 == stat(filename.c_str(), &statbuf)); /* Avoid removing non-socket files */ bool is_socket_type = (statbuf.st_mode & S_IFMT) == S_IFSOCK; return exists && is_socket_type; } bool socket_exists(std::string const& socket_name) { try { std::string socket_path{socket_name}; /* In case an abstract socket name exists with the same name*/ socket_path.insert(std::begin(socket_path), ' '); /* If the name is contained in this table, it signifies * a process is truly using that socket connection */ std::ifstream socket_names_file("/proc/net/unix"); std::string line; while (std::getline(socket_names_file, line)) { auto index = line.find(socket_path); /* check for complete match */ if (index != std::string::npos && (index + socket_path.length()) == line.length()) { return true; } } } catch (...) { /* Assume the socket exists */ return true; } return false; } std::string remove_if_stale(std::string const& socket_name) { if (socket_file_exists(socket_name) && !socket_exists(socket_name)) { if (std::remove(socket_name.c_str()) != 0) { BOOST_THROW_EXCEPTION( boost::enable_error_info( std::runtime_error("Failed removing stale socket file")) << boost::errinfo_errno(errno)); } } return socket_name; } } mf::PublishedSocketConnector::PublishedSocketConnector( const std::string& socket_file, std::shared_ptr const& connection_creator, EmergencyCleanupRegistry& emergency_cleanup_registry, std::shared_ptr const& report) : BasicConnector(connection_creator, report), socket_file(remove_if_stale(socket_file)), acceptor(io_service, socket_file) { emergency_cleanup_registry.add( [socket_file] { std::remove(socket_file.c_str()); }); start_accept(); } mf::PublishedSocketConnector::~PublishedSocketConnector() noexcept { std::remove(socket_file.c_str()); } void mf::PublishedSocketConnector::start_accept() { report->listening_on(socket_file); auto socket = std::make_shared(io_service); acceptor.async_accept( *socket, [this,socket](boost::system::error_code const& ec) { on_new_connection(socket, ec); }); } void mf::PublishedSocketConnector::on_new_connection( std::shared_ptr const& socket, boost::system::error_code const& ec) { if (!ec) { create_session_for(socket, [](std::shared_ptr const&) {}); } start_accept(); } mf::BasicConnector::BasicConnector( std::shared_ptr const& connection_creator, std::shared_ptr const& report) : work(io_service), report(report), connection_creator{connection_creator} { } void mf::BasicConnector::start() { auto run_io_service = [this] { mir::set_thread_name("Mir/IPC"); while (true) try { report->thread_start(); io_service.run(); report->thread_end(); return; } catch (std::exception const& e) { report->error(e); } }; io_service_thread = std::thread(run_io_service); } void mf::BasicConnector::stop() { /* Stop processing new requests */ io_service.stop(); /* Wait for io processing thread to finish */ if (io_service_thread.joinable()) io_service_thread.join(); /* Prepare for a potential restart */ io_service.reset(); } void mf::BasicConnector::create_session_for( std::shared_ptr const& server_socket, std::function const& session)> const& connect_handler) const { report->creating_session_for(server_socket->native_handle()); /* We set the SO_PASSCRED socket option in order to receive credentials */ auto const optval = 1; if (setsockopt(server_socket->native_handle(), SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set SO_PASSCRED")); connection_creator->create_connection_for(server_socket, {connect_handler, this}); } int mf::BasicConnector::client_socket_fd() const { return client_socket_fd([](std::shared_ptr const&) {}); } int mf::BasicConnector::client_socket_fd(std::function const& session)> const& connect_handler) const { enum { server, client, size }; int socket_fd[size]; if (socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fd)) { BOOST_THROW_EXCEPTION( boost::enable_error_info( std::runtime_error("Could not create socket pair")) << boost::errinfo_errno(errno)); } auto const server_socket = std::make_shared( io_service, boost::asio::local::stream_protocol(), socket_fd[server]); report->creating_socket_pair(socket_fd[server], socket_fd[client]); create_session_for(server_socket, connect_handler); return socket_fd[client]; } mf::BasicConnector::~BasicConnector() noexcept { stop(); } ./src/server/frontend/event_sender.cpp0000644000015600001650000001105512676616125020214 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #include "mir/frontend/client_constants.h" #include "mir/graphics/display_configuration.h" #include "mir/variable_length_array.h" #include "mir/input/device.h" #include "event_sender.h" #include "mir/events/serialization.h" #include "message_sender.h" #include "protobuf_buffer_packer.h" #include "mir/graphics/buffer.h" #include "mir_protobuf_wire.pb.h" #include "mir_protobuf.pb.h" namespace mg = mir::graphics; namespace mfd = mir::frontend::detail; namespace mev = mir::events; namespace mp = mir::protobuf; mfd::EventSender::EventSender( std::shared_ptr const& socket_sender, std::shared_ptr const& buffer_packer) : sender(socket_sender), buffer_packer(buffer_packer) { } void mfd::EventSender::handle_event(MirEvent const& e) { // Limit the types of events we wish to send over protobuf, for now. if (mir_event_get_type(&e) != mir_event_type_input) { // In future we might send multiple events, or insert them into messages // containing other responses, but for now we send them individually. mp::EventSequence seq; mp::Event *ev = seq.add_event(); ev->set_raw(mev::serialize_event(e)); send_event_sequence(seq, {}); } } void mfd::EventSender::handle_display_config_change( graphics::DisplayConfiguration const& display_config) { mp::EventSequence seq; auto protobuf_config = seq.mutable_display_configuration(); mfd::pack_protobuf_display_configuration(*protobuf_config, display_config); send_event_sequence(seq, {}); } void mfd::EventSender::handle_lifecycle_event( MirLifecycleState state) { mp::EventSequence seq; auto protobuf_life_event = seq.mutable_lifecycle_event(); protobuf_life_event->set_new_state(state); send_event_sequence(seq, {}); } void mfd::EventSender::send_ping(int32_t serial) { mp::EventSequence seq; auto protobuf_ping_event = seq.mutable_ping_event(); protobuf_ping_event->set_serial(serial); send_event_sequence(seq, {}); } void mfd::EventSender::handle_input_device_change(std::vector> const& devices) { mp::EventSequence seq; for(const auto & dev : devices) { auto dev_info = seq.add_input_devices(); dev_info->set_name(dev->name()); dev_info->set_id(dev->id()); dev_info->set_unique_id(dev->unique_id()); dev_info->set_capabilities(dev->capabilities().value()); } send_event_sequence(seq, {}); } void mfd::EventSender::send_event_sequence(mp::EventSequence& seq, FdSets const& fds) { mir::VariableLengthArray send_buffer{static_cast(seq.ByteSize())}; seq.SerializeWithCachedSizesToArray(send_buffer.data()); mir::protobuf::wire::Result result; result.add_events(send_buffer.data(), send_buffer.size()); send_buffer.resize(result.ByteSize()); result.SerializeWithCachedSizesToArray(send_buffer.data()); try { sender->send(reinterpret_cast(send_buffer.data()), send_buffer.size(), fds); } catch (std::exception const& error) { // TODO: We should report this state. (void) error; } } void mfd::EventSender::send_buffer(frontend::BufferStreamId id, graphics::Buffer& buffer, mg::BufferIpcMsgType type) { mp::EventSequence seq; auto request = seq.mutable_buffer_request(); request->mutable_id()->set_value(id.as_value()); request->mutable_buffer()->set_buffer_id(buffer.id().as_value()); mfd::ProtobufBufferPacker request_msg{const_cast(request->mutable_buffer())}; buffer_packer->pack_buffer(request_msg, buffer, type); std::vector set; for(auto& fd : request->buffer().fd()) set.emplace_back(mir::Fd(IntOwnedFd{fd})); request->mutable_buffer()->set_fds_on_side_channel(set.size()); send_event_sequence(seq, {set}); } ./src/server/frontend/event_sink_factory.h0000644000015600001650000000242312676616125021073 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_FRONTEND_EVENT_SINK_FACTORY_H_ #define MIR_FRONTEND_EVENT_SINK_FACTORY_H_ #include namespace mir { namespace frontend { class EventSink; class MessageSender; class EventSinkFactory { public: EventSinkFactory() = default; virtual ~EventSinkFactory() = default; EventSinkFactory(EventSinkFactory const&) = delete; EventSinkFactory& operator=(EventSinkFactory const&) = delete; virtual std::unique_ptr create_sink(std::shared_ptr const& sender) = 0; }; } } #endif //MIR_FRONTEND_EVENT_SINK_FACTORY_H_ ./src/server/frontend/reordering_message_sender.h0000644000015600001650000000343212676616125022404 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_FRONTEND_REORDERING_MESSAGE_SENDER_H_ #define MIR_FRONTEND_REORDERING_MESSAGE_SENDER_H_ #include "message_sender.h" #include namespace mir { namespace frontend { /** * A MessageSender that buffers all messages until triggered, * then forwards all messages to an underlying MessageSender */ class ReorderingMessageSender : public MessageSender { public: explicit ReorderingMessageSender(std::shared_ptr const& sink); void send(char const* data, size_t length, FdSets const& fds) override; /** * Stop diverting messages into the buffer. * * All messages sent prior to uncork() will be sent to the underlying MessageSender, * and all subsequent messages will be sent directly to the underlying MessageSender. */ void uncork(); private: struct Message { std::vector data; FdSets fds; }; std::mutex message_lock; bool corked; std::vector buffered_messages; std::shared_ptr const sink; }; } } #endif //MIR_FRONTEND_REORDERING_MESSAGE_SENDER_H_ ./src/server/frontend/CMakeLists.txt0000644000015600001650000000161312676616157017573 0ustar jenkinsjenkinsset( FRONTEND_SOURCES client_buffer_tracker.cpp connection_context.cpp no_prompt_shell.cpp session_mediator.cpp shell_wrapper.cpp protobuf_message_processor.cpp protobuf_responder.cpp protobuf_buffer_packer.cpp published_socket_connector.cpp protobuf_connection_creator.cpp socket_connection.cpp resource_cache.cpp socket_messenger.cpp event_sender.cpp buffer_stream_tracker.cpp authorizing_display_changer.cpp unauthorized_screencast.cpp session_credentials.cpp default_configuration.cpp default_ipc_factory.cpp protobuf_ipc_factory.h display_server.h message_receiver.h message_sender.h reordering_message_sender.cpp reordering_message_sender.h event_sink_factory.h ) add_library( mirfrontend OBJECT ${FRONTEND_SOURCES} ) # Fix build race condition - ensure the protobuf header is built before us. add_dependencies(mirfrontend mirprotobuf) ./src/server/frontend/socket_messenger.h0000644000015600001650000000361712676616125020545 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_FRONTEND_SOCKET_MESSENGER_H_ #define MIR_FRONTEND_SOCKET_MESSENGER_H_ #include "message_sender.h" #include "message_receiver.h" #include "mir/frontend/session_credentials.h" #include namespace mir { namespace frontend { namespace detail { class SocketMessenger : public MessageSender, public MessageReceiver { public: SocketMessenger(std::shared_ptr const& socket); void send(char const* data, size_t length, FdSets const& fds) override; void async_receive_msg(MirReadHandler const& handler, boost::asio::mutable_buffers_1 const& buffer) override; boost::system::error_code receive_msg(boost::asio::mutable_buffers_1 const& buffer) override; size_t available_bytes() override; SessionCredentials client_creds() override; void receive_fds(std::vector& fds) override; private: void set_passcred(int opt); void update_session_creds(); SessionCredentials creator_creds() const; std::shared_ptr socket; mir::Fd socket_fd; std::mutex message_lock; SessionCredentials session_creds{0, 0, 0}; }; } } } #endif /* MIR_FRONTEND_SOCKET_MESSENGER_H_ */ ./src/server/frontend/protobuf_responder.cpp0000644000015600001650000000377212676616125021463 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "protobuf_responder.h" #include "resource_cache.h" #include "message_sender.h" #include "mir/frontend/client_constants.h" #include "mir/variable_length_array.h" #include "socket_messenger.h" namespace mfd = mir::frontend::detail; mfd::ProtobufResponder::ProtobufResponder( std::shared_ptr const& sender, std::shared_ptr const& resource_cache) : sender(sender), resource_cache(resource_cache) { } void mfd::ProtobufResponder::send_response( ::google::protobuf::uint32 id, google::protobuf::MessageLite* response, FdSets const& fd_sets) { mir::VariableLengthArray send_response_buffer{static_cast(response->ByteSize())}; response->SerializeWithCachedSizesToArray(send_response_buffer.data()); { std::lock_guard lock{result_guard}; send_response_result.set_id(id); send_response_result.set_response(send_response_buffer.data(), send_response_buffer.size()); send_response_buffer.resize(send_response_result.ByteSize()); send_response_result.SerializeWithCachedSizesToArray(send_response_buffer.data()); } sender->send(reinterpret_cast(send_response_buffer.data()), send_response_buffer.size(), fd_sets); resource_cache->free_resource(response); } ./src/server/frontend/default_ipc_factory.cpp0000644000015600001650000001173512676616157021553 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #include "default_ipc_factory.h" #include "no_prompt_shell.h" #include "session_mediator.h" #include "authorizing_display_changer.h" #include "unauthorized_screencast.h" #include "resource_cache.h" #include "mir/frontend/session_authorizer.h" #include "mir/frontend/event_sink.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/cookie/authority.h" namespace mf = mir::frontend; namespace mg = mir::graphics; namespace mi = mir::input; namespace ms = mir::scene; mf::DefaultIpcFactory::DefaultIpcFactory( std::shared_ptr const& shell, std::shared_ptr const& sm_report, std::shared_ptr const& platform_ipc_operations, std::shared_ptr const& display_changer, std::shared_ptr const& buffer_allocator, std::shared_ptr const& screencast, std::shared_ptr const& session_authorizer, std::shared_ptr const& cursor_images, std::shared_ptr const& translator, std::shared_ptr const& anr_detector, std::shared_ptr const& cookie_authority, std::shared_ptr const& hub) : shell(shell), no_prompt_shell(std::make_shared(shell)), sm_report(sm_report), cache(std::make_shared()), platform_ipc_operations(platform_ipc_operations), display_changer(display_changer), buffer_allocator(buffer_allocator), screencast(screencast), session_authorizer(session_authorizer), cursor_images(cursor_images), translator{translator}, anr_detector{anr_detector}, cookie_authority(cookie_authority), hub(hub) { } std::shared_ptr mf::DefaultIpcFactory::make_ipc_server( SessionCredentials const &creds, std::shared_ptr const& sink_factory, std::shared_ptr const& message_sender, ConnectionContext const &connection_context) { bool configuration_is_authorized = session_authorizer->configure_display_is_allowed(creds); bool base_configuration_is_authorized = session_authorizer->set_base_display_configuration_is_allowed(creds); auto const changer = std::make_shared( display_changer, configuration_is_authorized, base_configuration_is_authorized); std::shared_ptr effective_screencast; if (session_authorizer->screencast_is_allowed(creds)) { effective_screencast = screencast; } else { effective_screencast = std::make_shared(); } auto const allow_prompt_session = session_authorizer->prompt_session_is_allowed(creds); auto const effective_shell = allow_prompt_session ? shell : no_prompt_shell; return make_mediator( effective_shell, platform_ipc_operations, changer, buffer_allocator, sm_report, sink_factory, message_sender, effective_screencast, connection_context, cursor_images); } std::shared_ptr mf::DefaultIpcFactory::resource_cache() { return cache; } std::shared_ptr mf::DefaultIpcFactory::make_mediator( std::shared_ptr const& shell, std::shared_ptr const& platform_ipc_operations, std::shared_ptr const& changer, std::shared_ptr const& buffer_allocator, std::shared_ptr const& sm_report, std::shared_ptr const& sink_factory, std::shared_ptr const& message_sender, std::shared_ptr const& effective_screencast, ConnectionContext const& connection_context, std::shared_ptr const& cursor_images) { return std::make_shared( shell, platform_ipc_operations, changer, buffer_allocator->supported_pixel_formats(), sm_report, sink_factory, message_sender, resource_cache(), effective_screencast, connection_context, cursor_images, translator, anr_detector, cookie_authority, hub); } ./src/server/display_server.cpp0000644000015600001650000001717212676616125016755 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Alan Griffiths * Thomas Voss */ #include "mir/display_server.h" #include "mir/server_configuration.h" #include "mir/main_loop.h" #include "mir/server_status_listener.h" #include "mir/display_changer.h" #include "mir/compositor/compositor.h" #include "mir/frontend/connector.h" #include "mir/graphics/display.h" #include "mir/graphics/display_configuration.h" #include "mir/input/input_manager.h" #include "mir/input/input_dispatcher.h" #include "mir/log.h" #include "mir/unwind_helpers.h" #include namespace mc = mir::compositor; namespace mf = mir::frontend; namespace mg = mir::graphics; namespace mi = mir::input; namespace msh = mir::shell; struct mir::DisplayServer::Private { Private(ServerConfiguration& config) : emergency_cleanup{config.the_emergency_cleanup()}, graphics_platform{config.the_graphics_platform()}, display{config.the_display()}, input_dispatcher{config.the_input_dispatcher()}, compositor{config.the_compositor()}, connector{config.the_connector()}, prompt_connector{config.the_prompt_connector()}, input_manager{config.the_input_manager()}, main_loop{config.the_main_loop()}, server_status_listener{config.the_server_status_listener()}, display_changer{config.the_display_changer()} { display->register_configuration_change_handler( *main_loop, [this] { return configure_display(); }); display->register_pause_resume_handlers( *main_loop, [this] { return pause(); }, [this] { return resume(); }); } bool pause() { try { // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62258 // After using rethrow_exception() (and catching the exception), // all subsequent calls to uncaught_exception() return `true'. bool really_unwinding = true; auto comm = try_but_revert_if_unwinding( [this] { connector->stop(); }, [&, this] { if (really_unwinding) connector->start(); }); auto prompt = try_but_revert_if_unwinding( [this] { prompt_connector->stop(); }, [&, this] { if (really_unwinding) prompt_connector->start(); }); auto dispatcher = try_but_revert_if_unwinding( [this] { input_dispatcher->stop(); }, [&, this] { if (really_unwinding) input_dispatcher->start(); }); auto input = try_but_revert_if_unwinding( [this] { input_manager->stop(); }, [&, this] { if (really_unwinding) input_manager->start(); }); auto display_config_processing = try_but_revert_if_unwinding( [this] { display_changer->pause_display_config_processing(); }, [&, this] { if (really_unwinding) display_changer->resume_display_config_processing(); }); auto comp = try_but_revert_if_unwinding( [this] { compositor->stop(); }, [&, this] { if (really_unwinding) compositor->start(); }); display->pause(); really_unwinding = false; } catch(std::runtime_error const&) { return false; } server_status_listener->paused(); return true; } bool resume() { try { // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62258 // After using rethrow_exception() (and catching the exception), // all subsequent calls to uncaught_exception() return `true'. bool really_unwinding = true; auto disp = try_but_revert_if_unwinding( [this] { display->resume(); }, [&, this] { if (really_unwinding) display->pause(); }); auto comp = try_but_revert_if_unwinding( [this] { compositor->start(); }, [&, this] { if (really_unwinding) compositor->stop(); }); auto display_config_processing = try_but_revert_if_unwinding( [this] { display_changer->resume_display_config_processing(); }, [&, this] { if (really_unwinding) display_changer->pause_display_config_processing(); }); auto input = try_but_revert_if_unwinding( [this] { input_manager->start(); }, [&, this] { if (really_unwinding) input_manager->stop(); }); auto dispatcher = try_but_revert_if_unwinding( [this] { input_dispatcher->start(); }, [&, this] { if (really_unwinding) input_dispatcher->stop(); }); auto prompt = try_but_revert_if_unwinding( [this] { prompt_connector->start(); }, [&, this] { if (really_unwinding) prompt_connector->stop(); }); connector->start(); really_unwinding = false; } catch(std::runtime_error const&) { return false; } server_status_listener->resumed(); return true; } void configure_display() { std::shared_ptr conf = display->configuration(); display_changer->configure_for_hardware_change( conf, DisplayChanger::PauseResumeSystem); } std::shared_ptr const emergency_cleanup; // Hold this so it does not get freed prematurely std::shared_ptr const graphics_platform; // Hold this so the platform is loaded once std::shared_ptr const display; std::shared_ptr const input_dispatcher; std::shared_ptr const compositor; std::shared_ptr const connector; std::shared_ptr const prompt_connector; std::shared_ptr const input_manager; std::shared_ptr const main_loop; std::shared_ptr const server_status_listener; std::shared_ptr const display_changer; }; mir::DisplayServer::DisplayServer(ServerConfiguration& config) : p(new DisplayServer::Private{config}) { } /* * Need to define the destructor in the source file, so that we * can define the 'p' member variable as a unique_ptr to an * incomplete type (DisplayServerPrivate) in the header. */ mir::DisplayServer::~DisplayServer() { delete p.load(); } void mir::DisplayServer::run() { mir::log_info("Mir version " MIR_VERSION); auto const& server = *p.load(); server.compositor->start(); server.input_manager->start(); server.input_dispatcher->start(); server.prompt_connector->start(); server.connector->start(); server.server_status_listener->started(); server.main_loop->run(); server.connector->stop(); server.prompt_connector->stop(); server.input_dispatcher->stop(); server.input_manager->stop(); server.compositor->stop(); } void mir::DisplayServer::stop() { p.load()->main_loop->stop(); } ./src/server/input/0000755000015600001650000000000012676616160014344 5ustar jenkinsjenkins./src/server/input/validator.cpp0000644000015600001650000002402012676616125017034 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "mir/input/validator.h" #include "mir_toolkit/event.h" #include "mir/events/event_private.h" #include #include namespace mi = mir::input; namespace mev = mir::events; mi::Validator::Validator(std::function const& dispatch_valid_event) : dispatch_valid_event(dispatch_valid_event) { } // Currently we only validate touch events as they are the most problematic void mi::Validator::validate_and_dispatch(MirEvent const& event) { if (mir_event_get_type(&event) != mir_event_type_input) { dispatch_valid_event(event); return; } auto iev = mir_event_get_input_event(&event); if (mir_input_event_get_type(iev) != mir_input_event_type_touch) { dispatch_valid_event(event); return; } auto tev = mir_input_event_get_touch_event(iev); handle_touch_event(mir_input_event_get_device_id(iev), tev); return; } namespace { void delete_event(MirEvent *e) { mir_event_unref(e); } mir::EventUPtr make_event_uptr(MirEvent *e) { return mir::EventUPtr(e, delete_event); } mir::EventUPtr copy_event(MirTouchEvent const* ev) { MirEvent *ret = new MirEvent; memcpy(ret, ev, sizeof(MirEvent)); return make_event_uptr(ret); } // Return a copy of ev with existing touch actions converted to change. // Note this is always a valid successor of ev in the event stream. mir::EventUPtr convert_touch_actions_to_change(MirTouchEvent const* ev) { auto ret = copy_event(ev); for (size_t i = 0; i < ret->motion.pointer_count; i++) { ret->motion.pointer_coordinates[i].action = mir_touch_action_change; } return ret; } // Helper function to find index for a given touch ID // TODO: Existence of this probably suggests a problem with TouchEvent API... int index_for_id(MirTouchEvent const* touch_ev, MirTouchId id) { MirEvent const* ev = reinterpret_cast(touch_ev); for (size_t i = 0; i < ev->motion.pointer_count; i++) { if (ev->motion.pointer_coordinates[i].id == id) return i; } return -1; } // Return an event which is a valid successor of valid_ev but contains a mir_touch_action_down for missing_id mir::EventUPtr add_missing_down(MirEvent const* valid_ev, MirTouchEvent const* ev, MirTouchId missing_id) { auto valid_tev = reinterpret_cast(valid_ev); // So as not to repeat already occurred actions, we copy the last valid (Delivered) event and replace all the actions // with change, then we will add a missing down for touch "missing_id" auto ret = convert_touch_actions_to_change(valid_tev); auto index = index_for_id(ev, missing_id); // In the case where a touch ID goes up and then reappears without a down we don't want to // add a new touch but merely replace the action in the last event. if (auto existing_index = index_for_id((MirTouchEvent const*)ret.get(), missing_id) >= 0) { ret->motion.pointer_coordinates[existing_index].action = mir_touch_action_down; } else { mev::add_touch(*ret, missing_id, mir_touch_action_down, mir_touch_event_tooltype(ev, index), mir_touch_event_axis_value(ev, index, mir_touch_axis_x), mir_touch_event_axis_value(ev, index, mir_touch_axis_y), mir_touch_event_axis_value(ev, index, mir_touch_axis_pressure), mir_touch_event_axis_value(ev, index, mir_touch_axis_touch_major), mir_touch_event_axis_value(ev, index, mir_touch_axis_touch_minor), mir_touch_event_axis_value(ev, index, mir_touch_axis_size)); } return ret; } // We copy valid_ev but replace the touch action for missign_an_up_id with mir_touch_action_up mir::EventUPtr add_missing_up(MirEvent const* valid_ev, MirTouchId missing_an_up_id) { auto tev = reinterpret_cast(valid_ev); // Because we can only have one action per ev, we copy the last valid (Delivered) event and replace all the actions // with change, then we will change the action for the ID which should have gone up to an up. auto ret = convert_touch_actions_to_change(tev); auto index = index_for_id(tev, missing_an_up_id); ret->motion.pointer_coordinates[index].action = mir_touch_action_up; return ret; } // We copy ev but remove touch points which have been released producing a valid successor of // ev mir::EventUPtr remove_old_releases_from(MirEvent const* ev) { auto tev = reinterpret_cast(ev); auto ret = copy_event(tev); ret->motion.pointer_count = 0; for (size_t i = 0; i < mir_touch_event_point_count(tev); i++) { auto action = mir_touch_event_action(tev, i); if (action == mir_touch_action_up) continue; mev::add_touch(*ret, mir_touch_event_id(tev, i), mir_touch_event_action(tev, i), mir_touch_event_tooltype(tev, i), mir_touch_event_axis_value(tev, i, mir_touch_axis_x), mir_touch_event_axis_value(tev, i, mir_touch_axis_y), mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure), mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major), mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor), mir_touch_event_axis_value(tev, i, mir_touch_axis_size)); } return ret; } } typedef std::unordered_set TouchSet; // This is the core of the touch validator. Given a valid event 'last_ev' which was the last_event to be dispatched // this function must dispatch events such that 'ev' is a valid event to dispatch. // Our requirements for touch validity are simple: // 1. A touch point (unique per ID) can not vanish without being released. // 2. A touch point can not appear without coming down. // Our algorithm to ensure a candidate event can be dispatched is as follows: // // First we look at the last event to build a set of expected touch ID's. That is to say // each touch point in the last event which did not have touch_action_up (signifying its dissapearance) // is expected to be in the candidate event. Likewise we go through the candidate event can produce a set of events // we have found. // // We now check for expected events which were not found, e.g. touch points which are missing a release. // For each of these touch points we can take the coordinates for these points from the last event // and dispatch an event which releases the missing point. // // Now we check for found touch points which were not expected. If these show up with mir_touch_action_down // things are fine. On the other hand if they show up with mir_touch_action_change then a touch point // has appeared before its gone down and thus we must inject an event signifying this touch going down. void mi::Validator::ensure_stream_validity_locked(std::lock_guard const&, MirTouchEvent const* ev, MirTouchEvent const* last_ev) { TouchSet expected; for (size_t i = 0; i < mir_touch_event_point_count(last_ev); i++) { auto action = mir_touch_event_action(last_ev, i); if (action == mir_touch_action_up) continue; expected.insert(mir_touch_event_id(last_ev, i)); } TouchSet found; for (size_t i = 0; i < mir_touch_event_point_count(ev); i++) { auto id = mir_touch_event_id(ev, i); found.insert(id); } // Insert missing touch releases auto last_ev_copy = remove_old_releases_from(reinterpret_cast(last_ev)); for (auto const& expected_id : expected) { if (found.find(expected_id) == found.end()) { auto inject_ev = add_missing_up(last_ev_copy.get(), expected_id); dispatch_valid_event(*inject_ev); last_ev_copy = remove_old_releases_from(inject_ev.get()); } } for (size_t i = 0; i < mir_touch_event_point_count(ev); i++) { auto id = mir_touch_event_id(ev, i); if (expected.find(id) == expected.end() && mir_touch_event_action(ev, i) != mir_touch_action_down) { auto inject_ev = add_missing_down(last_ev_copy.get(), ev, id); dispatch_valid_event(*inject_ev); last_ev_copy = std::move(inject_ev); } } } void mi::Validator::handle_touch_event(MirInputDeviceId id, MirTouchEvent const* ev) { std::lock_guard lg(state_guard); auto it = last_event_by_device.find(id); MirTouchEvent const* last_ev = nullptr; auto default_ev = mev::make_event(id, std::chrono::high_resolution_clock::now().time_since_epoch(), std::vector{}, /* No need for a mac, since there's no pointer count for a default event */ mir_input_event_modifier_none); if (it == last_event_by_device.end()) { last_event_by_device.insert(std::make_pair(id, copy_event(ev))); last_ev = reinterpret_cast(default_ev.get()); } else { last_ev = mir_input_event_get_touch_event(mir_event_get_input_event(it->second.get())); } ensure_stream_validity_locked(lg, ev, last_ev); // Seems to be no better way to replace a non default constructible value type in an unordered_map // C++17 will give us insert_or_assign last_event_by_device.erase(id); last_event_by_device.insert(std::make_pair(id, copy_event(ev))); dispatch_valid_event(*reinterpret_cast(ev)); } ./src/server/input/vt_filter.cpp0000644000015600001650000000526212676616125017054 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Ancell */ #include "mir/input/vt_filter.h" #include "mir_toolkit/event.h" #include #include #include #include #include namespace { void set_active_vt(int vt) { auto console_fd = open("/dev/console", O_RDONLY | O_NDELAY); ioctl(console_fd, VT_ACTIVATE, vt); close(console_fd); } } bool mir::input::VTFilter::handle(MirEvent const& event) { if (mir_event_get_type(&event) != mir_event_type_input) return false; auto const input_event = mir_event_get_input_event(&event); if (mir_input_event_get_type(input_event) != mir_input_event_type_key) return false; auto const keyboard_event = mir_input_event_get_keyboard_event(input_event); auto const modifier_state = mir_keyboard_event_modifiers(keyboard_event); if (mir_keyboard_event_action(keyboard_event) == mir_keyboard_action_down && (modifier_state & mir_input_event_modifier_alt) && (modifier_state & mir_input_event_modifier_ctrl)) { switch (mir_keyboard_event_scan_code(keyboard_event)) { case KEY_F1: set_active_vt(1); return true; case KEY_F2: set_active_vt(2); return true; case KEY_F3: set_active_vt(3); return true; case KEY_F4: set_active_vt(4); return true; case KEY_F5: set_active_vt(5); return true; case KEY_F6: set_active_vt(6); return true; case KEY_F7: set_active_vt(7); return true; case KEY_F8: set_active_vt(8); return true; case KEY_F9: set_active_vt(9); return true; case KEY_F10: set_active_vt(10); return true; case KEY_F11: set_active_vt(11); return true; case KEY_F12: set_active_vt(12); return true; } } return false; } ./src/server/input/default-theme.h0000644000015600001650000026770412676616125017262 0ustar jenkinsjenkins#include namespace { struct CursorData { CursorData(char const* name, unsigned int hotspot_x, unsigned int hotspot_y, char const* pixel_data) : name(name), hotspot_x(hotspot_x), hotspot_y(hotspot_y), pixel_data(reinterpret_cast(pixel_data)) {} unsigned int const width{24}; unsigned int const height{24}; char const* const name; unsigned int const hotspot_x; unsigned int const hotspot_y; unsigned char const* const pixel_data; }; auto const cursor_data = { CursorData{"arrow", 7, 4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1" "\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\207\0\0\0\14\0\0\0\2\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\11\0\0\0\377" "\3\3\3\231\0\0\0\23\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\25\25\25\371\2\2\2\225\0\0\0\22\0\0\0\2" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0" "\377\334\334\334\377\21\21\21\372\1\1\1\213\0\0\0\20\0\0\0\2\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\376\376\376\377\326\326" "\326\377\16\16\16\372\1\1\1\201\0\0\0\17\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\373\373\373\377\377\377\377\377\320\320\320" "\377\14\14\14\372\0\0\0\170\0\0\0\16\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\13\0\0\0\377\367\367\367\377\376\376\376\377\377\377\377\377\311\311" "\311\377\12\12\12\372\0\0\0\160\0\0\0\15\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\13\0\0\0\377\364\364\364\377\373\373\373\377\377\377\377\377\377\377\377" "\377\303\303\303\377\12\12\12\372\0\0\0\150\0\0\0\14\0\0\0\1\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\13\0\0\0\377\360\360\360\377\370\370\370\377\377\377\377\377\377\377" "\377\377\377\377\377\377\273\273\273\377\11\11\11\371\0\0\0\140\0\0\0\13\0\0\0\1" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\355\355\355\377\364\364\364\377\373\373\373" "\377\377\377\377\377\377\377\377\377\377\377\377\377\264\264\264\377\11\11\11" "\370\0\0\0\131\0\0\0\13\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\351\351\351\377\361" "\361\361\377\370\370\370\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\252\252\252\377\11\11\11\367\0\0\0\121\0\0\0\11\0\0\0\1\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0" "\0\0\377\346\346\346\377\355\355\355\377\364\364\364\377\373\373\373\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\242\242\242\376\11\11" "\11\365\0\0\0\113\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\343\343\343\377\352\352\352\377" "\361\361\361\377\370\370\370\377\375\375\375\377\53\53\53\375\10\10\10\374\10\10" "\10\374\10\10\10\374\1\1\1\377\2\2\2\356\0\0\0\72\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\337\337" "\337\377\346\346\346\377\327\327\327\377\234\234\234\377\374\374\374\377\220\220" "\220\376\12\12\12\337\0\0\0\153\0\0\0\134\0\0\0\131\0\0\0\123\0\0\0\61\0\0\0\10" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\13\0\0\0\377\334\334\334\377\313\313\313\377\32\32\32\372\13\13\13\373\353\353" "\353\377\365\365\365\377\16\16\16\372\0\0\0\147\0\0\0\32\0\0\0\17\0\0\0\16\0\0\0" "\11\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\13\0\0\0\377\300\300\300\377\26\26\26\372\4\4\4\265\10\10\10\317\176" "\176\176\375\374\374\374\377\177\177\177\375\11\11\11\310\0\0\0\33\0\0\0\2\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\13\0\0\0\377\24\24\24\372\3\3\3\256\0\0\0\105\0\0\0\116\14\14" "\14\371\355\355\355\377\357\357\357\377\12\12\12\372\0\0\0\115\0\0\0\11\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\13\0\0\0\376\2\2\2\244\0\0\0\101\0\0\0\23\0\0\0\15\11\11\11\304" "\201\201\201\375\374\374\374\377\71\71\71\370\0\0\0\211\0\0\0\22\0\0\0\1\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\11\0\0\0\176\0\0\0\65\0\0\0\22\0\0\0\2\0\0\0\3\0\0\0\102\10\10\10\370\76" "\76\76\370\12\12\12\365\0\0\0\156\0\0\0\21\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0" "\0\13\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\10\0\0\0\114\0\0\0\220\0\0\0\145\0\0\0\50\0" "\0\0\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0" "\0\0\7\0\0\0\22\0\0\0\21\0\0\0\6\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"busy", 14, 14, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2" "\0\0\0\3\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1" "\0\0\0\75\6\6\6\212\22\22\22\311\14\14\14\361\14\14\14\362\22\22\22\317\7\7\7" "\222\0\0\0\117\0\0\0\10\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\20\13\13\13\251\45\45\45" "\362\217\217\217\376\303\303\303\377\362\362\362\377\365\365\365\377\312\312\312" "\377\225\225\225\376\60\60\60\363\15\15\15\302\0\0\0\46\0\0\0\5\0\0\0\1\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\67\16\16" "\16\352\174\174\174\370\365\365\365\377\373\373\373\377\376\376\376\377\377\377" "\377\377\377\377\377\377\377\377\377\377\376\376\376\377\373\373\373\377\220\220" "\220\374\17\17\17\360\1\1\1\130\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\16\16\16\16\350\303\303\303\377\365\365\365\377" "\371\371\371\377\377\377\377\377\375\375\375\377\323\323\323\377\323\323\323\377" "\375\375\375\377\377\377\377\377\377\377\377\377\374\374\374\377\326\326\326\377" "\21\21\21\360\0\0\0\62\0\0\0\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\1\12\12\12\244\171\171\171\370\364\364\364\377\267\267\267\377\220\220" "\220\377\343\343\343\377\353\353\353\377\112\112\112\377\107\107\107\377\352\352" "\352\377\375\375\375\377\373\373\373\377\377\377\377\377\373\373\373\377\221\221" "\221\374\15\15\15\312\0\0\0\30\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\65\41\41\41\360\357\357\357\377\364\364\364\377\221\221\221\377\71\71\71" "\377\162\162\162\377\327\327\327\377\132\132\132\377\127\127\127\377\351\351\351" "\377\340\340\340\377\340\340\340\377\374\374\374\377\375\375\375\377\370\370\370" "\377\62\62\62\365\0\0\0\154\0\0\0\15\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\2" "\2\172\171\171\171\374\363\363\363\377\370\370\370\377\335\335\335\377\164\164" "\164\377\122\122\122\377\320\320\320\377\346\346\346\377\343\343\343\377\364\364" "\364\377\326\326\326\377\340\340\340\377\374\374\374\377\376\376\376\377\371\371" "\371\377\231\231\231\377\11\11\11\261\0\0\0\31\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\1\20\20\20\273\254\254\254\377\363\363\363\377\362\362\362\377\357\357\357" "\377\335\335\335\377\320\320\320\377\367\367\367\377\377\377\377\377\377\377\377" "\377\376\376\376\377\365\365\365\377\366\366\366\377\373\373\373\377\377\377\377" "\377\371\371\371\377\311\311\311\377\22\22\22\337\0\0\0\46\0\0\0\7\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\2\16\16\16\352\334\334\334\377\352\352\352\377\254\254\254\377" "\216\216\216\377\245\245\245\377\361\361\361\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\372\372\372\377\336\336\336\377\333\333\333\377" "\365\365\365\377\370\370\370\377\360\360\360\377\13\13\13\367\0\0\0\63\0\0\0\14" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\16\16\16\352\333\333\333\377\351\351\351\377" "\257\257\257\377\232\232\232\377\260\260\260\377\361\361\361\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\372\372\372\377\336\336\336\377" "\333\333\333\377\363\363\363\377\367\367\367\377\356\356\356\377\14\14\14\367\0" "\0\0\75\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\17\17\17\275\251\251\251\377" "\357\357\357\377\354\354\354\377\353\353\353\377\360\360\360\377\365\365\365\377" "\376\376\376\377\377\377\377\377\377\377\377\377\376\376\376\377\366\366\366\377" "\367\367\367\377\372\372\372\377\371\371\371\377\364\364\364\377\306\306\306\377" "\21\21\21\340\0\0\0\71\0\0\0\16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\2\2\2\175\163" "\163\163\373\354\354\354\377\361\361\361\377\361\361\361\377\334\334\334\377\324" "\324\324\377\364\364\364\377\372\372\372\377\372\372\372\377\365\365\365\377\326" "\326\326\377\340\340\340\377\370\370\370\377\366\366\366\377\361\361\361\377\224" "\224\224\377\10\10\10\265\0\0\0\55\0\0\0\12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\73\36\36\36\362\345\345\345\377\356\356\356\377\352\352\352\377\320\320\320" "\377\326\326\326\377\361\361\361\377\332\332\332\377\335\335\335\377\363\363\363" "\377\333\333\333\377\332\332\332\377\363\363\363\377\362\362\362\377\355\355\355" "\377\55\55\55\366\0\0\0\176\0\0\0\40\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\11\11\11\11\253\155\155\155\371\352\352\352\377\354\354\354\377\350\350\350" "\377\355\355\355\377\361\361\361\377\322\322\322\377\324\324\324\377\363\363\363" "\377\362\362\362\377\360\360\360\377\361\361\361\377\356\356\356\377\205\205\205" "\374\14\14\14\316\0\0\0\102\0\0\0\24\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\2\0\0\0\35\15\15\15\352\263\263\263\377\351\351\351\377\354\354\354\377\356" "\356\356\377\357\357\357\377\347\347\347\377\350\350\350\377\361\361\361\377\360" "\360\360\377\357\357\357\377\354\354\354\377\304\304\304\377\17\17\17\362\0\0\0" "\132\0\0\0\46\0\0\0\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0" "\0\0\112\15\15\15\355\153\153\153\371\342\342\342\377\351\351\351\377\353\353" "\353\377\354\354\354\377\354\354\354\377\354\354\354\377\353\353\353\377\350\350" "\350\377\176\176\176\373\16\16\16\362\1\1\1\175\0\0\0\63\0\0\0\17\0\0\0\2\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\11\0\0\0\54\11\11\11" "\267\36\36\36\365\175\175\175\375\254\254\254\377\331\331\331\377\336\336\336" "\377\263\263\263\377\202\202\202\376\46\46\46\365\13\13\13\314\0\0\0\132\0\0\0" "\64\0\0\0\24\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\1\0\0\0\10\0\0\0\32\0\0\0\135\3\3\3\241\20\20\20\322\15\15\15\362\14" "\14\14\364\20\20\20\327\4\4\4\253\0\0\0\170\0\0\0\102\0\0\0\46\0\0\0\20\0\0\0\4" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\4\0\0\0\16\0\0\0\33\0\0\0\50\0\0\0\65\0\0\0\76\0\0\0\73\0\0\0\56" "\0\0\0\42\0\0\0\25\0\0\0\10\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\4\0\0" "\0\10\0\0\0\15\0\0\0\20\0\0\0\17\0\0\0\12\0\0\0\6\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"caret", 13, 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\6\0\0\0\5\0\0\0\6\0\0\0\6\0\0\0\4\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\11\11\11\245\0\0\0\377\0\0\0\377\5\5\5" "\366\0\0\0\377\0\0\0\377\13\13\13\314\0\0\0\13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\5\7\7\7\371\343\343\343\377\376\376\376\377\236\236\236\377\371\371" "\371\377\371\371\371\377\5\5\5\375\0\0\0\65\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\4\11\11\11\264\0\0\0\377\0\0\0\377\357\357\357\377\14\14\14\375\0\0\0" "\377\13\13\13\326\0\0\0\44\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\10\0\0\0\53\7\7\7\372\357\357\357\377\10\10\10\373\0\0\0\123\0\0\0\43\0\0\0\4\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\11\7\7\7\371\357" "\357\357\377\10\10\10\373\0\0\0\73\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\7\7\7\371\357\357\357\377\10\10\10\373\0\0\0" "\72\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\6\7\7\7\371\357\357\357\377\10\10\10\373\0\0\0\72\0\0\0\4\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\7\7\7\371\357\357\357\377" "\10\10\10\373\0\0\0\72\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\6\7\7\7\371\357\357\357\377\10\10\10\373\0\0\0\72\0\0\0\4\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\7\7\7\371" "\357\357\357\377\10\10\10\373\0\0\0\72\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\7\7\7\371\357\357\357\377\10\10\10\373\0" "\0\0\72\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\6\7\7\7\371\357\357\357\377\10\10\10\373\0\0\0\72\0\0\0\4\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\13\7\7\7\371\357\357\357" "\377\10\10\10\373\0\0\0\75\0\0\0\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\1\11\11\11\245\0\0\0\377\0\0\0\377\357\357\357\377\14\14\14\375\0\0\0" "\377\13\13\13\314\0\0\0\13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\7\7\7" "\371\343\343\343\377\377\377\377\377\303\303\303\377\374\374\374\377\371\371\371" "\377\5\5\5\375\0\0\0\65\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\11\11" "\11\264\0\0\0\377\0\0\0\377\6\6\6\371\0\0\0\377\0\0\0\377\13\13\13\326\0\0\0\44" "\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\11\0\0\0\53\0\0\0\57\0\0" "\0\53\0\0\0\56\0\0\0\57\0\0\0\43\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"default", 7, 4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1" "\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\207\0\0\0\14\0\0\0\2\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\11\0\0\0\377" "\3\3\3\231\0\0\0\23\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\25\25\25\371\2\2\2\225\0\0\0\22\0\0\0\2" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0" "\377\334\334\334\377\21\21\21\372\1\1\1\213\0\0\0\20\0\0\0\2\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\376\376\376\377\326\326" "\326\377\16\16\16\372\1\1\1\201\0\0\0\17\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\373\373\373\377\377\377\377\377\320\320\320" "\377\14\14\14\372\0\0\0\170\0\0\0\16\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\13\0\0\0\377\367\367\367\377\376\376\376\377\377\377\377\377\311\311" "\311\377\12\12\12\372\0\0\0\160\0\0\0\15\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\13\0\0\0\377\364\364\364\377\373\373\373\377\377\377\377\377\377\377\377" "\377\303\303\303\377\12\12\12\372\0\0\0\150\0\0\0\14\0\0\0\1\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\13\0\0\0\377\360\360\360\377\370\370\370\377\377\377\377\377\377\377" "\377\377\377\377\377\377\273\273\273\377\11\11\11\371\0\0\0\140\0\0\0\13\0\0\0\1" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\355\355\355\377\364\364\364\377\373\373\373" "\377\377\377\377\377\377\377\377\377\377\377\377\377\264\264\264\377\11\11\11" "\370\0\0\0\131\0\0\0\13\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\351\351\351\377\361" "\361\361\377\370\370\370\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\252\252\252\377\11\11\11\367\0\0\0\121\0\0\0\11\0\0\0\1\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0" "\0\0\377\346\346\346\377\355\355\355\377\364\364\364\377\373\373\373\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\242\242\242\376\11\11" "\11\365\0\0\0\113\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\343\343\343\377\352\352\352\377" "\361\361\361\377\370\370\370\377\375\375\375\377\53\53\53\375\10\10\10\374\10\10" "\10\374\10\10\10\374\1\1\1\377\2\2\2\356\0\0\0\72\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\0\0\0\377\337\337" "\337\377\346\346\346\377\327\327\327\377\234\234\234\377\374\374\374\377\220\220" "\220\376\12\12\12\337\0\0\0\153\0\0\0\134\0\0\0\131\0\0\0\123\0\0\0\61\0\0\0\10" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\13\0\0\0\377\334\334\334\377\313\313\313\377\32\32\32\372\13\13\13\373\353\353" "\353\377\365\365\365\377\16\16\16\372\0\0\0\147\0\0\0\32\0\0\0\17\0\0\0\16\0\0\0" "\11\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\13\0\0\0\377\300\300\300\377\26\26\26\372\4\4\4\265\10\10\10\317\176" "\176\176\375\374\374\374\377\177\177\177\375\11\11\11\310\0\0\0\33\0\0\0\2\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\13\0\0\0\377\24\24\24\372\3\3\3\256\0\0\0\105\0\0\0\116\14\14" "\14\371\355\355\355\377\357\357\357\377\12\12\12\372\0\0\0\115\0\0\0\11\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\13\0\0\0\376\2\2\2\244\0\0\0\101\0\0\0\23\0\0\0\15\11\11\11\304" "\201\201\201\375\374\374\374\377\71\71\71\370\0\0\0\211\0\0\0\22\0\0\0\1\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\11\0\0\0\176\0\0\0\65\0\0\0\22\0\0\0\2\0\0\0\3\0\0\0\102\10\10\10\370\76" "\76\76\370\12\12\12\365\0\0\0\156\0\0\0\21\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\16\0\0" "\0\13\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\10\0\0\0\114\0\0\0\220\0\0\0\145\0\0\0\50\0" "\0\0\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0" "\0\0\7\0\0\0\22\0\0\0\21\0\0\0\6\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"pointing-hand", 11, 5, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0\11\0\0\0\4\0\0\0" "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\7\7\7" "\206\7\7\7\370\6\6\6\371\3\3\3\173\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\23\7\7\7\371\342\342\342\377\313\313\313\377" "\12\12\12\363\0\0\0\34\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\1\0\0\0\37\10\10\10\372\377\377\377\377\347\347\347\377\11\11\11\366\0\0\0" "\46\0\0\0\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\40\10" "\10\10\372\377\377\377\377\347\347\347\377\11\11\11\366\0\0\0\53\0\0\0\15\0\0\0" "\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\40\10\10\10\372\377\377" "\377\377\347\347\347\377\2\2\2\376\13\13\13\362\12\12\12\353\0\0\0\103\0\0\0\11" "\0\0\0\6\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\40\10\10\10\372\377\377\377\377" "\350\350\350\377\114\114\114\377\345\345\345\377\215\215\215\377\7\7\7\364\12\12" "\12\363\12\12\12\360\0\0\0\145\0\0\0\26\0\0\0\7\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\46\10\10\10\372" "\377\377\377\377\347\347\347\377\232\232\232\377\377\377\377\377\345\345\345\377" "\46\46\46\377\341\341\341\377\304\304\304\377\11\11\11\374\5\5\5\374\13\13\13" "\344\0\0\0\114\0\0\0\6\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\54\10\10\10\357\3\3\3\374\13\13\13\374\377\377\377\377\347\347\347\377\331" "\331\331\377\377\377\377\377\377\377\377\377\165\165\165\377\377\377\377\377\377" "\377\377\377\164\164\164\377\377\377\377\377\247\247\247\377\3\3\3\375\0\0\0\53" "\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\7\7\7\240\174\174\174" "\376\324\324\324\377\16\16\16\376\377\377\377\377\355\355\355\377\373\373\373" "\377\377\377\377\377\377\377\377\377\305\305\305\377\377\377\377\377\377\377\377" "\377\301\301\301\377\377\377\377\377\377\377\377\377\10\10\10\373\0\0\0\74\0\0\0" "\12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\7\17\17\17\332\301\301\301\377" "\343\343\343\377\16\16\16\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\376\376\376\377\377\377\377\377\377\377\377\377" "\374\374\374\377\377\377\377\377\377\377\377\377\10\10\10\373\0\0\0\77\0\0\0\13" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\15\15\15\355\333\333\333\377" "\352\352\352\377\17\17\17\377\377\377\377\377\353\353\353\377\335\335\335\377" "\374\374\374\377\356\356\356\377\332\332\332\377\370\370\370\377\363\363\363\377" "\327\327\327\377\377\377\377\377\377\377\377\377\10\10\10\373\0\0\0\77\0\0\0\13" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\14\13\13\13\362\344\344\344\377" "\374\374\374\377\322\322\322\377\376\376\376\377\235\235\235\377\240\240\240\377" "\373\373\373\377\236\236\236\377\242\242\242\377\365\365\365\377\240\240\240\377" "\245\245\245\377\377\377\377\377\377\377\377\377\10\10\10\373\0\0\0\77\0\0\0\13" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\15\7\7\7\367\350\350\350\377\373" "\373\373\377\373\373\373\377\373\373\373\377\373\373\373\377\373\373\373\377\373" "\373\373\377\373\373\373\377\373\373\373\377\373\373\373\377\373\373\373\377\373" "\373\373\377\373\373\373\377\373\373\373\377\10\10\10\373\0\0\0\77\0\0\0\13\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\16\4\4\4\373\343\343\343\377\355\355" "\355\377\355\355\355\377\355\355\355\377\355\355\355\377\355\355\355\377\355\355" "\355\377\355\355\355\377\355\355\355\377\355\355\355\377\355\355\355\377\355\355" "\355\377\355\355\355\377\353\353\353\377\6\6\6\374\0\0\0\74\0\0\0\13\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\14\13\13\13\346\241\241\241\377\337\337\337" "\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337" "\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337" "\377\337\337\337\377\217\217\217\377\14\14\14\350\0\0\0\57\0\0\0\12\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\142\7\7\7\366\10\10\10\372\14\14\14" "\371\13\13\13\372\12\12\12\372\12\12\12\372\12\12\12\372\11\11\11\372\11\11\11" "\372\11\11\11\372\10\10\10\373\10\10\10\373\10\10\10\373\11\11\11\362\0\0\0\175" "\0\0\0\40\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\12\0\0\0" "\41\0\0\0\105\0\0\0\122\0\0\0\122\0\0\0\122\0\0\0\122\0\0\0\120\0\0\0\117\0\0\0" "\117\0\0\0\115\0\0\0\115\0\0\0\115\0\0\0\113\0\0\0\73\0\0\0\41\0\0\0\12\0\0\0\1" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\11\0\0\0" "\14\0\0\0\14\0\0\0\14\0\0\0\14\0\0\0\14\0\0\0\14\0\0\0\14\0\0\0\13\0\0\0\13\0\0" "\0\13\0\0\0\13\0\0\0\11\0\0\0\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"open-hand", 11, 5, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0\11\0\0\0\4\0\0\0" "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\7\7\7" "\206\7\7\7\370\6\6\6\371\3\3\3\173\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\23\7\7\7\371\342\342\342\377\313\313\313\377" "\12\12\12\363\0\0\0\34\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\1\0\0\0\37\10\10\10\372\377\377\377\377\347\347\347\377\11\11\11\366\0\0\0" "\46\0\0\0\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\40\10" "\10\10\372\377\377\377\377\347\347\347\377\11\11\11\366\0\0\0\53\0\0\0\15\0\0\0" "\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\40\10\10\10\372\377\377" "\377\377\347\347\347\377\2\2\2\376\13\13\13\362\12\12\12\353\0\0\0\103\0\0\0\11" "\0\0\0\6\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\40\10\10\10\372\377\377\377\377" "\350\350\350\377\114\114\114\377\345\345\345\377\215\215\215\377\7\7\7\364\12\12" "\12\363\12\12\12\360\0\0\0\145\0\0\0\26\0\0\0\7\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\46\10\10\10\372" "\377\377\377\377\347\347\347\377\232\232\232\377\377\377\377\377\345\345\345\377" "\46\46\46\377\341\341\341\377\304\304\304\377\11\11\11\374\5\5\5\374\13\13\13" "\344\0\0\0\114\0\0\0\6\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\54\10\10\10\357\3\3\3\374\13\13\13\374\377\377\377\377\347\347\347\377\331" "\331\331\377\377\377\377\377\377\377\377\377\165\165\165\377\377\377\377\377\377" "\377\377\377\164\164\164\377\377\377\377\377\247\247\247\377\3\3\3\375\0\0\0\53" "\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\7\7\7\240\174\174\174" "\376\324\324\324\377\16\16\16\376\377\377\377\377\355\355\355\377\373\373\373" "\377\377\377\377\377\377\377\377\377\305\305\305\377\377\377\377\377\377\377\377" "\377\301\301\301\377\377\377\377\377\377\377\377\377\10\10\10\373\0\0\0\74\0\0\0" "\12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\7\17\17\17\332\301\301\301\377" "\343\343\343\377\16\16\16\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\376\376\376\377\377\377\377\377\377\377\377\377" "\374\374\374\377\377\377\377\377\377\377\377\377\10\10\10\373\0\0\0\77\0\0\0\13" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\15\15\15\355\333\333\333\377" "\352\352\352\377\17\17\17\377\377\377\377\377\353\353\353\377\335\335\335\377" "\374\374\374\377\356\356\356\377\332\332\332\377\370\370\370\377\363\363\363\377" "\327\327\327\377\377\377\377\377\377\377\377\377\10\10\10\373\0\0\0\77\0\0\0\13" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\14\13\13\13\362\344\344\344\377" "\374\374\374\377\322\322\322\377\376\376\376\377\235\235\235\377\240\240\240\377" "\373\373\373\377\236\236\236\377\242\242\242\377\365\365\365\377\240\240\240\377" "\245\245\245\377\377\377\377\377\377\377\377\377\10\10\10\373\0\0\0\77\0\0\0\13" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\15\7\7\7\367\350\350\350\377\373" "\373\373\377\373\373\373\377\373\373\373\377\373\373\373\377\373\373\373\377\373" "\373\373\377\373\373\373\377\373\373\373\377\373\373\373\377\373\373\373\377\373" "\373\373\377\373\373\373\377\373\373\373\377\10\10\10\373\0\0\0\77\0\0\0\13\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\16\4\4\4\373\343\343\343\377\355\355" "\355\377\355\355\355\377\355\355\355\377\355\355\355\377\355\355\355\377\355\355" "\355\377\355\355\355\377\355\355\355\377\355\355\355\377\355\355\355\377\355\355" "\355\377\355\355\355\377\353\353\353\377\6\6\6\374\0\0\0\74\0\0\0\13\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\14\13\13\13\346\241\241\241\377\337\337\337" "\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337" "\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337" "\377\337\337\337\377\217\217\217\377\14\14\14\350\0\0\0\57\0\0\0\12\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\142\7\7\7\366\10\10\10\372\14\14\14" "\371\13\13\13\372\12\12\12\372\12\12\12\372\12\12\12\372\11\11\11\372\11\11\11" "\372\11\11\11\372\10\10\10\373\10\10\10\373\10\10\10\373\11\11\11\362\0\0\0\175" "\0\0\0\40\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\12\0\0\0" "\41\0\0\0\105\0\0\0\122\0\0\0\122\0\0\0\122\0\0\0\122\0\0\0\120\0\0\0\117\0\0\0" "\117\0\0\0\115\0\0\0\115\0\0\0\115\0\0\0\113\0\0\0\73\0\0\0\41\0\0\0\12\0\0\0\1" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\11\0\0\0" "\14\0\0\0\14\0\0\0\14\0\0\0\14\0\0\0\14\0\0\0\14\0\0\0\14\0\0\0\13\0\0\0\13\0\0" "\0\13\0\0\0\13\0\0\0\11\0\0\0\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"closed-hand", 15, 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\7\7\7\214\5\5\5\371\2\2\2\374\1\1\1\143\0\0\0" "\47\0\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\2\2\140\7\7\7\364\3\3\3" "\373\15\15\15\372\351\351\351\377\320\320\320\377\33\33\33\371\27\27\27\364\10" "\10\10\370\1\1\1\145\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\10\10\10\365\313\313\313" "\377\367\367\367\377\142\142\142\377\377\377\377\377\377\377\377\377\116\116\116" "\377\377\377\377\377\323\323\323\377\7\7\7\371\0\0\0\103\0\0\0\25\0\0\0\3\0\0\0" "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\15\0\0" "\0\43\10\10\10\371\377\377\377\377\377\377\377\377\255\255\255\377\377\377\377" "\377\377\377\377\377\245\245\245\377\377\377\377\377\377\377\377\377\35\35\35" "\375\24\24\24\367\11\11\11\361\0\0\0\120\0\0\0\15\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\31\3\3\3\371\10\10\10\371\14\14\14\375\377\377" "\377\377\377\377\377\377\361\361\361\377\377\377\377\377\377\377\377\377\363\363" "\363\377\377\377\377\377\377\377\377\377\312\312\312\377\377\377\377\377\254\254" "\254\377\5\5\5\370\0\0\0\101\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\3\3\3\3\210\132\132\132\370\357\357\357\377\17\17\17\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\367\367\367\377" "\3\3\3\375\0\0\0\115\0\0\0\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\13" "\13\13\354\312\312\312\377\357\357\357\377\17\17\17\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\367\367\367\377\3\3\3" "\375\0\0\0\116\0\0\0\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\3\3\3" "\374\367\367\367\377\366\366\366\377\170\170\170\377\377\377\377\377\325\325\325" "\377\375\375\375\377\363\363\363\377\326\326\326\377\373\373\373\377\370\370\370" "\377\332\332\332\377\367\367\367\377\373\373\373\377\367\367\367\377\3\3\3\375\0" "\0\0\116\0\0\0\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\3\3\3\374\367" "\367\367\377\377\377\377\377\377\377\377\377\377\377\377\377\243\243\243\377\260" "\260\260\377\352\352\352\377\240\240\240\377\256\256\256\377\361\361\361\377\236" "\236\236\377\251\251\251\377\366\366\366\377\367\367\367\377\3\3\3\375\0\0\0\116" "\0\0\0\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\3\3\3\374\361\361\361" "\377\372\372\372\377\372\372\372\377\372\372\372\377\372\372\372\377\365\365\365" "\377\372\372\372\377\372\372\372\377\365\365\365\377\372\372\372\377\372\372\372" "\377\366\366\366\377\372\372\372\377\362\362\362\377\3\3\3\375\0\0\0\116\0\0\0" "\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\15\15\15\334\261\261\261\377" "\361\361\361\377\361\361\361\377\361\361\361\377\361\361\361\377\361\361\361\377" "\361\361\361\377\361\361\361\377\361\361\361\377\361\361\361\377\361\361\361\377" "\361\361\361\377\361\361\361\377\340\340\340\377\6\6\6\372\0\0\0\116\0\0\0\22\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\1\1\1\174\67\67\67\371\330\330" "\330\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350" "\350\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350" "\350\377\350\350\350\377\214\214\214\377\16\16\16\341\0\0\0\113\0\0\0\21\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\31\7\7\7\265\7\7\7\372\21\21\21" "\370\20\20\20\370\20\20\20\370\20\20\20\370\17\17\17\371\16\16\16\371\16\16\16" "\371\16\16\16\371\15\15\15\371\15\15\15\371\15\15\15\371\11\11\11\363\0\0\0\200" "\0\0\0\65\0\0\0\13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0" "\17\0\0\0\105\0\0\0\151\0\0\0\157\0\0\0\157\0\0\0\157\0\0\0\156\0\0\0\154\0\0\0" "\154\0\0\0\154\0\0\0\153\0\0\0\152\0\0\0\152\0\0\0\134\0\0\0\73\0\0\0\22\0\0\0\2" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0" "\10\0\0\0\11\0\0\0\11\0\0\0\11\0\0\0\11\0\0\0\10\0\0\0\10\0\0\0\10\0\0\0\10\0\0" "\0\10\0\0\0\10\0\0\0\10\0\0\0\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"horizontal-resize", 13, 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\147\0\0\0\4\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\2\0\0\0\127\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\2\2\2\147\0\0\0\375" "\0\0\0\25\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\11\3\3\3\362\2\2\2\161\0\0\0\10" "\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\1\2\2\2\143\21\21\21\367\0\0\0\377\0\0\0\50\0\0\0\14\0\0\0\1\0\0" "\0\0\0\0\0\1\0\0\0\16\7\7\7\370\20\20\20\372\3\3\3\173\0\0\0\10\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\135\21\21" "\21\367\326\326\326\377\0\0\0\377\0\0\0\56\0\0\0\15\0\0\0\1\0\0\0\0\0\0\0\1\0\0" "\0\20\7\7\7\371\314\314\314\377\25\25\25\370\3\3\3\175\0\0\0\10\0\0\0\1\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\131\17\17\17\366\324\324" "\324\377\377\377\377\377\0\0\0\377\0\0\0\57\0\0\0\15\0\0\0\2\0\0\0\0\0\0\0\2\0\0" "\0\20\7\7\7\371\357\357\357\377\336\336\336\377\26\26\26\370\4\4\4\200\0\0\0\10" "\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\125\17\17\17\366\322\322" "\322\377\377\377\377\377\377\377\377\377\0\0\0\377\14\14\14\360\14\14\14\356\14" "\14\14\356\14\14\14\355\14\14\14\356\14\14\14\356\1\1\1\377\357\357\357\377\377" "\377\377\377\340\340\340\377\27\27\27\370\3\3\3\201\0\0\0\10\0\0\0\1\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\120\17\17\17\366\320\320\320\377\377\377\377\377\377\377\377" "\377\377\377\377\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337" "\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\375\375\375" "\377\377\377\377\377\377\377\377\377\341\341\341\377\31\31\31\370\4\4\4\204\0\0" "\0\10\0\0\0\1\0\0\0\1\0\0\0\60\4\4\4\373\302\302\302\377\377\377\377\377\377\377" "\377\377\377\377\377\377\365\365\365\377\364\364\364\377\364\364\364\377\364\364" "\364\377\364\364\364\377\364\364\364\377\364\364\364\377\364\364\364\377\364\364" "\364\377\365\365\365\377\377\377\377\377\377\377\377\377\377\377\377\377\334\334" "\334\377\4\4\4\376\0\0\0\145\0\0\0\6\0\0\0\2\0\0\0\17\7\7\7\262\62\62\62\367\353" "\353\353\377\373\373\373\377\376\376\376\377\326\326\326\377\327\327\327\377\327" "\327\327\377\327\327\327\377\327\327\327\377\327\327\327\377\327\327\327\377\327" "\327\327\377\327\327\327\377\326\326\326\377\376\376\376\377\376\376\376\377\357" "\357\357\377\102\102\102\371\12\12\12\321\0\0\0\67\0\0\0\17\0\0\0\1\0\0\0\10\0\0" "\0\43\10\10\10\274\64\64\64\367\336\336\336\377\355\355\355\377\345\345\345\377" "\31\31\31\373\23\23\23\370\23\23\23\370\23\23\23\370\23\23\23\370\23\23\23\370" "\23\23\23\370\31\31\31\373\327\327\327\377\367\367\367\377\342\342\342\377\100" "\100\100\371\11\11\11\322\0\0\0\112\0\0\0\41\0\0\0\10\0\0\0\0\0\0\0\1\0\0\0\12\0" "\0\0\46\10\10\10\276\66\66\66\367\331\331\331\377\344\344\344\377\0\0\0\377\0\0" "\0\157\0\0\0\151\0\0\0\147\0\0\0\147\0\0\0\147\0\0\0\151\6\6\6\373\321\321\321" "\377\331\331\331\377\77\77\77\371\11\11\11\320\0\0\0\113\0\0\0\44\0\0\0\12\0\0\0" "\1\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\13\0\0\0\46\10\10\10\277\71\71\71\370\354\354" "\354\377\0\0\0\377\0\0\0\72\0\0\0\40\0\0\0\27\0\0\0\26\0\0\0\27\0\0\0\42\7\7\7" "\371\325\325\325\377\75\75\75\370\11\11\11\317\0\0\0\112\0\0\0\43\0\0\0\12\0\0\0" "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0\50\10\10\10\302\73\73" "\73\367\0\0\0\377\0\0\0\57\0\0\0\16\0\0\0\2\0\0\0\1\0\0\0\2\0\0\0\21\7\7\7\371" "\63\63\63\371\11\11\11\315\0\0\0\111\0\0\0\43\0\0\0\12\0\0\0\1\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0\50\10\10\10\303\0\0\0\377" "\0\0\0\56\0\0\0\15\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\20\4\4\4\366\11\11\11\310\0\0\0" "\110\0\0\0\43\0\0\0\12\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\2\0\0\0\14\0\0\0\51\0\0\0\271\0\0\0\51\0\0\0\14\0\0\0\1\0\0" "\0\0\0\0\0\1\0\0\0\17\0\0\0\247\0\0\0\73\0\0\0\42\0\0\0\12\0\0\0\1\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0" "\14\0\0\0\34\0\0\0\27\0\0\0\11\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\12\0\0\0\31\0\0\0" "\27\0\0\0\11\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\4\0\0\0\5\0\0\0\2\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\2\0\0\0\5\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"vertical-resize", 13, 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\2\0\0\0\76\0\0\0\6\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\5\5\5\202\1\1\1\376\5\5\5\214\0\0\0\14\0\0\0" "\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\4\4\4\200\40" "\40\40\366\324\324\324\377\40\40\40\367\4\4\4\220\0\0\0\15\0\0\0\2\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\4\4\4\176\36\36\36\365\351\351\351\377\377" "\377\377\377\351\351\351\377\36\36\36\367\4\4\4\216\0\0\0\15\0\0\0\2\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\2\4\4\4\175\35\35\35\366\347\347\347\377\377\377\377\377\377" "\377\377\377\377\377\377\377\347\347\347\377\35\35\35\367\4\4\4\215\0\0\0\14\0\0" "\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\2\4\4\4\174\33\33\33\366\346\346\346\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\346\346\346\377\33" "\33\33\367\4\4\4\214\0\0\0\14\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\3\3\3\172\32\32\32\366\345\345\345\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\345\345\345\377\32\32\32\367\3\3\3\212\0\0\0\14" "\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\161\0\0\0\377\2\2\2\376\4\4\4\375\4\4\4\375\5\5\5\376\377\377\377\377\377\377" "\377\377\377\377\377\377\5\5\5\376\4\4\4\375\4\4\4\375\2\2\2\376\0\0\0\377\0\0\0" "\202\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0" "\0\0\23\0\0\0\60\0\0\0\104\0\0\0\112\0\0\0\115\0\0\0\377\377\377\377\377\377\377" "\377\377\377\377\377\377\0\0\0\377\0\0\0\133\0\0\0\116\0\0\0\112\0\0\0\105\0\0\0" "\61\0\0\0\16\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\4\0\0\0\14\0\0\0\20\0\0\0\21\0\0\0\32\0\0\0\377\377\377\377\377\377\377\377" "\377\377\377\377\377\0\0\0\377\0\0\0\76\0\0\0\33\0\0\0\21\0\0\0\20\0\0\0\14\0\0" "\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\1\0\0\0\1\0\0\0\13\0\0\0\377\377\377\377\377\377\377\377\377\377\377" "\377\377\0\0\0\377\0\0\0\66\0\0\0\14\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\12\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0" "\377\0\0\0\66\0\0\0\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\12" "\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\66\0" "\0\0\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\4\0\0\0\5\0\0\0\6\0\0\0\17\0\0\0\377\377" "\377\377\377\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\70\0\0\0\21\0\0\0\6" "\0\0\0\5\0\0\0\4\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\2\0\0\0\212\4\4\4\365\7\7\7\370\7\7\7\370\7\7\7\370\0\0\0\377\377\377\377" "\377\377\377\377\377\377\377\377\377\0\0\0\377\7\7\7\371\7\7\7\370\7\7\7\370\4\4" "\4\365\0\0\0\222\0\0\0\12\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\1\0\0\0\16\7\7\7\255\43\43\43\371\334\334\334\377\357\357\357\377\357" "\357\357\377\377\377\377\377\377\377\377\377\377\377\377\377\357\357\357\377\357" "\357\357\377\336\336\336\377\47\47\47\370\7\7\7\271\0\0\0\61\0\0\0\16\0\0\0\1\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\30\7\7\7" "\257\50\50\50\370\340\340\340\377\372\372\372\377\377\377\377\377\377\377\377" "\377\376\376\376\377\372\372\372\377\343\343\343\377\55\55\55\370\7\7\7\275\0\0" "\0\100\0\0\0\32\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\30\6\6\6\252\45\45\45\370\326\326\326\377\360\360" "\360\377\375\375\375\377\355\355\355\377\332\332\332\377\53\53\53\370\7\7\7\274" "\0\0\0\100\0\0\0\32\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\27\5\5\5\246\42\42\42\370\320" "\320\320\377\331\331\331\377\331\331\331\377\51\51\51\370\6\6\6\272\0\0\0\100\0" "\0\0\32\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\27\5\5\5\241\37\37\37\370" "\313\313\313\377\47\47\47\370\6\6\6\270\0\0\0\77\0\0\0\32\0\0\0\5\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\26\4\4\4\234\1\1\1\376\6\6\6\266\0\0\0" "\77\0\0\0\32\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\4\0\0\0\25\0\0\0\145\0\0\0\67\0\0\0\31\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\14\0\0\0\16\0\0\0\4\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"diagonal-resize-bottom-to-top", 22, 5, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2" "\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\10\10\10\236\7\7\7\367\7\7\7\367\7\7\7" "\367\7\7\7\367\7\7\7\367\7\7\7\367\7\7\7\367\7\7\7\367\7\7\7\367\7\7\7\367\7\7\7" "\367\7\7\7\367\7\7\7\367\7\7\7\367\7\7\7\367\13\13\13\306\0\0\0\14\0\0\0\4\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\3\3\3\374\337\337\337\377\357\357\357\377\357" "\357\357\377\357\357\357\377\357\357\357\377\357\357\357\377\357\357\357\377\357" "\357\357\377\357\357\357\377\357\357\357\377\357\357\357\377\357\357\357\377\357" "\357\357\377\357\357\357\377\357\357\357\377\3\3\3\375\0\0\0\65\0\0\0\17\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\3\3\3\374\362\362\362\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\4\4\4\375\0\0\0\106\0\0\0\26\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\12\12\12\304\6\6\6\374\10\10\10\373\10\10\10" "\373\10\10\10\373\10\10\10\373\10\10\10\373\10\10\10\373\10\10\10\373\10\10\10" "\373\10\10\10\373\10\10\10\373\10\10\10\373\14\14\14\375\370\370\370\377\377\377" "\377\377\4\4\4\375\0\0\0\110\0\0\0\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0" "\0\0\32\0\0\0\106\0\0\0\131\0\0\0\134\0\0\0\133\0\0\0\133\0\0\0\133\0\0\0\133\0" "\0\0\133\0\0\0\133\0\0\0\133\0\0\0\133\0\0\0\135\3\3\3\375\367\367\367\377\377" "\377\377\377\4\4\4\375\0\0\0\111\0\0\0\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\1\0\0\0\66\2\2\2\346\7\7\7\370\7\7\7\370\7\7\7\370\7\7\7\370\7\7\7\370\7\7\7" "\370\7\7\7\370\7\7\7\370\7\7\7\370\4\4\4\366\0\0\0\67\3\3\3\374\367\367\367\377" "\377\377\377\377\4\4\4\375\0\0\0\111\0\0\0\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\7\0\0\0\116\11\11\11\367\246\246\246\377\357\357\357\377\357\357" "\357\377\357\357\357\377\357\357\357\377\357\357\357\377\357\357\357\377\357\357" "\357\377\10\10\10\373\0\0\0\110\3\3\3\374\367\367\367\377\377\377\377\377\4\4\4" "\375\0\0\0\111\0\0\0\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\20" "\0\0\0\132\12\12\12\367\264\264\264\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\376\376\376\377\375\375\375\377\10\10\10\373\0\0\0\124" "\3\3\3\374\367\367\367\377\377\377\377\377\4\4\4\375\0\0\0\111\0\0\0\30\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\20\0\0\0\132\12\12\12\367" "\262\262\262\377\377\377\377\377\377\377\377\377\377\377\377\377\375\375\375\377" "\367\367\367\377\10\10\10\373\0\0\0\125\3\3\3\374\367\367\367\377\377\377\377" "\377\4\4\4\375\0\0\0\111\0\0\0\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\3\0\0\0\20\2\2\2\243\40\40\40\373\372\372\372\377\377\377\377" "\377\377\377\377\377\375\375\375\377\362\362\362\377\10\10\10\373\0\0\0\125\3\3" "\3\374\367\367\367\377\377\377\377\377\4\4\4\375\0\0\0\111\0\0\0\30\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\3\3\3\162\27\27\27\366\332" "\332\332\377\351\351\351\377\332\332\332\377\375\375\375\377\373\373\373\377\355" "\355\355\377\10\10\10\373\0\0\0\125\3\3\3\374\367\367\367\377\377\377\377\377\4" "\4\4\375\0\0\0\111\0\0\0\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\1\3\3\3\160\27\27\27\366\333\333\333\377\352\352\352\377\241\241\241\377\4\4" "\4\375\244\244\244\376\352\352\352\377\345\345\345\377\10\10\10\373\0\0\0\125\3" "\3\3\374\367\367\367\377\377\377\377\377\4\4\4\375\0\0\0\111\0\0\0\30\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\2\2\2\157\27\27\27\366\334\334\334\377" "\355\355\355\377\254\254\254\377\11\11\11\370\0\0\0\234\11\11\11\367\245\245\245" "\376\340\340\340\377\10\10\10\373\0\0\0\125\3\3\3\374\367\367\367\377\377\377" "\377\377\4\4\4\375\0\0\0\111\0\0\0\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2" "\2\2\157\26\26\26\365\335\335\335\377\357\357\357\377\270\270\270\377\13\13\13" "\371\0\0\0\203\0\0\0\104\0\0\0\143\11\11\11\366\261\261\261\377\10\10\10\373\0\0" "\0\125\3\3\3\374\367\367\367\377\377\377\377\377\4\4\4\375\0\0\0\111\0\0\0\30\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\55\1\1\1\376\261\261\261\377\351\351\351" "\377\277\277\277\377\15\15\15\372\0\0\0\216\0\0\0\100\0\0\0\36\0\0\0\24\0\0\0" "\126\12\12\12\366\2\2\2\376\0\0\0\125\3\3\3\374\364\364\364\377\375\375\375\377" "\3\3\3\375\0\0\0\110\0\0\0\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\10\1\1\1\166" "\15\15\15\371\243\243\243\377\22\22\22\371\1\1\1\231\0\0\0\103\0\0\0\36\0\0\0\7" "\0\0\0\3\0\0\0\17\0\0\0\131\2\2\2\366\0\0\0\120\13\13\13\312\7\7\7\373\7\7\7\373" "\13\13\13\334\0\0\0\100\0\0\0\25\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0" "\23\0\0\0\160\3\3\3\373\3\3\3\244\0\0\0\105\0\0\0\41\0\0\0\10\0\0\0\0\0\0\0\0\0" "\0\0\3\0\0\0\20\0\0\0\125\0\0\0\67\0\0\0\55\0\0\0\106\0\0\0\126\0\0\0\107\0\0\0" "\51\0\0\0\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\24\0\0\0\104" "\0\0\0\70\0\0\0\42\0\0\0\12\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\13\0\0" "\0\20\0\0\0\15\0\0\0\20\0\0\0\24\0\0\0\22\0\0\0\12\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\13\0\0\0\17\0\0\0\10\0\0\0\1\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0" }, CursorData{"diagonal-resize-top_to_bottom", 24, 24, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\25\0\0\0" "\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\116\4\4\4\367\0\0\0\140\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\67\0\0\0\4\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\1\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\142\15\15\15\367\230\230\230" "\377\13\13\13\370\0\0\0\137\0\0\0\11\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\63\2" "\2\2\361\0\0\0\25\12\12\12\262\0\0\0\377\0\0\0\377\13\13\13\303\0\0\0\15\0\0\0\4" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\70\0\0\0\377\301\301\301\377\377\377\377" "\377\277\277\277\377\11\11\11\370\0\0\0\122\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\61\13" "\13\13\361\0\0\0\377\0\0\0\63\3\3\3\374\363\363\363\377\374\374\374\377\3\3\3" "\375\0\0\0\64\0\0\0\20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\11\6\6\6\230\46\46" "\46\367\355\355\355\377\377\377\377\377\261\261\261\377\11\11\11\366\0\0\0\106\0" "\0\0\7\0\0\0\56\13\13\13\360\265\265\265\377\0\0\0\377\0\0\0\105\3\3\3\374\367" "\367\367\377\377\377\377\377\4\4\4\375\0\0\0\106\0\0\0\30\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\4\0\0\0\27\6\6\6\244\46\46\46\370\355\355\355\377\377\377\377" "\377\241\241\241\376\11\11\11\362\0\0\0\141\13\13\13\360\260\260\260\376\377\377" "\377\377\0\0\0\377\0\0\0\110\3\3\3\374\367\367\367\377\377\377\377\377\4\4\4\375" "\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\32\6\6" "\6\246\46\46\46\370\355\355\355\377\377\377\377\377\216\216\216\375\2\2\2\376" "\254\254\254\376\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\111\3\3\3\374" "\367\367\367\377\377\377\377\377\4\4\4\375\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\32\7\7\7\250\46\46\46\370\355\355" "\355\377\377\377\377\377\327\327\327\377\377\377\377\377\377\377\377\377\376\376" "\376\377\0\0\0\377\0\0\0\111\3\3\3\374\367\367\367\377\377\377\377\377\4\4\4\375" "\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\6\0\0\0\33\5\5\5\274\62\62\62\373\376\376\376\377\377\377\377\377\377\377\377" "\377\375\375\375\377\371\371\371\377\0\0\0\377\0\0\0\111\3\3\3\374\367\367\367" "\377\377\377\377\377\4\4\4\375\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\77\13\13\13\365\267\267\267\377\376\376" "\376\377\376\376\376\377\376\376\376\377\373\373\373\377\361\361\361\377\0\0\0" "\377\0\0\0\111\3\3\3\374\367\367\367\377\377\377\377\377\4\4\4\375\0\0\0\112\0\0" "\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\73\13\13\13" "\363\261\261\261\377\335\335\335\377\331\331\331\377\332\332\332\377\332\332\332" "\377\332\332\332\377\350\350\350\377\0\0\0\377\0\0\0\111\3\3\3\374\367\367\367" "\377\377\377\377\377\4\4\4\375\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\73\14\14\14\363\272\272\272\377\353\353\353\377\344\344" "\344\377\345\345\345\377\345\345\345\377\345\345\345\377\346\346\346\377\362\362" "\362\377\0\0\0\377\0\0\0\111\3\3\3\374\367\367\367\377\377\377\377\377\4\4\4\375" "\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\75\2\2\2\364\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\107\3\3\3\374\367\367\367\377\377\377\377\377\4\4\4\375" "\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\10\0\0\0\41\0" "\0\0\76\0\0\0\115\0\0\0\120\0\0\0\120\0\0\0\120\0\0\0\120\0\0\0\120\0\0\0\120\0" "\0\0\120\0\0\0\113\0\0\0\72\3\3\3\374\367\367\367\377\377\377\377\377\4\4\4\375" "\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\11\11\11\257\0\0\0" "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\367\367\367\377\377\377\377\377\4\4" "\4\375\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\3\3\3\374\356" "\356\356\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\4\4" "\4\375\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\3\3\3\374\356" "\356\356\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\376\377\4\4" "\4\375\0\0\0\112\0\0\0\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\11\11\11\267\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\12\12\12" "\344\0\0\0\100\0\0\0\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\24\0\0\0" "\70\0\0\0\113\0\0\0\116\0\0\0\115\0\0\0\115\0\0\0\115\0\0\0\115\0\0\0\115\0\0\0" "\115\0\0\0\115\0\0\0\115\0\0\0\115\0\0\0\115\0\0\0\115\0\0\0\115\0\0\0\105\0\0\0" "\52\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\16\0\0\0\24\0" "\0\0\24\0\0\0\24\0\0\0\24\0\0\0\24\0\0\0\24\0\0\0\24\0\0\0\24\0\0\0\24\0\0\0\24" "\0\0\0\24\0\0\0\24\0\0\0\24\0\0\0\24\0\0\0\22\0\0\0\12\0\0\0\4\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"omnidirectional-resize", 15, 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\7\7\7\214\5\5\5\371\2\2\2\374\1\1\1\143\0\0\0" "\47\0\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\2\2\140\7\7\7\364\3\3\3" "\373\15\15\15\372\351\351\351\377\320\320\320\377\33\33\33\371\27\27\27\364\10" "\10\10\370\1\1\1\145\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\10\10\10\365\313\313\313" "\377\367\367\367\377\142\142\142\377\377\377\377\377\377\377\377\377\116\116\116" "\377\377\377\377\377\323\323\323\377\7\7\7\371\0\0\0\103\0\0\0\25\0\0\0\3\0\0\0" "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\15\0\0" "\0\43\10\10\10\371\377\377\377\377\377\377\377\377\255\255\255\377\377\377\377" "\377\377\377\377\377\245\245\245\377\377\377\377\377\377\377\377\377\35\35\35" "\375\24\24\24\367\11\11\11\361\0\0\0\120\0\0\0\15\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\31\3\3\3\371\10\10\10\371\14\14\14\375\377\377" "\377\377\377\377\377\377\361\361\361\377\377\377\377\377\377\377\377\377\363\363" "\363\377\377\377\377\377\377\377\377\377\312\312\312\377\377\377\377\377\254\254" "\254\377\5\5\5\370\0\0\0\101\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\3\3\3\3\210\132\132\132\370\357\357\357\377\17\17\17\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\367\367\367\377" "\3\3\3\375\0\0\0\115\0\0\0\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\13" "\13\13\354\312\312\312\377\357\357\357\377\17\17\17\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\367\367\367\377\3\3\3" "\375\0\0\0\116\0\0\0\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\3\3\3" "\374\367\367\367\377\366\366\366\377\170\170\170\377\377\377\377\377\325\325\325" "\377\375\375\375\377\363\363\363\377\326\326\326\377\373\373\373\377\370\370\370" "\377\332\332\332\377\367\367\367\377\373\373\373\377\367\367\367\377\3\3\3\375\0" "\0\0\116\0\0\0\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\3\3\3\374\367" "\367\367\377\377\377\377\377\377\377\377\377\377\377\377\377\243\243\243\377\260" "\260\260\377\352\352\352\377\240\240\240\377\256\256\256\377\361\361\361\377\236" "\236\236\377\251\251\251\377\366\366\366\377\367\367\367\377\3\3\3\375\0\0\0\116" "\0\0\0\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\3\3\3\374\361\361\361" "\377\372\372\372\377\372\372\372\377\372\372\372\377\372\372\372\377\365\365\365" "\377\372\372\372\377\372\372\372\377\365\365\365\377\372\372\372\377\372\372\372" "\377\366\366\366\377\372\372\372\377\362\362\362\377\3\3\3\375\0\0\0\116\0\0\0" "\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\15\15\15\334\261\261\261\377" "\361\361\361\377\361\361\361\377\361\361\361\377\361\361\361\377\361\361\361\377" "\361\361\361\377\361\361\361\377\361\361\361\377\361\361\361\377\361\361\361\377" "\361\361\361\377\361\361\361\377\340\340\340\377\6\6\6\372\0\0\0\116\0\0\0\22\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\1\1\1\174\67\67\67\371\330\330" "\330\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350" "\350\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350" "\350\377\350\350\350\377\214\214\214\377\16\16\16\341\0\0\0\113\0\0\0\21\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\31\7\7\7\265\7\7\7\372\21\21\21" "\370\20\20\20\370\20\20\20\370\20\20\20\370\17\17\17\371\16\16\16\371\16\16\16" "\371\16\16\16\371\15\15\15\371\15\15\15\371\15\15\15\371\11\11\11\363\0\0\0\200" "\0\0\0\65\0\0\0\13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0" "\17\0\0\0\105\0\0\0\151\0\0\0\157\0\0\0\157\0\0\0\157\0\0\0\156\0\0\0\154\0\0\0" "\154\0\0\0\154\0\0\0\153\0\0\0\152\0\0\0\152\0\0\0\134\0\0\0\73\0\0\0\22\0\0\0\2" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0" "\10\0\0\0\11\0\0\0\11\0\0\0\11\0\0\0\11\0\0\0\10\0\0\0\10\0\0\0\10\0\0\0\10\0\0" "\0\10\0\0\0\10\0\0\0\10\0\0\0\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"vsplit-resize", 13, 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\2\0\0\0\76\0\0\0\6\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\5\5\5\202\1\1\1\376\5\5\5\214\0\0\0\14\0\0\0" "\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\4\4\4\200\40" "\40\40\366\324\324\324\377\40\40\40\367\4\4\4\220\0\0\0\15\0\0\0\2\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\4\4\4\176\36\36\36\365\351\351\351\377\377" "\377\377\377\351\351\351\377\36\36\36\367\4\4\4\216\0\0\0\15\0\0\0\2\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\2\4\4\4\175\35\35\35\366\347\347\347\377\377\377\377\377\377" "\377\377\377\377\377\377\377\347\347\347\377\35\35\35\367\4\4\4\215\0\0\0\14\0\0" "\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\2\4\4\4\174\33\33\33\366\346\346\346\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\346\346\346\377\33" "\33\33\367\4\4\4\214\0\0\0\14\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\3\3\3\172\32\32\32\366\345\345\345\377" "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\377\377\377\377\345\345\345\377\32\32\32\367\3\3\3\212\0\0\0\14" "\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\161\0\0\0\377\2\2\2\376\4\4\4\375\4\4\4\375\5\5\5\376\377\377\377\377\377\377" "\377\377\377\377\377\377\5\5\5\376\4\4\4\375\4\4\4\375\2\2\2\376\0\0\0\377\0\0\0" "\202\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0" "\0\0\23\0\0\0\60\0\0\0\104\0\0\0\112\0\0\0\115\0\0\0\377\377\377\377\377\377\377" "\377\377\377\377\377\377\0\0\0\377\0\0\0\133\0\0\0\116\0\0\0\112\0\0\0\105\0\0\0" "\61\0\0\0\16\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\4\0\0\0\14\0\0\0\20\0\0\0\21\0\0\0\32\0\0\0\377\377\377\377\377\377\377\377" "\377\377\377\377\377\0\0\0\377\0\0\0\76\0\0\0\33\0\0\0\21\0\0\0\20\0\0\0\14\0\0" "\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\1\0\0\0\1\0\0\0\13\0\0\0\377\377\377\377\377\377\377\377\377\377\377" "\377\377\0\0\0\377\0\0\0\66\0\0\0\14\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\12\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0" "\377\0\0\0\66\0\0\0\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\12" "\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\66\0" "\0\0\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\4\0\0\0\5\0\0\0\6\0\0\0\17\0\0\0\377\377" "\377\377\377\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\70\0\0\0\21\0\0\0\6" "\0\0\0\5\0\0\0\4\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\2\0\0\0\212\4\4\4\365\7\7\7\370\7\7\7\370\7\7\7\370\0\0\0\377\377\377\377" "\377\377\377\377\377\377\377\377\377\0\0\0\377\7\7\7\371\7\7\7\370\7\7\7\370\4\4" "\4\365\0\0\0\222\0\0\0\12\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\1\0\0\0\16\7\7\7\255\43\43\43\371\334\334\334\377\357\357\357\377\357" "\357\357\377\377\377\377\377\377\377\377\377\377\377\377\377\357\357\357\377\357" "\357\357\377\336\336\336\377\47\47\47\370\7\7\7\271\0\0\0\61\0\0\0\16\0\0\0\1\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\30\7\7\7" "\257\50\50\50\370\340\340\340\377\372\372\372\377\377\377\377\377\377\377\377" "\377\376\376\376\377\372\372\372\377\343\343\343\377\55\55\55\370\7\7\7\275\0\0" "\0\100\0\0\0\32\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\30\6\6\6\252\45\45\45\370\326\326\326\377\360\360" "\360\377\375\375\375\377\355\355\355\377\332\332\332\377\53\53\53\370\7\7\7\274" "\0\0\0\100\0\0\0\32\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\27\5\5\5\246\42\42\42\370\320" "\320\320\377\331\331\331\377\331\331\331\377\51\51\51\370\6\6\6\272\0\0\0\100\0" "\0\0\32\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\27\5\5\5\241\37\37\37\370" "\313\313\313\377\47\47\47\370\6\6\6\270\0\0\0\77\0\0\0\32\0\0\0\5\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\26\4\4\4\234\1\1\1\376\6\6\6\266\0\0\0" "\77\0\0\0\32\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\4\0\0\0\25\0\0\0\145\0\0\0\67\0\0\0\31\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\14\0\0\0\16\0\0\0\4\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"hsplit-resize", 13, 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\147\0\0\0\4\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\2\0\0\0\127\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\2\2\2\147\0\0\0\375" "\0\0\0\25\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\11\3\3\3\362\2\2\2\161\0\0\0\10" "\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\1\2\2\2\143\21\21\21\367\0\0\0\377\0\0\0\50\0\0\0\14\0\0\0\1\0\0" "\0\0\0\0\0\1\0\0\0\16\7\7\7\370\20\20\20\372\3\3\3\173\0\0\0\10\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\135\21\21" "\21\367\326\326\326\377\0\0\0\377\0\0\0\56\0\0\0\15\0\0\0\1\0\0\0\0\0\0\0\1\0\0" "\0\20\7\7\7\371\314\314\314\377\25\25\25\370\3\3\3\175\0\0\0\10\0\0\0\1\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\131\17\17\17\366\324\324" "\324\377\377\377\377\377\0\0\0\377\0\0\0\57\0\0\0\15\0\0\0\2\0\0\0\0\0\0\0\2\0\0" "\0\20\7\7\7\371\357\357\357\377\336\336\336\377\26\26\26\370\4\4\4\200\0\0\0\10" "\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\125\17\17\17\366\322\322" "\322\377\377\377\377\377\377\377\377\377\0\0\0\377\14\14\14\360\14\14\14\356\14" "\14\14\356\14\14\14\355\14\14\14\356\14\14\14\356\1\1\1\377\357\357\357\377\377" "\377\377\377\340\340\340\377\27\27\27\370\3\3\3\201\0\0\0\10\0\0\0\1\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\120\17\17\17\366\320\320\320\377\377\377\377\377\377\377\377" "\377\377\377\377\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337" "\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\375\375\375" "\377\377\377\377\377\377\377\377\377\341\341\341\377\31\31\31\370\4\4\4\204\0\0" "\0\10\0\0\0\1\0\0\0\1\0\0\0\60\4\4\4\373\302\302\302\377\377\377\377\377\377\377" "\377\377\377\377\377\377\365\365\365\377\364\364\364\377\364\364\364\377\364\364" "\364\377\364\364\364\377\364\364\364\377\364\364\364\377\364\364\364\377\364\364" "\364\377\365\365\365\377\377\377\377\377\377\377\377\377\377\377\377\377\334\334" "\334\377\4\4\4\376\0\0\0\145\0\0\0\6\0\0\0\2\0\0\0\17\7\7\7\262\62\62\62\367\353" "\353\353\377\373\373\373\377\376\376\376\377\326\326\326\377\327\327\327\377\327" "\327\327\377\327\327\327\377\327\327\327\377\327\327\327\377\327\327\327\377\327" "\327\327\377\327\327\327\377\326\326\326\377\376\376\376\377\376\376\376\377\357" "\357\357\377\102\102\102\371\12\12\12\321\0\0\0\67\0\0\0\17\0\0\0\1\0\0\0\10\0\0" "\0\43\10\10\10\274\64\64\64\367\336\336\336\377\355\355\355\377\345\345\345\377" "\31\31\31\373\23\23\23\370\23\23\23\370\23\23\23\370\23\23\23\370\23\23\23\370" "\23\23\23\370\31\31\31\373\327\327\327\377\367\367\367\377\342\342\342\377\100" "\100\100\371\11\11\11\322\0\0\0\112\0\0\0\41\0\0\0\10\0\0\0\0\0\0\0\1\0\0\0\12\0" "\0\0\46\10\10\10\276\66\66\66\367\331\331\331\377\344\344\344\377\0\0\0\377\0\0" "\0\157\0\0\0\151\0\0\0\147\0\0\0\147\0\0\0\147\0\0\0\151\6\6\6\373\321\321\321" "\377\331\331\331\377\77\77\77\371\11\11\11\320\0\0\0\113\0\0\0\44\0\0\0\12\0\0\0" "\1\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\13\0\0\0\46\10\10\10\277\71\71\71\370\354\354" "\354\377\0\0\0\377\0\0\0\72\0\0\0\40\0\0\0\27\0\0\0\26\0\0\0\27\0\0\0\42\7\7\7" "\371\325\325\325\377\75\75\75\370\11\11\11\317\0\0\0\112\0\0\0\43\0\0\0\12\0\0\0" "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0\50\10\10\10\302\73\73" "\73\367\0\0\0\377\0\0\0\57\0\0\0\16\0\0\0\2\0\0\0\1\0\0\0\2\0\0\0\21\7\7\7\371" "\63\63\63\371\11\11\11\315\0\0\0\111\0\0\0\43\0\0\0\12\0\0\0\1\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\13\0\0\0\50\10\10\10\303\0\0\0\377" "\0\0\0\56\0\0\0\15\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\20\4\4\4\366\11\11\11\310\0\0\0" "\110\0\0\0\43\0\0\0\12\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\2\0\0\0\14\0\0\0\51\0\0\0\271\0\0\0\51\0\0\0\14\0\0\0\1\0\0" "\0\0\0\0\0\1\0\0\0\17\0\0\0\247\0\0\0\73\0\0\0\42\0\0\0\12\0\0\0\1\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0" "\14\0\0\0\34\0\0\0\27\0\0\0\11\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\12\0\0\0\31\0\0\0" "\27\0\0\0\11\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\4\0\0\0\5\0\0\0\2\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\2\0\0\0\5\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, CursorData{"crosshair", 13, 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1" "\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\2\10\10\10\233\0\0\0\377\0\0\0\377\10\10\10\240\0\0\0\10" "\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0" "\377\363\363\363\377\363\363\363\377\0\0\0\377\0\0\0\35\0\0\0\6\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\14\0\0\0\377\377\377\377\377\377" "\377\377\377\0\0\0\377\0\0\0\56\0\0\0\14\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\1\0\0\0\16\0\0\0\377\377\377\377\377\377\377\377\377\0\0\0\377\0" "\0\0\63\0\0\0\16\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0" "\16\0\0\0\377\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\63\0\0\0\16\0\0\0" "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\16\0\0\0\377\377\377" "\377\377\377\377\377\377\0\0\0\377\0\0\0\63\0\0\0\16\0\0\0\1\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\16\0\0\0\377\363\363\363\377\363\363\363\377" "\0\0\0\377\0\0\0\63\0\0\0\16\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0" "\0\3\0\0\0\16\10\10\10\253\0\0\0\377\0\0\0\377\10\10\10\272\0\0\0\56\0\0\0\16\0" "\0\0\3\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\2" "\10\10\10\233\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\10\10" "\10\243\0\0\0\45\0\0\0\76\0\0\0\111\0\0\0\76\10\10\10\247\0\0\0\377\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\10\10\10\240\0\0\0\10\0\0\0\2\0\0\0\0\0\0" "\0\6\0\0\0\377\363\363\363\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\377\377\363\363\363\377\0\0\0\377\0\0\0\45\0\0\0\32\0\0\0\31\0\0\0\32\0" "\0\0\377\363\363\363\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\377\377\363\363\363\377\0\0\0\377\0\0\0\35\0\0\0\6\0\0\0\0\0\0\0\14\0\0\0\377" "\363\363\363\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" "\363\363\363\377\0\0\0\377\0\0\0\56\0\0\0\15\0\0\0\4\0\0\0\15\0\0\0\377\363\363" "\363\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\363\363" "\363\377\0\0\0\377\0\0\0\55\0\0\0\14\0\0\0\1\0\0\0\14\10\10\10\252\0\0\0\377\0\0" "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\10\10\10\272\0\0\0\56\0\0\0\15\0" "\0\0\4\0\0\0\15\10\10\10\253\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0\377\10\10\10\272\0\0\0\55\0\0\0\14\0\0\0\1\0\0\0\6\0\0\0\35\0\0\0\74\0\0\0" "\112\0\0\0\114\0\0\0\114\0\0\0\114\0\0\0\113\0\0\0\76\10\10\10\247\0\0\0\377\0\0" "\0\377\10\10\10\243\0\0\0\45\0\0\0\76\0\0\0\113\0\0\0\114\0\0\0\114\0\0\0\114\0" "\0\0\112\0\0\0\74\0\0\0\35\0\0\0\6\0\0\0\0\0\0\0\2\0\0\0\10\0\0\0\24\0\0\0\31\0" "\0\0\32\0\0\0\32\0\0\0\32\0\0\0\31\0\0\0\32\0\0\0\377\363\363\363\377\363\363" "\363\377\0\0\0\377\0\0\0\45\0\0\0\32\0\0\0\31\0\0\0\32\0\0\0\32\0\0\0\32\0\0\0" "\31\0\0\0\24\0\0\0\10\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\2\0" "\0\0\2\0\0\0\2\0\0\0\3\0\0\0\16\0\0\0\377\377\377\377\377\377\377\377\377\0\0\0" "\377\0\0\0\56\0\0\0\16\0\0\0\3\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\1\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0" "\0\0\16\0\0\0\377\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\63\0\0\0\16\0" "\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\16\0\0\0\377\377" "\377\377\377\377\377\377\377\0\0\0\377\0\0\0\63\0\0\0\16\0\0\0\1\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\16\0\0\0\377\377\377\377\377\377\377\377" "\377\0\0\0\377\0\0\0\63\0\0\0\16\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\1\0\0\0\16\0\0\0\377\363\363\363\377\363\363\363\377\0\0\0\377\0\0\0\63\0" "\0\0\16\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\14\10\10" "\10\253\0\0\0\377\0\0\0\377\10\10\10\272\0\0\0\56\0\0\0\14\0\0\0\1\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\35\0\0\0\74\0\0\0\110\0\0\0\74" "\0\0\0\35\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\2\0\0\0\10\0\0\0\24\0\0\0\30\0\0\0\24\0\0\0\10\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\1" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }, }; } ./src/server/input/default_device.h0000644000015600001650000000400612676616157017466 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_DEFAULT_DEVICE_H_ #define MIR_INPUT_DEFAULT_DEVICE_H_ #include "mir_toolkit/event.h" #include "mir/input/device.h" #include "mir/input/input_device_info.h" #include "mir/input/pointer_settings.h" #include "mir/input/touchpad_settings.h" #include "mir/optional_value.h" #include namespace mir { namespace dispatch { class ActionQueue; } namespace input { class InputDevice; class DefaultDevice : public Device { public: DefaultDevice(MirInputDeviceId id, std::shared_ptr const& actions, InputDevice& device); MirInputDeviceId id() const override; DeviceCapabilities capabilities() const override; std::string name() const override; std::string unique_id() const override; optional_value pointer_configuration() const override; void apply_pointer_configuration(PointerConfiguration const&) override; optional_value touchpad_configuration() const override; void apply_touchpad_configuration(TouchpadConfiguration const&) override; private: MirInputDeviceId const device_id; InputDevice& device; InputDeviceInfo const info; optional_value pointer; optional_value touchpad; std::shared_ptr const actions; }; } } #endif ./src/server/input/cursor_controller.cpp0000644000015600001650000001707612676616125020644 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "cursor_controller.h" #include "mir/input/scene.h" #include "mir/input/surface.h" #include "mir/graphics/cursor.h" #include "mir/graphics/cursor_image.h" #include "mir/scene/observer.h" #include "mir/scene/null_surface_observer.h" #include "mir/scene/surface.h" #include #include namespace mi = mir::input; namespace mg = mir::graphics; namespace ms = mir::scene; namespace geom = mir::geometry; namespace { struct UpdateCursorOnSurfaceChanges : ms::NullSurfaceObserver { UpdateCursorOnSurfaceChanges(mi::CursorController* cursor_controller) : cursor_controller(cursor_controller) { } void attrib_changed(MirSurfaceAttrib, int) override { // Attribute changing alone wont trigger a cursor update } void resized_to(geom::Size const&) override { cursor_controller->update_cursor_image(); } void moved_to(geom::Point const&) override { cursor_controller->update_cursor_image(); } void hidden_set_to(bool) override { cursor_controller->update_cursor_image(); } void frame_posted(int, geom::Size const&) override { // The first frame posted will trigger a cursor update, since it // changes the visibility status of the surface, and can thus affect // the cursor. if (!first_frame_posted) { first_frame_posted = true; cursor_controller->update_cursor_image(); } } void alpha_set_to(float) override { cursor_controller->update_cursor_image(); } void transformation_set_to(glm::mat4 const&) override { cursor_controller->update_cursor_image(); } void reception_mode_set_to(mi::InputReceptionMode) override { cursor_controller->update_cursor_image(); } void cursor_image_set_to(mg::CursorImage const&) override { cursor_controller->update_cursor_image(); } void cursor_image_removed() override { cursor_controller->update_cursor_image(); } void orientation_set_to(MirOrientation /* orientation */) override { // No need to update cursor for orientation property change alone. } void client_surface_close_requested() override { // No need to update cursor for client close requests } mi::CursorController* const cursor_controller; bool first_frame_posted = false; }; struct UpdateCursorOnSceneChanges : ms::Observer { UpdateCursorOnSceneChanges(mi::CursorController* cursor_controller) : cursor_controller(cursor_controller) { } void add_surface_observer(ms::Surface* surface) { auto const observer = std::make_shared(cursor_controller); surface->add_observer(observer); { std::unique_lock lg(surface_observers_guard); surface_observers[surface] = observer; } } void surface_added(ms::Surface *surface) { add_surface_observer(surface); cursor_controller->update_cursor_image(); } void surface_removed(ms::Surface *surface) { { std::unique_lock lg(surface_observers_guard); auto it = surface_observers.find(surface); if (it != surface_observers.end()) { surface->remove_observer(it->second); surface_observers.erase(it); } } cursor_controller->update_cursor_image(); } void surfaces_reordered() { cursor_controller->update_cursor_image(); } void scene_changed() { cursor_controller->update_cursor_image(); } void surface_exists(ms::Surface *surface) { add_surface_observer(surface); cursor_controller->update_cursor_image(); } void end_observation() { std::unique_lock lg(surface_observers_guard); for (auto &kv : surface_observers) { auto surface = kv.first; if (surface) surface->remove_observer(kv.second); } surface_observers.clear(); } private: mi::CursorController* const cursor_controller; std::mutex surface_observers_guard; std::map> surface_observers; }; std::shared_ptr topmost_surface_containing_point( std::shared_ptr const& targets, geom::Point const& point) { std::shared_ptr top_surface_at_point; targets->for_each([&top_surface_at_point, &point] (std::shared_ptr const& surface) { if (surface->input_area_contains(point)) top_surface_at_point = surface; }); return top_surface_at_point; } bool is_empty(std::shared_ptr const& image) { auto const size = image->size(); return size.width.as_int() == 0 || size.height.as_int() == 0; } } mi::CursorController::CursorController(std::shared_ptr const& input_targets, std::shared_ptr const& cursor, std::shared_ptr const& default_cursor_image) : input_targets(input_targets), cursor(cursor), default_cursor_image(default_cursor_image), current_cursor(default_cursor_image) { // TODO: Add observer could return weak_ptr to eliminate this // pattern auto strong_observer = std::make_shared(this); input_targets->add_observer(strong_observer); observer = strong_observer; } mi::CursorController::~CursorController() { try { input_targets->remove_observer(observer); } catch (...) { std::terminate(); } } void mi::CursorController::set_cursor_image_locked(std::unique_lock& lock, std::shared_ptr const& image) { if (current_cursor == image) { return; } current_cursor = image; lock.unlock(); if (image && !is_empty(image)) cursor->show(*image); else cursor->hide(); } void mi::CursorController::update_cursor_image_locked(std::unique_lock& lock) { auto surface = topmost_surface_containing_point(input_targets, cursor_location); if (surface) { set_cursor_image_locked(lock, surface->cursor_image()); } else { set_cursor_image_locked(lock, default_cursor_image); } } void mi::CursorController::update_cursor_image() { std::unique_lock lock(cursor_state_guard); update_cursor_image_locked(lock); } void mi::CursorController::cursor_moved_to(float abs_x, float abs_y) { auto const new_location = geom::Point{geom::X{abs_x}, geom::Y{abs_y}}; { std::unique_lock lock(cursor_state_guard); cursor_location = new_location; update_cursor_image_locked(lock); } cursor->move_to(new_location); } ./src/server/input/key_repeat_dispatcher.cpp0000644000015600001650000001536112676616157021422 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "key_repeat_dispatcher.h" #include "mir/input/device.h" #include "mir/input/input_device_hub.h" #include "mir/time/alarm_factory.h" #include "mir/time/alarm.h" #include "mir/events/event_private.h" #include "mir/cookie/authority.h" #include #include #include #include namespace mi = mir::input; namespace { struct DeviceRemovalFilter : mi::InputDeviceObserver { DeviceRemovalFilter(mi::KeyRepeatDispatcher* dispatcher) : dispatcher{dispatcher} {} void device_added(std::shared_ptr const& device) override { if (device->name() == "mtk-tpd") { dispatcher->set_mtk_device(device->id()); } } void device_changed(std::shared_ptr const&) override { } void device_removed(std::shared_ptr const& device) override { dispatcher->remove_device(device->id()); } void changes_complete() override { } mi::KeyRepeatDispatcher* dispatcher; }; } mi::KeyRepeatDispatcher::KeyRepeatDispatcher( std::shared_ptr const& next_dispatcher, std::shared_ptr const& factory, std::shared_ptr const& cookie_authority, bool repeat_enabled, std::chrono::milliseconds repeat_timeout, std::chrono::milliseconds repeat_delay, bool disable_repeat_on_mtk_touchpad) : next_dispatcher(next_dispatcher), alarm_factory(factory), cookie_authority(cookie_authority), repeat_enabled(repeat_enabled), repeat_timeout(repeat_timeout), repeat_delay(repeat_delay), disable_repeat_on_mtk_touchpad(disable_repeat_on_mtk_touchpad) { } void mi::KeyRepeatDispatcher::set_input_device_hub(std::shared_ptr const& hub) { hub->add_observer(std::make_shared(this)); } void mi::KeyRepeatDispatcher::set_mtk_device(MirInputDeviceId id) { std::lock_guard lock(repeat_state_mutex); mtk_tpd_id = id; } void mi::KeyRepeatDispatcher::remove_device(MirInputDeviceId id) { std::lock_guard lock(repeat_state_mutex); repeat_state_by_device.erase(id); // destructor cancels alarms if (mtk_tpd_id.is_set() && mtk_tpd_id.value() == id) mtk_tpd_id.consume(); } mi::KeyRepeatDispatcher::KeyboardState& mi::KeyRepeatDispatcher::ensure_state_for_device_locked(std::lock_guard const&, MirInputDeviceId id) { repeat_state_by_device.insert(std::make_pair(id, KeyboardState())); return repeat_state_by_device[id]; } bool mi::KeyRepeatDispatcher::dispatch(MirEvent const& event) { if (!repeat_enabled) // if we made this mutable we'd need a guard { return next_dispatcher->dispatch(event); } if (mir_event_get_type(&event) == mir_event_type_input) { auto iev = mir_event_get_input_event(&event); if (mir_input_event_get_type(iev) != mir_input_event_type_key) return next_dispatcher->dispatch(event); auto device_id = mir_input_event_get_device_id(iev); if (disable_repeat_on_mtk_touchpad && mtk_tpd_id.is_set() && device_id == mtk_tpd_id.value()) return next_dispatcher->dispatch(event); if (!handle_key_input(mir_input_event_get_device_id(iev), mir_input_event_get_keyboard_event(iev))) return next_dispatcher->dispatch(event); else return true; } return next_dispatcher->dispatch(event); } namespace { MirEvent copy_to_repeat_ev(MirKeyboardEvent const* kev) { MirEvent repeat_ev(*reinterpret_cast(kev)); repeat_ev.key.action = mir_keyboard_action_repeat; return repeat_ev; } } // Returns true if the original event has been handled, that is ::dispatch should not pass it on. bool mi::KeyRepeatDispatcher::handle_key_input(MirInputDeviceId id, MirKeyboardEvent const* kev) { std::lock_guard lg(repeat_state_mutex); auto& device_state = ensure_state_for_device_locked(lg, id); auto scan_code = mir_keyboard_event_scan_code(kev); switch (mir_keyboard_event_action(kev)) { case mir_keyboard_action_up: { auto it = device_state.repeat_alarms_by_scancode.find(scan_code); if (it == device_state.repeat_alarms_by_scancode.end()) { return false; } device_state.repeat_alarms_by_scancode.erase(it); break; } case mir_keyboard_action_down: { MirEvent ev = copy_to_repeat_ev(kev); auto it = device_state.repeat_alarms_by_scancode.find(scan_code); if (it != device_state.repeat_alarms_by_scancode.end()) { // When we receive a duplicated down we just replace the action next_dispatcher->dispatch(ev); return true; } auto& capture_alarm = device_state.repeat_alarms_by_scancode[scan_code]; std::shared_ptr alarm = alarm_factory->create_alarm([this, &capture_alarm, ev]() mutable { std::lock_guard lg(repeat_state_mutex); ev.key.event_time = std::chrono::steady_clock::now().time_since_epoch(); auto const cookie = cookie_authority->make_cookie(ev.key.event_time.count()); auto const serialized_cookie = cookie->serialize(); std::copy_n(std::begin(serialized_cookie), ev.key.cookie.size(), std::begin(ev.key.cookie)); next_dispatcher->dispatch(ev); capture_alarm->reschedule_in(repeat_delay); }); alarm->reschedule_in(repeat_timeout); device_state.repeat_alarms_by_scancode[scan_code] = {alarm}; } case mir_keyboard_action_repeat: // Should we consume existing repeats? break; default: BOOST_THROW_EXCEPTION(std::logic_error("Unexpected key event action")); } return false; } void mi::KeyRepeatDispatcher::start() { next_dispatcher->start(); } void mi::KeyRepeatDispatcher::stop() { std::lock_guard lg(repeat_state_mutex); repeat_state_by_device.clear(); next_dispatcher->stop(); } ./src/server/input/touchspot_image.c0000644000015600001650000014526312676616125017716 0ustar jenkinsjenkins/* GIMP RGBA C-Source image dump (spot.c) */ static const struct { unsigned int width; unsigned int height; unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ unsigned char pixel_data[64 * 64 * 4 + 1]; } touchspot_image = { 64, 64, 4, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,5,0,0,0,65,0,0,0,104,0,0,0,129, 0,0,0,154,0,0,0,179,0,0,0,204,0,0,0,230, 0,0,0,230,0,0,0,204,0,0,0,179,0,0,0,154, 0,0,0,129,0,0,0,104,0,0,0,65,0,0,0,5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,15,0,0,0,88,0,0,0,166, 0,0,0,240,0,0,0,255,0,0,0,255,0,0,0,255, 0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255, 0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255, 0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,240, 0,0,0,166,0,0,0,88,0,0,0,15,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,95, 0,0,0,187,0,0,0,250,0,0,0,255,0,0,0,255, 0,0,0,255,0,15,28,255,0,45,82,255,0,60,108,255, 0,75,135,255,0,89,161,255,0,104,188,255,0,119,215,255, 0,119,215,255,0,104,188,255,0,89,161,255,0,75,135,255, 0,60,108,255,0,45,82,255,0,15,28,255,0,0,0,255, 0,0,0,255,0,0,0,255,0,0,0,250,0,0,0,187, 0,0,0,95,0,0,0,3,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,83,0,0,0,215,0,0,0,255, 0,0,0,255,0,0,0,255,0,27,49,255,0,70,126,255, 0,112,203,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,112,203,255, 0,70,126,255,0,27,49,255,0,0,0,255,0,0,0,255, 0,0,0,255,0,0,0,215,0,0,0,83,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69, 0,0,0,203,0,0,0,255,0,0,0,255,0,12,22,255, 0,77,139,255,0,124,223,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,124,223,255,0,77,139,255, 0,13,23,255,0,0,0,255,0,0,0,255,0,0,0,203, 0,0,0,69,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,24,0,0,0,187,0,0,0,255, 0,0,0,255,0,8,14,255,0,71,128,255,0,133,240,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,133,240,255,0,72,130,255,0,8,15,255,0,0,0,255, 0,0,0,255,0,0,0,187,0,0,0,24,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,53,0,0,0,228,0,0,0,255,0,4,7,255, 0,62,112,255,0,129,232,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,129,233,255,0,63,114,255, 0,4,8,255,0,0,0,255,0,0,0,228,0,0,0,53, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92, 0,0,0,247,0,0,0,255,0,28,51,255,0,122,220,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,123,221,255,0,28,50,255,0,0,0,255,0,0,0,247, 0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,3,0,0,0,140,0,0,0,255, 0,0,0,255,0,49,89,255,0,134,242,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,134,242,255,0,47,86,255,0,0,0,255, 0,0,0,255,0,0,0,140,0,0,0,3,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,5,0,0,0,181,0,0,0,255,0,1,2,255, 0,74,133,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,71,129,255, 0,1,1,255,0,0,0,255,0,0,0,178,0,0,0,3, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,151,0,0,0,255,0,7,14,255,0,98,177,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,95,172,255,0,6,12,255,0,0,0,255,0,0,0,143, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,103, 0,0,0,255,0,2,4,255,0,105,189,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,101,182,255,0,2,3,255,0,0,0,255, 0,0,0,95,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,61,0,0,0,250, 0,0,0,255,0,81,147,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,76,138,255,0,0,0,255, 0,0,0,248,0,0,0,55,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,30,0,0,0,234,0,0,0,255, 0,56,100,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,51,92,255, 0,0,0,255,0,0,0,230,0,0,0,26,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,195,0,0,0,255,0,33,59,255, 0,136,246,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,135,243,255, 0,29,52,255,0,0,0,255,0,0,0,193,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,79,0,0,0,255,0,8,14,255,0,126,228,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,124,224,255,0,8,15,255,0,0,0,255,0,0,0,77, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3, 0,0,0,213,0,0,0,255,0,71,129,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,72,130,255,0,0,0,255,0,0,0,211, 0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98, 0,0,0,255,0,14,25,255,0,134,241,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,133,240,255,0,13,24,255,0,0,0,255, 0,0,0,93,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,227, 0,0,0,255,0,83,150,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,81,147,255,0,0,0,255, 0,0,0,223,0,0,0,6,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,115,0,0,0,255, 0,21,37,255,0,137,247,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,136,246,255,0,19,34,255, 0,0,0,255,0,0,0,110,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,209,0,0,0,255, 0,88,159,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,88,158,255, 0,0,0,255,0,0,0,209,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,32,0,0,0,255,0,3,6,255, 0,131,237,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,132,239,255, 0,4,7,255,0,0,0,255,0,0,0,32,0,0,0,0, 0,0,0,0,0,0,0,111,0,0,0,255,0,38,69,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,39,71,255,0,0,0,255,0,0,0,111,0,0,0,0, 0,0,0,0,0,0,0,189,0,0,0,255,0,81,147,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,82,149,255,0,0,0,255,0,0,0,189,0,0,0,0, 0,0,0,17,0,0,0,251,0,1,1,255,0,124,224,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,124,224,255,0,1,1,255,0,0,0,251,0,0,0,17, 0,0,0,89,0,0,0,255,0,28,51,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,28,50,255,0,0,0,255,0,0,0,89, 0,0,0,128,0,0,0,255,0,63,114,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,58,104,255,0,0,0,255,0,0,0,128, 0,0,0,155,0,0,0,255,0,77,140,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,73,132,255,0,0,0,255,0,0,0,155, 0,0,0,181,0,0,0,255,0,91,164,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,88,158,255,0,0,0,255,0,0,0,181, 0,0,0,208,0,0,0,255,0,105,190,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,102,185,255,0,0,0,255,0,0,0,208, 0,0,0,234,0,0,0,255,0,119,215,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,117,212,255,0,0,0,255,0,0,0,234, 0,0,0,254,0,0,0,255,0,132,239,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,132,238,255,0,0,0,255,0,0,0,254, 0,0,0,254,0,0,0,255,0,132,239,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,132,238,255,0,0,0,255,0,0,0,254, 0,0,0,234,0,0,0,255,0,119,215,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,117,212,255,0,0,0,255,0,0,0,234, 0,0,0,208,0,0,0,255,0,105,190,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,102,185,255,0,0,0,255,0,0,0,208, 0,0,0,181,0,0,0,255,0,91,164,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,88,158,255,0,0,0,255,0,0,0,181, 0,0,0,155,0,0,0,255,0,77,140,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,73,132,255,0,0,0,255,0,0,0,155, 0,0,0,128,0,0,0,255,0,63,114,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,58,104,255,0,0,0,255,0,0,0,128, 0,0,0,89,0,0,0,255,0,28,51,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,28,50,255,0,0,0,255,0,0,0,89, 0,0,0,17,0,0,0,251,0,1,1,255,0,124,224,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,124,224,255,0,1,1,255,0,0,0,251,0,0,0,17, 0,0,0,0,0,0,0,189,0,0,0,255,0,81,147,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,82,149,255,0,0,0,255,0,0,0,189,0,0,0,0, 0,0,0,0,0,0,0,111,0,0,0,255,0,38,69,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,39,71,255,0,0,0,255,0,0,0,111,0,0,0,0, 0,0,0,0,0,0,0,32,0,0,0,255,0,3,6,255, 0,131,237,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,132,239,255, 0,4,7,255,0,0,0,255,0,0,0,32,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,209,0,0,0,255, 0,88,159,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,88,158,255, 0,0,0,255,0,0,0,209,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,115,0,0,0,255, 0,21,37,255,0,137,247,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,136,246,255,0,19,34,255, 0,0,0,255,0,0,0,110,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,227, 0,0,0,255,0,83,150,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,81,147,255,0,0,0,255, 0,0,0,223,0,0,0,6,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98, 0,0,0,255,0,14,25,255,0,134,241,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,133,240,255,0,13,24,255,0,0,0,255, 0,0,0,93,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3, 0,0,0,213,0,0,0,255,0,71,129,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,72,130,255,0,0,0,255,0,0,0,211, 0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,79,0,0,0,255,0,8,14,255,0,126,228,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,124,224,255,0,8,15,255,0,0,0,255,0,0,0,77, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,195,0,0,0,255,0,33,59,255, 0,136,246,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,135,243,255, 0,29,52,255,0,0,0,255,0,0,0,193,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,30,0,0,0,234,0,0,0,255, 0,56,100,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,51,92,255, 0,0,0,255,0,0,0,230,0,0,0,26,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,61,0,0,0,250, 0,0,0,255,0,81,147,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,76,138,255,0,0,0,255, 0,0,0,248,0,0,0,55,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,103, 0,0,0,255,0,2,4,255,0,105,189,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,101,182,255,0,2,3,255,0,0,0,255, 0,0,0,95,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,151,0,0,0,255,0,7,14,255,0,98,177,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,95,172,255,0,6,12,255,0,0,0,255,0,0,0,143, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,5,0,0,0,181,0,0,0,255,0,1,2,255, 0,74,133,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,71,129,255, 0,1,1,255,0,0,0,255,0,0,0,178,0,0,0,3, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,3,0,0,0,140,0,0,0,255, 0,0,0,255,0,49,89,255,0,134,242,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,134,242,255,0,47,86,255,0,0,0,255, 0,0,0,255,0,0,0,140,0,0,0,3,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92, 0,0,0,247,0,0,0,255,0,28,51,255,0,122,220,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,123,221,255,0,28,50,255,0,0,0,255,0,0,0,247, 0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,53,0,0,0,228,0,0,0,255,0,4,7,255, 0,62,112,255,0,129,232,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,129,233,255,0,63,114,255, 0,4,8,255,0,0,0,255,0,0,0,228,0,0,0,53, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,24,0,0,0,187,0,0,0,255, 0,0,0,255,0,8,14,255,0,71,128,255,0,133,240,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,133,240,255,0,72,130,255,0,8,15,255,0,0,0,255, 0,0,0,255,0,0,0,187,0,0,0,24,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69, 0,0,0,203,0,0,0,255,0,0,0,255,0,12,22,255, 0,77,139,255,0,124,223,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,124,223,255,0,77,139,255, 0,13,23,255,0,0,0,255,0,0,0,255,0,0,0,203, 0,0,0,69,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,83,0,0,0,215,0,0,0,255, 0,0,0,255,0,0,0,255,0,27,49,255,0,70,126,255, 0,112,203,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,139,251,255, 0,139,251,255,0,139,251,255,0,139,251,255,0,112,203,255, 0,70,126,255,0,27,49,255,0,0,0,255,0,0,0,255, 0,0,0,255,0,0,0,215,0,0,0,83,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,95, 0,0,0,187,0,0,0,250,0,0,0,255,0,0,0,255, 0,0,0,255,0,15,28,255,0,45,82,255,0,60,108,255, 0,75,135,255,0,89,161,255,0,104,188,255,0,119,215,255, 0,119,215,255,0,104,188,255,0,89,161,255,0,75,135,255, 0,60,108,255,0,45,82,255,0,15,28,255,0,0,0,255, 0,0,0,255,0,0,0,255,0,0,0,250,0,0,0,187, 0,0,0,95,0,0,0,3,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,15,0,0,0,88,0,0,0,166, 0,0,0,240,0,0,0,255,0,0,0,255,0,0,0,255, 0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255, 0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,255, 0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,240, 0,0,0,166,0,0,0,88,0,0,0,15,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,5,0,0,0,65,0,0,0,104,0,0,0,129, 0,0,0,154,0,0,0,179,0,0,0,204,0,0,0,230, 0,0,0,230,0,0,0,204,0,0,0,179,0,0,0,154, 0,0,0,129,0,0,0,104,0,0,0,65,0,0,0,5, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, }; ./src/server/input/null_input_dispatcher.h0000644000015600001650000000203512676616125021115 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_NULL_INPUT_DISPATCHER_H_ #define MIR_INPUT_NULL_INPUT_DISPATCHER_H_ #include "mir/input/input_dispatcher.h" namespace mir { namespace input { class NullInputDispatcher : public mir::input::InputDispatcher { public: bool dispatch(MirEvent const& event) override; void start() override; void stop() override; }; } } #endif ./src/server/input/null_input_dispatcher.cpp0000644000015600001650000000164412676616125021455 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "null_input_dispatcher.h" namespace mi = mir::input; bool mi::NullInputDispatcher::dispatch(MirEvent const& /*event*/) { return true; } void mi::NullInputDispatcher::start() { } void mi::NullInputDispatcher::stop() { } ./src/server/input/display_input_region.h0000644000015600001650000000250512676616125020747 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_INPUT_DISPLAY_INPUT_REGION_H_ #define MIR_INPUT_DISPLAY_INPUT_REGION_H_ #include "mir/input/input_region.h" #include "mir/geometry/rectangles.h" #include #include namespace mir { namespace input { class DisplayInputRegion : public InputRegion { public: DisplayInputRegion() = default; geometry::Rectangle bounding_rectangle() override; void confine(geometry::Point& point) override; void set_input_rectangles(geometry::Rectangles const& rectangles) override; private: std::mutex rectangle_guard; geometry::Rectangles rectangles; }; } } #endif /* MIR_INPUT_DISPLAY_INPUT_REGION_H_ */ ./src/server/input/key_repeat_dispatcher.h0000644000015600001650000000556412676616157021073 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_KEY_REPEAT_DISPATCHER_H_ #define MIR_INPUT_KEY_REPEAT_DISPATCHER_H_ #include "mir/input/input_dispatcher.h" #include "mir/input/input_device_observer.h" #include "mir/optional_value.h" #include #include #include #include namespace mir { namespace cookie { class Authority; } namespace time { class AlarmFactory; class Alarm; } namespace input { class InputDeviceHub; class KeyRepeatDispatcher : public InputDispatcher { public: KeyRepeatDispatcher(std::shared_ptr const& next_dispatcher, std::shared_ptr const& factory, std::shared_ptr const& cookie_authority, bool repeat_enabled, std::chrono::milliseconds repeat_timeout, /* timeout before sending first repeat */ std::chrono::milliseconds repeat_delay, /* delay between repeated keys */ bool disable_repeat_on_mtk_touchpad); // InputDispatcher bool dispatch(MirEvent const& event) override; void start() override; void stop() override; void set_input_device_hub(std::shared_ptr const& hub); void set_mtk_device(MirInputDeviceId id); void remove_device(MirInputDeviceId id); private: std::mutex repeat_state_mutex; std::shared_ptr const next_dispatcher; std::shared_ptr const alarm_factory; std::shared_ptr const cookie_authority; bool const repeat_enabled; std::chrono::milliseconds repeat_timeout; std::chrono::milliseconds repeat_delay; bool const disable_repeat_on_mtk_touchpad; optional_value mtk_tpd_id; struct KeyboardState { std::unordered_map> repeat_alarms_by_scancode; }; std::unordered_map repeat_state_by_device; KeyboardState& ensure_state_for_device_locked(std::lock_guard const&, MirInputDeviceId id); bool handle_key_input(MirInputDeviceId id, MirKeyboardEvent const* ev); }; } } #endif // MIR_INPUT_KEY_REPEAT_DISPATCHER_H_ ./src/server/input/input_modifier_utils.cpp0000644000015600001650000000522012676616125021305 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "input_modifier_utils.h" #include "mir_toolkit/events/input/input_event.h" #include "boost/throw_exception.hpp" #include "linux/input.h" #include namespace mi = mir::input; MirInputEventModifiers mi::to_modifiers(int32_t scan_code) { switch(scan_code) { case KEY_LEFTALT: return mir_input_event_modifier_alt_left; case KEY_RIGHTALT: return mir_input_event_modifier_alt_right; case KEY_RIGHTCTRL: return mir_input_event_modifier_ctrl_right; case KEY_LEFTCTRL: return mir_input_event_modifier_ctrl_left; case KEY_CAPSLOCK: return mir_input_event_modifier_caps_lock; case KEY_LEFTMETA: return mir_input_event_modifier_meta_left; case KEY_RIGHTMETA: return mir_input_event_modifier_meta_right; case KEY_SCROLLLOCK: return mir_input_event_modifier_scroll_lock; case KEY_NUMLOCK: return mir_input_event_modifier_num_lock; case KEY_LEFTSHIFT: return mir_input_event_modifier_shift_left; case KEY_RIGHTSHIFT: return mir_input_event_modifier_shift_right; default: return MirInputEventModifiers{0}; } } MirInputEventModifiers mi::expand_modifiers(MirInputEventModifiers modifiers) { if (modifiers == 0) return mir_input_event_modifier_none; if ((modifiers & mir_input_event_modifier_alt_left) || (modifiers & mir_input_event_modifier_alt_right)) modifiers |= mir_input_event_modifier_alt; if ((modifiers & mir_input_event_modifier_ctrl_left) || (modifiers & mir_input_event_modifier_ctrl_right)) modifiers |= mir_input_event_modifier_ctrl; if ((modifiers & mir_input_event_modifier_shift_left) || (modifiers & mir_input_event_modifier_shift_right)) modifiers |= mir_input_event_modifier_shift; if ((modifiers & mir_input_event_modifier_meta_left) || (modifiers & mir_input_event_modifier_meta_right)) modifiers |= mir_input_event_modifier_meta; return modifiers; } ./src/server/input/default_configuration.cpp0000644000015600001650000002731712676616157021443 0ustar jenkinsjenkins/* * Copyright © 2013-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/default_server_configuration.h" #include "android/input_sender.h" #include "android/input_channel_factory.h" #include "key_repeat_dispatcher.h" #include "display_input_region.h" #include "event_filter_chain_dispatcher.h" #include "cursor_controller.h" #include "touchspot_controller.h" #include "null_input_manager.h" #include "null_input_dispatcher.h" #include "null_input_targeter.h" #include "builtin_cursor_images.h" #include "null_input_channel_factory.h" #include "default_input_device_hub.h" #include "default_input_manager.h" #include "surface_input_dispatcher.h" #include "basic_seat.h" #include "../graphics/nested/mir_client_host_connection.h" #include "mir/input/touch_visualizer.h" #include "mir/input/input_probe.h" #include "mir/input/platform.h" #include "mir/options/configuration.h" #include "mir/options/option.h" #include "mir/dispatch/multiplexing_dispatchable.h" #include "mir/compositor/scene.h" #include "mir/emergency_cleanup.h" #include "mir/main_loop.h" #include "mir/abnormal_exit.h" #include "mir/glib_main_loop.h" #include "mir/log.h" #include "mir/shared_library.h" #include "mir/dispatch/action_queue.h" #include "mir_toolkit/cursors.h" namespace mi = mir::input; namespace mia = mi::android; namespace mr = mir::report; namespace ms = mir::scene; namespace mg = mir::graphics; namespace msh = mir::shell; namespace md = mir::dispatch; namespace { bool is_arale() { try { mir::SharedLibrary android_properties("libandroid-properties.so.1"); int (*property_get)(char const*, char*, char const*) = nullptr; property_get = android_properties.load_function("property_get"); const int property_value_max = 92; char default_value[] = ""; char value[property_value_max]; if (property_get == nullptr) return false; property_get("ro.product.device", value, default_value); return std::strcmp("arale", value) == 0; } catch(...) { } return false; } } std::shared_ptr mir::DefaultServerConfiguration::the_input_region() { return input_region( [this]() { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_composite_event_filter() { return composite_event_filter( [this]() { return the_event_filter_chain_dispatcher(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_event_filter_chain_dispatcher() { return event_filter_chain_dispatcher( [this]() -> std::shared_ptr { std::initializer_list const> filter_list {default_filter}; return std::make_shared(filter_list, the_surface_input_dispatcher()); }); } namespace { class NullInputSender : public mi::InputSender { public: virtual void send_event(MirEvent const&, std::shared_ptr const& ) {} }; } std::shared_ptr mir::DefaultServerConfiguration::the_input_sender() { return input_sender( [this]() -> std::shared_ptr { if (!the_options()->get(options::enable_input_opt)) return std::make_shared(); else return std::make_shared(the_scene(), the_main_loop(), the_input_report()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_input_targeter() { return input_targeter( [this]() -> std::shared_ptr { auto const options = the_options(); if (!options->get(options::enable_input_opt)) return std::make_shared(); else return the_surface_input_dispatcher(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_surface_input_dispatcher() { return surface_input_dispatcher( [this]() { return std::make_shared(the_input_scene()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_input_dispatcher() { return input_dispatcher( [this]() { std::chrono::milliseconds const key_repeat_timeout{500}; std::chrono::milliseconds const key_repeat_delay{50}; auto const options = the_options(); auto enable_repeat = options->get(options::enable_key_repeat_opt); return std::make_shared( the_event_filter_chain_dispatcher(), the_main_loop(), the_cookie_authority(), enable_repeat, key_repeat_timeout, key_repeat_delay, is_arale()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_input_channel_factory() { auto const options = the_options(); if (!options->get(options::enable_input_opt)) return std::make_shared(); else return std::make_shared(); } std::shared_ptr mir::DefaultServerConfiguration::the_cursor_listener() { return cursor_listener( [this]() -> std::shared_ptr { return wrap_cursor_listener(std::make_shared( the_input_scene(), the_cursor(), the_default_cursor_image())); }); } std::shared_ptr mir::DefaultServerConfiguration::wrap_cursor_listener( std::shared_ptr const& wrapped) { return wrapped; } std::shared_ptr mir::DefaultServerConfiguration::the_touch_visualizer() { return touch_visualizer( [this]() -> std::shared_ptr { auto visualizer = std::make_shared(the_buffer_allocator(), the_input_scene()); // The visualizer is disabled by default and can be enabled statically via // the MIR_SERVER_ENABLE_TOUCHSPOTS option. In the USC/unity8/autopilot case // it will be toggled at runtime via com.canonical.Unity.Screen DBus interface if (the_options()->is_set(options::touchspots_opt)) { visualizer->enable(); } return visualizer; }); } std::shared_ptr mir::DefaultServerConfiguration::the_default_cursor_image() { return default_cursor_image( [this]() { return the_cursor_images()->image(mir_default_cursor_name, mi::default_cursor_size); }); } std::shared_ptr mir::DefaultServerConfiguration::the_cursor_images() { return cursor_images( [this]() -> std::shared_ptr { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_input_manager() { return input_manager( [this]() -> std::shared_ptr { auto const options = the_options(); bool input_opt = options->get(options::enable_input_opt); if (!input_opt) { return std::make_shared(); } else if (options->is_set(options::host_socket_opt)) { // TODO nested input handling (== host_socket) should fold into a platform return std::make_shared(); } else { auto const emergency_cleanup = the_emergency_cleanup(); auto const device_registry = the_input_device_registry(); auto const input_report = the_input_report(); // Maybe the graphics platform also supplies input (e.g. mesa-x11 or nested) // NB this makes the (valid) assumption that graphics initializes before input auto platform = mi::input_platform_from_graphics_module( *the_graphics_platform(), *options, emergency_cleanup, device_registry, input_report); // otherwise (usually) we probe for it if (!platform) { platform = probe_input_platforms(*options, emergency_cleanup, device_registry, input_report, *the_shared_library_prober_report()); } return std::make_shared(the_input_reading_multiplexer(), std::move(platform)); } } ); } std::shared_ptr mir::DefaultServerConfiguration::the_input_reading_multiplexer() { return input_reading_multiplexer( [this]() -> std::shared_ptr { return std::make_shared(); } ); } std::shared_ptr mir::DefaultServerConfiguration::the_seat() { return seat( [this]() { return std::make_shared( the_input_dispatcher(), the_touch_visualizer(), the_cursor_listener(), the_input_region()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_input_device_registry() { return default_input_device_hub( [this]() { auto input_dispatcher = the_input_dispatcher(); auto key_repeater = std::dynamic_pointer_cast(input_dispatcher); auto hub = std::make_shared( the_global_event_sink(), the_seat(), the_input_reading_multiplexer(), the_main_loop(), the_cookie_authority()); if (key_repeater) key_repeater->set_input_device_hub(hub); return hub; }); } std::shared_ptr mir::DefaultServerConfiguration::the_input_device_hub() { auto options = the_options(); if (options->is_set(options::host_socket_opt)) { return the_mir_client_host_connection(); } else { return default_input_device_hub( [this]() { auto input_dispatcher = the_input_dispatcher(); auto key_repeater = std::dynamic_pointer_cast(input_dispatcher); auto hub = std::make_shared( the_global_event_sink(), the_seat(), the_input_reading_multiplexer(), the_main_loop(), the_cookie_authority()); if (key_repeater) key_repeater->set_input_device_hub(hub); return hub; }); } } ./src/server/input/default_event_builder.h0000644000015600001650000000404612676616125021055 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_DEFAULT_EVENT_BUILDER_H_ #define MIR_INPUT_DEFAULT_EVENT_BUILDER_H_ #include "mir/input/event_builder.h" #include namespace mir { namespace cookie { class Authority; } namespace input { class DefaultEventBuilder : public EventBuilder { public: explicit DefaultEventBuilder(MirInputDeviceId device_id, std::shared_ptr const& cookie_authority); EventUPtr key_event(Timestamp timestamp, MirKeyboardAction action, xkb_keysym_t key_code, int scan_code) override; EventUPtr touch_event(Timestamp timestamp) override; void add_touch(MirEvent& event, MirTouchId touch_id, MirTouchAction action, MirTouchTooltype tooltype, float x_axis_value, float y_axis_value, float pressure_value, float touch_major_value, float touch_minor_value, float size_value) override; EventUPtr pointer_event(Timestamp timestamp, MirPointerAction action, MirPointerButtons buttons_pressed, float hscroll_value, float vscroll_value, float relative_x_value, float relative_y_value) override; EventUPtr configuration_event(Timestamp timestamp, MirInputConfigurationAction action) override; private: MirInputDeviceId const device_id; std::shared_ptr const cookie_authority; }; } } #endif ./src/server/input/null_input_channel_factory.h0000644000015600001650000000204412676616125022126 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_NULL_INPUT_CHANNEL_FACTORY_H_ #define MIR_INPUT_NULL_INPUT_CHANNEL_FACTORY_H_ #include "mir/input/input_channel_factory.h" namespace mir { namespace input { class InputChannel; class NullInputChannelFactory : public mir::input::InputChannelFactory { std::shared_ptr make_input_channel() override; }; } } #endif ./src/server/input/default_event_builder.cpp0000644000015600001650000000724012676616125021407 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #include "default_event_builder.h" #include "mir/events/event_builders.h" #include "mir/cookie/authority.h" #include "mir/events/event_private.h" #include namespace me = mir::events; namespace mi = mir::input; mi::DefaultEventBuilder::DefaultEventBuilder(MirInputDeviceId device_id, std::shared_ptr const& cookie_authority) : device_id(device_id), cookie_authority(cookie_authority) { } mir::EventUPtr mi::DefaultEventBuilder::key_event(Timestamp timestamp, MirKeyboardAction action, xkb_keysym_t key_code, int scan_code) { auto const cookie = cookie_authority->make_cookie(timestamp.count()); return me::make_event(device_id, timestamp, cookie->serialize(), action, key_code, scan_code, mir_input_event_modifier_none); } mir::EventUPtr mi::DefaultEventBuilder::touch_event(Timestamp timestamp) { return me::make_event(device_id, timestamp, std::vector{}, mir_input_event_modifier_none); } void mi::DefaultEventBuilder::add_touch(MirEvent& event, MirTouchId touch_id, MirTouchAction action, MirTouchTooltype tooltype, float x_axis_value, float y_axis_value, float pressure_value, float touch_major_value, float touch_minor_value, float size_value) { if (action == mir_touch_action_up || action == mir_touch_action_down) { auto const cookie = cookie_authority->make_cookie(event.motion.event_time.count()); auto const serialized_cookie = cookie->serialize(); std::copy_n(std::begin(serialized_cookie), event.motion.cookie.size(), std::begin(event.motion.cookie)); } me::add_touch(event, touch_id, action, tooltype, x_axis_value, y_axis_value, pressure_value, touch_major_value, touch_minor_value, size_value); } mir::EventUPtr mi::DefaultEventBuilder::pointer_event(Timestamp timestamp, MirPointerAction action, MirPointerButtons buttons_pressed, float hscroll_value, float vscroll_value, float relative_x_value, float relative_y_value) { const float x_axis_value = 0; const float y_axis_value = 0; std::vector vec_cookie{}; if (action == mir_pointer_action_button_up || action == mir_pointer_action_button_down) { auto const cookie = cookie_authority->make_cookie(timestamp.count()); vec_cookie = cookie->serialize(); } return me::make_event(device_id, timestamp, vec_cookie, mir_input_event_modifier_none, action, buttons_pressed, x_axis_value, y_axis_value, hscroll_value, vscroll_value, relative_x_value, relative_y_value); } mir::EventUPtr mi::DefaultEventBuilder::configuration_event(Timestamp timestamp, MirInputConfigurationAction action) { return me::make_event(action, device_id, timestamp); } ./src/server/input/null_input_manager.h0000644000015600001650000000206012676616125020377 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_NULL_INPUT_MANAGER_H_ #define MIR_INPUT_NULL_INPUT_MANAGER_H_ #include "mir/input/input_manager.h" namespace mir { namespace input { class NullInputManager : public input::InputManager { void add_platform(std::shared_ptr const&) override { } void start() override { } void stop() override { } }; } } #endif ./src/server/input/default_input_manager.h0000644000015600001650000000413512676616157021063 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_DEFAULT_INPUT_MANAGER_H_ #define MIR_INPUT_DEFAULT_INPUT_MANAGER_H_ #include "mir/input/input_manager.h" #include #include #include namespace mir { namespace dispatch { class MultiplexingDispatchable; class ThreadedDispatcher; class ActionQueue; } namespace input { class Platform; class InputEventHandlerRegister; class InputDeviceRegistry; class DefaultInputManager : public InputManager { public: DefaultInputManager( std::shared_ptr const& multiplexer, std::shared_ptr const& platform); ~DefaultInputManager(); void start() override; void stop() override; private: // TODO Remove add_platform() when we next break mirserver ABI // when we do that we can also convert platforms to a std::shared_ptr const // and simplify start_platforms() & stop_platforms() void add_platform(std::shared_ptr const& platform) override; void start_platforms(); void stop_platforms(); std::vector> platforms; std::shared_ptr const multiplexer; std::shared_ptr const queue; std::unique_ptr input_thread; enum class State { started, stopped, starting, stopping }; std::atomic state; }; } } #endif ./src/server/input/android/0000755000015600001650000000000012676616125015765 5ustar jenkinsjenkins./src/server/input/android/input_sender.h0000644000015600001650000000767012676616125020647 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_ANDROID_INPUT_SENDER_H_ #define MIR_INPUT_ANDROID_INPUT_SENDER_H_ #include "mir/input/input_sender.h" #include "mir/scene/null_observer.h" #include "mir_toolkit/event.h" #include "mir/events/event_private.h" #include "androidfw/InputTransport.h" #include #include #include #include #include namespace droidinput = android; namespace mir { class MainLoop; namespace compositor { class Scene; } namespace scene { class InputRegistrar; } namespace input { class InputReport; class Surface; namespace android { class InputSender : public input::InputSender { public: InputSender(std::shared_ptr const& scene, std::shared_ptr const& main_loop, std::shared_ptr const& report); void send_event(MirEvent const& event, std::shared_ptr const& channel) override; private: struct InputSenderState; class SceneObserver : public scene::NullObserver { public: explicit SceneObserver(InputSenderState & state); private: void surface_added(scene::Surface* surface) override; void surface_removed(scene::Surface* surface) override; void surface_exists(scene::Surface* surface) override; void scene_changed() override; void remove_transfer_for(input::Surface* surface); InputSenderState & state; }; class ActiveTransfer { public: ActiveTransfer(InputSenderState & state, std::shared_ptr const& channel, input::Surface* surface); ~ActiveTransfer(); void send(uint32_t sequence_id, MirEvent const& event); bool used_for_surface(input::Surface const* surface) const; void subscribe(); void unsubscribe(); private: void on_finish_signal(); droidinput::status_t send_key_event(uint32_t sequence_id, MirEvent const& event); droidinput::status_t send_touch_event(uint32_t sequence_id, MirEvent const& event); droidinput::status_t send_pointer_event(uint32_t sequence_id, MirEvent const& event); InputSenderState & state; droidinput::InputPublisher publisher; input::Surface const* surface; std::shared_ptr const channel; std::atomic subscribed{false}; ActiveTransfer& operator=(ActiveTransfer const&) = delete; ActiveTransfer(ActiveTransfer const&) = delete; }; struct InputSenderState { InputSenderState(std::shared_ptr const& main_loop, std::shared_ptr const& report); void send_event(std::shared_ptr const& channel, MirEvent const& event); void add_transfer(std::shared_ptr const& channel, input::Surface* surface); void remove_transfer(int fd); std::shared_ptr const main_loop; std::shared_ptr const report; private: std::shared_ptr get_transfer(int fd); uint32_t next_seq(); uint32_t seq; std::unordered_map> transfers; std::mutex sender_mutex; }; InputSenderState state; std::shared_ptr scene; }; } } } #endif ./src/server/input/android/input_sender.cpp0000644000015600001650000003507712676616125021204 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "input_sender.h" #include "mir/input/android/event_conversion_helpers.h" #include "mir/input/input_channel.h" #include "mir/input/input_report.h" #include "mir/scene/surface.h" #include "mir/compositor/scene.h" #include "mir/main_loop.h" #include "mir/events/event_private.h" #include #include #include #include #include namespace mi = mir::input; namespace mia = mi::android; namespace droidinput = android; mia::InputSender::InputSender(std::shared_ptr const& scene, std::shared_ptr const& main_loop, std::shared_ptr const& report) : state{main_loop, report}, scene{scene} { scene->add_observer(std::make_shared(state)); } mia::InputSender::SceneObserver::SceneObserver(InputSenderState & state) : state(state) { } void mia::InputSender::SceneObserver::surface_added(scene::Surface* surface) { if (surface && surface->input_channel()) { state.add_transfer(surface->input_channel(), surface); } } void mia::InputSender::SceneObserver::surface_removed(scene::Surface* surface) { if (surface && surface->input_channel()) { remove_transfer_for(surface); } } void mia::InputSender::SceneObserver::surface_exists(scene::Surface* surface) { surface_added(surface); } void mia::InputSender::SceneObserver::scene_changed() { } void mia::InputSender::SceneObserver::remove_transfer_for(mi::Surface * surface) { std::shared_ptr closed_channel = surface->input_channel(); if (!closed_channel) return; state.remove_transfer(closed_channel->server_fd()); } mia::InputSender::InputSenderState::InputSenderState(std::shared_ptr const& main_loop, std::shared_ptr const& report) : main_loop{main_loop}, report{report}, seq{} { } void mia::InputSender::send_event(MirEvent const& event, std::shared_ptr const& channel) { state.send_event(channel, event); } std::shared_ptr mia::InputSender::InputSenderState::get_transfer(int fd) { auto pos = transfers.find(fd); if (pos == transfers.end()) return nullptr; return pos->second; } void mia::InputSender::InputSenderState::send_event(std::shared_ptr const& channel, MirEvent const& event) { std::unique_lock lock(sender_mutex); auto transfer = get_transfer(channel->server_fd()); if (!transfer) BOOST_THROW_EXCEPTION(std::runtime_error("Failure sending input event : Unknown channel provided")); lock.unlock(); transfer->send(next_seq(), event); } void mia::InputSender::InputSenderState::add_transfer(std::shared_ptr const& channel, mi::Surface * surface) { std::lock_guard lock(sender_mutex); std::shared_ptr transfer{get_transfer(channel->server_fd())}; if (transfer && transfer->used_for_surface(surface)) return; transfers[channel->server_fd()] = std::make_shared(*this, channel, surface); } void mia::InputSender::InputSenderState::remove_transfer(int fd) { std::unique_lock lock(sender_mutex); auto transfer = get_transfer(fd); if (transfer) { transfer->unsubscribe(); transfers.erase(fd); lock.unlock(); } } uint32_t mia::InputSender::InputSenderState::next_seq() { while(!++seq); return seq; } mia::InputSender::ActiveTransfer::ActiveTransfer(InputSenderState & state, std::shared_ptr const& channel, mi::Surface* surface) : state(state), publisher{droidinput::sp( new droidinput::InputChannel(droidinput::String8(surface->name()), channel->server_fd()))}, surface{surface}, channel{channel} { } void mia::InputSender::ActiveTransfer::send(uint32_t sequence_id, MirEvent const& event) { if (mir_event_get_type(&event) != mir_event_type_input) return; droidinput::status_t error_status; auto event_time = mir_input_event_get_event_time(mir_event_get_input_event(&event)); auto input_event = mir_event_get_input_event(&event); switch(mir_input_event_get_type(input_event)) { case mir_input_event_type_key: error_status = send_key_event(sequence_id, event); state.report->published_key_event(channel->server_fd(), sequence_id, event_time); break; case mir_input_event_type_touch: error_status = send_touch_event(sequence_id, event); state.report->published_motion_event(channel->server_fd(), sequence_id, event_time); break; case mir_input_event_type_pointer: error_status = send_pointer_event(sequence_id, event); state.report->published_motion_event(channel->server_fd(), sequence_id, event_time); break; default: BOOST_THROW_EXCEPTION(std::runtime_error("unknown input event type")); } switch(error_status) { case droidinput::OK: subscribe(); break; case droidinput::WOULD_BLOCK: // Not an error. Clients are allowed to ignore input. break; case droidinput::DEAD_OBJECT: // XXX This throw was recently added but is missing tests: BOOST_THROW_EXCEPTION(boost::enable_error_info(std::runtime_error("Client channel dead : ")) << boost::errinfo_errno(errno)); break; default: BOOST_THROW_EXCEPTION(boost::enable_error_info(std::runtime_error("Failure sending input event : ")) << boost::errinfo_errno(errno)); } } mia::InputSender::ActiveTransfer::~ActiveTransfer() { unsubscribe(); } void mia::InputSender::ActiveTransfer::unsubscribe() { bool expected = true; if (std::atomic_compare_exchange_strong(&subscribed, &expected, false)) state.main_loop->unregister_fd_handler(this); } void mia::InputSender::ActiveTransfer::subscribe() { bool expected = false; if (std::atomic_compare_exchange_strong(&subscribed, &expected, true)) state.main_loop->register_fd_handler( {publisher.getChannel()->getFd()}, this, [this](int) { on_finish_signal(); }); } droidinput::status_t mia::InputSender::ActiveTransfer::send_key_event(uint32_t seq, MirEvent const& event) { int32_t repeat_count = 0; auto input_event = mir_event_get_input_event(&event); auto key_event = mir_input_event_get_keyboard_event(input_event); auto const android_action = mia::android_keyboard_action_from_mir(repeat_count, mir_keyboard_event_action(key_event)); std::chrono::nanoseconds const event_time{mir_input_event_get_event_time(input_event)}; auto const flags = 0; return publisher.publishKeyEvent(seq, mir_input_event_get_device_id(input_event), AINPUT_SOURCE_KEYBOARD, android_action, flags, mir_keyboard_event_key_code(key_event), mir_keyboard_event_scan_code(key_event), mia::android_modifiers_from_mir(mir_keyboard_event_modifiers(key_event)), repeat_count, event.key.cookie, event_time, event_time); } droidinput::status_t mia::InputSender::ActiveTransfer::send_touch_event(uint32_t seq, MirEvent const& event) { droidinput::status_t ret = droidinput::OK; droidinput::PointerCoords coords[MIR_INPUT_EVENT_MAX_POINTER_COUNT]; droidinput::PointerProperties properties[MIR_INPUT_EVENT_MAX_POINTER_COUNT]; auto input_event = mir_event_get_input_event(&event); auto touch = mir_input_event_get_touch_event(input_event); std::chrono::nanoseconds const event_time{mir_input_event_get_event_time(input_event)}; auto const x_offset = 0.0f; auto const y_offset = 0.0f; auto const x_precision = 0; auto const y_precision = 0; auto const flags = 0; auto const edge_flags = 0; auto const button_state = 0; struct StateChange { int android_action; size_t index; }; std::vector state_changes; for (size_t i = 0, e = mir_touch_event_point_count(touch); i != e; ++i) { auto const action = mir_touch_event_action(touch, i); if (action == mir_touch_action_down) state_changes.push_back(StateChange{AMOTION_EVENT_ACTION_DOWN, i}); if (action == mir_touch_action_up) state_changes.push_back(StateChange{AMOTION_EVENT_ACTION_UP, i}); } if (state_changes.empty()) state_changes.push_back(StateChange{AMOTION_EVENT_ACTION_MOVE, 0}); for (auto state_change : state_changes) { std::memset(&coords, 0, sizeof(coords)); std::memset(&properties, 0, sizeof(properties)); int contacts_in_event = 0; int action_index = 0; for (size_t i = 0, e = mir_touch_event_point_count(touch); i != e; ++i) { auto const action = mir_touch_event_action(touch, i); if (i == state_change.index) action_index = contacts_in_event; // before a touch up state change got processed it is treated as 'change', skipped otherwise // after a touch down state change got processed it is treated as 'change', skipped otherwise if (i == state_change.index || (i < state_change.index && action != mir_touch_action_up) || (i > state_change.index && action != mir_touch_action_down)) { coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_X, mir_touch_event_axis_value(touch, i, mir_touch_axis_x)); coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_Y, mir_touch_event_axis_value(touch, i, mir_touch_axis_y)); coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, mir_touch_event_axis_value(touch, i, mir_touch_axis_touch_major)); coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, mir_touch_event_axis_value(touch, i, mir_touch_axis_touch_minor)); coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_SIZE, mir_touch_event_axis_value(touch, i, mir_touch_axis_size)); coords[contacts_in_event].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, mir_touch_event_axis_value(touch, i, mir_touch_axis_pressure)); properties[contacts_in_event].toolType = mia::android_tool_type_from_mir(mir_touch_event_tooltype(touch, i)); properties[contacts_in_event].id = mir_touch_event_id(touch, i); ++contacts_in_event; } } state_change.android_action |= (action_index << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); ret = publisher.publishMotionEvent(seq, mir_input_event_get_device_id(input_event), AINPUT_SOURCE_TOUCHSCREEN, state_change.android_action, flags, edge_flags, mia::android_modifiers_from_mir(mir_touch_event_modifiers(touch)), button_state, x_offset, y_offset, x_precision, y_precision, event.motion.cookie, event_time, event_time, contacts_in_event, properties, coords); } return ret; } droidinput::status_t mia::InputSender::ActiveTransfer::send_pointer_event(uint32_t seq, MirEvent const& event) { droidinput::PointerCoords pointer_coord; droidinput::PointerProperties pointer_properties; std::memset(&pointer_coord, 0, sizeof(pointer_coord)); std::memset(&pointer_properties, 0, sizeof(pointer_properties)); auto input_event = mir_event_get_input_event(&event); auto pointer = mir_input_event_get_pointer_event(input_event); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_X, mir_pointer_event_axis_value(pointer, mir_pointer_axis_x)); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_Y, mir_pointer_event_axis_value(pointer, mir_pointer_axis_y)); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, mir_pointer_event_axis_value(pointer, mir_pointer_axis_hscroll)); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, mir_pointer_event_axis_value(pointer, mir_pointer_axis_vscroll)); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_RX, mir_pointer_event_axis_value(pointer, mir_pointer_axis_relative_x)); pointer_coord.setAxisValue(AMOTION_EVENT_AXIS_RY, mir_pointer_event_axis_value(pointer, mir_pointer_axis_relative_y)); pointer_properties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; pointer_properties.id = 0; std::chrono::nanoseconds const event_time{mir_input_event_get_event_time(input_event)}; auto const x_offset = 0.0f; auto const y_offset = 0.0f; auto const x_precision = 0; auto const y_precision = 0; auto const flags = 0; auto const edge_flags = 0; return publisher.publishMotionEvent( seq, mir_input_event_get_device_id(input_event), AINPUT_SOURCE_MOUSE, mia::android_pointer_action_from_mir(mir_pointer_event_action(pointer), mir_pointer_event_buttons(pointer)), flags, edge_flags, mia::android_modifiers_from_mir(mir_pointer_event_modifiers(pointer)), mia::android_pointer_buttons_from_mir(mir_pointer_event_buttons(pointer)), x_offset, y_offset, x_precision, y_precision, event.motion.cookie, event_time, event_time, 1, &pointer_properties, &pointer_coord); } void mia::InputSender::ActiveTransfer::on_finish_signal() { uint32_t sequence; bool handled; while(droidinput::OK == publisher.receiveFinishedSignal(&sequence, &handled)); } bool mia::InputSender::ActiveTransfer::used_for_surface(input::Surface const* surface) const { return this->surface == surface; } ./src/server/input/android/input_channel_factory.cpp0000644000015600001650000000170612676616125023053 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "input_channel_factory.h" #include "android_input_channel.h" namespace mi = mir::input; namespace mia = mi::android; std::shared_ptr mia::InputChannelFactory::make_input_channel() { return std::make_shared(); } ./src/server/input/android/android_input_channel.h0000644000015600001650000000254312676616125022471 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_ANDROID_INPUT_CHANNEL_H_ #define MIR_INPUT_ANDROID_INPUT_CHANNEL_H_ #include "mir/input/input_channel.h" namespace android { class InputChannel; } namespace droidinput = android; namespace mir { namespace input { namespace android { class AndroidInputChannel : public InputChannel { public: explicit AndroidInputChannel(); virtual ~AndroidInputChannel(); int client_fd() const; int server_fd() const; protected: AndroidInputChannel(AndroidInputChannel const&) = delete; AndroidInputChannel& operator=(AndroidInputChannel const&) = delete; private: int s_fd, c_fd; }; } } } // namespace mir #endif // MIR_INPUT_ANDROID_INPUT_CHANNEL_H_ ./src/server/input/android/android_input_constants.h0000644000015600001650000000204112676616125023066 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_ANDROID_INPUT_CONSTANTS_H_ #define MIR_INPUT_ANDROID_INPUT_CONSTANTS_H_ namespace mir { namespace input { namespace android { static const bool DispatchEnabled = true; static const bool DispatchDisabled = false; static const bool DispatchFrozen = true; static const bool DispatchUnfrozen = false; } } } #endif // MIR_INPUT_ANDROID_INPUT_CONSTANTS_H ./src/server/input/android/android_input_channel.cpp0000644000015600001650000000225212676616125023021 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "android_input_channel.h" #include #include namespace mia = mir::input::android; namespace droidinput = android; mia::AndroidInputChannel::AndroidInputChannel() { droidinput::InputChannel::openInputFdPair(s_fd, c_fd); } mia::AndroidInputChannel::~AndroidInputChannel() { close(s_fd); close(c_fd); } int mia::AndroidInputChannel::client_fd() const { return c_fd; } int mia::AndroidInputChannel::server_fd() const { return s_fd; } ./src/server/input/android/input_channel_factory.h0000644000015600001650000000235512676616125022521 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_ANDROID_INPUT_CHANNEL_FACTORY_H_ #define MIR_INPUT_ANDROID_INPUT_CHANNEL_FACTORY_H_ #include "mir/input/input_channel_factory.h" namespace mir { namespace input { namespace android { class InputChannelFactory : public mir::input::InputChannelFactory { public: std::shared_ptr make_input_channel() override; InputChannelFactory() = default; InputChannelFactory(InputChannelFactory const&) = delete; InputChannelFactory& operator=(InputChannelFactory const&) = delete; }; } } } // namespace mir #endif ./src/server/input/android/CMakeLists.txt0000644000015600001650000000037112676616125020526 0ustar jenkinsjenkinslist( APPEND INPUT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/android_input_channel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/input_channel_factory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/input_sender.cpp ) set( INPUT_SOURCES ${INPUT_SOURCES} PARENT_SCOPE ) ./src/server/input/builtin_cursor_images.cpp0000644000015600001650000000336712676616125021452 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "builtin_cursor_images.h" #include "mir/graphics/cursor_image.h" #include "default-theme.h" #include namespace mi = mir::input; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { struct CursorImage : public mg::CursorImage { CursorImage(CursorData const* const cursor_data) : cursor_data(cursor_data) {} void const* as_argb_8888() const { return cursor_data->pixel_data; } geom::Size size() const { return { cursor_data->width, cursor_data->height }; } geom::Displacement hotspot() const { return {cursor_data->hotspot_x, cursor_data->hotspot_y}; } CursorData const* const cursor_data; }; } std::shared_ptr mi::BuiltinCursorImages::image(std::string const& cursor_name, geom::Size const& /* size */) { auto const match = std::find_if(begin(cursor_data), end(cursor_data), [&](CursorData const& data) { return cursor_name == data.name; }); if (match != end(cursor_data)) return std::make_shared(&*match); return {}; } ./src/server/input/touchspot_controller.cpp0000644000015600001650000001122412676616125021344 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "touchspot_controller.h" #include "touchspot_image.c" #include "mir/geometry/displacement.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/buffer_properties.h" #include "mir/graphics/buffer.h" #include "mir/graphics/renderable.h" #include "mir/geometry/dimensions.h" #include "mir/input/scene.h" namespace mi = mir::input; namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { geom::Size const touchspot_size = {touchspot_image.width, touchspot_image.height}; MirPixelFormat const touchspot_pixel_format = mir_pixel_format_argb_8888; } class mi::TouchspotRenderable : public mg::Renderable { public: TouchspotRenderable(std::shared_ptr const& buffer) : buffer_(buffer), position({0, 0}) { } // mg::Renderable mg::Renderable::ID id() const override { return this; } std::shared_ptr buffer() const override { return buffer_; } geom::Rectangle screen_position() const override { return {position, buffer_->size()}; } float alpha() const override { return 1.0; } glm::mat4 transformation() const override { return glm::mat4(); } bool shaped() const override { return true; } // TouchspotRenderable void move_center_to(geom::Point pos) { std::lock_guard lg(guard); position = pos - geom::Displacement{0.5*touchspot_image.width, 0.5*touchspot_image.height}; } private: std::shared_ptr const buffer_; std::mutex guard; geom::Point position; }; mi::TouchspotController::TouchspotController(std::shared_ptr const& allocator, std::shared_ptr const& scene) : touchspot_buffer(allocator->alloc_buffer({touchspot_size, touchspot_pixel_format, mg::BufferUsage::software})), scene(scene), enabled(false), renderables_in_use(0) { unsigned int const pixels_size = touchspot_size.width.as_uint32_t()*touchspot_size.height.as_uint32_t() * MIR_BYTES_PER_PIXEL(touchspot_pixel_format); touchspot_buffer->write(touchspot_image.pixel_data, pixels_size); } void mi::TouchspotController::visualize_touches(std::vector const& touches) { // The compositor is unable to track damage to the touchspot renderables via the SurfaceObserver // interface as it does with application window surfaces. So if our last action is moving a spot // we must ask the scene to emit a scene changed. In the case of adding or removing a visualiza- // tion we expect the scene to handle this for us. bool must_update_scene = false; { std::lock_guard lg(guard); unsigned int const num_touches = enabled ? touches.size() : 0; while (touchspot_renderables.size() < num_touches) touchspot_renderables.push_back(std::make_shared(touchspot_buffer)); for (unsigned int i = 0; i < num_touches; i++) { auto const& renderable = touchspot_renderables[i]; renderable->move_center_to(touches[i].touch_location); if (i >= renderables_in_use) scene->add_input_visualization(renderable); must_update_scene = true; } for (unsigned int i = num_touches; i < renderables_in_use; i++) scene->remove_input_visualization(touchspot_renderables[i]); renderables_in_use = num_touches; } // release mutex // TODO (hackish): We may have just moved renderables which with the current // architecture of surface observers will not trigger a propagation to the // compositor damage callback we need this "emit_scene_changed". if (must_update_scene) scene->emit_scene_changed(); } void mi::TouchspotController::enable() { std::lock_guard lg(guard); enabled = true; } void mi::TouchspotController::disable() { std::lock_guard lg(guard); enabled = false; } ./src/server/input/event_filter_chain_dispatcher.cpp0000644000015600001650000000440412676616125023111 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "event_filter_chain_dispatcher.h" namespace mi = mir::input; mi::EventFilterChainDispatcher::EventFilterChainDispatcher( std::initializer_list const> const& values, std::shared_ptr const& next_dispatcher) : filters(values.begin(), values.end()), next_dispatcher(next_dispatcher) { } // TODO: It probably makes sense to provide keymapped events. bool mi::EventFilterChainDispatcher::handle(MirEvent const& event) { std::lock_guard lg(filter_guard); auto it = filters.begin(); while (it != filters.end()) { auto filter = (*it).lock(); if (!filter) { it = filters.erase(it); continue; } if (filter->handle(event)) return true; ++it; } return false; } void mi::EventFilterChainDispatcher::append(std::shared_ptr const& filter) { std::lock_guard lg(filter_guard); filters.push_back(filter); } void mi::EventFilterChainDispatcher::prepend(std::shared_ptr const& filter) { std::lock_guard lg(filter_guard); filters.insert(filters.begin(), filter); } bool mi::EventFilterChainDispatcher::dispatch(MirEvent const& event) { if (!handle(event)) return next_dispatcher->dispatch(event); return true; } // Should we start/stop dispatch of filter chain here? // Why would we want to? void mi::EventFilterChainDispatcher::start() { next_dispatcher->start(); } void mi::EventFilterChainDispatcher::stop() { next_dispatcher->stop(); } ./src/server/input/event_filter_chain_dispatcher.h0000644000015600001650000000341712676616125022561 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_EVENT_FILTER_CHAIN_DISPATCHER_H_ #define MIR_INPUT_EVENT_FILTER_CHAIN_DISPATCHER_H_ #include "mir/input/composite_event_filter.h" #include "mir/input/input_dispatcher.h" #include #include namespace mir { namespace input { class EventFilterChainDispatcher : public CompositeEventFilter, public mir::input::InputDispatcher { public: EventFilterChainDispatcher( std::initializer_list const> const& values, std::shared_ptr const& next_dispatcher); // CompositeEventFilter bool handle(MirEvent const& event) override; void append(std::shared_ptr const& filter) override; void prepend(std::shared_ptr const& filter) override; // InputDispatcher bool dispatch(MirEvent const& event) override; void start() override; void stop() override; private: std::mutex filter_guard; std::vector> filters; std::shared_ptr const next_dispatcher; }; } } #endif // MIR_INPUT_EVENT_FILTER_CHAIN_DISPATCHER_H_ ./src/server/input/default_input_device_hub.h0000644000015600001650000001037012676616157021544 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_DEFAULT_INPUT_DEVICE_HUB_H_ #define MIR_INPUT_DEFAULT_INPUT_DEVICE_HUB_H_ #include "default_event_builder.h" #include "mir/input/input_device_registry.h" #include "mir/input/input_sink.h" #include "mir/input/seat.h" #include "mir/input/input_device_hub.h" #include "mir/input/input_device_info.h" #include "mir_toolkit/event.h" #include #include #include #include namespace mir { class ServerActionQueue; namespace frontend { class EventSink; } namespace cookie { class Authority; } namespace dispatch { class Dispatchable; class MultiplexingDispatchable; class ActionQueue; } namespace input { class InputSink; class InputDeviceObserver; class DefaultDevice; class Seat; class DefaultInputDeviceHub : public InputDeviceRegistry, public InputDeviceHub { public: DefaultInputDeviceHub(std::shared_ptr const& sink, std::shared_ptr const& seat, std::shared_ptr const& input_multiplexer, std::shared_ptr const& observer_queue, std::shared_ptr const& cookie_authority); // InputDeviceRegistry - calls from mi::Platform void add_device(std::shared_ptr const& device) override; void remove_device(std::shared_ptr const& device) override; // InputDeviceHub - calls from server components / shell void add_observer(std::shared_ptr const&) override; void remove_observer(std::weak_ptr const&) override; void for_each_input_device(std::function const& callback) override; private: void update_spots(); void add_device_handle(std::shared_ptr const& handle); void remove_device_handle(MirInputDeviceId id); MirInputDeviceId create_new_device_id(); std::shared_ptr const seat; std::shared_ptr const sink; std::shared_ptr const input_dispatchable; std::mutex observer_guard; std::shared_ptr const observer_queue; std::shared_ptr const device_queue; std::shared_ptr const cookie_authority; struct RegisteredDevice : public InputSink { public: RegisteredDevice(std::shared_ptr const& dev, MirInputDeviceId dev_id, std::shared_ptr const& multiplexer, std::shared_ptr const& cookie_authority, std::shared_ptr const& handle); void handle_input(MirEvent& event) override; mir::geometry::Rectangle bounding_rectangle() const override; bool device_matches(std::shared_ptr const& dev) const; void start(std::shared_ptr const& seat); void stop(); MirInputDeviceId id(); std::shared_ptr seat; const std::shared_ptr handle; private: MirInputDeviceId device_id; DefaultEventBuilder builder; std::shared_ptr const device; std::shared_ptr const multiplexer; }; std::vector> handles; std::vector> devices; std::vector> observers; MirInputDeviceId device_id_generator; }; } } #endif ./src/server/input/default_device.cpp0000644000015600001650000001101212676616157020014 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #include "default_device.h" #include "mir/dispatch/action_queue.h" #include "mir/input/device_capability.h" #include "mir/input/input_device.h" #include "mir/input/touchpad_configuration.h" #include "mir/input/pointer_configuration.h" #include #include namespace mi = mir::input; mi::DefaultDevice::DefaultDevice(MirInputDeviceId id, std::shared_ptr const& actions, InputDevice& device) : device_id{id}, device{device}, info(device.get_device_info()), pointer{device.get_pointer_settings()}, touchpad{device.get_touchpad_settings()}, actions{actions} { } mi::DeviceCapabilities mi::DefaultDevice::capabilities() const { return info.capabilities; } std::string mi::DefaultDevice::name() const { return info.name; } std::string mi::DefaultDevice::unique_id() const { return info.unique_id; } MirInputDeviceId mi::DefaultDevice::id() const { return device_id; } mir::optional_value mi::DefaultDevice::pointer_configuration() const { if (!pointer.is_set()) return {}; auto const& settings = pointer.value(); return PointerConfiguration(settings.handedness, settings.acceleration, settings.cursor_acceleration_bias, settings.horizontal_scroll_scale, settings.vertical_scroll_scale); } mir::optional_value mi::DefaultDevice::touchpad_configuration() const { if (!touchpad.is_set()) return {}; auto const& settings = touchpad.value(); return TouchpadConfiguration(settings.click_mode, settings.scroll_mode, settings.button_down_scroll_button, settings.tap_to_click, settings.disable_while_typing, settings.disable_with_mouse, settings.middle_mouse_button_emulation); } void mi::DefaultDevice::apply_pointer_configuration(mi::PointerConfiguration const& conf) { if (!pointer.is_set()) BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot apply a pointer configuration")); if (conf.cursor_acceleration_bias < -1.0 || conf.cursor_acceleration_bias > 1.0) BOOST_THROW_EXCEPTION(std::invalid_argument("Cursor acceleration bias out of range")); PointerSettings settings; settings.handedness = conf.handedness; settings.acceleration = conf.acceleration; settings.cursor_acceleration_bias = conf.cursor_acceleration_bias; settings.vertical_scroll_scale = conf.vertical_scroll_scale; settings.horizontal_scroll_scale = conf.horizontal_scroll_scale; pointer = settings; actions->enqueue([settings = std::move(settings), dev=&device] { dev->apply_settings(settings); }); } void mi::DefaultDevice::apply_touchpad_configuration(mi::TouchpadConfiguration const& conf) { if (!touchpad.is_set()) BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot apply a touchpad configuration")); if (conf.scroll_mode & mir_touchpad_scroll_mode_button_down_scroll && conf.button_down_scroll_button == mi::no_scroll_button) BOOST_THROW_EXCEPTION(std::invalid_argument("No scroll button configured")); TouchpadSettings settings; settings.click_mode = conf.click_mode; settings.scroll_mode = conf.scroll_mode; settings.button_down_scroll_button = conf.button_down_scroll_button; settings.disable_with_mouse = conf.disable_with_mouse; settings.disable_while_typing = conf.disable_while_typing; settings.tap_to_click = conf.tap_to_click; settings.middle_mouse_button_emulation = conf.middle_mouse_button_emulation; touchpad = settings; actions->enqueue([settings = std::move(settings), dev=&device] { dev->apply_settings(settings); }); } ./src/server/input/basic_seat.h0000644000015600001650000000323512676616157016623 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_BASIC_SEAT_H_ #define MIR_BASIC_SEAT_H_ #include "mir/input/seat.h" #include "mir/frontend/event_sink.h" #include "seat_input_device_tracker.h" #include namespace mir { namespace input { class TouchVisualizer; class CursorListener; class InputRegion; class InputDispatcher; class BasicSeat : public Seat { public: BasicSeat(std::shared_ptr const& dispatcher, std::shared_ptr const& touch_visualizer, std::shared_ptr const& cursor_listener, std::shared_ptr const& input_region); // Seat methods: void add_device(Device const& device) override; void remove_device(Device const& device) override; void dispatch_event(MirEvent& event) override; geometry::Rectangle get_rectangle_for(Device const& dev) override; private: SeatInputDeviceTracker input_state_tracker; std::shared_ptr const input_region; }; } } #endif ./src/server/input/basic_seat.cpp0000644000015600001650000000416112676616157017155 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #include "basic_seat.h" #include "mir/input/device.h" #include "mir/input/input_region.h" #include namespace mi = mir::input; namespace mf = mir::frontend; mi::BasicSeat::BasicSeat(std::shared_ptr const& dispatcher, std::shared_ptr const& touch_visualizer, std::shared_ptr const& cursor_listener, std::shared_ptr const& input_region) : input_state_tracker{dispatcher, touch_visualizer, cursor_listener, input_region}, input_region{input_region} { } void mi::BasicSeat::add_device(input::Device const& device) { input_state_tracker.add_device(device.id()); } void mi::BasicSeat::remove_device(input::Device const& device) { input_state_tracker.remove_device(device.id()); } void mi::BasicSeat::dispatch_event(MirEvent& event) { input_state_tracker.dispatch(event); } mir::geometry::Rectangle mi::BasicSeat::get_rectangle_for(input::Device const&) { // TODO: With knowledge of the outputs attached to this seat and the output the given input // device is associated this method should only return the rectangle of that output. For now // we rely on the existing workaround in DisplayInputRegion::bounding_rectangle() which // assumes that only the first output may have a touch screen associated to it. return input_region->bounding_rectangle(); } ./src/server/input/input_probe.cpp0000644000015600001650000001066712676616157017416 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/input/input_probe.h" #include "mir/input/platform.h" #include "mir/options/configuration.h" #include "mir/options/option.h" #include "mir/shared_library_prober.h" #include "mir/shared_library.h" #include "mir/log.h" #include "mir/libname.h" #include namespace mi = mir::input; namespace mo = mir::options; namespace { mir::UniqueModulePtr create_input_platform( mir::SharedLibrary const& lib, mir::options::Option const& options, std::shared_ptr const& cleanup_registry, std::shared_ptr const& registry, std::shared_ptr const& report) { auto desc = lib.load_function("describe_input_module", MIR_SERVER_INPUT_PLATFORM_VERSION)(); auto create = lib.load_function("create_input_platform", MIR_SERVER_INPUT_PLATFORM_VERSION); auto result = create(options, cleanup_registry, registry, report); mir::log_info( "Selected input driver: %s (version: %d.%d.%d)", desc->name, desc->major_version, desc->minor_version, desc->micro_version); return result; } } mir::UniqueModulePtr mi::probe_input_platforms( mo::Option const& options, std::shared_ptr const& emergency_cleanup, std::shared_ptr const& device_registry, std::shared_ptr const& input_report, mir::SharedLibraryProberReport& prober_report) { auto reject_platform_priority = mi::PlatformPriority::dummy; std::shared_ptr platform_module; std::vector module_names; auto const module_selector = [&](std::shared_ptr const& module) { try { auto const probe = module->load_function( "probe_input_platform", MIR_SERVER_INPUT_PLATFORM_VERSION); if (probe(options) > reject_platform_priority) { platform_module = module; return Selection::quit; } } catch (std::runtime_error const&) { // Assume we were handed a SharedLibrary that's not an input module of the correct vintage. } return Selection::persist; }; if (options.is_set(mo::platform_input_lib)) { reject_platform_priority = PlatformPriority::unsupported; module_selector(std::make_shared(options.get(mo::platform_input_lib))); } else { select_libraries_for_path(options.get(mo::platform_path), module_selector, prober_report); } if (!platform_module) BOOST_THROW_EXCEPTION(std::runtime_error{"No appropriate input platform module found"}); return create_input_platform(*platform_module, options, emergency_cleanup, device_registry, input_report); } auto mi::input_platform_from_graphics_module( graphics::Platform const& graphics_platform, options::Option const& options, std::shared_ptr const& emergency_cleanup, std::shared_ptr const& device_registry, std::shared_ptr const& input_report) -> mir::UniqueModulePtr { try { // Yes, this is dirty code that assumes the object layout. Sorry! auto* const vtab = (void*&)(graphics_platform); SharedLibrary const platform_module{detail::libname_impl(vtab)}; return create_input_platform(platform_module, options, emergency_cleanup, device_registry, input_report); } catch (std::runtime_error const&) { // Assume the graphics platform is not also an input module. return {}; } } ./src/server/input/touchspot_controller.h0000644000015600001650000000404212676616125021011 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_TOUCHSPOT_CONTROLLER_H_ #define MIR_INPUT_TOUCHSPOT_CONTROLLER_H_ #include "mir/input/touch_visualizer.h" #include #include namespace mir { namespace graphics { class GraphicBufferAllocator; class Buffer; class Renderable; } namespace input { class Scene; class TouchspotRenderable; /// Receives touchspot events out of the input stack and manages appearance /// of touchspot renderables for visualization. Touchspot visualization is /// disabled by default and must be enabled through a call to ::enable. class TouchspotController : public TouchVisualizer { public: TouchspotController(std::shared_ptr const& allocator, std::shared_ptr const& scene); virtual ~TouchspotController() = default; void enable() override; void disable() override; void visualize_touches(std::vector const& touches) override; protected: TouchspotController(TouchspotController const&) = delete; TouchspotController& operator=(TouchspotController const&) = delete; private: std::shared_ptr touchspot_buffer; std::shared_ptr scene; std::mutex guard; bool enabled; unsigned int renderables_in_use; std::vector> touchspot_renderables; }; } } #endif // MIR_INPUT_TOUCHSPOT_CONTROLLER_H_ ./src/server/input/seat_input_device_tracker.h0000644000015600001650000000572112676616125021730 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_SEAT_INPUT_DEVICE_TRACKER_H #define MIR_INPUT_SEAT_INPUT_DEVICE_TRACKER_H #include "mir/input/touch_visualizer.h" #include "mir/geometry/point.h" #include "mir_toolkit/event.h" #include #include namespace mir { namespace input { class CursorListener; class InputRegion; class InputDispatcher; /* * The SeatInputDeviceTracker bundles the input device properties of a group of devices defined by a seat: * - a single cursor position, * - modifier key states (i.e alt, ctrl ..) * - a single mouse button state for all pointing devices * - visible touch spots */ class SeatInputDeviceTracker { public: SeatInputDeviceTracker(std::shared_ptr const& dispatcher, std::shared_ptr const& touch_visualizer, std::shared_ptr const& cursor_listener, std::shared_ptr const& input_region); void add_device(MirInputDeviceId); void remove_device(MirInputDeviceId); void dispatch(MirEvent & event); MirPointerButtons button_state() const; geometry::Point cursor_position() const; MirInputEventModifiers event_modifier() const; MirInputEventModifiers event_modifier(MirInputDeviceId) const; private: void update_seat_properties(MirInputEvent const* event); void update_cursor(MirPointerEvent const* event); void update_spots(); void update_states(); std::shared_ptr const dispatcher; std::shared_ptr const touch_visualizer; std::shared_ptr const cursor_listener; std::shared_ptr const input_region; struct DeviceData { DeviceData() {} bool update_modifier(MirKeyboardAction action, int scan_code); bool update_button_state(MirPointerButtons button_state); bool update_spots(MirTouchEvent const* event); MirInputEventModifiers mod{0}; MirPointerButtons buttons{0}; std::vector spots; }; mir::geometry::Point cursor_pos; MirInputEventModifiers modifier; MirPointerButtons buttons; std::unordered_map device_data; std::vector spots; }; } } #endif ./src/server/input/builtin_cursor_images.h0000644000015600001650000000207112676616125021106 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_BUILTIN_CURSOR_IMAGES_H_ #define MIR_INPUT_BUILTIN_CURSOR_IMAGES_H_ #include "mir/input/cursor_images.h" namespace mir { namespace input { class BuiltinCursorImages : public CursorImages { public: std::shared_ptr image(std::string const& cursor_name, geometry::Size const& size); }; } } #endif /* MIR_INPUT_BUILTIN_CURSOR_IMAGES_H_ */ ./src/server/input/display_input_region.cpp0000644000015600001650000000336512676616125021307 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "display_input_region.h" #include "mir/graphics/display_configuration.h" #include "mir/geometry/rectangle.h" #include "mir/geometry/rectangles.h" namespace mi = mir::input; namespace mg = mir::graphics; namespace geom = mir::geometry; void mi::DisplayInputRegion::set_input_rectangles(geometry::Rectangles const& config) { std::unique_lock lock(rectangle_guard); rectangles = config; } geom::Rectangle mi::DisplayInputRegion::bounding_rectangle() { //TODO: This region is mainly used for scaling touchscreen coordinates, so the caller // probably wants the full list of rectangles. Additional work is needed // to group a touchscreen with a display. So for now, just return the view area // of the first display, as that matches the most common systems (laptops with touchscreens, // phone/tablets with touchscreens). if (rectangles.size() != 0) return *rectangles.begin(); else return geom::Rectangle{}; } void mi::DisplayInputRegion::confine(geom::Point& point) { rectangles.confine(point); } ./src/server/input/null_input_targeter.h0000644000015600001650000000220712676616125020605 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_NULL_INPUT_TARGETER_H_ #define MIR_INPUT_NULL_INPUT_TARGETER_H_ #include "mir/shell/input_targeter.h" namespace mir { namespace input { class InputChannel; struct NullInputTargeter : public shell::InputTargeter { NullInputTargeter() = default; virtual ~NullInputTargeter() noexcept(true) = default; void set_focus(std::shared_ptr const&) override { } void clear_focus() override { } }; } } #endif ./src/server/input/cursor_controller.h0000644000015600001650000000406712676616125020305 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_CURSOR_CONTROLLER_H_ #define MIR_INPUT_CURSOR_CONTROLLER_H_ #include "mir/input/cursor_listener.h" #include "mir/geometry/point.h" #include #include namespace mir { namespace graphics { class Cursor; class CursorImage; } namespace scene { class Observer; } namespace input { class Scene; class CursorController : public CursorListener { public: CursorController(std::shared_ptr const& input_targets, std::shared_ptr const& cursor, std::shared_ptr const& default_cursor_image); virtual ~CursorController(); void cursor_moved_to(float abs_x, float abs_y); // Trigger an update of the cursor image without cursor motion, e.g. // in response to scene changes. void update_cursor_image(); private: std::shared_ptr const input_targets; std::shared_ptr const cursor; std::shared_ptr const default_cursor_image; std::mutex cursor_state_guard; geometry::Point cursor_location; std::shared_ptr current_cursor; std::weak_ptr observer; void update_cursor_image_locked(std::unique_lock&); void set_cursor_image_locked(std::unique_lock&, std::shared_ptr const& image); }; } } #endif // MIR_INPUT_CURSOR_CONTROLLER_H_ ./src/server/input/input_modifier_utils.h0000644000015600001650000000173612676616125020762 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_INPUT_MODIFIER_UTILS_H_ #define MIR_INPUT_INPUT_MODIFIER_UTILS_H_ #include "mir_toolkit/event.h" namespace mir { namespace input { MirInputEventModifiers to_modifiers(int32_t scan_code); MirInputEventModifiers expand_modifiers(MirInputEventModifiers modifiers); } } #endif ./src/server/input/default_input_manager.cpp0000644000015600001650000001322012676616157021411 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "default_input_manager.h" #include "mir/input/platform.h" #include "mir/dispatch/action_queue.h" #include "mir/dispatch/multiplexing_dispatchable.h" #include "mir/dispatch/threaded_dispatcher.h" #include "mir/main_loop.h" #include "mir/thread_name.h" #include "mir/unwind_helpers.h" #include "mir/terminate_with_current_exception.h" #include namespace mi = mir::input; mi::DefaultInputManager::DefaultInputManager( std::shared_ptr const& multiplexer, std::shared_ptr const& platform) : platforms{platform}, multiplexer{multiplexer}, queue{std::make_shared()}, state{State::stopped} { } mi::DefaultInputManager::~DefaultInputManager() { stop(); } void mi::DefaultInputManager::add_platform(std::shared_ptr const& platform) { if (state == State::started) { queue->enqueue([this, platform]() { platforms.push_back(platform); platform->start(); multiplexer->add_watch(platform->dispatchable()); }); } else { queue->enqueue([this, platform]() { platforms.push_back(platform); }); } } void mi::DefaultInputManager::start() { auto expected = State::stopped; if (!state.compare_exchange_strong(expected, State::starting)) return; auto reset_to_stopped_on_failure = on_unwind([this] { auto expected = State::starting; state.compare_exchange_strong(expected, State::stopped); }); multiplexer->add_watch(queue); auto unregister_queue = on_unwind([this] { // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62258 // After using rethrow_exception() (and catching the exception), // all subsequent calls to uncaught_exception() return `true'. if (state == State::started) return; multiplexer->remove_watch(queue); }); auto started_promise = std::make_shared>(); auto started_future = started_promise->get_future(); /* * We need the starting-lambda to own started_promise so that it is guaranteed that * started_future gets signalled; either by ->set_value in the success path or * by the destruction of started_promise generating a broken_promise exception. */ queue->enqueue([this,promise = std::move(started_promise)]() { start_platforms(); promise->set_value(); }); input_thread = std::make_unique( "Mir/Input Reader", multiplexer, [this]() { stop_platforms(); multiplexer->remove_watch(queue); state = State::stopped; mir::terminate_with_current_exception(); }); started_future.wait(); expected = State::starting; state.compare_exchange_strong(expected, State::started); } void mi::DefaultInputManager::stop() { auto expected = State::started; if (!state.compare_exchange_strong(expected, State::stopping)) return; auto reset_to_started_on_failure = on_unwind([this] { auto expected = State::stopping; state.compare_exchange_strong(expected, State::started); }); auto const stop_promise = std::make_shared>(); queue->enqueue([stop_promise,this]() { stop_platforms(); stop_promise->set_value(); }); stop_promise->get_future().wait(); auto restore_platforms = on_unwind( [this] { // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62258 // After using rethrow_exception() (and catching the exception), // all subsequent calls to uncaught_exception() return `true'. if (state == State::stopped) return; queue->enqueue([this](){ start_platforms(); }); }); multiplexer->remove_watch(queue); auto register_queue = on_unwind([this] { // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62258 // After using rethrow_exception() (and catching the exception), // all subsequent calls to uncaught_exception() return `true'. if (state == State::stopped) return; multiplexer->add_watch(queue); }); input_thread.reset(); state = State::stopped; } void mi::DefaultInputManager::start_platforms() { for (auto const& platform : platforms) { platform->start(); multiplexer->add_watch(platform->dispatchable()); } } void mi::DefaultInputManager::stop_platforms() { for (auto const& platform : platforms) { multiplexer->remove_watch(platform->dispatchable()); platform->stop(); } } ./src/server/input/CMakeLists.txt0000644000015600001650000000140512676616125017105 0ustar jenkinsjenkinsinclude_directories(${MIR_3RD_PARTY_INCLUDE_DIRECTORIES}) include_directories(${MIR_ANDROID_INCLUDE_DIRECTORIES}) set( INPUT_SOURCES basic_seat.cpp builtin_cursor_images.cpp cursor_controller.cpp default_configuration.cpp default_device.cpp default_event_builder.cpp default_input_device_hub.cpp default_input_manager.cpp display_input_region.cpp event_filter_chain_dispatcher.cpp input_modifier_utils.cpp input_probe.cpp key_repeat_dispatcher.cpp null_input_channel_factory.cpp null_input_dispatcher.cpp seat_input_device_tracker.cpp surface_input_dispatcher.cpp touchspot_controller.cpp validator.cpp vt_filter.cpp ) add_subdirectory(android) add_library( mirinput OBJECT ${INPUT_SOURCES} ) uses_android_input(mirinput) ./src/server/input/default_input_device_hub.cpp0000644000015600001650000002035312676616157022101 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "default_input_device_hub.h" #include "default_device.h" #include "mir/input/input_device.h" #include "mir/input/input_device_observer.h" #include "mir/geometry/point.h" #include "mir/dispatch/multiplexing_dispatchable.h" #include "mir/dispatch/action_queue.h" #include "mir/frontend/event_sink.h" #include "mir/server_action_queue.h" #include "mir/cookie/authority.h" #define MIR_LOG_COMPONENT "Input" #include "mir/log.h" #include "boost/throw_exception.hpp" #include #include namespace mi = mir::input; namespace mf = mir::frontend; mi::DefaultInputDeviceHub::DefaultInputDeviceHub( std::shared_ptr const& sink, std::shared_ptr const& seat, std::shared_ptr const& input_multiplexer, std::shared_ptr const& observer_queue, std::shared_ptr const& cookie_authority) : seat{seat}, sink{sink}, input_dispatchable{input_multiplexer}, observer_queue(observer_queue), device_queue(std::make_shared()), cookie_authority(cookie_authority), device_id_generator{0} { input_dispatchable->add_watch(device_queue); } void mi::DefaultInputDeviceHub::add_device(std::shared_ptr const& device) { if (!device) BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid input device")); auto it = find_if(devices.cbegin(), devices.cend(), [&device](std::unique_ptr const& item) { return item->device_matches(device); }); if (it == end(devices)) { auto id = create_new_device_id(); auto handle = std::make_shared(id, device_queue, *device); // send input device info to observer loop.. devices.push_back(std::make_unique( device, id, input_dispatchable, cookie_authority, handle)); auto const& dev = devices.back(); seat->add_device(*handle); dev->start(seat); // pass input device handle to observer loop.. observer_queue->enqueue(this, [this, handle]() { add_device_handle(handle); }); } else { log_error("Input device %s added twice", device->get_device_info().name.c_str()); BOOST_THROW_EXCEPTION(std::logic_error("Input device already managed by server")); } } void mi::DefaultInputDeviceHub::remove_device(std::shared_ptr const& device) { if (!device) BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid input device")); auto pos = remove_if( begin(devices), end(devices), [&device,this](std::unique_ptr const& item) { if (item->device_matches(device)) { auto seat = item->seat; if (seat) { seat->remove_device(*item->handle); item->stop(); } // send input device info to observer queue.. observer_queue->enqueue( this, [this,id = item->id()]() { remove_device_handle(id); }); return true; } return false; }); if (pos == end(devices)) { log_error("Input device %s not found", device->get_device_info().name.c_str()); BOOST_THROW_EXCEPTION(std::logic_error("Input device not managed by server")); } devices.erase(pos, end(devices)); } mi::DefaultInputDeviceHub::RegisteredDevice::RegisteredDevice( std::shared_ptr const& dev, MirInputDeviceId device_id, std::shared_ptr const& multiplexer, std::shared_ptr const& cookie_authority, std::shared_ptr const& handle) : handle(handle), device_id(device_id), builder(device_id, cookie_authority), device(dev), multiplexer(multiplexer) { } MirInputDeviceId mi::DefaultInputDeviceHub::create_new_device_id() { return ++device_id_generator; } MirInputDeviceId mi::DefaultInputDeviceHub::RegisteredDevice::id() { return device_id; } void mi::DefaultInputDeviceHub::RegisteredDevice::handle_input(MirEvent& event) { auto type = mir_event_get_type(&event); if (type != mir_event_type_input) BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid input event received from device")); if (!seat) return; seat->dispatch_event(event); } bool mi::DefaultInputDeviceHub::RegisteredDevice::device_matches(std::shared_ptr const& dev) const { return dev == device; } void mi::DefaultInputDeviceHub::RegisteredDevice::start(std::shared_ptr const& seat) { this->seat = seat; device->start(this, &builder); } void mi::DefaultInputDeviceHub::RegisteredDevice::stop() { device->stop(); seat = nullptr; } mir::geometry::Rectangle mi::DefaultInputDeviceHub::RegisteredDevice::bounding_rectangle() const { if (!seat) BOOST_THROW_EXCEPTION(std::runtime_error("Device not started and has no seat assigned")); return seat->get_rectangle_for(*handle); } void mi::DefaultInputDeviceHub::add_observer(std::shared_ptr const& observer) { observer_queue->enqueue( this, [observer,this] { std::unique_lock lock(observer_guard); observers.push_back(observer); for (auto const& item : handles) { observer->device_added(item); } observer->changes_complete(); } ); } void mi::DefaultInputDeviceHub::for_each_input_device(std::function const& callback) { std::unique_lock lock(observer_guard); for (auto const& item : handles) callback(*item); } void mi::DefaultInputDeviceHub::remove_observer(std::weak_ptr const& element) { auto observer = element.lock(); observer_queue->enqueue(this, [observer, this] { std::unique_lock lock(observer_guard); observers.erase(remove(begin(observers), end(observers), observer), end(observers)); }); } void mi::DefaultInputDeviceHub::add_device_handle(std::shared_ptr const& handle) { std::unique_lock lock(observer_guard); handles.push_back(handle); sink->handle_input_device_change(handles); for (auto const& observer : observers) { observer->device_added(handles.back()); observer->changes_complete(); } } void mi::DefaultInputDeviceHub::remove_device_handle(MirInputDeviceId id) { std::unique_lock lock(observer_guard); auto handle_it = remove_if( begin(handles), end(handles), [this,&id](auto const& handle) { if (handle->id() != id) return false; for (auto const& observer : observers) { observer->device_removed(handle); observer->changes_complete(); } return true; }); if (handle_it == end(handles)) return; handles.erase(handle_it, end(handles)); sink->handle_input_device_change(handles); } ./src/server/input/null_input_channel_factory.cpp0000644000015600001650000000216612676616125022466 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "null_input_channel_factory.h" #include "mir/input/input_channel.h" namespace mi = mir::input; namespace { class NullInputChannel : public mi::InputChannel { int client_fd() const override { return -1; } int server_fd() const override { return -1; } }; } std::shared_ptr mi::NullInputChannelFactory::make_input_channel() { return std::make_shared(); } ./src/server/input/surface_input_dispatcher.h0000644000015600001650000000706012676616125021576 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_DEFAULT_INPUT_DISPATCHER_H_ #define MIR_INPUT_DEFAULT_INPUT_DISPATCHER_H_ #include "mir/input/input_dispatcher.h" #include "mir/shell/input_targeter.h" #include "mir/geometry/point.h" #include #include #include #include namespace mir { namespace scene { class Observer; class Surface; } namespace input { class Surface; class Scene; class SurfaceInputDispatcher : public mir::input::InputDispatcher, public shell::InputTargeter { public: SurfaceInputDispatcher(std::shared_ptr const& scene); ~SurfaceInputDispatcher(); // mir::input::InputDispatcher bool dispatch(MirEvent const& event) override; void start() override; void stop() override; // InputTargeter void set_focus(std::shared_ptr const& target) override; void clear_focus() override; private: void device_reset(MirInputDeviceId reset_device_id, std::chrono::nanoseconds when); bool dispatch_key(MirInputDeviceId id, MirKeyboardEvent const* kev); bool dispatch_pointer(MirInputDeviceId id, MirPointerEvent const* pev); bool dispatch_touch(MirInputDeviceId id, MirTouchEvent const* tev); void send_enter_exit_event(std::shared_ptr const& surface, MirPointerEvent const* triggering_ev, MirPointerAction action); std::shared_ptr find_target_surface(geometry::Point const& target); void set_focus_locked(std::lock_guard const&, std::shared_ptr const&); void surface_removed(scene::Surface* surface); struct KeyInputState { bool handle_event(MirInputDeviceId id, MirKeyboardEvent const* kev); bool press_key(MirInputDeviceId id, int scan_code); bool repeat_key(MirInputDeviceId id, int scan_code); bool release_key(MirInputDeviceId id, int scan_code); void clear(); std::unordered_map> depressed_scancodes; } focus_surface_key_state; // Look in to homognizing index on KeyInputState and PointerInputState (wrt to device id) struct PointerInputState { std::shared_ptr current_target; std::shared_ptr gesture_owner; }; std::unordered_map pointer_state_by_id; PointerInputState& ensure_pointer_state(MirInputDeviceId id); struct TouchInputState { std::shared_ptr gesture_owner; }; std::unordered_map touch_state_by_id; TouchInputState& ensure_touch_state(MirInputDeviceId id); std::shared_ptr const scene; std::shared_ptr scene_observer; std::mutex dispatcher_mutex; std::weak_ptr focus_surface; bool started; }; } } #endif // MIR_INPUT_DEFAULT_INPUT_DISPATCHER_H_ ./src/server/input/surface_input_dispatcher.cpp0000644000015600001650000003565412676616125022143 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "surface_input_dispatcher.h" #include "mir/input/scene.h" #include "mir/input/surface.h" #include "mir/scene/observer.h" #include "mir/scene/surface.h" #include "mir/events/event_builders.h" #include "mir/events/event_private.h" #include #include #include #include namespace mi = mir::input; namespace ms = mir::scene; namespace mev = mir::events; namespace geom = mir::geometry; namespace { struct InputDispatcherSceneObserver : public ms::Observer { InputDispatcherSceneObserver(std::function const& on_removed) : on_removed(on_removed) { } void surface_added(ms::Surface* surface) { (void) surface; } void surface_removed(ms::Surface* surface) { on_removed(surface); } void surfaces_reordered() { } void scene_changed() { } void surface_exists(ms::Surface* surface) { (void) surface; } void end_observation() { } std::function const on_removed; }; template void deliver(std::shared_ptr const& surface, T const* ev) { MirEvent to_deliver; to_deliver = *reinterpret_cast(ev); if (to_deliver.type == mir_event_type_motion) { auto sx = surface->input_bounds().top_left.x.as_int(); auto sy = surface->input_bounds().top_left.y.as_int(); for (unsigned i = 0; i < to_deliver.motion.pointer_count; i++) { to_deliver.motion.pointer_coordinates[i].x -= sx; to_deliver.motion.pointer_coordinates[i].y -= sy; } } surface->consume(&to_deliver); } } mi::SurfaceInputDispatcher::SurfaceInputDispatcher(std::shared_ptr const& scene) : scene(scene), started(false) { scene_observer = std::make_shared([this](ms::Surface* s){surface_removed(s);}); scene->add_observer(scene_observer); } mi::SurfaceInputDispatcher::~SurfaceInputDispatcher() { scene->remove_observer(scene_observer); } namespace { bool compare_surfaces(std::shared_ptr const& input_surface, ms::Surface *surface) { return input_surface.get() == static_cast(surface); } } void mi::SurfaceInputDispatcher::surface_removed(ms::Surface *surface) { std::lock_guard lg(dispatcher_mutex); auto strong_focus = focus_surface.lock(); if (strong_focus && compare_surfaces(strong_focus, surface)) { set_focus_locked(lg, nullptr); } for (auto& kv : pointer_state_by_id) { auto& state = kv.second; if (compare_surfaces(state.current_target, surface)) state.current_target.reset(); if (compare_surfaces(state.gesture_owner, surface)) state.gesture_owner.reset(); } for (auto& kv : touch_state_by_id) { auto& state = kv.second; if (compare_surfaces(state.gesture_owner, surface)) state.gesture_owner.reset(); } } void mi::SurfaceInputDispatcher::device_reset(MirInputDeviceId reset_device_id, std::chrono::nanoseconds /* when */) { std::lock_guard lg(dispatcher_mutex); if (!started) return; auto key_it = focus_surface_key_state.depressed_scancodes.find(reset_device_id); if (key_it != focus_surface_key_state.depressed_scancodes.end()) focus_surface_key_state.depressed_scancodes.erase(key_it); auto pointer_it = pointer_state_by_id.find(reset_device_id); if (pointer_it != pointer_state_by_id.end()) pointer_state_by_id.erase(pointer_it); auto touch_it = touch_state_by_id.find(reset_device_id); if (touch_it != touch_state_by_id.end()) touch_state_by_id.erase(touch_it); } bool mi::SurfaceInputDispatcher::dispatch_key(MirInputDeviceId id, MirKeyboardEvent const* kev) { std::lock_guard lg(dispatcher_mutex); if (!started) return false; auto strong_focus = focus_surface.lock(); if (!strong_focus) return false; if (!focus_surface_key_state.handle_event(id, kev)) return false; deliver(strong_focus, kev); return true; } namespace { void for_pressed_buttons(MirPointerEvent const* pev, std::function const& exec) { auto const buttons = { mir_pointer_button_primary, mir_pointer_button_secondary, mir_pointer_button_tertiary, mir_pointer_button_back, mir_pointer_button_forward }; for (auto button : buttons) if (mir_pointer_event_button_state(pev, button)) exec(button); } bool is_gesture_terminator(MirPointerEvent const* pev) { bool any_pressed = false; for_pressed_buttons(pev, [&any_pressed](MirPointerButton){ any_pressed = true; }); return !any_pressed && mir_pointer_event_action(pev) == mir_pointer_action_button_up; } } std::shared_ptr mi::SurfaceInputDispatcher::find_target_surface(geom::Point const& point) { std::shared_ptr top_target = nullptr; scene->for_each([&top_target, &point](std::shared_ptr const& target) { if (target->input_area_contains(point)) top_target = target; }); return top_target; } void mi::SurfaceInputDispatcher::send_enter_exit_event(std::shared_ptr const& surface, MirPointerEvent const* pev, MirPointerAction action) { auto iev = (MirInputEvent const*)pev; deliver(surface, &*mev::make_event(mir_input_event_get_device_id(iev), std::chrono::nanoseconds(mir_input_event_get_event_time(iev)), std::vector{}, mir_pointer_event_modifiers(pev), action, mir_pointer_event_buttons(pev), mir_pointer_event_axis_value(pev,mir_pointer_axis_x), mir_pointer_event_axis_value(pev,mir_pointer_axis_y), mir_pointer_event_axis_value(pev,mir_pointer_axis_hscroll), mir_pointer_event_axis_value(pev,mir_pointer_axis_vscroll), mir_pointer_event_axis_value(pev, mir_pointer_axis_relative_x), mir_pointer_event_axis_value(pev, mir_pointer_axis_relative_y))); } mi::SurfaceInputDispatcher::PointerInputState& mi::SurfaceInputDispatcher::ensure_pointer_state(MirInputDeviceId id) { pointer_state_by_id.insert(std::make_pair(id, PointerInputState())); return pointer_state_by_id[id]; } mi::SurfaceInputDispatcher::TouchInputState& mi::SurfaceInputDispatcher::ensure_touch_state(MirInputDeviceId id) { touch_state_by_id.insert(std::make_pair(id, TouchInputState())); return touch_state_by_id[id]; } bool mi::SurfaceInputDispatcher::dispatch_pointer(MirInputDeviceId id, MirPointerEvent const* pev) { std::lock_guard lg(dispatcher_mutex); auto action = mir_pointer_event_action(pev); auto& pointer_state = ensure_pointer_state(id); geom::Point event_x_y = { mir_pointer_event_axis_value(pev,mir_pointer_axis_x), mir_pointer_event_axis_value(pev,mir_pointer_axis_y) }; if (pointer_state.gesture_owner) { deliver(pointer_state.gesture_owner, pev); if (is_gesture_terminator(pev)) { pointer_state.gesture_owner.reset(); auto target = find_target_surface(event_x_y); if (pointer_state.current_target != target) { if (pointer_state.current_target) send_enter_exit_event(pointer_state.current_target, pev, mir_pointer_action_leave); pointer_state.current_target = target; if (target) send_enter_exit_event(target, pev, mir_pointer_action_enter); } } return true; } else if (action == mir_pointer_action_button_up) { // If we have an up but no gesture owner // then we never delivered the corresponding // down to anyone so we drop this event. return false; } else { auto target = find_target_surface(event_x_y); bool sent_ev = false; if (pointer_state.current_target != target) { if (pointer_state.current_target) send_enter_exit_event(pointer_state.current_target, pev, mir_pointer_action_leave); pointer_state.current_target = target; if (target) send_enter_exit_event(target, pev, mir_pointer_action_enter); sent_ev = true; } if (!target) return sent_ev; if (action == mir_pointer_action_button_down) { pointer_state.gesture_owner = target; } deliver(target, pev); return true; } return false; } namespace { bool is_gesture_start(MirTouchEvent const* tev) { auto const point_count = mir_touch_event_point_count(tev); for (auto i = 0u; i != point_count; ++i) if (mir_touch_event_action(tev, i) != mir_touch_action_down) return false; return true; } bool is_gesture_end(MirTouchEvent const* tev) { auto const point_count = mir_touch_event_point_count(tev); for (auto i = 0u; i != point_count; ++i) if (mir_touch_event_action(tev, i) != mir_touch_action_up) return false; return true; } } bool mi::SurfaceInputDispatcher::dispatch_touch(MirInputDeviceId id, MirTouchEvent const* tev) { std::lock_guard lg(dispatcher_mutex); auto& gesture_owner = ensure_touch_state(id).gesture_owner; // We record the gesture_owner if the event signifies the start of a new // gesture. This prevents gesture ownership from transfering in the event // a gesture receiver closes mid gesture (e.g. when a surface closes mid // swipe we do not want the surface under to receive events). This also // allows a gesture to continue outside of the target surface, providing // it started in the target surface. if (is_gesture_start(tev)) { geom::Point event_x_y = { mir_touch_event_axis_value(tev, 0, mir_touch_axis_x), mir_touch_event_axis_value(tev, 0, mir_touch_axis_y) }; gesture_owner = find_target_surface(event_x_y); } if (gesture_owner) { deliver(gesture_owner, tev); if (is_gesture_end(tev)) gesture_owner.reset(); return true; } return false; } bool mi::SurfaceInputDispatcher::dispatch(MirEvent const& event) { if (mir_event_get_type(&event) == mir_event_type_input_configuration) { auto idev = mir_event_get_input_configuration_event(&event); if (mir_input_configuration_event_get_action(idev) == mir_input_configuration_action_device_reset) device_reset(mir_input_configuration_event_get_device_id(idev), std::chrono::nanoseconds(mir_input_configuration_event_get_time(idev))); return true; } if (mir_event_get_type(&event) != mir_event_type_input) BOOST_THROW_EXCEPTION(std::logic_error("InputDispatcher got an unexpected event type")); auto iev = mir_event_get_input_event(&event); auto id = mir_input_event_get_device_id(iev); switch (mir_input_event_get_type(iev)) { case mir_input_event_type_key: return dispatch_key(id, mir_input_event_get_keyboard_event(iev)); case mir_input_event_type_touch: return dispatch_touch(id, mir_input_event_get_touch_event(iev)); case mir_input_event_type_pointer: return dispatch_pointer(id, mir_input_event_get_pointer_event(iev)); default: BOOST_THROW_EXCEPTION(std::logic_error("InputDispatcher got an input event of unknown type")); } return true; } void mi::SurfaceInputDispatcher::start() { std::lock_guard lg(dispatcher_mutex); started = true; } void mi::SurfaceInputDispatcher::stop() { std::lock_guard lg(dispatcher_mutex); focus_surface_key_state.clear(); pointer_state_by_id.clear(); touch_state_by_id.clear(); started = false; } void mi::SurfaceInputDispatcher::set_focus_locked(std::lock_guard const&, std::shared_ptr const& target) { focus_surface_key_state.clear(); focus_surface = target; } void mi::SurfaceInputDispatcher::set_focus(std::shared_ptr const& target) { std::lock_guard lg(dispatcher_mutex); set_focus_locked(lg, target); } void mi::SurfaceInputDispatcher::clear_focus() { std::lock_guard lg(dispatcher_mutex); set_focus_locked(lg, nullptr); } bool mi::SurfaceInputDispatcher::KeyInputState::handle_event(MirInputDeviceId id, MirKeyboardEvent const* kev) { auto action = mir_keyboard_event_action(kev); auto scan_code = mir_keyboard_event_scan_code(kev); if (action == mir_keyboard_action_up) { return release_key(id, scan_code); } else if (action == mir_keyboard_action_down) { return press_key(id, scan_code); } else if (action == mir_keyboard_action_repeat) { return repeat_key(id, scan_code); } return false; } bool mi::SurfaceInputDispatcher::KeyInputState::press_key(MirInputDeviceId id, int scan_code) { // First key press for a device if (depressed_scancodes.find(id) == depressed_scancodes.end()) { depressed_scancodes[id] = {}; } auto& device_key_state = depressed_scancodes[id]; if (device_key_state.find(scan_code) != device_key_state.end()) return false; device_key_state.insert(scan_code); return true; } bool mi::SurfaceInputDispatcher::KeyInputState::release_key(MirInputDeviceId id, int scan_code) { if (depressed_scancodes.find(id) == depressed_scancodes.end()) { return false; } auto& device_key_state = depressed_scancodes[id]; if (device_key_state.find(scan_code) == device_key_state.end()) return false; device_key_state.erase(scan_code); return true; } bool mi::SurfaceInputDispatcher::KeyInputState::repeat_key(MirInputDeviceId id, int scan_code) { if (depressed_scancodes.find(id) == depressed_scancodes.end()) return false; auto& device_key_state = depressed_scancodes[id]; if (device_key_state.find(scan_code) == device_key_state.end()) return false; return true; } void mi::SurfaceInputDispatcher::KeyInputState::clear() { depressed_scancodes.clear(); } ./src/server/input/seat_input_device_tracker.cpp0000644000015600001650000001625412676616157022273 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #include "seat_input_device_tracker.h" #include "mir/input/device.h" #include "mir/input/cursor_listener.h" #include "mir/input/input_region.h" #include "mir/input/input_dispatcher.h" #include "mir/geometry/displacement.h" #include "mir/events/event_builders.h" #include "input_modifier_utils.h" #include #include #include #include #include namespace mi = mir::input; namespace mev = mir::events; mi::SeatInputDeviceTracker::SeatInputDeviceTracker(std::shared_ptr const& dispatcher, std::shared_ptr const& touch_visualizer, std::shared_ptr const& cursor_listener, std::shared_ptr const& input_region) : dispatcher{dispatcher}, touch_visualizer{touch_visualizer}, cursor_listener{cursor_listener}, input_region{input_region}, modifier{0}, buttons{0} { } void mi::SeatInputDeviceTracker::add_device(MirInputDeviceId id) { device_data[id]; } void mi::SeatInputDeviceTracker::remove_device(MirInputDeviceId id) { auto stored_data = device_data.find(id); if (stored_data == end(device_data)) BOOST_THROW_EXCEPTION(std::logic_error("Modifier for unknown device changed")); bool state_update_needed = stored_data->second.mod != mir_input_event_modifier_none || stored_data->second.buttons != 0; bool spot_update_needed = !stored_data->second.spots.empty(); device_data.erase(stored_data); if (state_update_needed) update_states(); if (spot_update_needed) update_spots(); } void mi::SeatInputDeviceTracker::dispatch(MirEvent &event) { auto input_event = mir_event_get_input_event(&event); update_seat_properties(input_event); if (mir_input_event_type_key == mir_input_event_get_type(input_event)) mev::set_modifier(event, event_modifier(mir_input_event_get_device_id(input_event))); else mev::set_modifier(event, event_modifier()); if (mir_input_event_type_pointer == mir_input_event_get_type(input_event)) { mev::set_cursor_position(event, cursor_position()); mev::set_button_state(event, button_state()); } dispatcher->dispatch(event); } MirInputEventModifiers mi::SeatInputDeviceTracker::event_modifier() const { return expand_modifiers(modifier); } MirInputEventModifiers mi::SeatInputDeviceTracker::event_modifier(MirInputDeviceId id) const { auto stored_data = device_data.find(id); if (stored_data == end(device_data)) BOOST_THROW_EXCEPTION(std::logic_error("Modifier for unknown device requested")); return expand_modifiers(stored_data->second.mod); } void mi::SeatInputDeviceTracker::update_seat_properties(MirInputEvent const* event) { auto id = mir_input_event_get_device_id(event); auto stored_data = device_data.find(id); if (stored_data == end(device_data)) BOOST_THROW_EXCEPTION(std::logic_error("Event of unknown device received")); switch(mir_input_event_get_type(event)) { case mir_input_event_type_key: { auto const* key = mir_input_event_get_keyboard_event(event); if (stored_data->second.update_modifier(mir_keyboard_event_action(key), mir_keyboard_event_scan_code(key))) update_states(); break; } case mir_input_event_type_touch: if (stored_data->second.update_spots(mir_input_event_get_touch_event(event))) update_spots(); break; case mir_input_event_type_pointer: { auto const* pointer = mir_input_event_get_pointer_event(event); update_cursor(pointer); if(stored_data->second.update_button_state(mir_pointer_event_buttons(pointer))) update_states(); break; } default: break; } } bool mi::SeatInputDeviceTracker::DeviceData::update_modifier(MirKeyboardAction key_action, int scan_code) { auto mod_change = to_modifiers(scan_code); if (mod_change == 0 || key_action == mir_keyboard_action_repeat) return false; if (key_action == mir_keyboard_action_down) mod |= mod_change; else if (key_action == mir_keyboard_action_up) mod &= ~mod_change; return true; } bool mi::SeatInputDeviceTracker::DeviceData::update_button_state(MirPointerButtons button_state) { if (buttons == button_state) return false; buttons = button_state; return true; } bool mi::SeatInputDeviceTracker::DeviceData::update_spots(MirTouchEvent const* event) { auto count = mir_touch_event_point_count(event); spots.clear(); for (decltype(count) i = 0; i != count; ++i) { if (mir_touch_event_action(event, i) == mir_touch_action_up) continue; spots.push_back({{mir_touch_event_axis_value(event, i, mir_touch_axis_x), mir_touch_event_axis_value(event, i, mir_touch_axis_y)}, mir_touch_event_axis_value(event, i, mir_touch_axis_pressure)}); } return true; } void mi::SeatInputDeviceTracker::update_spots() { spots.clear(); for (auto const& dev : device_data) spots.insert(end(spots), begin(dev.second.spots), end(dev.second.spots)); touch_visualizer->visualize_touches(spots); } void mi::SeatInputDeviceTracker::update_states() { std::tie(modifier, buttons) = std::accumulate(begin(device_data), end(device_data), std::make_pair(MirInputEventModifiers{0}, MirPointerButtons{0}), [](auto const& acc, auto const& item) { return std::make_pair(acc.first | item.second.mod, acc.second | item.second.buttons); }); } mir::geometry::Point mi::SeatInputDeviceTracker::cursor_position() const { return cursor_pos; } MirPointerButtons mi::SeatInputDeviceTracker::button_state() const { return buttons; } void mi::SeatInputDeviceTracker::update_cursor(MirPointerEvent const* event) { mir::geometry::Displacement movement{ mir_pointer_event_axis_value(event, mir_pointer_axis_relative_x), mir_pointer_event_axis_value(event, mir_pointer_axis_relative_y), }; cursor_pos = cursor_pos + movement; input_region->confine(cursor_pos); cursor_listener->cursor_moved_to(cursor_pos.x.as_float(), cursor_pos.y.as_float()); } ./src/server/glib_main_loop_sources.cpp0000644000015600001650000004244112676616125020434 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis * Alberto Aguirre */ #include "mir/glib_main_loop_sources.h" #include "mir/lockable_callback.h" #include "mir/raii.h" #include #include #include #include #include #include #include #include #include namespace md = mir::detail; namespace { class GSourceRef { public: GSourceRef(GSource* gsource) : gsource{gsource} {} ~GSourceRef() { if (gsource) g_source_unref(gsource); } operator GSource*() const { return gsource; } private: GSourceRef(GSourceRef const&) = delete; GSourceRef& operator=(GSourceRef const&) = delete; GSource* gsource; }; #ifndef GLIB_HAS_FIXED_LP_1401488 gboolean idle_callback(gpointer) { return G_SOURCE_REMOVE; // Remove the idle source. Only needed once. } void destroy_idler(gpointer user_data) { // idle_callback is never totally guaranteed to be called. However this // function is, so we do the unref here... auto gsource = static_cast(user_data); g_source_unref(gsource); } #endif } /***************** * GSourceHandle * *****************/ md::GSourceHandle::GSourceHandle() : GSourceHandle(nullptr, [](GSource*){}) { } md::GSourceHandle::GSourceHandle( GSource* gsource, std::function const& terminate_dispatch) : gsource(gsource), terminate_dispatch(terminate_dispatch) { } md::GSourceHandle::GSourceHandle(GSourceHandle&& other) : gsource(std::move(other.gsource)), terminate_dispatch(std::move(other.terminate_dispatch)) { other.gsource = nullptr; other.terminate_dispatch = [](GSource*){}; } md::GSourceHandle& md::GSourceHandle::operator=(GSourceHandle other) { std::swap(other.gsource, gsource); std::swap(other.terminate_dispatch, terminate_dispatch); return *this; } md::GSourceHandle::~GSourceHandle() { if (gsource) { #ifdef GLIB_HAS_FIXED_LP_1401488 g_source_destroy(gsource); g_source_unref(gsource); #else /* * https://bugs.launchpad.net/mir/+bug/1401488 * We would like to g_source_unref(gsource); here but it's unsafe * to do so. The reason being we could be in some arbitrary thread, * and so the main loop might be mid-iteration of the same source * making callbacks. And glib lacks protection to prevent sources * getting fully free()'d mid-callback (TODO: fix glib?). So we defer * the final unref of the source to a point in the loop when it's safe. */ if (!g_source_is_destroyed(gsource)) { auto main_context = g_source_get_context(gsource); g_source_destroy(gsource); auto idler = g_idle_source_new(); g_source_set_callback(idler, idle_callback, gsource, destroy_idler); g_source_attach(idler, main_context); g_source_unref(idler); } else { // The source is already destroyed so it's safe to unref now g_source_unref(gsource); } #endif } } void md::GSourceHandle::ensure_no_further_dispatch() { if (gsource) { terminate_dispatch(gsource); } } md::GSourceHandle::operator GSource*() const { return gsource; } /***************** * add_*_gsource * *****************/ void md::add_idle_gsource( GMainContext* main_context, int priority, std::function const& callback) { struct IdleContext { static gboolean static_call(IdleContext* ctx) { ctx->callback(); return G_SOURCE_REMOVE; } static void static_destroy(IdleContext* ctx) { delete ctx; } std::function const callback; }; GSourceRef gsource{g_idle_source_new()}; g_source_set_priority(gsource, priority); g_source_set_callback( gsource, reinterpret_cast(&IdleContext::static_call), new IdleContext{callback}, reinterpret_cast(&IdleContext::static_destroy)); g_source_attach(gsource, main_context); } void md::add_server_action_gsource( GMainContext* main_context, void const* owner, std::function const& action, std::function const& should_dispatch) { struct ServerActionContext { void const* const owner; std::function const action; std::function const should_dispatch; }; struct ServerActionGSource { GSource gsource; ServerActionContext ctx; bool ctx_constructed; static gboolean prepare(GSource* source, gint *timeout) { *timeout = -1; auto const& ctx = reinterpret_cast(source)->ctx; return ctx.should_dispatch(ctx.owner); } static gboolean check(GSource* source) { auto const& ctx = reinterpret_cast(source)->ctx; return ctx.should_dispatch(ctx.owner); } static gboolean dispatch(GSource* source, GSourceFunc, gpointer) { auto const& ctx = reinterpret_cast(source)->ctx; ctx.action(); return FALSE; } static void finalize(GSource* source) { auto const sa_gsource = reinterpret_cast(source); if (sa_gsource->ctx_constructed) sa_gsource->ctx.~ServerActionContext(); } }; static GSourceFuncs gsource_funcs{ ServerActionGSource::prepare, ServerActionGSource::check, ServerActionGSource::dispatch, ServerActionGSource::finalize, nullptr, nullptr }; GSourceRef gsource{g_source_new(&gsource_funcs, sizeof(ServerActionGSource))}; auto const sa_gsource = reinterpret_cast(static_cast(gsource)); sa_gsource->ctx_constructed = false; new (&sa_gsource->ctx) decltype(sa_gsource->ctx){owner, action, should_dispatch}; sa_gsource->ctx_constructed = true; g_source_attach(gsource, main_context); } md::GSourceHandle md::add_timer_gsource( GMainContext* main_context, std::shared_ptr const& clock, std::shared_ptr const& handler, std::function const& exception_handler, time::Timestamp target_time) { struct TimerContext { TimerContext(std::shared_ptr const& clock, std::shared_ptr const& handler, std::function const& exception_handler, time::Timestamp target_time) : clock{clock}, handler{handler}, exception_handler{exception_handler}, target_time{target_time}, enabled{true} { } std::shared_ptr clock; std::shared_ptr handler; std::function exception_handler; time::Timestamp target_time; std::recursive_mutex mutex; bool enabled; }; struct TimerGSource { GSource gsource; TimerContext ctx; bool ctx_constructed; static gboolean prepare(GSource* source, gint *timeout) { auto const& ctx = reinterpret_cast(source)->ctx; auto const now = ctx.clock->now(); bool const ready = (now >= ctx.target_time); if (ready) *timeout = -1; else *timeout = std::chrono::duration_cast( ctx.clock->min_wait_until(ctx.target_time)).count(); return ready; } static gboolean check(GSource* source) { auto const& ctx = reinterpret_cast(source)->ctx; auto const now = ctx.clock->now(); bool const ready = (now >= ctx.target_time); return ready; } static gboolean dispatch(GSource* source, GSourceFunc, gpointer) { auto& ctx = reinterpret_cast(source)->ctx; try { // Attempt to preserve locking order during callback dispatching // so we acquire the caller's lock before our own. auto& handler = *ctx.handler; std::lock_guard handler_lock{handler}; std::lock_guard lock{ctx.mutex}; if (ctx.enabled) handler(); } catch(...) { ctx.exception_handler(); } return FALSE; } static void finalize(GSource* source) { auto const timer_gsource = reinterpret_cast(source); if (timer_gsource->ctx_constructed) timer_gsource->ctx.~TimerContext(); } static void disable(GSource* source) { auto& ctx = reinterpret_cast(source)->ctx; std::lock_guard lock{ctx.mutex}; ctx.enabled = false; } }; static GSourceFuncs gsource_funcs{ TimerGSource::prepare, TimerGSource::check, TimerGSource::dispatch, TimerGSource::finalize, nullptr, nullptr }; GSourceHandle gsource{ g_source_new(&gsource_funcs, sizeof(TimerGSource)), [](GSource* gsource) { TimerGSource::disable(gsource); }}; auto const timer_gsource = reinterpret_cast(static_cast(gsource)); timer_gsource->ctx_constructed = false; new (&timer_gsource->ctx) TimerContext{clock, handler, exception_handler, target_time}; timer_gsource->ctx_constructed = true; g_source_attach(gsource, main_context); return gsource; } /************* * FdSources * *************/ struct md::FdSources::FdContext { FdContext(std::function const& handler) : handler{handler}, enabled{true} { } void disable_callback() { std::lock_guard lock{mutex}; enabled = false; } static gboolean static_call(int fd, GIOCondition, FdContext* ctx) { std::lock_guardmutex)> lock{ctx->mutex}; if (ctx->enabled) ctx->handler(fd); return G_SOURCE_CONTINUE; } static void static_destroy(FdContext* ctx) { delete ctx; } private: std::function handler; bool enabled; std::recursive_mutex mutex; }; struct md::FdSources::FdSource { ~FdSource() { gsource.ensure_no_further_dispatch(); } GSourceHandle gsource; void const* const owner; }; md::FdSources::FdSources(GMainContext* main_context) : main_context{main_context} { } md::FdSources::~FdSources() = default; void md::FdSources::add( int fd, void const* owner, std::function const& handler) { auto const fd_context = new FdContext{handler}; // g_source_destroy() may be called while the source is about to be // dispatched, so there is no guarantee that after g_source_destroy() // returns any associated handlers won't be called. Since we want a // stronger guarantee we need to disable the callback manually before // destruction. GSourceHandle gsource{ g_unix_fd_source_new(fd, G_IO_IN), [=] (GSource*) { fd_context->disable_callback(); }}; g_source_set_callback( gsource, reinterpret_cast(&FdContext::static_call), fd_context, reinterpret_cast(&FdContext::static_destroy)); std::lock_guard lock{sources_mutex}; sources.emplace_back(new FdSource{std::move(gsource), owner}); g_source_attach(sources.back()->gsource, main_context); } void md::FdSources::remove_all_owned_by(void const* owner) { std::lock_guard lock{sources_mutex}; auto const new_end = std::remove_if( sources.begin(), sources.end(), [&] (std::unique_ptr const& fd_source) { return fd_source->owner == owner; }); sources.erase(new_end, sources.end()); } /***************** * SignalSources * *****************/ class md::SignalSources::SourceRegistration { public: SourceRegistration(int write_fd) : write_fd{write_fd} { init_write_fds(); add_write_fd(); } ~SourceRegistration() { remove_write_fd(); } static void notify_sources_of_signal(int sig) { for (auto const& write_fd : write_fds) { // There is a benign race here: write_fd may have changed // between checking and using it. This doesn't matter // since in the worst case we will call write() with an invalid // fd (-1) which is harmless. if (write_fd >= 0 && write(write_fd, &sig, sizeof(sig))) {} } } private: void init_write_fds() { static std::once_flag once; std::call_once(once, [&] { for (auto& wfd : write_fds) wfd = -1; }); } void add_write_fd() { for (auto& wfd : write_fds) { int v = -1; if (wfd.compare_exchange_strong(v, write_fd)) return; } BOOST_THROW_EXCEPTION( std::runtime_error( "Failed to add signal write fd. Have you created too many main loops?")); } void remove_write_fd() { for (auto& wfd : write_fds) { int v = write_fd; if (wfd.compare_exchange_strong(v, -1)) break; } } static int const max_write_fds{10}; static std::array, max_write_fds> write_fds; int const write_fd; }; std::array,10> md::SignalSources::SourceRegistration::write_fds; md::SignalSources::SignalSources(md::FdSources& fd_sources) : fd_sources(fd_sources) { int pipefd[2]; if (pipe(pipefd) == -1) { BOOST_THROW_EXCEPTION( std::system_error(errno, std::system_category(), "Failed to create signal pipe")); } signal_read_fd = mir::Fd(pipefd[0]); signal_write_fd = mir::Fd(pipefd[1]); fcntl(signal_read_fd, F_SETFD, FD_CLOEXEC); fcntl(signal_write_fd, F_SETFD, FD_CLOEXEC); // Make the signal_write_fd non-blocking, to avoid blocking in the signal handler fcntl(signal_write_fd, F_SETFL, O_NONBLOCK); source_registration.reset(new SourceRegistration{signal_write_fd}); fd_sources.add(signal_read_fd, this, [this] (int) { dispatch_pending_signal(); }); } md::SignalSources::~SignalSources() { for (auto const& handled : handled_signals) sigaction(handled.first, &handled.second, nullptr); fd_sources.remove_all_owned_by(this); } void md::SignalSources::dispatch_pending_signal() { auto const sig = read_pending_signal(); if (sig != -1) dispatch_signal(sig); } int md::SignalSources::read_pending_signal() { int sig = -1; size_t total = 0; do { auto const nread = read( signal_read_fd, reinterpret_cast(&sig) + total, sizeof(sig) - total); if (nread < 0) { if (errno != EINTR) return -1; } else { total += nread; } } while (total < sizeof(sig)); return sig; } void md::SignalSources::add( std::vector const& sigs, std::function const& handler) { handlers.add({sigs, handler}); for (auto sig : sigs) ensure_signal_is_handled(sig); } void md::SignalSources::ensure_signal_is_handled(int sig) { std::lock_guard lock{handled_signals_mutex}; if (handled_signals.find(sig) != handled_signals.end()) return; static int const no_flags{0}; struct sigaction old_action; struct sigaction new_action; new_action.sa_handler = SourceRegistration::notify_sources_of_signal; sigfillset(&new_action.sa_mask); new_action.sa_flags = no_flags; if (sigaction(sig, &new_action, &old_action) == -1) { std::stringstream msg; msg << "Failed to register action for signal " << sig; BOOST_THROW_EXCEPTION( std::system_error(errno, std::system_category(), msg.str())); } handled_signals.emplace(sig, old_action); } void md::SignalSources::dispatch_signal(int sig) { handlers.for_each( [&] (HandlerElement const& element) { if (std::find(element.sigs.begin(), element.sigs.end(), sig) != element.sigs.end()) element.handler(sig); }); } ./src/server/terminate_with_current_exception.cpp0000644000015600001650000000275212676616125022563 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/terminate_with_current_exception.h" #include #include #include #include namespace { std::exception_ptr termination_exception; std::mutex termination_exception_mutex; } void mir::clear_termination_exception() { std::lock_guard lock{termination_exception_mutex}; termination_exception = nullptr; } void mir::check_for_termination_exception() { std::lock_guard lock{termination_exception_mutex}; if (termination_exception) std::rethrow_exception(termination_exception); } void mir::terminate_with_current_exception() { std::lock_guard lock{termination_exception_mutex}; if (!termination_exception) { termination_exception = std::current_exception(); kill(getpid(), SIGTERM); } } ./src/server/run_mir.cpp0000644000015600001650000000600512676616125015366 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/run_mir.h" #include "mir/terminate_with_current_exception.h" #include "mir/display_server.h" #include "mir/fatal.h" #include "mir/main_loop.h" #include "mir/server_configuration.h" #include "mir/frontend/connector.h" #include "mir/raii.h" #include "mir/emergency_cleanup.h" #include #include #include #include #include #include #include #include namespace { auto const intercepted = { SIGQUIT, SIGABRT, SIGFPE, SIGSEGV, SIGBUS }; std::weak_ptr weak_emergency_cleanup; extern "C" void perform_emergency_cleanup() { if (auto emergency_cleanup = weak_emergency_cleanup.lock()) { weak_emergency_cleanup.reset(); (*emergency_cleanup)(); } } extern "C" { typedef void (*sig_handler)(int); } volatile sig_handler old_handler[SIGUNUSED] = { nullptr }; extern "C" void fatal_signal_cleanup(int sig) { perform_emergency_cleanup(); signal(sig, old_handler[sig]); raise(sig); } } void mir::run_mir(ServerConfiguration& config, std::function init) { run_mir(config, init, {}); } void mir::run_mir( ServerConfiguration& config, std::function init, std::function const& terminator) { DisplayServer* server_ptr{nullptr}; clear_termination_exception(); auto const main_loop = config.the_main_loop(); if (terminator) { main_loop->register_signal_handler({SIGINT, SIGTERM}, terminator); } else { main_loop->register_signal_handler( {SIGINT, SIGTERM}, [&server_ptr](int) { assert(server_ptr); server_ptr->stop(); }); } FatalErrorStrategy fatal_error_strategy{config.the_fatal_error_strategy()}; DisplayServer server(config); server_ptr = &server; weak_emergency_cleanup = config.the_emergency_cleanup(); static std::atomic concurrent_calls{0}; auto const raii = raii::paired_calls( [&]{ if (!concurrent_calls++) for (auto sig : intercepted) old_handler[sig] = signal(sig, fatal_signal_cleanup); }, [&]{ if (!--concurrent_calls) for (auto sig : intercepted) signal(sig, old_handler[sig]); }); init(server); server.run(); check_for_termination_exception(); } ./src/server/version.h.in0000644000015600001650000000421312676616125015451 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef MIR_VERSION_H_ #define MIR_VERSION_H_ #include "mir_toolkit/mir_version_number.h" // ======================================================================================= // ==> mir/version.h is generated by cmake. DO NOT EDIT! (edit version.h.in instead) <== // ======================================================================================= /** * MIR_SERVER_MAJOR_VERSION * * The major server API version. This will increase once per API incompatible release. * * See also: http://semver.org/ */ #define MIR_SERVER_MAJOR_VERSION (@MIR_VERSION_MAJOR@) /** * MIR_SERVER_MINOR_VERSION * * The minor server API version. This will increase each time new backwards-compatible API * is added, and will reset to 0 each time MIR_SERVER_MAJOR_VERSION is incremented. * * See also: http://semver.org/ */ #define MIR_SERVER_MINOR_VERSION (@MIR_VERSION_MINOR@) /** * MIR_SERVER_MICRO_VERSION * * The micro server API version. This will increment each release. * This is usually uninteresting for server code, but may be of use in selecting * whether to use a feature that has previously been buggy. * * This corresponds to the PATCH field of http://semver.org/ */ #define MIR_SERVER_MICRO_VERSION (@MIR_VERSION_PATCH@) /** * MIR_SERVER_VERSION * * The current version of the Mir server headers in use. */ #define MIR_SERVER_VERSION \ MIR_VERSION_NUMBER(MIR_SERVER_MAJOR_VERSION, \ MIR_SERVER_MINOR_VERSION, \ MIR_SERVER_MICRO_VERSION) #endif /* MIR_VERSION_H_ */ ./src/server/symbols.map0000644000015600001650000013063012676616157015405 0ustar jenkinsjenkinsMIR_SERVER_0.20 { global: extern "C++" { # Symbols not yet picked up by script vtable?for?mir::input::NullInputDispatcher; VTT?for?mir::DefaultServerConfiguration; VTT?for?mir::shell::ShellWrapper; VTT?for?mir::shell::SystemCompositorWindowManager; # The following symbols come from running a script over the generated docs. Vis: # ../tools/process_doxygen_xml.py doc/xml/*.xml | grep "^mirserver public" | sed "s/mirserver public: / /" | sort mir::check_for_termination_exception*; mir::clear_termination_exception*; mir::compositor::Compositor::Compositor*; mir::compositor::Compositor::operator*; mir::compositor::DisplayBufferCompositor::?DisplayBufferCompositor*; mir::compositor::DisplayBufferCompositor::DisplayBufferCompositor*; mir::compositor::DisplayBufferCompositorFactory::?DisplayBufferCompositorFactory*; mir::compositor::DisplayBufferCompositorFactory::DisplayBufferCompositorFactory*; mir::compositor::DisplayBufferCompositorFactory::operator*; mir::compositor::DisplayBufferCompositor::operator*; mir::compositor::DisplayListener::?DisplayListener*; mir::compositor::DisplayListener::DisplayListener*; mir::compositor::DisplayListener::operator*; mir::compositor::SceneElement::operator*; mir::compositor::SceneElement::?SceneElement*; mir::compositor::SceneElement::SceneElement*; mir::compositor::Scene::Scene*; mir::frontend::BufferSink::?BufferSink*; mir::frontend::BufferSink::BufferSink*; mir::frontend::BufferSink::operator*; mir::frontend::BufferStream::?BufferStream*; mir::frontend::BufferStream::BufferStream*; mir::frontend::BufferStream::operator*; mir::frontend::PromptSession::operator*; mir::frontend::PromptSession::?PromptSession*; mir::frontend::PromptSession::PromptSession*; mir::frontend::SessionAuthorizer::operator*; mir::frontend::SessionAuthorizer::?SessionAuthorizer*; mir::frontend::SessionAuthorizer::SessionAuthorizer*; mir::frontend::SessionCredentials::gid*; mir::frontend::SessionCredentials::pid*; mir::frontend::SessionCredentials::SessionCredentials*; mir::frontend::SessionCredentials::uid*; mir::frontend::SessionMediatorReport::?SessionMediatorReport*; mir::frontend::Session::operator*; mir::frontend::Session::?Session*; mir::frontend::Session::Session*; mir::frontend::Surface::operator*; mir::frontend::Surface::?Surface*; mir::frontend::Surface::Surface*; mir::graphics::CloneDisplayConfigurationPolicy::apply_to*; mir::graphics::DisplayConfigurationReport::?DisplayConfigurationReport*; mir::graphics::DisplayConfigurationReport::DisplayConfigurationReport*; mir::graphics::DisplayConfigurationReport::operator*; mir::graphics::SideBySideDisplayConfigurationPolicy::apply_to*; mir::graphics::SingleDisplayConfigurationPolicy::apply_to*; mir::input::CursorImages::?CursorImages*; mir::input::CursorImages::CursorImages*; mir::input::CursorImages::operator*; mir::input::CursorListener::?CursorListener*; mir::input::CursorListener::CursorListener*; mir::input::CursorListener::operator*; mir::input::Device::?Device*; mir::input::Device::Device*; mir::input::EventFilter::?EventFilter*; mir::input::EventFilter::EventFilter*; mir::input::EventFilter::operator*; mir::input::InputChannel::InputChannel*; mir::input::InputChannel::operator*; mir::input::InputDeviceHub::?InputDeviceHub*; mir::input::InputDeviceHub::InputDeviceHub*; mir::input::InputDeviceHub::operator*; mir::input::InputDeviceObserver::?InputDeviceObserver*; mir::input::InputDeviceObserver::InputDeviceObserver*; mir::input::InputDeviceObserver::operator*; mir::input::InputDispatcher::?InputDispatcher*; mir::input::InputManager::InputManager*; mir::input::InputManager::operator*; mir::input::Surface::operator*; mir::input::Surface::?Surface*; mir::input::Surface::Surface*; mir::input::TouchVisualizer::operator*; mir::input::TouchVisualizer::?TouchVisualizer*; mir::input::TouchVisualizer::TouchVisualizer*; mir::LockableCallback::?LockableCallback*; mir::LockableCallback::LockableCallback*; mir::LockableCallback::operator*; mir::report_exception*; mir::scene::ApplicationNotRespondingDetector::?ApplicationNotRespondingDetector*; mir::scene::ApplicationNotRespondingDetector::ApplicationNotRespondingDetector*; mir::scene::ApplicationNotRespondingDetector::Observer::?Observer*; mir::scene::ApplicationNotRespondingDetector::Observer::Observer*; mir::scene::a_surface*; mir::scene::BufferStreamFactory::?BufferStreamFactory*; mir::scene::BufferStreamFactory::BufferStreamFactory*; mir::scene::BufferStreamFactory::operator*; mir::scene::CoordinateTranslator::?CoordinateTranslator*; mir::scene::NullSessionListener::?NullSessionListener*; mir::scene::NullSessionListener::NullSessionListener*; mir::scene::NullSessionListener::operator*; mir::scene::NullSurfaceObserver::alpha_set_to*; mir::scene::NullSurfaceObserver::attrib_changed*; mir::scene::NullSurfaceObserver::client_surface_close_requested*; mir::scene::NullSurfaceObserver::cursor_image_removed*; mir::scene::NullSurfaceObserver::cursor_image_set_to*; mir::scene::NullSurfaceObserver::frame_posted*; mir::scene::NullSurfaceObserver::hidden_set_to*; mir::scene::NullSurfaceObserver::keymap_changed*; mir::scene::NullSurfaceObserver::moved_to*; mir::scene::NullSurfaceObserver::?NullSurfaceObserver*; mir::scene::NullSurfaceObserver::NullSurfaceObserver*; mir::scene::NullSurfaceObserver::operator*; mir::scene::NullSurfaceObserver::orientation_set_to*; mir::scene::NullSurfaceObserver::reception_mode_set_to*; mir::scene::NullSurfaceObserver::renamed*; mir::scene::NullSurfaceObserver::resized_to*; mir::scene::NullSurfaceObserver::transformation_set_to*; mir::scene::Observer::?Observer*; mir::scene::Observer::Observer*; mir::scene::Observer::operator*; mir::scene::operator*; mir::scene::PromptSessionListener::operator*; mir::scene::PromptSessionListener::?PromptSessionListener*; mir::scene::PromptSessionListener::PromptSessionListener*; mir::scene::PromptSessionManager::operator*; mir::scene::PromptSessionManager::?PromptSessionManager*; mir::scene::PromptSessionManager::PromptSessionManager*; mir::scene::SessionCoordinator::operator*; mir::scene::SessionCoordinator::?SessionCoordinator*; mir::scene::SessionCoordinator::SessionCoordinator*; mir::scene::SessionListener::operator*; mir::scene::SessionListener::?SessionListener*; mir::scene::SessionListener::SessionListener*; mir::scene::SurfaceCreationParameters::of_buffer_usage*; mir::scene::SurfaceCreationParameters::of_name*; mir::scene::SurfaceCreationParameters::of_pixel_format*; mir::scene::SurfaceCreationParameters::of_position*; mir::scene::SurfaceCreationParameters::of_size*; mir::scene::SurfaceCreationParameters::of_type*; mir::scene::SurfaceCreationParameters::SurfaceCreationParameters*; mir::scene::SurfaceCreationParameters::with_aux_rect*; mir::scene::SurfaceCreationParameters::with_buffer_stream*; mir::scene::SurfaceCreationParameters::with_edge_attachment*; mir::scene::SurfaceCreationParameters::with_input_mode*; mir::scene::SurfaceCreationParameters::with_output_id*; mir::scene::SurfaceCreationParameters::with_parent_id*; mir::scene::SurfaceCreationParameters::with_preferred_orientation*; mir::scene::SurfaceCreationParameters::with_state*; mir::scene::SurfaceFactory::?SurfaceFactory*; mir::scene::SurfaceFactory::SurfaceFactory*; mir::scene::SurfaceObserver::operator*; mir::scene::SurfaceObserver::?SurfaceObserver*; mir::scene::SurfaceObserver::SurfaceObserver*; mir::ServerActionQueue::operator*; mir::ServerActionQueue::?ServerActionQueue*; mir::ServerActionQueue::ServerActionQueue*; mir::Server::add_configuration_option*; mir::Server::add_emergency_cleanup*; mir::Server::add_init_callback*; mir::Server::apply_settings*; mir::Server::exited_normally*; mir::Server::get_options*; mir::Server::open_client_socket*; mir::Server::open_prompt_socket*; mir::Server::override_the_application_not_responding_detector*; mir::Server::override_the_compositor*; mir::Server::override_the_cookie_authority*; mir::Server::override_the_coordinate_translator*; mir::Server::override_the_cursor_images*; mir::Server::override_the_display_buffer_compositor_factory*; mir::Server::override_the_display_configuration_report*; mir::Server::override_the_gl_config*; mir::Server::override_the_host_lifecycle_event_listener*; mir::Server::override_the_input_dispatcher*; mir::Server::override_the_logger*; mir::Server::override_the_prompt_session_listener*; mir::Server::override_the_prompt_session_manager*; mir::Server::override_the_server_status_listener*; mir::Server::override_the_session_authorizer*; mir::Server::override_the_session_listener*; mir::Server::override_the_session_mediator_report*; mir::Server::override_the_shell*; mir::Server::override_the_window_manager_builder*; mir::Server::run*; mir::Server::Server*; mir::Server::set_command_line*; mir::Server::set_command_line_handler*; mir::Server::set_config_filename*; mir::Server::set_exception_handler*; mir::Server::set_terminator*; mir::ServerStatusListener::operator*; mir::ServerStatusListener::?ServerStatusListener*; mir::ServerStatusListener::ServerStatusListener*; mir::Server::stop*; mir::Server::supported_pixel_formats*; mir::Server::the_application_not_responding_detector*; mir::Server::the_buffer_stream_factory*; mir::Server::the_composite_event_filter*; mir::Server::the_compositor*; mir::Server::the_cursor*; mir::Server::the_cursor_listener*; mir::Server::the_display*; mir::Server::the_display_configuration_controller*; mir::Server::the_focus_controller*; mir::Server::the_gl_config*; mir::Server::the_graphics_platform*; mir::Server::the_input_device_hub*; mir::Server::the_input_targeter*; mir::Server::the_logger*; mir::Server::the_main_loop*; mir::Server::the_prompt_session_listener*; mir::Server::the_prompt_session_manager*; mir::Server::the_session_authorizer*; mir::Server::the_session_coordinator*; mir::Server::the_session_listener*; mir::Server::the_shell*; mir::Server::the_shell_display_layout*; mir::Server::the_surface_factory*; mir::Server::the_surface_stack*; mir::Server::the_touch_visualizer*; mir::Server::wrap_cursor*; mir::Server::wrap_cursor_listener*; mir::Server::wrap_display_buffer_compositor_factory*; mir::Server::wrap_display_configuration_policy*; mir::Server::wrap_shell*; mir::Server::wrap_surface_stack*; mir::shell::AbstractShell::?AbstractShell*; mir::shell::AbstractShell::AbstractShell*; mir::shell::AbstractShell::add_display*; mir::shell::AbstractShell::add_prompt_provider_for*; mir::shell::AbstractShell::close_session*; mir::shell::AbstractShell::create_surface*; mir::shell::AbstractShell::destroy_surface*; mir::shell::AbstractShell::focused_session*; mir::shell::AbstractShell::focused_surface*; mir::shell::AbstractShell::focus_next_session*; mir::shell::AbstractShell::get_surface_attribute*; mir::shell::AbstractShell::handle*; mir::shell::AbstractShell::modify_surface*; mir::shell::AbstractShell::open_session*; mir::shell::AbstractShell::raise*; mir::shell::AbstractShell::raise_surface*; mir::shell::AbstractShell::remove_display*; mir::shell::AbstractShell::set_focus_to*; mir::shell::AbstractShell::set_surface_attribute*; mir::shell::AbstractShell::start_prompt_session_for*; mir::shell::AbstractShell::stop_prompt_session*; mir::shell::AbstractShell::surface_at*; mir::shell::DisplayConfigurationController::?DisplayConfigurationController*; mir::shell::DisplayConfigurationController::DisplayConfigurationController*; mir::shell::DisplayConfigurationController::operator*; mir::shell::DisplayLayout::?DisplayLayout*; mir::shell::DisplayLayout::DisplayLayout*; mir::shell::DisplayLayout::operator*; mir::shell::FocusController::?FocusController*; mir::shell::FocusController::FocusController*; mir::shell::FocusController::operator*; mir::shell::HostLifecycleEventListener::?HostLifecycleEventListener*; mir::shell::HostLifecycleEventListener::HostLifecycleEventListener*; mir::shell::HostLifecycleEventListener::operator*; mir::shell::InputTargeter::?InputTargeter*; mir::shell::InputTargeter::InputTargeter*; mir::shell::InputTargeter::operator*; mir::shell::PersistentSurfaceStore::Id::Id*; mir::shell::PersistentSurfaceStore::Id::operator*; mir::shell::PersistentSurfaceStore::Id::serialize_to_string*; mir::shell::PersistentSurfaceStore::?PersistentSurfaceStore*; mir::shell::ShellReport::operator*; mir::shell::ShellReport::?ShellReport*; mir::shell::ShellReport::ShellReport*; mir::shell::ShellWrapper::add_display*; mir::shell::ShellWrapper::add_prompt_provider_for*; mir::shell::ShellWrapper::close_session*; mir::shell::ShellWrapper::create_surface*; mir::shell::ShellWrapper::destroy_surface*; mir::shell::ShellWrapper::focused_session*; mir::shell::ShellWrapper::focused_surface*; mir::shell::ShellWrapper::focus_next_session*; mir::shell::ShellWrapper::get_surface_attribute*; mir::shell::ShellWrapper::handle*; mir::shell::ShellWrapper::modify_surface*; mir::shell::ShellWrapper::open_session*; mir::shell::ShellWrapper::raise*; mir::shell::ShellWrapper::raise_surface*; mir::shell::ShellWrapper::remove_display*; mir::shell::ShellWrapper::set_focus_to*; mir::shell::ShellWrapper::set_surface_attribute*; mir::shell::ShellWrapper::ShellWrapper*; mir::shell::ShellWrapper::start_prompt_session_for*; mir::shell::ShellWrapper::stop_prompt_session*; mir::shell::ShellWrapper::surface_at*; mir::shell::SurfaceReadyObserver::frame_posted*; mir::shell::SurfaceReadyObserver::?SurfaceReadyObserver*; mir::shell::SurfaceReadyObserver::SurfaceReadyObserver*; mir::shell::SurfaceSpecification::is_empty*; mir::shell::SurfaceStack::operator*; mir::shell::SurfaceStack::?SurfaceStack*; mir::shell::SurfaceStack::SurfaceStack*; mir::shell::SurfaceStackWrapper::add_surface*; mir::shell::SurfaceStackWrapper::raise*; mir::shell::SurfaceStackWrapper::remove_surface*; mir::shell::SurfaceStackWrapper::surface_at*; mir::shell::SurfaceStackWrapper::SurfaceStackWrapper*; mir::shell::SystemCompositorWindowManager::add_display*; mir::shell::SystemCompositorWindowManager::add_session*; mir::shell::SystemCompositorWindowManager::add_surface*; mir::shell::SystemCompositorWindowManager::handle_keyboard_event*; mir::shell::SystemCompositorWindowManager::handle_pointer_event*; mir::shell::SystemCompositorWindowManager::handle_raise_surface*; mir::shell::SystemCompositorWindowManager::handle_touch_event*; mir::shell::SystemCompositorWindowManager::handle_raise_surface*; mir::shell::SystemCompositorWindowManager::modify_surface*; mir::shell::SystemCompositorWindowManager::on_session_added*; mir::shell::SystemCompositorWindowManager::on_session_ready*; mir::shell::SystemCompositorWindowManager::on_session_removed*; mir::shell::SystemCompositorWindowManager::remove_display*; mir::shell::SystemCompositorWindowManager::remove_session*; mir::shell::SystemCompositorWindowManager::remove_surface*; mir::shell::SystemCompositorWindowManager::set_surface_attribute*; mir::shell::SystemCompositorWindowManager::SystemCompositorWindowManager*; mir::shell::WindowManager::operator*; mir::shell::WindowManager::?WindowManager*; mir::shell::WindowManager::WindowManager*; mir::terminate_with_current_exception*; mir::time::Alarm::?Alarm*; mir::time::Alarm::Alarm*; mir::time::AlarmFactory::?AlarmFactory*; mir::time::AlarmFactory::AlarmFactory*; mir::time::AlarmFactory::operator*; mir::time::Alarm::operator*; non-virtual?thunk?to?mir::compositor::DisplayBufferCompositor::?DisplayBufferCompositor*; non-virtual?thunk?to?mir::compositor::DisplayBufferCompositorFactory::?DisplayBufferCompositorFactory*; non-virtual?thunk?to?mir::compositor::DisplayListener::?DisplayListener*; non-virtual?thunk?to?mir::compositor::SceneElement::?SceneElement*; non-virtual?thunk?to?mir::frontend::BufferSink::?BufferSink*; non-virtual?thunk?to?mir::frontend::BufferStream::?BufferStream*; non-virtual?thunk?to?mir::frontend::PromptSession::?PromptSession*; non-virtual?thunk?to?mir::frontend::SessionAuthorizer::?SessionAuthorizer*; non-virtual?thunk?to?mir::frontend::SessionMediatorReport::?SessionMediatorReport*; non-virtual?thunk?to?mir::frontend::Session::?Session*; non-virtual?thunk?to?mir::frontend::Surface::?Surface*; non-virtual?thunk?to?mir::graphics::CloneDisplayConfigurationPolicy::apply_to*; non-virtual?thunk?to?mir::graphics::DisplayConfigurationReport::?DisplayConfigurationReport*; non-virtual?thunk?to?mir::graphics::SideBySideDisplayConfigurationPolicy::apply_to*; non-virtual?thunk?to?mir::graphics::SingleDisplayConfigurationPolicy::apply_to*; non-virtual?thunk?to?mir::input::CursorImages::?CursorImages*; non-virtual?thunk?to?mir::input::CursorListener::?CursorListener*; non-virtual?thunk?to?mir::input::Device::?Device*; non-virtual?thunk?to?mir::input::EventFilter::?EventFilter*; non-virtual?thunk?to?mir::input::InputDeviceHub::?InputDeviceHub*; non-virtual?thunk?to?mir::input::InputDeviceObserver::?InputDeviceObserver*; non-virtual?thunk?to?mir::input::InputDispatcher::?InputDispatcher*; non-virtual?thunk?to?mir::input::Surface::?Surface*; non-virtual?thunk?to?mir::input::TouchVisualizer::?TouchVisualizer*; non-virtual?thunk?to?mir::LockableCallback::?LockableCallback*; non-virtual?thunk?to?mir::scene::ApplicationNotRespondingDetector::?ApplicationNotRespondingDetector*; non-virtual?thunk?to?mir::scene::ApplicationNotRespondingDetector::Observer::?Observer*; non-virtual?thunk?to?mir::scene::BufferStreamFactory::?BufferStreamFactory*; non-virtual?thunk?to?mir::scene::CoordinateTranslator::?CoordinateTranslator*; non-virtual?thunk?to?mir::scene::NullSessionListener::?NullSessionListener*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::alpha_set_to*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::attrib_changed*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::client_surface_close_requested*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::cursor_image_set_to*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::frame_posted*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::hidden_set_to*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::keymap_changed*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::moved_to*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::?NullSurfaceObserver*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::orientation_set_to*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::reception_mode_set_to*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::renamed*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::resized_to*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::transformation_set_to*; non-virtual?thunk?to?mir::scene::Observer::?Observer*; non-virtual?thunk?to?mir::scene::PromptSessionListener::?PromptSessionListener*; non-virtual?thunk?to?mir::scene::PromptSessionManager::?PromptSessionManager*; non-virtual?thunk?to?mir::scene::SessionCoordinator::?SessionCoordinator*; non-virtual?thunk?to?mir::scene::SessionListener::?SessionListener*; non-virtual?thunk?to?mir::scene::SurfaceFactory::?SurfaceFactory*; non-virtual?thunk?to?mir::scene::SurfaceObserver::?SurfaceObserver*; non-virtual?thunk?to?mir::ServerActionQueue::?ServerActionQueue*; non-virtual?thunk?to?mir::ServerStatusListener::?ServerStatusListener*; non-virtual?thunk?to?mir::shell::AbstractShell::add_display*; non-virtual?thunk?to?mir::shell::AbstractShell::add_prompt_provider_for*; non-virtual?thunk?to?mir::shell::AbstractShell::close_session*; non-virtual?thunk?to?mir::shell::AbstractShell::create_surface*; non-virtual?thunk?to?mir::shell::AbstractShell::destroy_surface*; non-virtual?thunk?to?mir::shell::AbstractShell::focused_session*; non-virtual?thunk?to?mir::shell::AbstractShell::focused_surface*; non-virtual?thunk?to?mir::shell::AbstractShell::focus_next_session*; non-virtual?thunk?to?mir::shell::AbstractShell::get_surface_attribute*; non-virtual?thunk?to?mir::shell::AbstractShell::handle*; non-virtual?thunk?to?mir::shell::AbstractShell::modify_surface*; non-virtual?thunk?to?mir::shell::AbstractShell::open_session*; non-virtual?thunk?to?mir::shell::AbstractShell::raise*; non-virtual?thunk?to?mir::shell::AbstractShell::raise_surface*; non-virtual?thunk?to?mir::shell::AbstractShell::remove_display*; non-virtual?thunk?to?mir::shell::AbstractShell::set_focus_to*; non-virtual?thunk?to?mir::shell::AbstractShell::set_surface_attribute*; non-virtual?thunk?to?mir::shell::AbstractShell::start_prompt_session_for*; non-virtual?thunk?to?mir::shell::AbstractShell::stop_prompt_session*; non-virtual?thunk?to?mir::shell::AbstractShell::surface_at*; non-virtual?thunk?to?mir::shell::DisplayConfigurationController::?DisplayConfigurationController*; non-virtual?thunk?to?mir::shell::DisplayLayout::?DisplayLayout*; non-virtual?thunk?to?mir::shell::FocusController::?FocusController*; non-virtual?thunk?to?mir::shell::HostLifecycleEventListener::?HostLifecycleEventListener*; non-virtual?thunk?to?mir::shell::InputTargeter::?InputTargeter*; non-virtual?thunk?to?mir::shell::PersistentSurfaceStore::?PersistentSurfaceStore*; non-virtual?thunk?to?mir::shell::ShellReport::?ShellReport*; non-virtual?thunk?to?mir::shell::ShellWrapper::add_display*; non-virtual?thunk?to?mir::shell::ShellWrapper::add_prompt_provider_for*; non-virtual?thunk?to?mir::shell::ShellWrapper::close_session*; non-virtual?thunk?to?mir::shell::ShellWrapper::create_surface*; non-virtual?thunk?to?mir::shell::ShellWrapper::destroy_surface*; non-virtual?thunk?to?mir::shell::ShellWrapper::focused_session*; non-virtual?thunk?to?mir::shell::ShellWrapper::focused_surface*; non-virtual?thunk?to?mir::shell::ShellWrapper::focus_next_session*; non-virtual?thunk?to?mir::shell::ShellWrapper::get_surface_attribute*; non-virtual?thunk?to?mir::shell::ShellWrapper::handle*; non-virtual?thunk?to?mir::shell::ShellWrapper::modify_surface*; non-virtual?thunk?to?mir::shell::ShellWrapper::open_session*; non-virtual?thunk?to?mir::shell::ShellWrapper::raise*; non-virtual?thunk?to?mir::shell::ShellWrapper::raise_surface*; non-virtual?thunk?to?mir::shell::ShellWrapper::remove_display*; non-virtual?thunk?to?mir::shell::ShellWrapper::set_focus_to*; non-virtual?thunk?to?mir::shell::ShellWrapper::set_surface_attribute*; non-virtual?thunk?to?mir::shell::ShellWrapper::start_prompt_session_for*; non-virtual?thunk?to?mir::shell::ShellWrapper::stop_prompt_session*; non-virtual?thunk?to?mir::shell::ShellWrapper::surface_at*; non-virtual?thunk?to?mir::shell::SurfaceReadyObserver::frame_posted*; non-virtual?thunk?to?mir::shell::SurfaceStack::?SurfaceStack*; non-virtual?thunk?to?mir::shell::SurfaceStackWrapper::add_surface*; non-virtual?thunk?to?mir::shell::SurfaceStackWrapper::raise*; non-virtual?thunk?to?mir::shell::SurfaceStackWrapper::remove_surface*; non-virtual?thunk?to?mir::shell::SurfaceStackWrapper::surface_at*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::add_display*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::add_session*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::add_surface*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::handle_keyboard_event*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::handle_pointer_event*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::handle_raise_surface*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::handle_touch_event*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::modify_surface*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::on_session_added*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::on_session_ready*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::on_session_removed*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::remove_display*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::remove_session*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::remove_surface*; non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::set_surface_attribute*; non-virtual?thunk?to?mir::shell::WindowManager::?WindowManager*; non-virtual?thunk?to?mir::time::Alarm::?Alarm*; non-virtual?thunk?to?mir::time::AlarmFactory::?AlarmFactory*; typeinfo?for?mir::compositor::Compositor; typeinfo?for?mir::compositor::DisplayBufferCompositor; typeinfo?for?mir::compositor::DisplayBufferCompositorFactory; typeinfo?for?mir::compositor::DisplayListener; typeinfo?for?mir::compositor::Scene; typeinfo?for?mir::compositor::SceneElement; typeinfo?for?mir::frontend::BufferSink; typeinfo?for?mir::frontend::BufferStream; typeinfo?for?mir::frontend::PromptSession; typeinfo?for?mir::frontend::Session; typeinfo?for?mir::frontend::SessionAuthorizer; typeinfo?for?mir::frontend::SessionCredentials; typeinfo?for?mir::frontend::SessionMediatorReport; typeinfo?for?mir::frontend::Surface; typeinfo?for?mir::graphics::CloneDisplayConfigurationPolicy; typeinfo?for?mir::graphics::DisplayConfigurationReport; typeinfo?for?mir::graphics::SideBySideDisplayConfigurationPolicy; typeinfo?for?mir::graphics::SingleDisplayConfigurationPolicy; typeinfo?for?mir::input::CompositeEventFilter; typeinfo?for?mir::input::CursorImages; typeinfo?for?mir::input::CursorListener; typeinfo?for?mir::input::Device; typeinfo?for?mir::input::EventFilter; typeinfo?for?mir::input::InputChannel; typeinfo?for?mir::input::InputDeviceHub; typeinfo?for?mir::input::InputDeviceObserver; typeinfo?for?mir::input::InputDispatcher; typeinfo?for?mir::input::InputManager; typeinfo?for?mir::input::PointerConfiguration; typeinfo?for?mir::input::Surface; typeinfo?for?mir::input::TouchpadConfiguration; typeinfo?for?mir::input::TouchVisualizer; typeinfo?for?mir::input::TouchVisualizer::Spot; typeinfo?for?mir::LockableCallback; typeinfo?for?mir::MainLoop; typeinfo?for?mir::scene::ApplicationNotRespondingDetector; typeinfo?for?mir::scene::ApplicationNotRespondingDetector::Observer; typeinfo?for?mir::scene::BufferStreamFactory; typeinfo?for?mir::scene::CoordinateTranslator; typeinfo?for?mir::scene::NullSessionListener; typeinfo?for?mir::scene::NullSurfaceObserver; typeinfo?for?mir::scene::Observer; typeinfo?for?mir::scene::PromptSession; typeinfo?for?mir::scene::PromptSessionCreationParameters; typeinfo?for?mir::scene::PromptSessionListener; typeinfo?for?mir::scene::PromptSessionManager; typeinfo?for?mir::scene::Session; typeinfo?for?mir::scene::SessionCoordinator; typeinfo?for?mir::scene::SessionListener; typeinfo?for?mir::scene::Snapshot; typeinfo?for?mir::scene::StreamInfo; typeinfo?for?mir::scene::Surface; typeinfo?for?mir::scene::SurfaceCreationParameters; typeinfo?for?mir::scene::SurfaceFactory; typeinfo?for?mir::scene::SurfaceObserver; typeinfo?for?mir::Server; typeinfo?for?mir::ServerActionQueue; typeinfo?for?mir::ServerStatusListener; typeinfo?for?mir::shell::AbstractShell; typeinfo?for?mir::shell::DisplayConfigurationController; typeinfo?for?mir::shell::DisplayLayout; typeinfo?for?mir::shell::FocusController; typeinfo?for?mir::shell::HostLifecycleEventListener; typeinfo?for?mir::shell::InputTargeter; typeinfo?for?mir::shell::PersistentSurfaceStore; typeinfo?for?mir::shell::PersistentSurfaceStore::Id; typeinfo?for?mir::shell::Shell; typeinfo?for?mir::shell::ShellReport; typeinfo?for?mir::shell::ShellWrapper; typeinfo?for?mir::shell::StreamSpecification; typeinfo?for?mir::shell::SurfaceAspectRatio; typeinfo?for?mir::shell::SurfaceReadyObserver; typeinfo?for?mir::shell::SurfaceSpecification; typeinfo?for?mir::shell::SurfaceStack; typeinfo?for?mir::shell::SurfaceStackWrapper; typeinfo?for?mir::shell::SystemCompositorWindowManager; typeinfo?for?mir::shell::WindowManager; typeinfo?for?mir::time::Alarm; typeinfo?for?mir::time::AlarmFactory; vtable?for?mir::compositor::Compositor; vtable?for?mir::compositor::DisplayBufferCompositor; vtable?for?mir::compositor::DisplayBufferCompositorFactory; vtable?for?mir::compositor::DisplayListener; vtable?for?mir::compositor::Scene; vtable?for?mir::compositor::SceneElement; vtable?for?mir::frontend::BufferSink; vtable?for?mir::frontend::BufferStream; vtable?for?mir::frontend::PromptSession; vtable?for?mir::frontend::Session; vtable?for?mir::frontend::SessionAuthorizer; vtable?for?mir::frontend::SessionCredentials; vtable?for?mir::frontend::SessionMediatorReport; vtable?for?mir::frontend::Surface; vtable?for?mir::graphics::CloneDisplayConfigurationPolicy; vtable?for?mir::graphics::DisplayConfigurationReport; vtable?for?mir::graphics::SideBySideDisplayConfigurationPolicy; vtable?for?mir::graphics::SingleDisplayConfigurationPolicy; vtable?for?mir::input::CompositeEventFilter; vtable?for?mir::input::CursorImages; vtable?for?mir::input::CursorListener; vtable?for?mir::input::Device; vtable?for?mir::input::EventFilter; vtable?for?mir::input::InputChannel; vtable?for?mir::input::InputDeviceHub; vtable?for?mir::input::InputDeviceObserver; vtable?for?mir::input::InputDispatcher; vtable?for?mir::input::InputManager; vtable?for?mir::input::PointerConfiguration; vtable?for?mir::input::Surface; vtable?for?mir::input::TouchpadConfiguration; vtable?for?mir::input::TouchVisualizer; vtable?for?mir::input::TouchVisualizer::Spot; vtable?for?mir::LockableCallback; vtable?for?mir::MainLoop; vtable?for?mir::scene::ApplicationNotRespondingDetector; vtable?for?mir::scene::ApplicationNotRespondingDetector::Observer; vtable?for?mir::scene::BufferStreamFactory; vtable?for?mir::scene::CoordinateTranslator; vtable?for?mir::scene::NullSessionListener; vtable?for?mir::scene::NullSurfaceObserver; vtable?for?mir::scene::Observer; vtable?for?mir::scene::PromptSession; vtable?for?mir::scene::PromptSessionCreationParameters; vtable?for?mir::scene::PromptSessionListener; vtable?for?mir::scene::PromptSessionManager; vtable?for?mir::scene::Session; vtable?for?mir::scene::SessionCoordinator; vtable?for?mir::scene::SessionListener; vtable?for?mir::scene::Snapshot; vtable?for?mir::scene::StreamInfo; vtable?for?mir::scene::Surface; vtable?for?mir::scene::SurfaceCreationParameters; vtable?for?mir::scene::SurfaceFactory; vtable?for?mir::scene::SurfaceObserver; vtable?for?mir::Server; vtable?for?mir::ServerActionQueue; vtable?for?mir::ServerStatusListener; vtable?for?mir::shell::AbstractShell; vtable?for?mir::shell::DisplayConfigurationController; vtable?for?mir::shell::DisplayLayout; vtable?for?mir::shell::FocusController; vtable?for?mir::shell::HostLifecycleEventListener; vtable?for?mir::shell::InputTargeter; vtable?for?mir::shell::PersistentSurfaceStore; vtable?for?mir::shell::PersistentSurfaceStore::Id; vtable?for?mir::shell::Shell; vtable?for?mir::shell::ShellReport; vtable?for?mir::shell::ShellWrapper; vtable?for?mir::shell::StreamSpecification; vtable?for?mir::shell::SurfaceAspectRatio; vtable?for?mir::shell::SurfaceReadyObserver; vtable?for?mir::shell::SurfaceSpecification; vtable?for?mir::shell::SurfaceStack; vtable?for?mir::shell::SurfaceStackWrapper; vtable?for?mir::shell::SystemCompositorWindowManager; vtable?for?mir::shell::WindowManager; vtable?for?mir::time::Alarm; vtable?for?mir::time::AlarmFactory; # needed but not picked up by current version of script virtual?thunk?to?mir::shell::AbstractShell::?AbstractShell*; virtual?thunk?to?mir::shell::AbstractShell::add_display*; virtual?thunk?to?mir::shell::AbstractShell::add_prompt_provider_for*; virtual?thunk?to?mir::shell::AbstractShell::close_session*; virtual?thunk?to?mir::shell::AbstractShell::create_surface*; virtual?thunk?to?mir::shell::AbstractShell::destroy_surface*; virtual?thunk?to?mir::shell::AbstractShell::focus_next_session*; virtual?thunk?to?mir::shell::AbstractShell::focused_session*; virtual?thunk?to?mir::shell::AbstractShell::get_surface_attribute*; virtual?thunk?to?mir::shell::AbstractShell::handle*; virtual?thunk?to?mir::shell::AbstractShell::handle_surface_created*; virtual?thunk?to?mir::shell::AbstractShell::open_session*; virtual?thunk?to?mir::shell::AbstractShell::remove_display*; virtual?thunk?to?mir::shell::AbstractShell::set_focus_to*; virtual?thunk?to?mir::shell::AbstractShell::set_surface_attribute*; virtual?thunk?to?mir::shell::AbstractShell::setting_focus_to*; virtual?thunk?to?mir::shell::AbstractShell::start_prompt_session_for*; virtual?thunk?to?mir::shell::AbstractShell::stop_prompt_session*; virtual?thunk?to?mir::shell::ShellWrapper::add_display*; virtual?thunk?to?mir::shell::ShellWrapper::handle*; virtual?thunk?to?mir::shell::ShellWrapper::remove_display*; # these symbols are needed by mir_proving_server but are not intended to be public mir::renderer::gl::ProgramFamily::*; mir::renderer::gl::Renderer::Renderer*; mir::renderer::gl::Renderer::?Renderer*; mir::renderer::gl::Renderer::Program::Program*; mir::renderer::gl::Renderer::draw*; mir::renderer::gl::Renderer::render*; mir::renderer::gl::Renderer::set_rotation*; mir::renderer::gl::Renderer::set_viewport*; mir::renderer::gl::Renderer::suspend*; mir::renderer::gl::Renderer::tessellate*; mir::renderer::gl::Renderer::vshader; typeinfo?for?mir::renderer::gl::Renderer; vtable?for?mir::renderer::gl::Renderer; # these symbols are needed by the test framework but are not intended to be public mir::shell::CanonicalWindowManagerPolicy::handle_set_state*; mir::shell::CanonicalWindowManagerPolicy::CanonicalWindowManagerPolicy*; mir::shell::CanonicalWindowManagerPolicy::handle_session_info_updated*; mir::shell::CanonicalWindowManagerPolicy::handle_session_info_updated*; mir::shell::CanonicalWindowManagerPolicy::handle_place_new_surface*; mir::shell::CanonicalWindowManagerPolicy::handle_modify_surface*; mir::shell::CanonicalWindowManagerPolicy::handle_new_surface*; mir::shell::CanonicalSurfaceInfo::CanonicalSurfaceInfo*; mir::shell::CanonicalWindowManagerPolicy::handle_delete_surface*; mir::shell::CanonicalWindowManagerPolicy::handle_displays_updated*; mir::shell::CanonicalWindowManagerPolicy::handle_displays_updated*; mir::shell::CanonicalWindowManagerPolicy::handle_keyboard_event*; mir::shell::CanonicalWindowManagerPolicy::handle_touch_event*; mir::shell::CanonicalWindowManagerPolicy::handle_pointer_event*; mir::shell::CanonicalWindowManagerPolicy::handle_raise_surface*; mir::shell::DefaultWindowManager::DefaultWindowManager*; mir::DefaultServerConfiguration::clock*; mir::DefaultServerConfiguration::DefaultServerConfiguration*; mir::DefaultServerConfiguration::new_ipc_factory*; mir::DefaultServerConfiguration::the_android_input_dispatcher*; mir::DefaultServerConfiguration::the_application_not_responding_detector*; mir::DefaultServerConfiguration::the_buffer_allocator*; mir::DefaultServerConfiguration::the_buffer_stream_factory*; mir::DefaultServerConfiguration::the_clock*; mir::DefaultServerConfiguration::the_composite_event_filter*; mir::DefaultServerConfiguration::the_cookie_authority*; mir::DefaultServerConfiguration::the_event_filter_chain_dispatcher*; mir::DefaultServerConfiguration::the_surface_input_dispatcher; mir::DefaultServerConfiguration::the_compositor*; mir::DefaultServerConfiguration::the_compositor_report*; mir::DefaultServerConfiguration::the_connection_creator*; mir::DefaultServerConfiguration::the_connector*; mir::DefaultServerConfiguration::the_connector_report*; mir::DefaultServerConfiguration::the_coordinate_translator*; mir::DefaultServerConfiguration::the_cursor*; mir::DefaultServerConfiguration::the_cursor_images*; mir::DefaultServerConfiguration::the_cursor_listener*; mir::DefaultServerConfiguration::the_default_cursor_image*; mir::DefaultServerConfiguration::the_dispatcher_policy*; mir::DefaultServerConfiguration::the_dispatcher_thread*; mir::DefaultServerConfiguration::the_display*; mir::DefaultServerConfiguration::the_display_buffer_compositor_factory*; mir::DefaultServerConfiguration::the_display_changer*; mir::DefaultServerConfiguration::the_display_configuration_controller*; mir::DefaultServerConfiguration::the_display_configuration_policy*; mir::DefaultServerConfiguration::the_display_report*; mir::DefaultServerConfiguration::the_emergency_cleanup*; mir::DefaultServerConfiguration::the_event_filter_chain_dispatcher*; mir::DefaultServerConfiguration::the_fatal_error_strategy*; mir::DefaultServerConfiguration::the_focus_controller*; mir::DefaultServerConfiguration::the_frame_dropping_policy_factory*; mir::DefaultServerConfiguration::the_frontend_display_changer*; mir::DefaultServerConfiguration::the_frontend_shell*; mir::DefaultServerConfiguration::the_gl_config*; mir::DefaultServerConfiguration::the_global_event_sink*; mir::DefaultServerConfiguration::the_graphics_platform*; mir::DefaultServerConfiguration::the_host_connection*; mir::DefaultServerConfiguration::the_host_lifecycle_event_listener*; mir::DefaultServerConfiguration::the_input_channel_factory*; mir::DefaultServerConfiguration::the_input_device_hub*; mir::DefaultServerConfiguration::the_input_device_registry*; mir::DefaultServerConfiguration::the_input_dispatcher*; mir::DefaultServerConfiguration::the_new_input_dispatcher*; mir::DefaultServerConfiguration::the_input_manager*; mir::DefaultServerConfiguration::the_input_reading_multiplexer*; mir::DefaultServerConfiguration::the_input_region*; mir::DefaultServerConfiguration::the_input_registrar*; mir::DefaultServerConfiguration::the_input_report*; mir::DefaultServerConfiguration::the_input_scene*; mir::DefaultServerConfiguration::the_input_sender*; mir::DefaultServerConfiguration::the_input_target_enumerator*; mir::DefaultServerConfiguration::the_input_targeter*; mir::DefaultServerConfiguration::the_input_translator*; mir::DefaultServerConfiguration::the_legacy_input_dispatchable*; mir::DefaultServerConfiguration::the_logger*; mir::DefaultServerConfiguration::the_main_loop*; mir::DefaultServerConfiguration::the_mediating_display_changer*; mir::DefaultServerConfiguration::the_message_processor_report*; mir::DefaultServerConfiguration::the_options*; mir::DefaultServerConfiguration::the_persistent_surface_store*; mir::DefaultServerConfiguration::the_pixel_buffer*; mir::DefaultServerConfiguration::the_placement_strategy*; mir::DefaultServerConfiguration::the_prompt_connection_creator*; mir::DefaultServerConfiguration::the_prompt_connector*; mir::DefaultServerConfiguration::the_prompt_session_listener*; mir::DefaultServerConfiguration::the_prompt_session_manager*; mir::DefaultServerConfiguration::the_renderer_factory*; mir::DefaultServerConfiguration::the_scene*; mir::DefaultServerConfiguration::the_scene_report*; mir::DefaultServerConfiguration::the_screencast*; mir::DefaultServerConfiguration::the_server_action_queue*; mir::DefaultServerConfiguration::the_server_status_listener*; mir::DefaultServerConfiguration::the_session_authorizer*; mir::DefaultServerConfiguration::the_session_container*; mir::DefaultServerConfiguration::the_session_coordinator*; mir::DefaultServerConfiguration::the_session_event_handler_register*; mir::DefaultServerConfiguration::the_session_event_sink*; mir::DefaultServerConfiguration::the_session_listener*; mir::DefaultServerConfiguration::the_session_mediator_report*; mir::DefaultServerConfiguration::the_shared_library_prober_report*; mir::DefaultServerConfiguration::the_shell*; mir::DefaultServerConfiguration::the_shell_display_layout*; mir::DefaultServerConfiguration::the_snapshot_strategy*; mir::DefaultServerConfiguration::the_socket_file*; mir::DefaultServerConfiguration::the_surface_configurator*; mir::DefaultServerConfiguration::the_surface_stack*; mir::DefaultServerConfiguration::the_surface_input_dispatcher*; mir::DefaultServerConfiguration::the_surface_factory*; mir::DefaultServerConfiguration::the_surface_stack_model*; mir::DefaultServerConfiguration::the_touch_visualizer*; mir::DefaultServerConfiguration::the_window_manager_builder*; mir::DefaultServerConfiguration::wrap_cursor*; mir::DefaultServerConfiguration::wrap_cursor_listener*; mir::DefaultServerConfiguration::wrap_display_buffer_compositor_factory*; mir::DefaultServerConfiguration::wrap_display_configuration_policy*; mir::DefaultServerConfiguration::wrap_shell*; mir::DefaultServerConfiguration::wrap_surface_stack*; typeinfo?for?mir::DefaultServerConfiguration; mir::run_mir*; mir::DisplayServer::stop*; }; local: *; }; MIR_SERVER_0.21 { # New symbols in Mir 0.21 global: extern "C++" { mir::shell::BasicWindowManager::active_display*; mir::shell::BasicWindowManager::add_display*; mir::shell::BasicWindowManager::add_session*; mir::shell::BasicWindowManager::add_surface*; mir::shell::BasicWindowManager::BasicWindowManager*; mir::shell::BasicWindowManager::find_session*; mir::shell::BasicWindowManager::focused_session*; mir::shell::BasicWindowManager::focused_surface*; mir::shell::BasicWindowManager::focus_next_session*; mir::shell::BasicWindowManager::forget*; mir::shell::BasicWindowManager::handle_keyboard_event*; mir::shell::BasicWindowManager::handle_pointer_event*; mir::shell::BasicWindowManager::handle_raise_surface*; mir::shell::BasicWindowManager::handle_touch_event*; mir::shell::BasicWindowManager::info_for*; mir::shell::BasicWindowManager::modify_surface*; mir::shell::BasicWindowManager::raise_tree*; mir::shell::BasicWindowManager::remove_display*; mir::shell::BasicWindowManager::remove_session*; mir::shell::BasicWindowManager::remove_surface*; mir::shell::BasicWindowManager::set_focus_to*; mir::shell::BasicWindowManager::set_surface_attribute*; mir::shell::BasicWindowManager::surface_at*; typeinfo?for?mir::shell::BasicWindowManager; virtual?thunk?to?mir::shell::BasicWindowManager::add_display*; virtual?thunk?to?mir::shell::BasicWindowManager::add_session*; virtual?thunk?to?mir::shell::BasicWindowManager::add_surface*; virtual?thunk?to?mir::shell::BasicWindowManager::handle_keyboard_event*; virtual?thunk?to?mir::shell::BasicWindowManager::handle_pointer_event*; virtual?thunk?to?mir::shell::BasicWindowManager::handle_raise_surface*; virtual?thunk?to?mir::shell::BasicWindowManager::handle_touch_event*; virtual?thunk?to?mir::shell::BasicWindowManager::modify_surface*; virtual?thunk?to?mir::shell::BasicWindowManager::remove_display*; virtual?thunk?to?mir::shell::BasicWindowManager::remove_session*; virtual?thunk?to?mir::shell::BasicWindowManager::remove_surface*; virtual?thunk?to?mir::shell::BasicWindowManager::set_surface_attribute*; VTT?for?mir::shell::BasicWindowManager; mir::DefaultServerConfiguration::the_seat*; typeinfo?for?mir::shell::CanonicalWindowManagerPolicy; vtable?for?mir::shell::CanonicalWindowManagerPolicy; VTT?for?mir::shell::CanonicalWindowManagerPolicy; }; } MIR_SERVER_0.20; ./src/server/CMakeLists.txt0000644000015600001650000000721412676616125015752 0ustar jenkinsjenkinspkg_check_modules(UUID REQUIRED uuid) add_definitions( -DMIR_LOG_COMPONENT_FALLBACK="mirserver" -DMIR_VERSION="${MIR_VERSION}" ) include_directories( ${PROJECT_SOURCE_DIR}/include/platform ${PROJECT_SOURCE_DIR}/include/client ${PROJECT_SOURCE_DIR}/include/server ${PROJECT_SOURCE_DIR}/include/renderers/gl ${PROJECT_SOURCE_DIR}/src/include/platform ${PROJECT_SOURCE_DIR}/src/include/client ${PROJECT_SOURCE_DIR}/src/include/server ${PROJECT_SOURCE_DIR}/include/cookie ${PROJECT_SOURCE_DIR}/src/include/cookie ${GLIB_INCLUDE_DIRS} ) add_definitions(-DMIR_SERVER_INPUT_PLATFORM_VERSION="${MIR_SERVER_INPUT_PLATFORM_VERSION}") add_definitions(-DMIR_SERVER_PLATFORM_PATH="${MIR_SERVER_PLATFORM_PATH}") add_definitions(-DMIR_SERVER_GRAPHICS_PLATFORM_VERSION="${MIR_SERVER_GRAPHICS_PLATFORM_VERSION}") add_subdirectory(compositor/) add_subdirectory(graphics/) add_subdirectory(input/) add_subdirectory(report/) add_subdirectory(scene/) add_subdirectory(frontend/) add_subdirectory(shell/) add_subdirectory(thread/) set(PREFIX "${CMAKE_INSTALL_PREFIX}") set(EXEC_PREFIX "${CMAKE_INSTALL_PREFIX}") set(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}") set(INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include/mirserver") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/mirserver.pc.in ${CMAKE_CURRENT_BINARY_DIR}/mirserver.pc ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${PROJECT_SOURCE_DIR}/include/server/mir/version.h ) add_library(mirserverobjects OBJECT run_mir.cpp report_exception.cpp terminate_with_current_exception.cpp display_server.cpp default_server_configuration.cpp glib_main_loop.cpp glib_main_loop_sources.cpp default_emergency_cleanup.cpp server.cpp lockable_callback_wrapper.cpp basic_callback.cpp ) set(MIR_SERVER_OBJECTS $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ) set(MIR_SERVER_REFERENCES ${EGL_LDFLAGS} ${EGL_LIBRARIES} ${GLESv2_LDFLAGS} ${GLESv2_LIBRARIES} ${UDEV_LDFLAGS} ${UDEV_LIBRARIES} ${GLIB_LDFLAGS} ${GLIB_LIBRARIES} ${UUID_LDFLAGS} ${UUID_LIBRARIES} ) set(MIR_SERVER_OBJECTS ${MIR_SERVER_OBJECTS} PARENT_SCOPE) set(MIR_SERVER_REFERENCES ${MIR_SERVER_REFERENCES} PARENT_SCOPE) mir_add_library_with_symbols(mirserver SHARED symbols.map ${MIR_SERVER_OBJECTS} ) target_link_libraries(mirserver LINK_PUBLIC mirclient mirplatform mircommon mirprotobuf mircookie ${GLog_LIBRARY} ${GFlags_LIBRARY} ${EGL_LDFLAGS} ${EGL_LIBRARIES} ${GLESv2_LDFLAGS} ${GLESv2_LIBRARIES} ${UDEV_LDFLAGS} ${UDEV_LIBRARIES} ${GLIB_LDFLAGS} ${GLIB_LIBRARIES} ${UUID_LDFLAGS} ${UUID_LIBRARIES} ) install(TARGETS mirserver LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/platform/mir DESTINATION "include/mirplatform" ) install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/server/mir DESTINATION "include/mirserver" ) set(MIRSERVER_ABI 38) # Be sure to increment MIR_VERSION_MINOR at the same time set(symbol_map ${CMAKE_CURRENT_SOURCE_DIR}/symbols.map) set_target_properties( mirserver PROPERTIES SOVERSION ${MIRSERVER_ABI} LINK_FLAGS "-Wl,--exclude-libs=ALL -Wl,--version-script,${symbol_map}" ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mirserver.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) ./src/server/graphics/0000755000015600001650000000000012676616160015005 5ustar jenkinsjenkins./src/server/graphics/default_display_configuration_policy.cpp0000644000015600001650000001306112676616125025172 0ustar jenkinsjenkins/* * Copyright © 2013-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/default_display_configuration_policy.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/pixel_format_utils.h" #include #include namespace mg = mir::graphics; namespace geom = mir::geometry; namespace { size_t select_mode_index(size_t mode_index, std::vector const & modes) { if (modes.empty()) return std::numeric_limits::max(); if (mode_index >= modes.size()) return 0; return mode_index; } MirPixelFormat select_opaque_format(MirPixelFormat format, std::vector const& formats) { auto const format_in_formats = formats.end() != std::find(formats.begin(), formats.end(), format); if (!mg::contains_alpha(format) && format_in_formats) return format; // format is either unavailable or transparent auto const first_opaque = std::find_if_not(formats.begin(), formats.end(), mg::contains_alpha); if (first_opaque != formats.end()) return *first_opaque; // only tranparent options - allow choice if available if (format_in_formats) return format; if (formats.size()) return formats.at(0); return mir_pixel_format_invalid; } } void mg::CloneDisplayConfigurationPolicy::apply_to(DisplayConfiguration& conf) { static MirPowerMode const default_power_state = mir_power_mode_on; std::unordered_map available_outputs_for_card; conf.for_each_card( [&](DisplayConfigurationCard const& card) { available_outputs_for_card[card.id] = card.max_simultaneous_outputs; }); conf.for_each_output( [&](UserDisplayConfigurationOutput& conf_output) { if (!conf_output.connected || conf_output.modes.empty() || available_outputs_for_card[conf_output.card_id] == 0) { conf_output.used = false; conf_output.power_mode = default_power_state; return; } size_t preferred_mode_index{select_mode_index(conf_output.preferred_mode_index, conf_output.modes)}; MirPixelFormat format{select_opaque_format(conf_output.current_format, conf_output.pixel_formats)}; conf_output.used = true; conf_output.top_left = geom::Point{0, 0}; conf_output.current_mode_index = preferred_mode_index; conf_output.current_format = format; conf_output.power_mode = default_power_state; conf_output.orientation = mir_orientation_normal; --available_outputs_for_card[conf_output.card_id]; }); } void mg::SideBySideDisplayConfigurationPolicy::apply_to(graphics::DisplayConfiguration& conf) { int max_x = 0; std::unordered_map available_outputs_for_card; conf.for_each_card( [&](mg::DisplayConfigurationCard const& card) { available_outputs_for_card[card.id] = card.max_simultaneous_outputs; }); conf.for_each_output( [&](mg::UserDisplayConfigurationOutput& conf_output) { if (conf_output.connected && conf_output.modes.size() > 0 && available_outputs_for_card[conf_output.card_id] > 0) { conf_output.used = true; conf_output.top_left = geom::Point{max_x, 0}; size_t preferred_mode_index{select_mode_index(conf_output.preferred_mode_index, conf_output.modes)}; conf_output.current_mode_index = preferred_mode_index; conf_output.power_mode = mir_power_mode_on; conf_output.orientation = mir_orientation_normal; max_x += conf_output.modes[preferred_mode_index].size.width.as_int(); --available_outputs_for_card[conf_output.card_id]; } else { conf_output.used = false; conf_output.power_mode = mir_power_mode_off; } }); } void mg::SingleDisplayConfigurationPolicy::apply_to(graphics::DisplayConfiguration& conf) { bool done{false}; conf.for_each_output( [&](mg::UserDisplayConfigurationOutput& conf_output) { if (!done && conf_output.connected && conf_output.modes.size() > 0) { conf_output.used = true; conf_output.top_left = geom::Point{0, 0}; size_t preferred_mode_index{select_mode_index(conf_output.preferred_mode_index, conf_output.modes)}; conf_output.current_mode_index = preferred_mode_index; conf_output.power_mode = mir_power_mode_on; done = true; } else { conf_output.used = false; conf_output.power_mode = mir_power_mode_off; } }); } ./src/server/graphics/offscreen/0000755000015600001650000000000012676616125016760 5ustar jenkinsjenkins./src/server/graphics/offscreen/display_configuration.cpp0000644000015600001650000000472712676616125024072 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "display_configuration.h" namespace mg = mir::graphics; namespace mgo = mg::offscreen; namespace geom = mir::geometry; mgo::DisplayConfiguration::DisplayConfiguration(geom::Size const& display_size) : output{mg::DisplayConfigurationOutputId{1}, mg::DisplayConfigurationCardId{0}, mg::DisplayConfigurationOutputType::lvds, {mir_pixel_format_xrgb_8888}, {mg::DisplayConfigurationMode{display_size,0.0f}}, 0, geom::Size{0,0}, true, true, geom::Point{0,0}, 0, mir_pixel_format_xrgb_8888, mir_power_mode_on, mir_orientation_normal, 1.0f, mir_form_factor_monitor}, card{mg::DisplayConfigurationCardId{0}, 1} { } mgo::DisplayConfiguration::DisplayConfiguration(DisplayConfiguration const& other) : mg::DisplayConfiguration(), output(other.output), card(other.card) { } mgo::DisplayConfiguration& mgo::DisplayConfiguration::operator=(DisplayConfiguration const& other) { if (&other != this) { output = other.output; card = other.card; } return *this; } void mgo::DisplayConfiguration::for_each_card( std::function f) const { f(card); } void mgo::DisplayConfiguration::for_each_output( std::function f) const { f(output); } void mgo::DisplayConfiguration::for_each_output( std::function f) { mg::UserDisplayConfigurationOutput user(output); f(user); } std::unique_ptr mgo::DisplayConfiguration::clone() const { return std::make_unique(*this); } ./src/server/graphics/offscreen/display_buffer.cpp0000644000015600001650000001106012676616125022460 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "display_buffer.h" #include "mir/graphics/gl_extensions_base.h" #include "mir/raii.h" #include #include #include #include namespace mg = mir::graphics; namespace mgo = mg::offscreen; namespace geom = mir::geometry; namespace { class GLExtensions : public mg::GLExtensionsBase { public: GLExtensions() : mg::GLExtensionsBase{ reinterpret_cast(glGetString(GL_EXTENSIONS))} { } }; } mgo::detail::GLFramebufferObject::GLFramebufferObject(geom::Size const& size) : size{size}, color_renderbuffer{0}, depth_renderbuffer{0}, fbo{0} { /* Save previous FBO state */ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &old_fbo); glGetIntegerv(GL_VIEWPORT, old_viewport); GLExtensions const extensions; GLenum gl_color_format{GL_RGBA4}; GLenum const gl_depth_format{GL_DEPTH_COMPONENT16}; if (extensions.support("GL_ARM_rgba8") || extensions.support("GL_OES_rgb8_rgba8")) { gl_color_format = GL_RGBA8_OES; } /* Create a renderbuffer for the color attachment */ glGenRenderbuffers(1, &color_renderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, color_renderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, gl_color_format, size.width.as_int(), size.height.as_int()); /* Create a renderbuffer for the depth attachment */ glGenRenderbuffers(1, &depth_renderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, depth_renderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, size.width.as_int(), size.height.as_int()); /* Create a FBO and set it up */ glGenFramebuffers(1, &fbo); auto const fbo_raii = mir::raii::paired_calls( [this] { glBindFramebuffer(GL_FRAMEBUFFER, fbo); }, [this] { glBindFramebuffer(GL_FRAMEBUFFER, old_fbo); }); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_renderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_renderbuffer); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set up FBO")); } mgo::detail::GLFramebufferObject::~GLFramebufferObject() { if (fbo) glDeleteFramebuffers(1, &fbo); if (color_renderbuffer) glDeleteRenderbuffers(1, &color_renderbuffer); if (depth_renderbuffer) glDeleteRenderbuffers(1, &depth_renderbuffer); } void mgo::detail::GLFramebufferObject::bind() const { glBindFramebuffer(GL_FRAMEBUFFER, fbo); glViewport(0, 0, size.width.as_int(), size.height.as_int()); } void mgo::detail::GLFramebufferObject::unbind() const { glBindFramebuffer(GL_FRAMEBUFFER, old_fbo); glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]); } mgo::DisplayBuffer::DisplayBuffer(SurfacelessEGLContext egl_context, geom::Rectangle const& area) : egl_context{std::move(egl_context)}, fbo{area.size}, area(area) { } geom::Rectangle mgo::DisplayBuffer::view_area() const { return area; } void mgo::DisplayBuffer::make_current() { egl_context.make_current(); fbo.bind(); } void mgo::DisplayBuffer::release_current() { fbo.unbind(); egl_context.release_current(); } void mgo::DisplayBuffer::swap_buffers() { glFinish(); } bool mgo::DisplayBuffer::post_renderables_if_optimizable(RenderableList const&) { return false; } MirOrientation mgo::DisplayBuffer::orientation() const { /* * The display buffer's already constructed with rotated dimensions, * so nothing more to do. */ return mir_orientation_normal; } mg::NativeDisplayBuffer* mgo::DisplayBuffer::native_display_buffer() { return this; } ./src/server/graphics/offscreen/display_configuration.h0000644000015600001650000000315212676616125023526 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MIR_GRAPHICS_OFFSCREEN_DISPLAY_CONFIGURATION_H_ #define MIR_GRAPHICS_OFFSCREEN_DISPLAY_CONFIGURATION_H_ #include "mir/graphics/display_configuration.h" namespace mir { namespace graphics { namespace offscreen { class DisplayConfiguration : public graphics::DisplayConfiguration { public: DisplayConfiguration(geometry::Size const& display_size); DisplayConfiguration(DisplayConfiguration const& other); DisplayConfiguration& operator=(DisplayConfiguration const& other); void for_each_card(std::function f) const override; void for_each_output(std::function f) const override; void for_each_output(std::function f) override; std::unique_ptr clone() const override; private: DisplayConfigurationOutput output; DisplayConfigurationCard card; }; } } } #endif /* MIR_GRAPHICS_OFFSCREEN_DISPLAY_CONFIGURATION_H_ */ ./src/server/graphics/offscreen/display.h0000644000015600001650000000657012676616125020606 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_OFFSCREEN_DISPLAY_H_ #define MIR_GRAPHICS_OFFSCREEN_DISPLAY_H_ #include "mir/graphics/display.h" #include "display_configuration.h" #include "mir/graphics/surfaceless_egl_context.h" #include #include #include namespace mir { namespace graphics { class DisplayConfigurationPolicy; class DisplayReport; namespace offscreen { namespace detail { class EGLDisplayHandle { public: explicit EGLDisplayHandle(EGLNativeDisplayType native_type); EGLDisplayHandle(EGLDisplayHandle&&); ~EGLDisplayHandle() noexcept; void initialize(); operator EGLDisplay() const { return egl_display; } private: EGLDisplayHandle(EGLDisplayHandle const&) = delete; EGLDisplayHandle operator=(EGLDisplayHandle const&) = delete; EGLDisplay egl_display; }; class DisplaySyncGroup : public graphics::DisplaySyncGroup { public: DisplaySyncGroup(std::unique_ptr output); void for_each_display_buffer(std::function const&) override; void post() override; std::chrono::milliseconds recommended_sleep() const override; private: std::unique_ptr const output; }; } class Display : public graphics::Display { public: Display(EGLNativeDisplayType egl_native_display, std::shared_ptr const& initial_conf_policy, std::shared_ptr const& listener); ~Display() noexcept; void for_each_display_sync_group(std::function const& f) override; std::unique_ptr configuration() const override; void configure(graphics::DisplayConfiguration const& conf) override; void register_configuration_change_handler( EventHandlerRegister& handlers, DisplayConfigurationChangeHandler const& conf_change_handler) override; void register_pause_resume_handlers( EventHandlerRegister& handlers, DisplayPauseHandler const& pause_handler, DisplayResumeHandler const& resume_handler) override; void pause() override; void resume() override; std::shared_ptr create_hardware_cursor(std::shared_ptr const& initial_image) override; std::unique_ptr create_gl_context() override; std::unique_ptr create_virtual_output(int width, int height) override; private: detail::EGLDisplayHandle const egl_display; SurfacelessEGLContext const egl_context_shared; mutable std::mutex configuration_mutex; DisplayConfiguration current_display_configuration; std::vector> display_sync_groups; }; } } } #endif /* MIR_GRAPHICS_OFFSCREEN_DISPLAY_H_ */ ./src/server/graphics/offscreen/display_buffer.h0000644000015600001650000000437212676616125022135 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_OFFSCREEN_DISPLAY_BUFFER_H_ #define MIR_GRAPHICS_OFFSCREEN_DISPLAY_BUFFER_H_ #include "mir/graphics/surfaceless_egl_context.h" #include "mir/graphics/display_buffer.h" #include "mir/geometry/size.h" #include "mir/geometry/rectangle.h" #include "mir/renderer/gl/render_target.h" #include namespace mir { namespace graphics { namespace offscreen { namespace detail { class GLFramebufferObject { public: GLFramebufferObject(geometry::Size const& size); ~GLFramebufferObject(); void bind() const; void unbind() const; private: geometry::Size const size; int old_fbo; int old_viewport[4]; unsigned int color_renderbuffer; unsigned int depth_renderbuffer; unsigned int fbo; }; } class DisplayBuffer : public graphics::DisplayBuffer, public graphics::NativeDisplayBuffer, public renderer::gl::RenderTarget { public: DisplayBuffer(SurfacelessEGLContext egl_context, geometry::Rectangle const& area); geometry::Rectangle view_area() const override; void make_current() override; void release_current() override; void swap_buffers() override; MirOrientation orientation() const override; bool post_renderables_if_optimizable(RenderableList const& renderlist) override; NativeDisplayBuffer* native_display_buffer() override; private: SurfacelessEGLContext const egl_context; detail::GLFramebufferObject const fbo; geometry::Rectangle const area; }; } } } #endif /* MIR_GRAPHICS_OFFSCREEN_DISPLAY_BUFFER_H_ */ ./src/server/graphics/offscreen/display.cpp0000644000015600001650000001254512676616125021140 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "display.h" #include "display_buffer.h" #include "mir/graphics/display_configuration_policy.h" #include "mir/graphics/egl_error.h" #include "mir/graphics/virtual_output.h" #include "mir/geometry/size.h" #include #include namespace mg = mir::graphics; namespace mgo = mg::offscreen; namespace geom = mir::geometry; namespace { mgo::detail::EGLDisplayHandle create_and_initialize_display(EGLNativeDisplayType egl_native_display) { mgo::detail::EGLDisplayHandle egl_display{egl_native_display}; egl_display.initialize(); return egl_display; } } mgo::detail::EGLDisplayHandle::EGLDisplayHandle(EGLNativeDisplayType native_display) : egl_display{eglGetDisplay(native_display)} { if (egl_display == EGL_NO_DISPLAY) BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get EGL display")); } mgo::detail::EGLDisplayHandle::EGLDisplayHandle(EGLDisplayHandle&& other) : egl_display{other.egl_display} { other.egl_display = EGL_NO_DISPLAY; } void mgo::detail::EGLDisplayHandle::initialize() { int major, minor; if (eglInitialize(egl_display, &major, &minor) == EGL_FALSE) BOOST_THROW_EXCEPTION(mg::egl_error("Failed to initialize EGL")); if ((major != 1) || (minor != 4)) BOOST_THROW_EXCEPTION(std::runtime_error("EGL version 1.4 needed")); } mgo::detail::EGLDisplayHandle::~EGLDisplayHandle() noexcept { if (egl_display != EGL_NO_DISPLAY) eglTerminate(egl_display); } mgo::detail::DisplaySyncGroup::DisplaySyncGroup(std::unique_ptr output) : output(std::move(output)) { } void mgo::detail::DisplaySyncGroup::for_each_display_buffer( std::function const& f) { f(*output); } void mgo::detail::DisplaySyncGroup::post() { } std::chrono::milliseconds mgo::detail::DisplaySyncGroup::recommended_sleep() const { return std::chrono::milliseconds::zero(); } mgo::Display::Display( EGLNativeDisplayType egl_native_display, std::shared_ptr const& initial_conf_policy, std::shared_ptr const&) : egl_display{create_and_initialize_display(egl_native_display)}, egl_context_shared{egl_display, EGL_NO_CONTEXT}, current_display_configuration{geom::Size{1024,768}} { /* * Make the shared context current. This needs to be done before we configure() * since mgo::DisplayBuffer creation needs a current GL context. */ egl_context_shared.make_current(); initial_conf_policy->apply_to(current_display_configuration); configure(current_display_configuration); } mgo::Display::~Display() noexcept { } void mgo::Display::for_each_display_sync_group( std::function const& f) { std::lock_guard lock{configuration_mutex}; for (auto& dg_ptr : display_sync_groups) f(*dg_ptr); } std::unique_ptr mgo::Display::configuration() const { std::lock_guard lock{configuration_mutex}; return std::make_unique( current_display_configuration); } void mgo::Display::configure(mg::DisplayConfiguration const& conf) { if (!conf.valid()) { BOOST_THROW_EXCEPTION( std::logic_error("Invalid or inconsistent display configuration")); } std::lock_guard lock{configuration_mutex}; display_sync_groups.clear(); conf.for_each_output( [this] (DisplayConfigurationOutput const& output) { if (output.connected && output.preferred_mode_index < output.modes.size()) { auto raw_db = new mgo::DisplayBuffer{ SurfacelessEGLContext{egl_display, egl_context_shared}, output.extents()}; display_sync_groups.emplace_back( new mgo::detail::DisplaySyncGroup(std::unique_ptr(raw_db))); } }); } void mgo::Display::register_configuration_change_handler( EventHandlerRegister&, DisplayConfigurationChangeHandler const&) { } void mgo::Display::register_pause_resume_handlers( EventHandlerRegister&, DisplayPauseHandler const&, DisplayResumeHandler const&) { } void mgo::Display::pause() { } void mgo::Display::resume() { } std::shared_ptr mgo::Display::create_hardware_cursor(std::shared_ptr const& /* initial_image */) { return {}; } std::unique_ptr mgo::Display::create_gl_context() { return std::make_unique(egl_display, egl_context_shared); } std::unique_ptr mgo::Display::create_virtual_output(int /*width*/, int /*height*/) { return nullptr; } ./src/server/graphics/offscreen/CMakeLists.txt0000644000015600001650000000026312676616125021521 0ustar jenkinsjenkinsinclude_directories( ${PROJECT_SOURCE_DIR}/include/renderers/gl ) add_library( miroffscreengraphics OBJECT display.cpp display_configuration.cpp display_buffer.cpp ) ./src/server/graphics/nested/0000755000015600001650000000000012676616160016267 5ustar jenkinsjenkins./src/server/graphics/nested/cursor.h0000644000015600001650000000255512676616125017765 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_GRAPHICS_NESTED_CURSOR_H_ #define MIR_GRAPHICS_NESTED_CURSOR_H_ #include "mir/graphics/cursor.h" namespace mir { namespace graphics { namespace nested { class HostConnection; class Cursor : public graphics::Cursor { public: Cursor(std::shared_ptr const& host_connection, std::shared_ptr const& default_image); ~Cursor(); void show(CursorImage const& image) override; void show() override; void hide() override; void move_to(geometry::Point position) override; private: std::shared_ptr const connection; std::shared_ptr const default_image; }; } } } #endif // MIR_GRAPHICS_NESTED_CURSOR_H_ ./src/server/graphics/nested/display_buffer.cpp0000644000015600001650000001041412676616125021772 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "display_buffer.h" #include "host_connection.h" #include "mir/input/input_dispatcher.h" #include "mir/graphics/pixel_format_utils.h" #include "mir/input/cursor_listener.h" #include "mir/graphics/egl_error.h" #include "mir/events/event_private.h" #include #include namespace mi = mir::input; namespace mg = mir::graphics; namespace mgn = mir::graphics::nested; namespace geom = mir::geometry; mgn::detail::DisplayBuffer::DisplayBuffer( EGLDisplayHandle const& egl_display, std::shared_ptr const& host_surface, geometry::Rectangle const& area, std::shared_ptr const& dispatcher, std::shared_ptr const& cursor_listener, MirPixelFormat preferred_format) : egl_display(egl_display), host_surface{host_surface}, egl_config{egl_display.choose_windowed_es_config(preferred_format)}, egl_context{egl_display, eglCreateContext(egl_display, egl_config, egl_display.egl_context(), nested_egl_context_attribs)}, area{area.top_left, area.size}, dispatcher{dispatcher}, cursor_listener{cursor_listener}, egl_surface{egl_display, host_surface->egl_native_window(), egl_config} { host_surface->set_event_handler(event_thunk, this); } geom::Rectangle mgn::detail::DisplayBuffer::view_area() const { return area; } void mgn::detail::DisplayBuffer::make_current() { if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) != EGL_TRUE) BOOST_THROW_EXCEPTION(mg::egl_error("Nested Mir Display Error: Failed to update EGL surface")); } void mgn::detail::DisplayBuffer::release_current() { eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } void mgn::detail::DisplayBuffer::swap_buffers() { eglSwapBuffers(egl_display, egl_surface); } bool mgn::detail::DisplayBuffer::post_renderables_if_optimizable(RenderableList const&) { return false; } MirOrientation mgn::detail::DisplayBuffer::orientation() const { /* * Always normal orientation. The real rotation is handled by the * native display. */ return mir_orientation_normal; } mgn::detail::DisplayBuffer::~DisplayBuffer() noexcept { } void mgn::detail::DisplayBuffer::event_thunk( MirSurface* /*surface*/, MirEvent const* event, void* context) try { static_cast(context)->mir_event(*event); } catch (std::exception const&) { // Just in case: do not allow exceptions to propagate. } void mgn::detail::DisplayBuffer::mir_event(MirEvent const& event) { if (mir_event_get_type(&event) != mir_event_type_input) return; if (event.type == mir_event_type_motion) { auto my_event = event; auto iev = mir_event_get_input_event(&my_event); if (mir_input_event_get_type(iev) == mir_input_event_type_pointer) { auto& motion = my_event.motion; for (size_t i = 0; i != motion.pointer_count; ++i) { motion.pointer_coordinates[i].x += area.top_left.x.as_float(); motion.pointer_coordinates[i].y += area.top_left.y.as_float(); } auto pev = mir_input_event_get_pointer_event(iev); auto x = mir_pointer_event_axis_value(pev, mir_pointer_axis_x); auto y = mir_pointer_event_axis_value(pev, mir_pointer_axis_y); cursor_listener->cursor_moved_to(x, y); } dispatcher->dispatch(my_event); } else { dispatcher->dispatch(event); } } mg::NativeDisplayBuffer* mgn::detail::DisplayBuffer::native_display_buffer() { return this; } ./src/server/graphics/nested/mir_client_host_connection.cpp0000644000015600001650000003707312676616157024414 0ustar jenkinsjenkins/* * Copyright © 2014,2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir_client_host_connection.h" #include "host_surface.h" #include "mir_toolkit/mir_client_library.h" #include "mir/raii.h" #include "mir/graphics/platform_operation_message.h" #include "mir/graphics/cursor_image.h" #include "mir/input/device.h" #include "mir/input/device_capability.h" #include "mir/input/pointer_configuration.h" #include "mir/input/touchpad_configuration.h" #include "mir/input/input_device_observer.h" #include "mir/frontend/event_sink.h" #include "mir/server_action_queue.h" #include #include #include #include #include namespace mg = mir::graphics; namespace mgn = mir::graphics::nested; namespace mi = mir::input; namespace mf = mir::frontend; namespace { mgn::UniqueInputConfig make_empty_config() { return mgn::UniqueInputConfig(nullptr, [](MirInputConfig const*){}); } mgn::UniqueInputConfig make_input_config(MirConnection* con) { return mgn::UniqueInputConfig(mir_connection_create_input_config(con), mir_input_config_destroy); } class NestedDevice : public mi::Device { public: NestedDevice(MirInputDevice const* dev) { update(dev); } void update(MirInputDevice const* dev) { device_id = mir_input_device_get_id(dev); device_name = mir_input_device_get_name(dev); unique_device_id = mir_input_device_get_unique_id(dev); caps = mi::DeviceCapabilities(mir_input_device_get_capabilities(dev)); } MirInputDeviceId id() const { return device_id; } mi::DeviceCapabilities capabilities() const { return caps; } std::string name() const { return device_name; } std::string unique_id() const { return unique_device_id; } mir::optional_value pointer_configuration() const { return pointer_conf; } void apply_pointer_configuration(mi::PointerConfiguration const&) { // TODO requires c api support } mir::optional_value touchpad_configuration() const { return touchpad_conf; } void apply_touchpad_configuration(mi::TouchpadConfiguration const&) { // TODO requires c api support } private: MirInputDeviceId device_id; std::string device_name; std::string unique_device_id; mi::DeviceCapabilities caps; mir::optional_value pointer_conf; mir::optional_value touchpad_conf; }; void display_config_callback_thunk(MirConnection* /*connection*/, void* context) { (*static_cast*>(context))(); } void platform_operation_callback( MirConnection*, MirPlatformMessage* reply, void* context) { auto reply_ptr = static_cast(context); *reply_ptr = reply; } static void nested_lifecycle_event_callback_thunk(MirConnection* /*connection*/, MirLifecycleState state, void *context) { msh::HostLifecycleEventListener* listener = static_cast(context); listener->lifecycle_event_occurred(state); } MirPixelFormat const cursor_pixel_format = mir_pixel_format_argb_8888; void copy_image(MirGraphicsRegion const& g, mg::CursorImage const& image) { assert(g.pixel_format == cursor_pixel_format); auto const image_stride = image.size().width.as_int() * MIR_BYTES_PER_PIXEL(cursor_pixel_format); auto const image_height = image.size().height.as_int(); auto dest = g.vaddr; auto src = static_cast(image.as_argb_8888()); for (int row = 0; row != image_height; ++row) { memcpy(dest, src, image_stride); dest += g.stride; src += image_stride; } } class MirClientHostSurface : public mgn::HostSurface { public: MirClientHostSurface( MirConnection* mir_connection, MirSurfaceSpec* spec) : mir_connection(mir_connection), mir_surface{ mir_surface_create_sync(spec)} { if (!mir_surface_is_valid(mir_surface)) { BOOST_THROW_EXCEPTION( std::runtime_error(mir_surface_get_error_message(mir_surface))); } } ~MirClientHostSurface() { if (cursor) mir_buffer_stream_release_sync(cursor); mir_surface_release_sync(mir_surface); } EGLNativeWindowType egl_native_window() override { return reinterpret_cast( mir_buffer_stream_get_egl_native_window(mir_surface_get_buffer_stream(mir_surface))); } void set_event_handler(mir_surface_event_callback cb, void* context) override { mir_surface_set_event_handler(mir_surface, cb, context); } void set_cursor_image(mg::CursorImage const& image) { auto const image_width = image.size().width.as_int(); auto const image_height = image.size().height.as_int(); MirGraphicsRegion g; if (cursor) { mir_buffer_stream_get_graphics_region(cursor, &g); if (image_width != g.width || image_height != g.height) { mir_buffer_stream_release_sync(cursor); cursor = nullptr; } } bool const new_cursor{!cursor}; if (new_cursor) { cursor = mir_connection_create_buffer_stream_sync( mir_connection, image_width, image_height, cursor_pixel_format, mir_buffer_usage_software); mir_buffer_stream_get_graphics_region(cursor, &g); } copy_image(g, image); mir_buffer_stream_swap_buffers_sync(cursor); if (new_cursor || cursor_hotspot != image.hotspot()) { cursor_hotspot = image.hotspot(); auto conf = mir_cursor_configuration_from_buffer_stream( cursor, cursor_hotspot.dx.as_int(), cursor_hotspot.dy.as_int()); mir_surface_configure_cursor(mir_surface, conf); mir_cursor_configuration_destroy(conf); } } void hide_cursor() { if (cursor) { mir_buffer_stream_release_sync(cursor); cursor = nullptr; } auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name); mir_surface_configure_cursor(mir_surface, conf); mir_cursor_configuration_destroy(conf); } private: MirConnection* const mir_connection; MirSurface* const mir_surface; MirBufferStream* cursor{nullptr}; mir::geometry::Displacement cursor_hotspot; }; } mgn::MirClientHostConnection::MirClientHostConnection( std::string const& host_socket, std::string const& name, std::shared_ptr const& host_lifecycle_event_listener, std::shared_ptr const& sink, std::shared_ptr const& input_observer_queue) : mir_connection{mir_connect_sync(host_socket.c_str(), name.c_str())}, conf_change_callback{[]{}}, host_lifecycle_event_listener{host_lifecycle_event_listener}, sink{sink}, observer_queue{input_observer_queue}, config{make_empty_config()} { if (!mir_connection_is_valid(mir_connection)) { std::string const msg = "Nested Mir Platform Connection Error: " + std::string(mir_connection_get_error_message(mir_connection)); BOOST_THROW_EXCEPTION(std::runtime_error(msg)); } mir_connection_set_lifecycle_event_callback( mir_connection, nested_lifecycle_event_callback_thunk, std::static_pointer_cast(host_lifecycle_event_listener).get()); mir_connection_set_input_config_change_callback( mir_connection, [](MirConnection*, void* context) { auto obj = static_cast(context); obj->update_input_devices(); }, this); update_input_devices(); } mgn::MirClientHostConnection::~MirClientHostConnection() { mir_connection_release(mir_connection); } std::vector mgn::MirClientHostConnection::platform_fd_items() { MirPlatformPackage pkg; mir_connection_get_platform(mir_connection, &pkg); return std::vector(pkg.fd, pkg.fd + pkg.fd_items); } EGLNativeDisplayType mgn::MirClientHostConnection::egl_native_display() { return reinterpret_cast( mir_connection_get_egl_native_display(mir_connection)); } auto mgn::MirClientHostConnection::create_display_config() -> std::shared_ptr { return std::shared_ptr( mir_connection_create_display_config(mir_connection), [] (MirDisplayConfiguration* c) { if (c) mir_display_config_destroy(c); }); } void mgn::MirClientHostConnection::set_display_config_change_callback( std::function const& callback) { mir_connection_set_display_config_change_callback( mir_connection, &display_config_callback_thunk, &(conf_change_callback = callback)); } void mgn::MirClientHostConnection::apply_display_config( MirDisplayConfiguration& display_config) { mir_wait_for(mir_connection_apply_display_config(mir_connection, &display_config)); } std::shared_ptr mgn::MirClientHostConnection::create_surface( int width, int height, MirPixelFormat pf, char const* name, MirBufferUsage usage, uint32_t output_id) { std::lock_guard lg(surfaces_mutex); auto spec = mir::raii::deleter_for( mir_connection_create_spec_for_normal_surface(mir_connection, width, height, pf), mir_surface_spec_release); mir_surface_spec_set_name(spec.get(), name); mir_surface_spec_set_buffer_usage(spec.get(), usage); mir_surface_spec_set_fullscreen_on_output(spec.get(), output_id); auto surf = std::shared_ptr( new MirClientHostSurface(mir_connection, spec.get()), [this](MirClientHostSurface *surf) { std::lock_guard lg(surfaces_mutex); auto it = std::find(surfaces.begin(), surfaces.end(), surf); surfaces.erase(it); delete surf; }); surfaces.push_back(surf.get()); return surf; } mg::PlatformOperationMessage mgn::MirClientHostConnection::platform_operation( unsigned int op, mg::PlatformOperationMessage const& request) { auto const msg = mir::raii::deleter_for( mir_platform_message_create(op), mir_platform_message_release); mir_platform_message_set_data(msg.get(), request.data.data(), request.data.size()); mir_platform_message_set_fds(msg.get(), request.fds.data(), request.fds.size()); MirPlatformMessage* raw_reply{nullptr}; auto const wh = mir_connection_platform_operation( mir_connection, msg.get(), platform_operation_callback, &raw_reply); mir_wait_for(wh); auto const reply = mir::raii::deleter_for( raw_reply, mir_platform_message_release); auto reply_data = mir_platform_message_get_data(reply.get()); auto reply_fds = mir_platform_message_get_fds(reply.get()); return PlatformOperationMessage{ {static_cast(reply_data.data), static_cast(reply_data.data) + reply_data.size}, {reply_fds.fds, reply_fds.fds + reply_fds.num_fds}}; } void mgn::MirClientHostConnection::set_cursor_image(mg::CursorImage const& image) { std::lock_guard lg(surfaces_mutex); for (auto s : surfaces) { auto surface = static_cast(s); surface->set_cursor_image(image); } } void mgn::MirClientHostConnection::hide_cursor() { std::lock_guard lg(surfaces_mutex); for (auto s : surfaces) { auto surface = static_cast(s); surface->hide_cursor(); } } auto mgn::MirClientHostConnection::graphics_platform_library() -> std::string { MirModuleProperties properties = { nullptr, 0, 0, 0, nullptr }; mir_connection_get_graphics_module(mir_connection, &properties); if (properties.filename == nullptr) { BOOST_THROW_EXCEPTION(std::runtime_error("Cannot identify host graphics platform")); } return properties.filename; } void mgn::MirClientHostConnection::update_input_devices() { std::lock_guard lock(devices_guard); config = make_input_config(mir_connection); auto deleted = std::move(devices); std::vector> new_devs; auto config_ptr = config.get(); for (size_t i = 0, e = mir_input_config_device_count(config_ptr); i!=e; ++i) { auto dev = mir_input_config_get_device(config_ptr, i); auto it = std::find_if( begin(deleted), end(deleted), [id = mir_input_device_get_id(dev)](auto const& dev) { return id == dev->id(); }); if (it != end(deleted)) { std::static_pointer_cast(*it)->update(dev); devices.push_back(*it); deleted.erase(it); } else { devices.push_back(std::make_shared(dev)); new_devs.push_back(devices.back()); } } sink->handle_input_device_change(devices); if ((deleted.empty() && new_devs.empty()) || observers.empty()) return; observer_queue->enqueue( this, [this, new_devs = std::move(new_devs), deleted = std::move(deleted)] { std::lock_guard lock(devices_guard); for (auto const observer : observers) { for (auto const& item : new_devs) observer->device_added(item); for (auto const& item : deleted) observer->device_removed(item); observer->changes_complete(); } }); } void mgn::MirClientHostConnection::add_observer(std::shared_ptr const& observer) { observer_queue->enqueue( this, [observer,this] { std::lock_guard lock(devices_guard); observers.push_back(observer); for (auto const& item : devices) { observer->device_added(item); } observer->changes_complete(); } ); } void mgn::MirClientHostConnection::remove_observer(std::weak_ptr const& element) { auto observer = element.lock(); observer_queue->enqueue(this, [observer, this] { observers.erase(remove(begin(observers), end(observers), observer), end(observers)); }); } void mgn::MirClientHostConnection::for_each_input_device(std::function const& callback) { std::lock_guard lock(devices_guard); for (auto const& item : devices) { callback(*item); } } ./src/server/graphics/nested/display.h0000644000015600001650000001215212676616125020107 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Eleni Maria Stea */ #ifndef MIR_GRAPHICS_NESTED_DISPLAY_H_ #define MIR_GRAPHICS_NESTED_DISPLAY_H_ #include "mir/graphics/display.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/egl_resources.h" #include "mir_toolkit/client_types.h" #include #include #include namespace mir { namespace input { class InputDispatcher; class CursorListener; } namespace geometry { struct Rectangle; } namespace graphics { class DisplayReport; class DisplayBuffer; class DisplayConfigurationPolicy; class GLConfig; class Platform; namespace nested { class NestedDisplayConfiguration; namespace detail { class EGLSurfaceHandle { public: explicit EGLSurfaceHandle(EGLDisplay display, EGLNativeWindowType native_window, EGLConfig cfg); ~EGLSurfaceHandle() noexcept; operator EGLSurface() const { return egl_surface; } private: EGLDisplay const egl_display; EGLSurface const egl_surface; }; class EGLDisplayHandle { public: EGLDisplayHandle(EGLNativeDisplayType native_display, std::shared_ptr const& gl_config); ~EGLDisplayHandle() noexcept; void initialize(MirPixelFormat format); EGLConfig choose_windowed_es_config(MirPixelFormat format) const; EGLContext egl_context() const; std::unique_ptr create_gl_context(); operator EGLDisplay() const { return egl_display; } private: EGLDisplay egl_display; EGLContext egl_context_; std::shared_ptr const gl_config; MirPixelFormat pixel_format; EGLDisplayHandle(EGLDisplayHandle const&) = delete; EGLDisplayHandle operator=(EGLDisplayHandle const&) = delete; }; class DisplayBuffer; class DisplaySyncGroup : public graphics::DisplaySyncGroup { public: DisplaySyncGroup(std::shared_ptr const& output); void for_each_display_buffer(std::function const&) override; void post() override; std::chrono::milliseconds recommended_sleep() const override; geometry::Rectangle view_area() const; private: std::shared_ptr const output; }; extern EGLint const nested_egl_context_attribs[]; } class HostConnection; class Display : public graphics::Display { public: Display( std::shared_ptr const& platform, std::shared_ptr const& connection, std::shared_ptr const& dispatcher, std::shared_ptr const& display_report, std::shared_ptr const& conf_policy, std::shared_ptr const& gl_config, std::shared_ptr const& cursor_listener); ~Display() noexcept; void for_each_display_sync_group(std::functionconst& f) override; std::unique_ptr configuration() const override; void configure(DisplayConfiguration const&) override; void register_configuration_change_handler( EventHandlerRegister& handlers, DisplayConfigurationChangeHandler const& conf_change_handler) override; void register_pause_resume_handlers( EventHandlerRegister& handlers, DisplayPauseHandler const& pause_handler, DisplayResumeHandler const& resume_handler) override; void pause() override; void resume() override; std::shared_ptr create_hardware_cursor(std::shared_ptr const& initial_image) override; std::unique_ptr create_gl_context() override; std::unique_ptr create_virtual_output(int width, int height) override; private: std::shared_ptr const platform; std::shared_ptr const connection; std::shared_ptr const dispatcher; std::shared_ptr const display_report; detail::EGLDisplayHandle egl_display; std::shared_ptr const cursor_listener; std::mutex outputs_mutex; std::unordered_map> outputs; std::mutex mutable configuration_mutex; std::unique_ptr current_configuration; void create_surfaces(mir::graphics::DisplayConfiguration const& configuration); void complete_display_initialization(MirPixelFormat format); }; } } } #endif // MIR_GRAPHICS_NESTED_DISPLAY_H_ ./src/server/graphics/nested/mir_client_host_connection.h0000644000015600001650000000703512676616157024054 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_NESTED_MIR_CLIENT_HOST_CONNECTION_H_ #define MIR_GRAPHICS_NESTED_MIR_CLIENT_HOST_CONNECTION_H_ #include "host_connection.h" #include "mir/shell/host_lifecycle_event_listener.h" #include "mir/input/input_device_hub.h" #include #include struct MirConnection; namespace msh = mir::shell; namespace mir { class ServerActionQueue; namespace frontend { class EventSink; } namespace input { class InputDeviceObserver; class Device; } namespace graphics { namespace nested { using UniqueInputConfig = std::unique_ptr; class MirClientHostConnection : public HostConnection, public input::InputDeviceHub { public: MirClientHostConnection(std::string const& host_socket, std::string const& name, std::shared_ptr const& host_lifecycle_event_listener, std::shared_ptr const& sink, std::shared_ptr const& observer_queue); ~MirClientHostConnection(); std::vector platform_fd_items() override; EGLNativeDisplayType egl_native_display() override; std::shared_ptr create_display_config() override; std::shared_ptr create_surface( int width, int height, MirPixelFormat pf, char const* name, MirBufferUsage usage, uint32_t output_id) override; void set_display_config_change_callback(std::function const& cb) override; void apply_display_config(MirDisplayConfiguration&) override; void set_cursor_image(CursorImage const& image) override; void hide_cursor() override; auto graphics_platform_library() -> std::string override; virtual PlatformOperationMessage platform_operation( unsigned int op, PlatformOperationMessage const& request) override; // InputDeviceHub void add_observer(std::shared_ptr const&) override; void remove_observer(std::weak_ptr const&) override; void for_each_input_device(std::function const& callback) override; private: void update_input_devices(); std::mutex surfaces_mutex; MirConnection* const mir_connection; std::function conf_change_callback; std::shared_ptr const host_lifecycle_event_listener; std::vector surfaces; std::shared_ptr const sink; std::shared_ptr const observer_queue; std::vector> observers; std::mutex devices_guard; std::vector> devices; UniqueInputConfig config; }; } } } #endif /* MIR_GRAPHICS_NESTED_MIR_CLIENT_HOST_CONNECTION_H_ */ ./src/server/graphics/nested/display_buffer.h0000644000015600001650000000516512676616125021446 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_GRAPHICS_NESTED_DETAIL_NESTED_OUTPUT_H_ #define MIR_GRAPHICS_NESTED_DETAIL_NESTED_OUTPUT_H_ #include "mir/graphics/display_buffer.h" #include "mir/renderer/gl/render_target.h" #include "display.h" #include "host_surface.h" #include namespace mir { namespace input { class CursorListener; } namespace graphics { namespace nested { class HostSurface; namespace detail { class DisplayBuffer : public graphics::DisplayBuffer, public graphics::NativeDisplayBuffer, public renderer::gl::RenderTarget { public: DisplayBuffer( EGLDisplayHandle const& egl_display, std::shared_ptr const& host_surface, geometry::Rectangle const& area, std::shared_ptr const& input_dispatcher, std::shared_ptr const& cursor, MirPixelFormat preferred_format); ~DisplayBuffer() noexcept; geometry::Rectangle view_area() const override; void make_current() override; void release_current() override; void swap_buffers() override; MirOrientation orientation() const override; bool post_renderables_if_optimizable(RenderableList const& renderlist) override; NativeDisplayBuffer* native_display_buffer() override; DisplayBuffer(DisplayBuffer const&) = delete; DisplayBuffer operator=(DisplayBuffer const&) = delete; private: EGLDisplayHandle const& egl_display; std::shared_ptr const host_surface; EGLConfig const egl_config; EGLContextStore const egl_context; geometry::Rectangle const area; std::shared_ptr const dispatcher; std::shared_ptr const cursor_listener; EGLSurfaceHandle const egl_surface; static void event_thunk(MirSurface* surface, MirEvent const* event, void* context); void mir_event(MirEvent const& event); }; } } } } #endif /* MIR_GRAPHICS_NESTED_DETAIL_NESTED_OUTPUT_H_ */ ./src/server/graphics/nested/nested_display_configuration.cpp0000644000015600001650000001752512676616125024744 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Eleni Maria Stea */ #include "nested_display_configuration.h" #include "host_connection.h" #include "mir/graphics/pixel_format_utils.h" #include "mir/raii.h" #include "mir_toolkit/mir_connection.h" #include "mir_toolkit/mir_blob.h" #include #include #include namespace mg = mir::graphics; namespace mgn = mg::nested; namespace { auto copy_config(MirDisplayConfiguration* conf) -> std::shared_ptr { auto const blob = mir::raii::deleter_for( mir_blob_from_display_configuration(conf), [] (MirBlob* b) { mir_blob_release(b); }); return std::shared_ptr{ mir_blob_to_display_configuration(blob.get()), [] (MirDisplayConfiguration* c) { if (c) mir_display_config_destroy(c); }}; } } mgn::NestedDisplayConfiguration::NestedDisplayConfiguration( std::shared_ptr const& display_config) : display_config{display_config} { } mgn::NestedDisplayConfiguration::NestedDisplayConfiguration( NestedDisplayConfiguration const& other) : mg::DisplayConfiguration(), display_config{copy_config(other.display_config.get())} { std::lock_guard lock{other.local_config_mutex}; local_config = other.local_config; } void mgn::NestedDisplayConfiguration::for_each_card( std::function f) const { std::for_each( display_config->cards, display_config->cards+display_config->num_cards, [&f](MirDisplayCard const& mir_card) { f({DisplayConfigurationCardId(mir_card.card_id), mir_card.max_simultaneous_outputs}); }); } void mgn::NestedDisplayConfiguration::for_each_output(std::function f) const { std::for_each( display_config->outputs, display_config->outputs+display_config->num_outputs, [&f, this](MirDisplayOutput const& mir_output) { std::vector formats; formats.reserve(mir_output.num_output_formats); for (auto p = mir_output.output_formats; p != mir_output.output_formats+mir_output.num_output_formats; ++p) formats.push_back(MirPixelFormat(*p)); std::vector modes; modes.reserve(mir_output.num_modes); for (auto p = mir_output.modes; p != mir_output.modes+mir_output.num_modes; ++p) modes.push_back(DisplayConfigurationMode{{p->horizontal_resolution, p->vertical_resolution}, p->refresh_rate}); auto local_config = get_local_config_for(mir_output.output_id); DisplayConfigurationOutput const output{ DisplayConfigurationOutputId(mir_output.output_id), DisplayConfigurationCardId(mir_output.card_id), DisplayConfigurationOutputType(mir_output.type), std::move(formats), std::move(modes), mir_output.preferred_mode, geometry::Size{mir_output.physical_width_mm, mir_output.physical_height_mm}, !!mir_output.connected, !!mir_output.used, geometry::Point{mir_output.position_x, mir_output.position_y}, mir_output.current_mode, mir_output.current_format, mir_output.power_mode, mir_output.orientation, local_config.scale, local_config.form_factor }; f(output); }); } void mgn::NestedDisplayConfiguration::for_each_output( std::function f) { // This is mostly copied and pasted from the const version above, but this // mutable version copies user-changes to the output structure at the end. std::for_each( display_config->outputs, display_config->outputs+display_config->num_outputs, [&f, this](MirDisplayOutput& mir_output) { std::vector formats; formats.reserve(mir_output.num_output_formats); for (auto p = mir_output.output_formats; p != mir_output.output_formats+mir_output.num_output_formats; ++p) { formats.push_back(*p); } std::vector modes; modes.reserve(mir_output.num_modes); for (auto p = mir_output.modes; p != mir_output.modes+mir_output.num_modes; ++p) { modes.push_back( DisplayConfigurationMode{ {p->horizontal_resolution, p->vertical_resolution}, p->refresh_rate}); } auto local_config = get_local_config_for(mir_output.output_id); DisplayConfigurationOutput output{ DisplayConfigurationOutputId(mir_output.output_id), DisplayConfigurationCardId(mir_output.card_id), DisplayConfigurationOutputType(mir_output.type), std::move(formats), std::move(modes), mir_output.preferred_mode, geometry::Size{mir_output.physical_width_mm, mir_output.physical_height_mm}, !!mir_output.connected, !!mir_output.used, geometry::Point{mir_output.position_x, mir_output.position_y}, mir_output.current_mode, mir_output.current_format, mir_output.power_mode, mir_output.orientation, local_config.scale, local_config.form_factor }; UserDisplayConfigurationOutput user(output); f(user); set_local_config_for(mir_output.output_id, {user.scale, user.form_factor }); mir_output.current_mode = output.current_mode_index; mir_output.current_format = output.current_format; mir_output.position_x = output.top_left.x.as_int(); mir_output.position_y = output.top_left.y.as_int(); mir_output.used = output.used; mir_output.power_mode = output.power_mode; mir_output.orientation = output.orientation; }); } std::unique_ptr mgn::NestedDisplayConfiguration::clone() const { return std::make_unique(*this); } mgn::NestedDisplayConfiguration::LocalOutputConfig mgn::NestedDisplayConfiguration::get_local_config_for(uint32_t output_id) const { std::lock_guard lock{local_config_mutex}; constexpr LocalOutputConfig default_values {1.0f, mir_form_factor_monitor }; bool inserted; decltype(local_config)::iterator keypair; std::tie(keypair, inserted) = local_config.insert(std::make_pair(output_id, default_values)); // We don't care whether we inserted the default values or retrieved the previously set value. return keypair->second; } void mgn::NestedDisplayConfiguration::set_local_config_for(uint32_t output_id, LocalOutputConfig const& config) { std::lock_guard lock{local_config_mutex}; local_config[output_id] = config; } ./src/server/graphics/nested/display.cpp0000644000015600001650000003007612676616125020447 0ustar jenkinsjenkins/* * Copyright © 2013, 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Eleni Maria Stea */ #include "display.h" #include "nested_display_configuration.h" #include "display_buffer.h" #include "host_connection.h" #include "mir/geometry/rectangle.h" #include "mir/graphics/pixel_format_utils.h" #include "mir/graphics/gl_context.h" #include "mir/graphics/surfaceless_egl_context.h" #include "mir/graphics/display_configuration_policy.h" #include "mir/graphics/overlapping_output_grouping.h" #include "mir/graphics/gl_config.h" #include "mir/graphics/egl_error.h" #include "mir/graphics/virtual_output.h" #include "mir_toolkit/mir_connection.h" #include "mir/raii.h" #include #include #include namespace mi = mir::input; namespace mg = mir::graphics; namespace mgn = mir::graphics::nested; namespace geom = mir::geometry; EGLint const mgn::detail::nested_egl_context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; mgn::detail::EGLSurfaceHandle::EGLSurfaceHandle(EGLDisplay display, EGLNativeWindowType native_window, EGLConfig cfg) : egl_display(display), egl_surface(eglCreateWindowSurface(egl_display, cfg, native_window, NULL)) { if (egl_surface == EGL_NO_SURFACE) { BOOST_THROW_EXCEPTION(mg::egl_error("Nested Mir Display Error: Failed to create EGL surface.")); } } mgn::detail::EGLSurfaceHandle::~EGLSurfaceHandle() noexcept { eglDestroySurface(egl_display, egl_surface); } mgn::detail::EGLDisplayHandle::EGLDisplayHandle( EGLNativeDisplayType native_display, std::shared_ptr const& gl_config) : egl_display(EGL_NO_DISPLAY), egl_context_(EGL_NO_CONTEXT), gl_config{gl_config}, pixel_format{mir_pixel_format_invalid} { egl_display = eglGetDisplay(native_display); if (egl_display == EGL_NO_DISPLAY) BOOST_THROW_EXCEPTION(mg::egl_error("Nested Mir Display Error: Failed to fetch EGL display.")); } void mgn::detail::EGLDisplayHandle::initialize(MirPixelFormat format) { int major; int minor; if (eglInitialize(egl_display, &major, &minor) != EGL_TRUE) { BOOST_THROW_EXCEPTION(mg::egl_error("Nested Mir Display Error: Failed to initialize EGL.")); } pixel_format = format; egl_context_ = eglCreateContext(egl_display, choose_windowed_es_config(format), EGL_NO_CONTEXT, detail::nested_egl_context_attribs); if (egl_context_ == EGL_NO_CONTEXT) BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create shared EGL context")); } EGLConfig mgn::detail::EGLDisplayHandle::choose_windowed_es_config(MirPixelFormat format) const { EGLint const nested_egl_config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, mg::red_channel_depth(format), EGL_GREEN_SIZE, mg::green_channel_depth(format), EGL_BLUE_SIZE, mg::blue_channel_depth(format), EGL_ALPHA_SIZE, mg::alpha_channel_depth(format), EGL_DEPTH_SIZE, gl_config->depth_buffer_bits(), EGL_STENCIL_SIZE, gl_config->stencil_buffer_bits(), EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; EGLConfig result; int n; int res = eglChooseConfig(egl_display, nested_egl_config_attribs, &result, 1, &n); if ((res != EGL_TRUE) || (n != 1)) BOOST_THROW_EXCEPTION(mg::egl_error("Nested Mir Display Error: Failed to choose EGL configuration.")); return result; } EGLContext mgn::detail::EGLDisplayHandle::egl_context() const { return egl_context_; } std::unique_ptr mgn::detail::EGLDisplayHandle::create_gl_context() { EGLint const attribs[] = { EGL_SURFACE_TYPE, EGL_DONT_CARE, EGL_RED_SIZE, mg::red_channel_depth(pixel_format), EGL_GREEN_SIZE, mg::green_channel_depth(pixel_format), EGL_BLUE_SIZE, mg::blue_channel_depth(pixel_format), EGL_ALPHA_SIZE, mg::alpha_channel_depth(pixel_format), EGL_DEPTH_SIZE, gl_config->depth_buffer_bits(), EGL_STENCIL_SIZE, gl_config->stencil_buffer_bits(), EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; return std::make_unique(egl_display, attribs, EGL_NO_CONTEXT); } mgn::detail::EGLDisplayHandle::~EGLDisplayHandle() noexcept { eglTerminate(egl_display); } mgn::detail::DisplaySyncGroup::DisplaySyncGroup( std::shared_ptr const& output) : output(output) { } void mgn::detail::DisplaySyncGroup::for_each_display_buffer( std::function const& f) { f(*output); } void mgn::detail::DisplaySyncGroup::post() { } std::chrono::milliseconds mgn::detail::DisplaySyncGroup::recommended_sleep() const { // TODO: Might make sense in future with nested bypass. We could save // almost another frame of lag! return std::chrono::milliseconds::zero(); } geom::Rectangle mgn::detail::DisplaySyncGroup::view_area() const { return output->view_area(); } mgn::Display::Display( std::shared_ptr const& platform, std::shared_ptr const& connection, std::shared_ptr const& dispatcher, std::shared_ptr const& display_report, std::shared_ptr const& initial_conf_policy, std::shared_ptr const& gl_config, std::shared_ptr const& cursor_listener) : platform{platform}, connection{connection}, dispatcher{dispatcher}, display_report{display_report}, egl_display{connection->egl_native_display(), gl_config}, cursor_listener{cursor_listener}, outputs{}, current_configuration(std::make_unique(connection->create_display_config())) { decltype(current_configuration) conf{dynamic_cast(current_configuration->clone().release())}; initial_conf_policy->apply_to(*conf); if (*current_configuration != *conf) { connection->apply_display_config(**conf); swap(current_configuration, conf); } create_surfaces(*current_configuration); } mgn::Display::~Display() noexcept { if (eglGetCurrentContext() == egl_display.egl_context()) eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } void mgn::Display::for_each_display_sync_group(std::function const& f) { std::unique_lock lock(outputs_mutex); for (auto& i : outputs) f(*i.second); } std::unique_ptr mgn::Display::configuration() const { std::lock_guard lock(configuration_mutex); return current_configuration->clone(); } void mgn::Display::complete_display_initialization(MirPixelFormat format) { if (egl_display.egl_context() != EGL_NO_CONTEXT) return; egl_display.initialize(format); eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_display.egl_context()); } void mgn::Display::configure(mg::DisplayConfiguration const& configuration) { decltype(current_configuration) new_config{dynamic_cast(configuration.clone().release())}; { std::lock_guard lock(configuration_mutex); swap(current_configuration, new_config); create_surfaces(*current_configuration); } connection->apply_display_config(**current_configuration); } namespace { long area_of(geom::Rectangle const& rect) { return static_cast(rect.size.width.as_int())*rect.size.height.as_int(); } } void mgn::Display::create_surfaces(mg::DisplayConfiguration const& configuration) { if (!configuration.valid()) { BOOST_THROW_EXCEPTION(std::logic_error("Invalid or inconsistent display configuration")); } decltype(outputs) result; OverlappingOutputGrouping unique_outputs{configuration}; unique_outputs.for_each_group( [&](mg::OverlappingOutputGroup const& group) { geometry::Rectangle const area = group.bounding_rectangle(); long max_overlap_area = 0; mg::DisplayConfigurationOutput best_output; group.for_each_output([&](mg::DisplayConfigurationOutput const& output) { if (area_of(area.intersection_with(output.extents())) > max_overlap_area) { max_overlap_area = area_of(area.intersection_with(output.extents())); best_output = output; } }); auto const& egl_config_format = best_output.current_format; geometry::Rectangle const& extents = best_output.extents(); auto& display_buffer = result[best_output.id]; { std::unique_lock lock(outputs_mutex); display_buffer = outputs[best_output.id]; } if (display_buffer) { if (display_buffer->view_area() != extents) display_buffer.reset(); } if (!display_buffer) { complete_display_initialization(egl_config_format); std::ostringstream surface_title; surface_title << "Mir nested display for output #" << best_output.id.as_value(); auto const host_surface = connection->create_surface( extents.size.width.as_int(), extents.size.height.as_int(), egl_config_format, surface_title.str().c_str(), mir_buffer_usage_hardware, static_cast(best_output.id.as_value())); display_buffer = std::make_shared( std::make_shared( egl_display, host_surface, extents, dispatcher, cursor_listener, best_output.current_format)); } }); { std::unique_lock lock(outputs_mutex); outputs.swap(result); } } void mgn::Display::register_configuration_change_handler( EventHandlerRegister& /*handlers*/, DisplayConfigurationChangeHandler const& conf_change_handler) { auto const handler = [this, conf_change_handler] { { std::lock_guard lock(configuration_mutex); current_configuration = std::make_unique(connection->create_display_config()); } conf_change_handler(); }; connection->set_display_config_change_callback(handler); } void mgn::Display::register_pause_resume_handlers( EventHandlerRegister& /*handlers*/, DisplayPauseHandler const& /*pause_handler*/, DisplayResumeHandler const& /*resume_handler*/) { // No need to do anything } void mgn::Display::pause() { // No need to do anything } void mgn::Display::resume() { // No need to do anything } auto mgn::Display::create_hardware_cursor(std::shared_ptr const& /*initial_image*/) -> std::shared_ptr { BOOST_THROW_EXCEPTION(std::logic_error("Initialization loop: we already need the Cursor when creating the Display")); // So we can't do this: return std::make_shared(connection, initial_image); } std::unique_ptr mgn::Display::create_gl_context() { return egl_display.create_gl_context(); } std::unique_ptr mgn::Display::create_virtual_output(int /*width*/, int /*height*/) { return nullptr; } ./src/server/graphics/nested/cursor.cpp0000644000015600001650000000261612676616125020316 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "cursor.h" #include "host_connection.h" namespace mg = mir::graphics; namespace mgn = mir::graphics::nested; namespace geom = mir::geometry; mgn::Cursor::Cursor(std::shared_ptr const& host_connection, std::shared_ptr const& default_image) : connection(host_connection), default_image(default_image) { connection->set_cursor_image(*default_image); } mgn::Cursor::~Cursor() { } void mgn::Cursor::move_to(geom::Point) { } void mgn::Cursor::show(mg::CursorImage const& cursor_image) { connection->set_cursor_image(cursor_image); } void mgn::Cursor::show() { connection->set_cursor_image(*default_image); } void mgn::Cursor::hide() { connection->hide_cursor(); } ./src/server/graphics/nested/host_surface.h0000644000015600001650000000257612676616125021140 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Eleni Maria Stea * Kevin DuBois */ #ifndef MIR_GRAPHICS_NESTED_HOST_SURFACE_H_ #define MIR_GRAPHICS_NESTED_HOST_SURFACE_H_ #include "mir_toolkit/client_types.h" #include namespace mir { namespace graphics { namespace nested { class HostSurface { public: virtual ~HostSurface() = default; virtual EGLNativeWindowType egl_native_window() = 0; virtual void set_event_handler(mir_surface_event_callback cb, void* context) = 0; protected: HostSurface() = default; HostSurface(HostSurface const&) = delete; HostSurface& operator=(HostSurface const&) = delete; }; } } } #endif // MIR_GRAPHICS_NESTED_HOST_SURFACE_H_ ./src/server/graphics/nested/CMakeLists.txt0000644000015600001650000000034412676616125021031 0ustar jenkinsjenkinsinclude_directories( ${PROJECT_SOURCE_DIR}/include/renderers/gl ) add_library( mirnestedgraphics OBJECT display.cpp nested_display_configuration.cpp display_buffer.cpp mir_client_host_connection.cpp cursor.cpp ) ./src/server/graphics/nested/host_connection.h0000644000015600001650000000373212676616125021642 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Eleni Maria Stea */ #ifndef MIR_GRAPHICS_NESTED_HOST_CONNECTION_H_ #define MIR_GRAPHICS_NESTED_HOST_CONNECTION_H_ #include "mir_toolkit/client_types.h" #include "mir/graphics/nested_context.h" #include #include #include #include namespace mir { namespace graphics { class CursorImage; namespace nested { class HostSurface; class HostConnection : public NestedContext { public: virtual ~HostConnection() = default; virtual EGLNativeDisplayType egl_native_display() = 0; virtual std::shared_ptr create_display_config() = 0; virtual void set_display_config_change_callback(std::function const& cb) = 0; virtual void apply_display_config(MirDisplayConfiguration&) = 0; virtual std::shared_ptr create_surface( int width, int height, MirPixelFormat pf, char const* name, MirBufferUsage usage, uint32_t output_id) = 0; virtual void set_cursor_image(CursorImage const& image) = 0; virtual void hide_cursor() = 0; virtual auto graphics_platform_library() -> std::string = 0; protected: HostConnection() = default; HostConnection(HostConnection const&) = delete; HostConnection& operator=(HostConnection const&) = delete; }; } } } #endif // MIR_GRAPHICS_NESTED_HOST_CONNECTION_H_ ./src/server/graphics/nested/nested_display_configuration.h0000644000015600001650000000465412676616125024410 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Eleni Maria Stea */ #ifndef MIR_GRAPHICS_NESTED_NESTED_DISPLAY_CONFIGURATION_H_ #define MIR_GRAPHICS_NESTED_NESTED_DISPLAY_CONFIGURATION_H_ #include "mir/graphics/display_configuration.h" #include "mir_toolkit/client_types.h" #include #include #include namespace mir { namespace graphics { namespace nested { class NestedDisplayConfiguration : public DisplayConfiguration { public: NestedDisplayConfiguration( std::shared_ptr const& display_config); NestedDisplayConfiguration(NestedDisplayConfiguration const&); void for_each_card(std::function) const override; void for_each_output(std::function) const override; void for_each_output(std::function) override; std::unique_ptr clone() const override; operator MirDisplayConfiguration*() const { return display_config.get(); } private: std::shared_ptr display_config; /* * The client display config doesn't currently expose the form factor or scaling factor, nor is it * entirely clear that it should allow a client to set them. * * We therefore need to store these explicitly in the NestedConfiguration. */ std::mutex mutable local_config_mutex; struct LocalOutputConfig { float scale; MirFormFactor form_factor; }; std::unordered_map mutable local_config; LocalOutputConfig get_local_config_for(uint32_t output_id) const; void set_local_config_for(uint32_t output_id, LocalOutputConfig const& config); }; } } } #endif // MIR_GRAPHICS_NESTED_NESTED_DISPLAY_CONFIGURATION_H_ ./src/server/graphics/gl_extensions_base.cpp0000644000015600001650000000262512676616125021372 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/gl_extensions_base.h" #include #include #include namespace mg = mir::graphics; mg::GLExtensionsBase::GLExtensionsBase(char const* extensions) : extensions{extensions} { if (!extensions) { BOOST_THROW_EXCEPTION( std::runtime_error("Couldn't get list of GL extensions")); } } bool mg::GLExtensionsBase::support(char const* ext) const { char const* ext_ptr = extensions; size_t const len = strlen(ext); while ((ext_ptr = strstr(ext_ptr, ext)) != nullptr) { if (ext_ptr[len] == ' ' || ext_ptr[len] == '\0') break; ext_ptr += len; } return ext_ptr != nullptr; } ./src/server/graphics/software_cursor.cpp0000644000015600001650000001330612676616125020744 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "software_cursor.h" #include "mir/graphics/cursor_image.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/pixel_format_utils.h" #include "mir/graphics/renderable.h" #include "mir/graphics/buffer_properties.h" #include "mir/input/scene.h" #include namespace mg = mir::graphics; namespace mi = mir::input; namespace geom = mir::geometry; namespace { MirPixelFormat get_8888_format(std::vector const& formats) { for (auto format : formats) { if (mg::red_channel_depth(format) == 8 && mg::green_channel_depth(format) == 8 && mg::blue_channel_depth(format) == 8 && mg::alpha_channel_depth(format) == 8) { return format; } } return mir_pixel_format_invalid; } } class mg::detail::CursorRenderable : public mg::Renderable { public: CursorRenderable(std::shared_ptr const& buffer, geom::Point const& position) : buffer_{buffer}, position{position} { } mg::Renderable::ID id() const override { return this; } std::shared_ptr buffer() const override { return buffer_; } geom::Rectangle screen_position() const override { std::lock_guard lock{position_mutex}; return {position, buffer_->size()}; } float alpha() const override { return 1.0; } glm::mat4 transformation() const override { return glm::mat4(); } bool shaped() const override { return true; } void move_to(geom::Point new_position) { std::lock_guard lock{position_mutex}; position = new_position; } private: std::shared_ptr const buffer_; mutable std::mutex position_mutex; geom::Point position; }; mg::SoftwareCursor::SoftwareCursor( std::shared_ptr const& allocator, std::shared_ptr const& scene) : allocator{allocator}, scene{scene}, format{get_8888_format(allocator->supported_pixel_formats())}, visible(false), hotspot{0,0} { } mg::SoftwareCursor::~SoftwareCursor() { hide(); } void mg::SoftwareCursor::show() { bool needs_scene_change = false; { std::lock_guard lg{guard}; if (!visible) visible = needs_scene_change = true; } if (needs_scene_change && renderable) scene->add_input_visualization(renderable); } void mg::SoftwareCursor::show(CursorImage const& cursor_image) { std::shared_ptr new_renderable; std::shared_ptr old_renderable; bool old_visibility = false; // Do a lock dance to make this function threadsafe, // while avoiding calling scene methods under lock { geom::Point position{0,0}; std::lock_guard lg{guard}; if (renderable) position = renderable->screen_position().top_left; new_renderable = create_renderable_for(cursor_image, position); old_visibility = visible; visible = true; } // Add the new renderable first, then remove the old one to avoid // visual glitches scene->add_input_visualization(new_renderable); // The second part of the lock dance { std::lock_guard lg{guard}; old_renderable = renderable; renderable = new_renderable; hotspot = cursor_image.hotspot(); } if (old_renderable && old_visibility) scene->remove_input_visualization(old_renderable); } std::shared_ptr mg::SoftwareCursor::create_renderable_for(CursorImage const& cursor_image, geom::Point position) { auto new_renderable = std::make_shared( allocator->alloc_buffer({cursor_image.size(), format, mg::BufferUsage::software}), position + hotspot - cursor_image.hotspot()); size_t const pixels_size = cursor_image.size().width.as_uint32_t() * cursor_image.size().height.as_uint32_t() * MIR_BYTES_PER_PIXEL(format); // TODO: The buffer pixel format may not be argb_8888, leading to // incorrect cursor colors. We need to transform the data to match // the buffer pixel format. new_renderable->buffer()->write( static_cast(cursor_image.as_argb_8888()), pixels_size); return new_renderable; } void mg::SoftwareCursor::hide() { bool needs_scene_change = false; { std::lock_guard lg{guard}; if (visible) { visible = false; needs_scene_change = true; } } if (needs_scene_change && renderable) scene->remove_input_visualization(renderable); } void mg::SoftwareCursor::move_to(geometry::Point position) { { std::lock_guard lg{guard}; if (!renderable) return; renderable->move_to(position - hotspot); } scene->emit_scene_changed(); } ./src/server/graphics/default_configuration.cpp0000644000015600001650000002377412676616157022107 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/default_server_configuration.h" #include "mir/options/configuration.h" #include "mir/options/option.h" #include "mir/graphics/default_display_configuration_policy.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "nested/mir_client_host_connection.h" #include "nested/cursor.h" #include "nested/display.h" #include "offscreen/display.h" #include "software_cursor.h" #include "mir/graphics/gl_config.h" #include "mir/graphics/platform.h" #include "mir/graphics/cursor.h" #include "mir/graphics/platform_probe.h" #include "mir/shared_library.h" #include "mir/shared_library_prober.h" #include "mir/abnormal_exit.h" #include "mir/emergency_cleanup.h" #include "mir/log.h" #include "mir/main_loop.h" #include "mir/report_exception.h" #include "mir_toolkit/common.h" #include #include #include namespace mg = mir::graphics; namespace ml = mir::logging; namespace mgn = mir::graphics::nested; std::shared_ptr mir::DefaultServerConfiguration::the_display_configuration_policy() { return display_configuration_policy( [this] { return wrap_display_configuration_policy( std::make_shared()); }); } std::shared_ptr mir::DefaultServerConfiguration::wrap_display_configuration_policy( std::shared_ptr const& wrapped) { return wrapped; } std::shared_ptr mir::DefaultServerConfiguration::the_graphics_platform() { return graphics_platform( [this]()->std::shared_ptr { std::shared_ptr platform_library; std::stringstream error_report; try { // if a host socket is set we should use the host graphics module to create a "guest" platform if (the_options()->is_set(options::host_socket_opt)) { auto const host_connection = the_host_connection(); platform_library = std::make_shared(host_connection->graphics_platform_library()); auto create_guest_platform = platform_library->load_function( "create_guest_platform", MIR_SERVER_GRAPHICS_PLATFORM_VERSION); return create_guest_platform(the_display_report(), host_connection); } // fallback to standalone if host socket is unset if (the_options()->is_set(options::platform_graphics_lib)) { platform_library = std::make_shared(the_options()->get(options::platform_graphics_lib)); } else { auto const& path = the_options()->get(options::platform_path); auto platforms = mir::libraries_for_path(path, *the_shared_library_prober_report()); if (platforms.empty()) { auto msg = "Failed to find any platform plugins in: " + path; throw std::runtime_error(msg.c_str()); } platform_library = mir::graphics::module_for_device(platforms, dynamic_cast(*the_options())); } auto create_host_platform = platform_library->load_function( "create_host_platform", MIR_SERVER_GRAPHICS_PLATFORM_VERSION); auto describe_module = platform_library->load_function( "describe_graphics_module", MIR_SERVER_GRAPHICS_PLATFORM_VERSION); auto description = describe_module(); mir::log_info("Selected driver: %s (version %d.%d.%d)", description->name, description->major_version, description->minor_version, description->micro_version); return create_host_platform(the_options(), the_emergency_cleanup(), the_display_report()); } catch(...) { // access exception information before platform library gets unloaded error_report << "Exception while creating graphics platform" << std::endl; mir::report_exception(error_report); } BOOST_THROW_EXCEPTION(std::runtime_error(error_report.str())); }); } std::shared_ptr mir::DefaultServerConfiguration::the_buffer_allocator() { return buffer_allocator( [&]() { return the_graphics_platform()->create_buffer_allocator(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_display() { return display( [this]() -> std::shared_ptr { if (the_options()->is_set(options::offscreen_opt)) { return std::make_shared( the_graphics_platform()->egl_native_display(), the_display_configuration_policy(), the_display_report()); } else if (the_options()->is_set(options::host_socket_opt)) { return std::make_shared( the_graphics_platform(), the_host_connection(), the_input_dispatcher(), the_display_report(), the_display_configuration_policy(), the_gl_config(), the_cursor_listener()); } { return the_graphics_platform()->create_display( the_display_configuration_policy(), the_gl_config()); } }); } std::shared_ptr mir::DefaultServerConfiguration::the_cursor() { return cursor( [this]() -> std::shared_ptr { // In nested mode we delegate cursor presentation to the host if (the_options()->is_set(options::host_socket_opt)) { mir::log_info("Using nested cursor"); return wrap_cursor(std::make_shared(the_host_connection(), the_default_cursor_image())); } // Otherwise we try to create a hardware cursor if (auto hardware_cursor = the_display()->create_hardware_cursor(the_default_cursor_image())) { mir::log_info("Using hardware cursor"); return wrap_cursor(hardware_cursor); } // If other options fail we use a software cursor mir::log_info("Using software cursor"); auto const cursor = wrap_cursor(std::make_shared( the_buffer_allocator(), the_input_scene())); cursor->show(*the_default_cursor_image()); return cursor; }); } std::shared_ptr mir::DefaultServerConfiguration::wrap_cursor(std::shared_ptr const& wrapped) { return wrapped; } auto mir::DefaultServerConfiguration::the_host_connection() -> std::shared_ptr { return the_mir_client_host_connection(); } auto mir::DefaultServerConfiguration::the_mir_client_host_connection() -> std::shared_ptr { return host_connection( [this]() { auto const options = the_options(); if (!options->is_set(options::host_socket_opt)) BOOST_THROW_EXCEPTION(mir::AbnormalExit( std::string("Exiting Mir! Reason: Nested Mir needs either $MIR_SOCKET or --") + options::host_socket_opt)); auto host_socket = options->get(options::host_socket_opt); std::string server_socket{"none"}; if (!the_options()->is_set(options::no_server_socket_opt)) { server_socket = the_socket_file(); if (server_socket == host_socket) BOOST_THROW_EXCEPTION(mir::AbnormalExit( "Exiting Mir! Reason: Nested Mir and Host Mir cannot use " "the same socket file to accept connections!")); } auto const my_name = options->is_set(options::name_opt) ? options->get(options::name_opt) : "nested-mir@:" + server_socket; return std::make_shared( host_socket, my_name, the_host_lifecycle_event_listener(), the_global_event_sink(), the_main_loop() ); }); } std::shared_ptr mir::DefaultServerConfiguration::the_gl_config() { return gl_config( [this] { struct NoGLConfig : public mg::GLConfig { int depth_buffer_bits() const override { return 0; } int stencil_buffer_bits() const override { return 0; } }; return std::make_shared(); }); } ./src/server/graphics/CMakeLists.txt0000644000015600001650000000046412676616125017552 0ustar jenkinsjenkinsinclude_directories(${GLESv2_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}) add_library( mirgraphics OBJECT default_configuration.cpp default_display_configuration_policy.cpp gl_extensions_base.cpp surfaceless_egl_context.cpp software_cursor.cpp ) add_subdirectory(nested/) add_subdirectory(offscreen/) ./src/server/graphics/software_cursor.h0000644000015600001650000000354112676616125020411 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_SOFTWARE_CURSOR_H_ #define MIR_GRAPHICS_SOFTWARE_CURSOR_H_ #include "mir/graphics/cursor.h" #include "mir_toolkit/client_types.h" #include "mir/geometry/displacement.h" #include namespace mir { namespace input { class Scene; } namespace graphics { class GraphicBufferAllocator; class Renderable; namespace detail { class CursorRenderable; } class SoftwareCursor : public Cursor { public: SoftwareCursor( std::shared_ptr const& allocator, std::shared_ptr const& scene); ~SoftwareCursor(); void show() override; void show(CursorImage const& cursor_image) override; void hide() override; void move_to(geometry::Point position) override; private: std::shared_ptr create_renderable_for( CursorImage const& cursor_image, geometry::Point position); std::shared_ptr const allocator; std::shared_ptr const scene; MirPixelFormat const format; std::mutex guard; std::shared_ptr renderable; bool visible; geometry::Displacement hotspot; }; } } #endif ./src/server/graphics/surfaceless_egl_context.cpp0000644000015600001650000001301612676616125022425 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "mir/graphics/surfaceless_egl_context.h" #include "mir/graphics/gl_extensions_base.h" #include "mir/graphics/egl_error.h" #include #include #include namespace mg = mir::graphics; namespace { class EGLExtensions : public mg::GLExtensionsBase { public: EGLExtensions(EGLDisplay egl_display) : mg::GLExtensionsBase{eglQueryString(egl_display, EGL_EXTENSIONS)} { } }; bool supports_surfaceless_context(EGLDisplay egl_display) { EGLExtensions const extensions{egl_display}; return extensions.support("EGL_KHR_surfaceless_context"); } std::vector ensure_pbuffer_set(EGLint const* attribs) { bool has_preferred_surface = false; std::vector attribs_with_surface_type; int i = 0; while (attribs[i] != EGL_NONE) { attribs_with_surface_type.push_back(attribs[i]); if (attribs[i] == EGL_SURFACE_TYPE) { has_preferred_surface = true; if (attribs[i+1] == EGL_DONT_CARE) { /* Need to treat EGL_DONT_CARE specially, as it is defined as all-bits-set */ attribs_with_surface_type.push_back(EGL_PBUFFER_BIT); } else { attribs_with_surface_type.push_back(attribs[i+1] | EGL_PBUFFER_BIT); } } else { attribs_with_surface_type.push_back(attribs[i+1]); } i += 2; } if (!has_preferred_surface) { attribs_with_surface_type.push_back(EGL_SURFACE_TYPE); attribs_with_surface_type.push_back(EGL_PBUFFER_BIT); } attribs_with_surface_type.push_back(EGL_NONE); return attribs_with_surface_type; } EGLConfig choose_config(EGLDisplay egl_display, EGLint const* attribs, bool surfaceless) { EGLConfig egl_config{0}; int num_egl_configs{0}; std::vector validated_attribs; if (!surfaceless) { validated_attribs = ensure_pbuffer_set(attribs); attribs = validated_attribs.data(); } if (eglChooseConfig(egl_display, attribs, &egl_config, 1, &num_egl_configs) == EGL_FALSE || num_egl_configs != 1) { BOOST_THROW_EXCEPTION(mg::egl_error("Failed to choose EGL config")); } return egl_config; } EGLSurface create_surface(EGLDisplay egl_display, EGLConfig egl_config) { static EGLint const dummy_pbuffer_attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; return eglCreatePbufferSurface(egl_display, egl_config, dummy_pbuffer_attribs); } EGLint const default_egl_context_attr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; EGLint const default_attr[] = { EGL_SURFACE_TYPE, EGL_DONT_CARE, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 0, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; } mg::SurfacelessEGLContext::SurfacelessEGLContext(EGLDisplay egl_display, EGLContext shared_context) : SurfacelessEGLContext(egl_display, default_attr, shared_context) { } mg::SurfacelessEGLContext::SurfacelessEGLContext( EGLDisplay egl_display, EGLint const* attribs, EGLContext shared_context) : egl_display{egl_display}, surfaceless{supports_surfaceless_context(egl_display)}, egl_config{choose_config(egl_display, attribs, surfaceless)}, egl_surface{egl_display, surfaceless ? EGL_NO_SURFACE : create_surface(egl_display, egl_config), surfaceless ? EGLSurfaceStore::AllowNoSurface : EGLSurfaceStore::DisallowNoSurface}, egl_context{egl_display, eglCreateContext(egl_display, egl_config, shared_context, default_egl_context_attr)} { } mg::SurfacelessEGLContext::SurfacelessEGLContext(SurfacelessEGLContext&& move) : egl_display(move.egl_display), surfaceless(move.surfaceless), egl_config(move.egl_config), egl_surface{std::move(move.egl_surface)}, egl_context{std::move(move.egl_context)} { move.egl_display = EGL_NO_DISPLAY; } mg::SurfacelessEGLContext::~SurfacelessEGLContext() noexcept { release_current(); } void mg::SurfacelessEGLContext::make_current() const { if (eglGetCurrentContext() == egl_context) return; if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) == EGL_FALSE) { BOOST_THROW_EXCEPTION( mg::egl_error("could not make context current")); } } void mg::SurfacelessEGLContext::release_current() const { if (egl_context != EGL_NO_CONTEXT && eglGetCurrentContext() == egl_context) eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } mg::SurfacelessEGLContext::operator EGLContext() const { return egl_context; } ./src/server/report/0000755000015600001650000000000012676616125014521 5ustar jenkinsjenkins./src/server/report/null/0000755000015600001650000000000012676616125015473 5ustar jenkinsjenkins./src/server/report/null/input_report.cpp0000644000015600001650000000244512676616125020736 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #include "input_report.h" namespace mrn = mir::report::null; void mrn::InputReport::received_event_from_kernel(int64_t /* when */, int /* type */, int /* code */, int /* value */) { } void mrn::InputReport::published_key_event(int /* dest_fd */, uint32_t /* seq_id */, int64_t /* event_time */) { } void mrn::InputReport::published_motion_event(int /* dest_fd */, uint32_t /* seq_id */, int64_t /* event_time */) { } void mrn::InputReport::opened_input_device(char const* /* name */, char const* /* platform */) { } void mrn::InputReport::failed_to_open_input_device(char const* /* name */, char const* /* platform */) { } ./src/server/report/null/message_processor_report.cpp0000644000015600001650000000231412676616125023315 0ustar jenkinsjenkins/* * Copyright © 2013,2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "message_processor_report.h" namespace mrn = mir::report::null; void mrn::MessageProcessorReport::received_invocation(void const*, int, std::string const&) { } void mrn::MessageProcessorReport::completed_invocation(void const*, int, bool) { } void mrn::MessageProcessorReport::unknown_method(void const*, int, std::string const&) { } void mrn::MessageProcessorReport::exception_handled(void const*, int, std::exception const&) { } void mrn::MessageProcessorReport::exception_handled(void const*, std::exception const&) { } ./src/server/report/null/display_report.cpp0000644000015600001650000000264612676616125021247 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "display_report.h" namespace mrn = mir::report::null; void mrn::DisplayReport::report_successful_setup_of_native_resources() {} void mrn::DisplayReport::report_successful_egl_make_current_on_construction() {} void mrn::DisplayReport::report_successful_egl_buffer_swap_on_construction() {} void mrn::DisplayReport::report_successful_drm_mode_set_crtc_on_construction() {} void mrn::DisplayReport::report_successful_display_construction() {} void mrn::DisplayReport::report_drm_master_failure(int) {} void mrn::DisplayReport::report_vt_switch_away_failure() {} void mrn::DisplayReport::report_vt_switch_back_failure() {} void mrn::DisplayReport::report_egl_configuration(EGLDisplay, EGLConfig) {} void mrn::DisplayReport::report_vsync(unsigned int) {} ./src/server/report/null/display_report.h0000644000015600001650000000312412676616125020704 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_NULL_DISPLAY_REPORT_H_ #define MIR_REPORT_NULL_DISPLAY_REPORT_H_ #include "mir/graphics/display_report.h" namespace mir { namespace report { namespace null { class DisplayReport : public graphics::DisplayReport { public: void report_successful_setup_of_native_resources() override; void report_successful_egl_make_current_on_construction() override; void report_successful_egl_buffer_swap_on_construction() override; void report_successful_drm_mode_set_crtc_on_construction() override; void report_successful_display_construction() override; void report_drm_master_failure(int error) override; void report_vt_switch_away_failure() override; void report_vt_switch_back_failure() override; void report_egl_configuration(EGLDisplay disp, EGLConfig cfg) override; void report_vsync(unsigned int display_id) override; }; } } } #endif /* MIR_REPORT_NULL_DISPLAY_REPORT_H_ */ ./src/server/report/null/shell_report.h0000644000015600001650000000461212676616125020351 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_NULL_SHELL_REPORT_H #define MIR_REPORT_NULL_SHELL_REPORT_H #include "mir/shell/shell_report.h" namespace mir { namespace report { namespace null { class ShellReport : public shell::ShellReport { void opened_session(scene::Session const& /*session*/) override; void closing_session(scene::Session const& /*session*/) override; void created_surface( scene::Session const& /*session*/, frontend::SurfaceId /*surface_id*/) override; void update_surface( scene::Session const& /*session*/, scene::Surface const& /*surface*/, shell::SurfaceSpecification const& /*modifications*/) override; void update_surface( scene::Session const& /*session*/, scene::Surface const& /*surface*/, MirSurfaceAttrib /*attrib*/, int /*value*/) override; void destroying_surface( scene::Session const& /*session*/, frontend::SurfaceId /*surface*/) override; void started_prompt_session( scene::PromptSession const& /*prompt_session*/, scene::Session const& /*session*/) override; void added_prompt_provider( scene::PromptSession const& /*prompt_session*/, scene::Session const& /*session*/) override; void stopping_prompt_session( scene::PromptSession const& /*prompt_session*/) override; void adding_display(geometry::Rectangle const& /*area*/) override; void removing_display(geometry::Rectangle const& /*area*/) override; void input_focus_set_to( scene::Session const* /*focus_session*/, scene::Surface const* /*focus_surface*/) override; void surfaces_raised(shell::SurfaceSet const& /*surfaces*/) override; }; } } } #endif //MIR_REPORT_NULL_SHELL_REPORT_H ./src/server/report/null/scene_report.cpp0000644000015600001650000000224712676616125020674 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths * Andreas Pokorny */ #include "scene_report.h" namespace mrn = mir::report::null; void mrn::SceneReport::surface_created(BasicSurfaceId /*id*/, std::string const& /*name*/) { } void mrn::SceneReport::surface_added(BasicSurfaceId /*id*/, std::string const& /*name*/) { } void mrn::SceneReport::surface_removed(BasicSurfaceId /*id*/, std::string const& /*name*/) { } void mrn::SceneReport::surface_deleted(BasicSurfaceId /*id*/, std::string const& /*name*/) { } ./src/server/report/null/session_mediator_report.h0000644000015600001650000000523512676616125022613 0ustar jenkinsjenkins/* * Copyright © 2012,2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths * Andreas Pokorny */ #ifndef MIR_REPORT_NULL_SESSION_MEDIATOR_REPORT_H_ #define MIR_REPORT_NULL_SESSION_MEDIATOR_REPORT_H_ #include "mir/frontend/session_mediator_report.h" #include namespace mir { namespace report { namespace null { // Do-nothing implementation to satisfy dependencies class SessionMediatorReport : public frontend::SessionMediatorReport { void session_connect_called(std::string const& app_name) override; void session_create_surface_called(std::string const& app_name) override; void session_next_buffer_called(std::string const& app_name) override; void session_exchange_buffer_called(std::string const& app_name) override; void session_submit_buffer_called(std::string const& app_name) override; void session_allocate_buffers_called(std::string const& app_name) override; void session_release_buffers_called(std::string const& app_name) override; void session_release_surface_called(std::string const& app_name) override; void session_disconnect_called(std::string const& app_name) override; void session_configure_surface_called(std::string const& app_name) override; void session_configure_surface_cursor_called(std::string const& app_name) override; void session_configure_display_called(std::string const& app_name) override; void session_set_base_display_configuration_called(std::string const& app_name) override; void session_start_prompt_session_called(std::string const& app_name, pid_t application_process) override; void session_stop_prompt_session_called(std::string const& app_name) override; void session_create_buffer_stream_called(std::string const& app_name) override; void session_release_buffer_stream_called(std::string const& app_name) override; void session_error( std::string const& app_name, char const* method, std::string const& what) override; }; } } } #endif /* MIR_FRONTEND_SESSION_MEDIATOR_REPORT_H_ */ ./src/server/report/null/compositor_report.h0000644000015600001650000000276012676616125021442 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_REPORT_NULL_COMPOSITOR_REPORT_H_ #define MIR_REPORT_NULL_COMPOSITOR_REPORT_H_ #include "mir/compositor/compositor_report.h" namespace mir { namespace report { namespace null { class CompositorReport : public compositor::CompositorReport { public: void added_display(int width, int height, int x, int y, SubCompositorId id) override; void began_frame(SubCompositorId id) override; void renderables_in_frame(SubCompositorId id, graphics::RenderableList const& renderables) override; void rendered_frame(SubCompositorId id) override; void finished_frame(SubCompositorId id) override; void started() override; void stopped() override; void scheduled() override; }; } // namespace compositor } // namespace report } // namespace mir #endif // MIR_REPORT_NULL_COMPOSITOR_REPORT_H_ ./src/server/report/null/shell_report.cpp0000644000015600001650000000434312676616125020705 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "shell_report.h" namespace mrn = mir::report::null; void mrn::ShellReport::opened_session(scene::Session const& /*session*/) { } void mrn::ShellReport::closing_session(scene::Session const& /*session*/) { } void mrn::ShellReport::created_surface( scene::Session const& /*session*/, frontend::SurfaceId /*surface_id*/) { } void mrn::ShellReport::update_surface( scene::Session const& /*session*/, scene::Surface const& /*surface*/, shell::SurfaceSpecification const& /*modifications*/) { } void mrn::ShellReport::update_surface( scene::Session const& /*session*/, scene::Surface const& /*surface*/, MirSurfaceAttrib /*attrib*/, int /*value*/) { } void mrn::ShellReport::destroying_surface( scene::Session const& /*session*/, frontend::SurfaceId /*surface*/) { } void mrn::ShellReport::started_prompt_session( scene::PromptSession const& /*prompt_session*/, scene::Session const& /*session*/) { } void mrn::ShellReport::added_prompt_provider( scene::PromptSession const& /*prompt_session*/, scene::Session const& /*session*/) { } void mrn::ShellReport::stopping_prompt_session( scene::PromptSession const& /*prompt_session*/) { } void mrn::ShellReport::adding_display(geometry::Rectangle const& /*area*/) { } void mrn::ShellReport::removing_display(geometry::Rectangle const& /*area*/) { } void mrn::ShellReport::input_focus_set_to( scene::Session const* /*focus_session*/, scene::Surface const* /*focus_surface*/) { } void mrn::ShellReport::surfaces_raised(shell::SurfaceSet const& /*surfaces*/) { } ./src/server/report/null/connector_report.cpp0000644000015600001650000000222012676616125021560 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "connector_report.h" namespace mrn = mir::report::null; void mrn::ConnectorReport::thread_start() {} void mrn::ConnectorReport::thread_end() {} void mrn::ConnectorReport::creating_session_for(int /*socket_handle*/) {} void mrn::ConnectorReport::creating_socket_pair(int /*server_handle*/, int /*client_handle*/) {} void mrn::ConnectorReport::listening_on(std::string const& /*endpoint*/) {} void mrn::ConnectorReport::error(std::exception const& /*error*/) {} ./src/server/report/null/message_processor_report.h0000644000015600001650000000252212676616125022763 0ustar jenkinsjenkins/* * Copyright © 2013,2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_NULL_MESSAGE_PROCESSOR_REPORT_H_ #define MIR_REPORT_NULL_MESSAGE_PROCESSOR_REPORT_H_ #include "mir/frontend/message_processor_report.h" namespace mir { namespace report { namespace null { class MessageProcessorReport : public frontend::MessageProcessorReport { void received_invocation(void const*, int, std::string const&); void completed_invocation(void const*, int, bool); void unknown_method(void const*, int, std::string const&); void exception_handled(void const*, int, std::exception const&); void exception_handled(void const*, std::exception const&); }; } } } #endif /* MIR_REPORT_NULL_MESSAGE_PROCESSOR_REPORT_H_ */ ./src/server/report/null/connector_report.h0000644000015600001650000000245612676616125021240 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_NULL_CONNECTOR_REPORT_H_ #define MIR_REPORT_NULL_CONNECTOR_REPORT_H_ #include "mir/frontend/connector_report.h" namespace mir { namespace report { namespace null { class ConnectorReport : public frontend::ConnectorReport { public: void thread_start() override; void thread_end() override; void creating_session_for(int socket_handle) override; void creating_socket_pair(int server_handle, int client_handle) override; void listening_on(std::string const& endpoint) override; void error(std::exception const& error) override; }; } } } #endif // MIR_REPORT_NULL_CONNECTOR_REPORT_H_ ./src/server/report/null/scene_report.h0000644000015600001650000000273512676616125020343 0ustar jenkinsjenkins/* * Copyright © 2013,2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths * Andreas Pokorny */ #ifndef MIR_REPORT_NULL_SCENE_REPORT_H_ #define MIR_REPORT_NULL_SCENE_REPORT_H_ #include "mir/scene/scene_report.h" namespace mir { namespace report { namespace null { class SceneReport : public scene::SceneReport { public: virtual void surface_created(BasicSurfaceId /*id*/, std::string const& /*name*/) override; virtual void surface_added(BasicSurfaceId /*id*/, std::string const& /*name*/) override; virtual void surface_removed(BasicSurfaceId /*id*/, std::string const& /*name*/) override; virtual void surface_deleted(BasicSurfaceId /*id*/, std::string const& /*name*/) override; SceneReport() = default; virtual ~SceneReport() noexcept(true) = default; }; } } } #endif /* MIR_REPORT_NULL_SCENE_REPORT_H_ */ ./src/server/report/null/session_mediator_report.cpp0000644000015600001650000000513712676616125023147 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "session_mediator_report.h" void mir::report::null::SessionMediatorReport::session_connect_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_create_surface_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_next_buffer_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_exchange_buffer_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_submit_buffer_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_allocate_buffers_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_release_buffers_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_release_surface_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_disconnect_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_configure_surface_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_configure_surface_cursor_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_configure_display_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_set_base_display_configuration_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_start_prompt_session_called(std::string const&, pid_t) { } void mir::report::null::SessionMediatorReport::session_stop_prompt_session_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_create_buffer_stream_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_release_buffer_stream_called(std::string const&) { } void mir::report::null::SessionMediatorReport::session_error( std::string const&, char const* , std::string const& ) { } ./src/server/report/null/null_report_factory.cpp0000644000015600001650000000725612676616125022305 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "../null_report_factory.h" #include "compositor_report.h" #include "connector_report.h" #include "message_processor_report.h" #include "session_mediator_report.h" #include "display_report.h" #include "input_report.h" #include "shell_report.h" #include "scene_report.h" #include "mir/logging/null_shared_library_prober_report.h" std::shared_ptr mir::report::NullReportFactory::create_compositor_report() { return std::make_shared(); } std::shared_ptr mir::report::NullReportFactory::create_display_report() { return std::make_shared(); } std::shared_ptr mir::report::NullReportFactory::create_scene_report() { return std::make_shared(); } std::shared_ptr mir::report::NullReportFactory::create_connector_report() { return std::make_shared(); } std::shared_ptr mir::report::NullReportFactory::create_session_mediator_report() { return std::make_shared(); } std::shared_ptr mir::report::NullReportFactory::create_message_processor_report() { return std::make_shared(); } std::shared_ptr mir::report::NullReportFactory::create_input_report() { return std::make_shared(); } std::shared_ptr mir::report::NullReportFactory::create_shared_library_prober_report() { return std::make_shared(); } std::shared_ptr mir::report::NullReportFactory::create_shell_report() { return std::make_shared(); } std::shared_ptr mir::report::null_compositor_report() { return NullReportFactory{}.create_compositor_report(); } std::shared_ptr mir::report::null_shared_library_prober_report() { return NullReportFactory{}.create_shared_library_prober_report(); } std::shared_ptr mir::report::null_display_report() { return NullReportFactory{}.create_display_report(); } std::shared_ptr mir::report::null_scene_report() { return NullReportFactory{}.create_scene_report(); } std::shared_ptr mir::report::null_connector_report() { return NullReportFactory{}.create_connector_report(); } std::shared_ptr mir::report::null_session_mediator_report() { return NullReportFactory{}.create_session_mediator_report(); } std::shared_ptr mir::report::null_message_processor_report() { return NullReportFactory{}.create_message_processor_report(); } std::shared_ptr mir::report::null_input_report() { return NullReportFactory{}.create_input_report(); } ./src/server/report/null/CMakeLists.txt0000644000015600001650000000044212676616125020233 0ustar jenkinsjenkinsadd_library( mirnullreport OBJECT compositor_report.cpp connector_report.cpp display_report.cpp input_report.cpp message_processor_report.cpp null_report_factory.cpp scene_report.cpp session_mediator_report.cpp shell_report.cpp shell_report.h ) ./src/server/report/null/compositor_report.cpp0000644000015600001650000000237712676616125022001 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "compositor_report.h" namespace mrn = mir::report::null; void mrn::CompositorReport::added_display(int, int, int, int, SubCompositorId) { } void mrn::CompositorReport::began_frame(SubCompositorId) { } void mrn::CompositorReport::renderables_in_frame(SubCompositorId, mir::graphics::RenderableList const&) { } void mrn::CompositorReport::rendered_frame(SubCompositorId) { } void mrn::CompositorReport::finished_frame(SubCompositorId) { } void mrn::CompositorReport::started() { } void mrn::CompositorReport::stopped() { } void mrn::CompositorReport::scheduled() { } ./src/server/report/null/input_report.h0000644000015600001650000000276312676616125020406 0ustar jenkinsjenkins/* * Copyright © 2013,2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_NULL_INPUT_REPORT_H_ #define MIR_REPORT_NULL_INPUT_REPORT_H_ #include "mir/input/input_report.h" namespace mir { namespace report { namespace null { class InputReport : public input::InputReport { public: InputReport() = default; virtual ~InputReport() noexcept(true) = default; void received_event_from_kernel(int64_t when, int type, int code, int value) override; void published_key_event(int dest_fd, uint32_t seq_id, int64_t event_time) override; void published_motion_event(int dest_fd, uint32_t seq_id, int64_t event_time) override; void opened_input_device(char const* device_name, char const* input_platform) override; void failed_to_open_input_device(char const* device_name, char const* input_platform) override; }; } } } #endif /* MIR_REPORT_NULL_INPUT_REPORT_H_ */ ./src/server/report/lttng/0000755000015600001650000000000012676616160015650 5ustar jenkinsjenkins./src/server/report/lttng/session_mediator_report_tp.h0000644000015600001650000000602112676616125023466 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER mir_server_session_mediator #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "./session_mediator_report_tp.h" #if !defined(MIR_LTTNG_SESSION_MEDIATOR_REPORT_TP_H_) || defined(TRACEPOINT_HEADER_MULTI_READ) #define MIR_LTTNG_SESSION_MEDIATOR_REPORT_TP_H_ #include "lttng_utils.h" TRACEPOINT_EVENT_CLASS( mir_server_session_mediator, application_event, TP_ARGS(char const*, application), TP_FIELDS( ctf_string(application, application) ) ) #define MIR_SESSION_MEDIATOR_EVENT(event) \ TRACEPOINT_EVENT_INSTANCE(mir_server_session_mediator, application_event, event, TP_ARGS(char const*, application)) MIR_SESSION_MEDIATOR_EVENT(session_connect_called) MIR_SESSION_MEDIATOR_EVENT(session_create_surface_called) MIR_SESSION_MEDIATOR_EVENT(session_next_buffer_called) MIR_SESSION_MEDIATOR_EVENT(session_exchange_buffer_called) MIR_SESSION_MEDIATOR_EVENT(session_submit_buffer_called) MIR_SESSION_MEDIATOR_EVENT(session_allocate_buffers_called) MIR_SESSION_MEDIATOR_EVENT(session_release_buffers_called) MIR_SESSION_MEDIATOR_EVENT(session_release_surface_called) MIR_SESSION_MEDIATOR_EVENT(session_disconnect_called) MIR_SESSION_MEDIATOR_EVENT(session_configure_surface_called) MIR_SESSION_MEDIATOR_EVENT(session_configure_surface_cursor_called) MIR_SESSION_MEDIATOR_EVENT(session_configure_display_called) MIR_SESSION_MEDIATOR_EVENT(session_set_base_display_configuration_called) MIR_SESSION_MEDIATOR_EVENT(session_stop_prompt_session_called) MIR_SESSION_MEDIATOR_EVENT(session_create_buffer_stream_called) MIR_SESSION_MEDIATOR_EVENT(session_release_buffer_stream_called) TRACEPOINT_EVENT( mir_server_session_mediator, session_start_prompt_session_called, TP_ARGS(char const*, application, pid_t, application_process), TP_FIELDS( ctf_string(application, application) ctf_integer(pid_t, application_process, application_process) ) ) TRACEPOINT_EVENT( mir_server_session_mediator, session_error, TP_ARGS(char const*, application, char const*, method, char const*, what), TP_FIELDS( ctf_string(application, application) ctf_string(method, method) ctf_string(what, what) ) ) #undef MIR_SESSION_MEDIATOR_EVENT #endif /* MIR_LTTNG_SESSION_MEDIATOR_REPORT_TP_H_ */ #include ./src/server/report/lttng/input_report.cpp0000644000015600001650000000346012676616125021112 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/report/lttng/mir_tracepoint.h" #include "input_report.h" #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE #include "input_report_tp.h" void mir::report::lttng::InputReport::received_event_from_kernel(int64_t when, int type, int code, int value) { mir_tracepoint(mir_server_input, received_event_from_kernel, when, type, code, value); } void mir::report::lttng::InputReport::published_key_event(int dest_fd, uint32_t seq_id, int64_t event_time) { mir_tracepoint(mir_server_input, published_key_event, dest_fd, seq_id, event_time); } void mir::report::lttng::InputReport::published_motion_event(int dest_fd, uint32_t seq_id, int64_t event_time) { mir_tracepoint(mir_server_input, published_motion_event, dest_fd, seq_id, event_time); } void mir::report::lttng::InputReport::opened_input_device(char const* name, char const* platform) { mir_tracepoint(mir_server_input, opened_input_device, name, platform); } void mir::report::lttng::InputReport::failed_to_open_input_device(char const* name, char const* platform) { mir_tracepoint(mir_server_input, failed_to_open_input_device, name, platform); } ./src/server/report/lttng/message_processor_report.cpp0000644000015600001650000000371412676616125023500 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "message_processor_report.h" #include "mir/report/lttng/mir_tracepoint.h" #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE #include "message_processor_report_tp.h" void mir::report::lttng::MessageProcessorReport::received_invocation( void const* mediator, int id, std::string const& method) { mir_tracepoint(mir_server_msgproc, received_invocation, mediator, id, method.c_str()); } void mir::report::lttng::MessageProcessorReport::completed_invocation( void const* mediator, int id, bool result) { mir_tracepoint(mir_server_msgproc, completed_invocation, mediator, id, result); } void mir::report::lttng::MessageProcessorReport::unknown_method( void const* mediator, int id, std::string const& method) { mir_tracepoint(mir_server_msgproc, unknown_method, mediator, id, method.c_str()); } void mir::report::lttng::MessageProcessorReport::exception_handled( void const* mediator, int id, std::exception const& error) { mir_tracepoint(mir_server_msgproc, exception_handled, mediator, id, error.what()); } void mir::report::lttng::MessageProcessorReport::exception_handled( void const* mediator, std::exception const& error) { mir_tracepoint(mir_server_msgproc, exception_handled_wo_invocation, mediator, error.what()); } ./src/server/report/lttng/input_report_tp.h0000644000015600001650000000461212676616125021262 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER mir_server_input #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "./input_report_tp.h" #if !defined(MIR_LTTNG_INPUT_REPORT_TP_H_) || defined(TRACEPOINT_HEADER_MULTI_READ) #define MIR_LTTNG_INPUT_REPORT_TP_H_ #include "lttng_utils.h" TRACEPOINT_EVENT( mir_server_input, received_event_from_kernel, TP_ARGS(int64_t, when, int, type, int, code, int, value), TP_FIELDS( ctf_integer(int64_t, when, when) ctf_integer(int, type, type) ctf_integer(int, code, code) ctf_integer(int, value, value) ) ) TRACEPOINT_EVENT( mir_server_input, published_key_event, TP_ARGS(int, dest_fd, uint32_t, seq_id, int64_t, event_time), TP_FIELDS( ctf_integer(int, dest_fd, dest_fd) ctf_integer(uint32_t, seq_id, seq_id) ctf_integer(int64_t, event_time, event_time) ) ) TRACEPOINT_EVENT( mir_server_input, published_motion_event, TP_ARGS(int, dest_fd, uint32_t, seq_id, int64_t, event_time), TP_FIELDS( ctf_integer(int, dest_fd, dest_fd) ctf_integer(uint32_t, seq_id, seq_id) ctf_integer(int64_t, event_time, event_time) ) ) TRACEPOINT_EVENT( mir_server_input, opened_input_device, TP_ARGS(const char*, device, const char*, platform), TP_FIELDS( ctf_string(device, device) ctf_string(platform, platform) ) ) TRACEPOINT_EVENT( mir_server_input, failed_to_open_input_device, TP_ARGS(const char*, device, const char*, platform), TP_FIELDS( ctf_string(device, device) ctf_string(platform, platform) ) ) #endif /* MIR_LTTNG_DISPLAY_REPORT_TP_H_ */ #include ./src/server/report/lttng/display_report.cpp0000644000015600001650000000363512676616125021424 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "display_report.h" #include "mir/report/lttng/mir_tracepoint.h" #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE #include "display_report_tp.h" #include "lttng_utils.h" #define DISPLAY_REPORT_TRACE_CALL(name) MIR_LTTNG_VOID_TRACE_CALL(DisplayReport,mir_server_display,name) DISPLAY_REPORT_TRACE_CALL(report_successful_setup_of_native_resources) DISPLAY_REPORT_TRACE_CALL(report_successful_egl_make_current_on_construction) DISPLAY_REPORT_TRACE_CALL(report_successful_egl_buffer_swap_on_construction) DISPLAY_REPORT_TRACE_CALL(report_successful_display_construction) DISPLAY_REPORT_TRACE_CALL(report_successful_drm_mode_set_crtc_on_construction) DISPLAY_REPORT_TRACE_CALL(report_vt_switch_away_failure) DISPLAY_REPORT_TRACE_CALL(report_vt_switch_back_failure) #undef DISPLAY_REPORT_TRACE_CALL void mir::report::lttng::DisplayReport::report_egl_configuration(EGLDisplay /*disp*/, EGLConfig /*config*/) { } void mir::report::lttng::DisplayReport::report_drm_master_failure(int error) { mir_tracepoint(mir_server_display, report_drm_master_failure, strerror(error)); } void mir::report::lttng::DisplayReport::report_vsync(unsigned int display_id) { mir_tracepoint(mir_server_display, report_vsync, display_id); } ./src/server/report/lttng/server_tracepoint_provider.h0000644000015600001650000000210312676616125023466 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_LTTNG_SERVER_TRACEPOINT_PROVIDER_H_ #define MIR_LTTNG_SERVER_TRACEPOINT_PROVIDER_H_ #include "mir/report/lttng/tracepoint_provider.h" namespace mir { namespace report { namespace lttng { class ServerTracepointProvider : public mir::report::lttng::TracepointProvider { public: ServerTracepointProvider(); }; } } } #endif /* MIR_LTTNG_SERVER_TRACEPOINT_PROVIDER_H_ */ ./src/server/report/lttng/connector_report_tp.h0000644000015600001650000000412412676616125022113 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER mir_server_connector #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "./connector_report_tp.h" #if !defined(MIR_LTTNG_CONNECTOR_REPORT_TP_H_) || defined(TRACEPOINT_HEADER_MULTI_READ) #define MIR_LTTNG_CONNECTOR_REPORT_TP_H_ #include "lttng_utils.h" MIR_LTTNG_VOID_TRACE_CLASS(mir_server_connector) #define CONNECTOR_TRACE_POINT(name) MIR_LTTNG_VOID_TRACE_POINT(mir_server_connector, name) CONNECTOR_TRACE_POINT(thread_start) CONNECTOR_TRACE_POINT(thread_end) CONNECTOR_TRACE_POINT(scheduled) #undef CONNECTOR_TRACE_POINT TRACEPOINT_EVENT(TRACEPOINT_PROVIDER, creating_session_for, TP_ARGS(int, socket), TP_FIELDS(ctf_integer(int, socket, socket))) TRACEPOINT_EVENT(TRACEPOINT_PROVIDER, creating_socket_pair, TP_ARGS(int, server, int, client), TP_FIELDS(ctf_integer(int, server, server) ctf_integer(int, client, client))) TRACEPOINT_EVENT(TRACEPOINT_PROVIDER, listening_on, TP_ARGS(char const*, endpoint), TP_FIELDS(ctf_string(endpoint, endpoint))) TRACEPOINT_EVENT(TRACEPOINT_PROVIDER, error, TP_ARGS(char const*, diagnostics), TP_FIELDS(ctf_string(diagnostics, diagnostics))) #endif /* MIR_LTTNG_CONNECTOR_REPORT_TP_H_ */ #include ./src/server/report/lttng/display_report.h0000644000015600001650000000347512676616125021073 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_LTTNG_DISPLAY_REPORT_H_ #define MIR_REPORT_LTTNG_DISPLAY_REPORT_H_ #include "server_tracepoint_provider.h" #include "mir/graphics/display_report.h" namespace mir { namespace report { namespace lttng { class DisplayReport : public graphics::DisplayReport { public: DisplayReport() = default; virtual ~DisplayReport() noexcept(true) = default; virtual void report_successful_setup_of_native_resources() override; virtual void report_successful_egl_make_current_on_construction() override; virtual void report_successful_egl_buffer_swap_on_construction() override; virtual void report_successful_display_construction() override; virtual void report_egl_configuration(EGLDisplay disp, EGLConfig cfg) override; virtual void report_successful_drm_mode_set_crtc_on_construction() override; virtual void report_drm_master_failure(int error) override; virtual void report_vt_switch_away_failure() override; virtual void report_vt_switch_back_failure() override; virtual void report_vsync(unsigned int display_id) override; private: ServerTracepointProvider tp_provider; }; } } } #endif ./src/server/report/lttng/lttng_report_factory.cpp0000644000015600001650000000473512676616125022640 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "../lttng_report_factory.h" #include "compositor_report.h" #include "connector_report.h" #include "display_report.h" #include "input_report.h" #include "message_processor_report.h" #include "scene_report.h" #include "session_mediator_report.h" #include "shared_library_prober_report.h" std::shared_ptr mir::report::LttngReportFactory::create_compositor_report() { return std::make_shared(); } std::shared_ptr mir::report::LttngReportFactory::create_display_report() { return std::make_shared(); } std::shared_ptr mir::report::LttngReportFactory::create_scene_report() { return std::make_shared(); } std::shared_ptr mir::report::LttngReportFactory::create_connector_report() { return std::make_shared(); } std::shared_ptr mir::report::LttngReportFactory::create_session_mediator_report() { return std::make_shared(); } std::shared_ptr mir::report::LttngReportFactory::create_message_processor_report() { return std::make_shared(); } std::shared_ptr mir::report::LttngReportFactory::create_input_report() { return std::make_shared(); } std::shared_ptr mir::report::LttngReportFactory::create_shared_library_prober_report() { return std::make_shared(); } std::shared_ptr mir::report::LttngReportFactory::create_shell_report() { BOOST_THROW_EXCEPTION(std::logic_error("Not implemented")); } ./src/server/report/lttng/shared_library_prober_report.h0000644000015600001650000000274412676616125023767 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_LTTNG_SHARED_LIBRARY_PROBER_REPORT_H_ #define MIR_REPORT_LTTNG_SHARED_LIBRARY_PROBER_REPORT_H_ #include "server_tracepoint_provider.h" #include "mir/shared_library_prober_report.h" namespace mir { namespace report { namespace lttng { class SharedLibraryProberReport : public mir::SharedLibraryProberReport { public: void probing_path(boost::filesystem::path const& path) override; void probing_failed(boost::filesystem::path const& path, std::exception const& error) override; void loading_library(boost::filesystem::path const& filename) override; void loading_failed(boost::filesystem::path const& filename, std::exception const& error) override; private: ServerTracepointProvider tp_provider; }; } } } #endif /* MIR_REPORT_LTTNG_SHARED_LIBRARY_PROBER_REPORT_H_ */ ./src/server/report/lttng/scene_report.cpp0000644000015600001650000000300012676616125021036 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "scene_report.h" #include "mir/report/lttng/mir_tracepoint.h" #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE #include "scene_report_tp.h" void mir::report::lttng::SceneReport::surface_created(BasicSurfaceId, std::string const& name) { mir_tracepoint(mir_server_scene, surface_created, name.c_str()); } void mir::report::lttng::SceneReport::surface_added(BasicSurfaceId, std::string const& name) { mir_tracepoint(mir_server_scene, surface_added, name.c_str()); } void mir::report::lttng::SceneReport::surface_removed(BasicSurfaceId, std::string const& name) { mir_tracepoint(mir_server_scene, surface_removed, name.c_str()); } void mir::report::lttng::SceneReport::surface_deleted(BasicSurfaceId, std::string const& name) { mir_tracepoint(mir_server_scene, surface_deleted, name.c_str()); } ./src/server/report/lttng/shared_library_prober_report.cpp0000644000015600001650000000344712676616125024323 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "shared_library_prober_report.h" #include "mir/report/lttng/mir_tracepoint.h" #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE #include "shared_library_prober_report_tp.h" namespace mrl = mir::report::lttng; namespace bf = boost::filesystem; void mrl::SharedLibraryProberReport::probing_path(bf::path const& path) { mir_tracepoint(mir_server_shared_library_prober, probing_path, path.string().c_str()); } void mrl::SharedLibraryProberReport::probing_failed(bf::path const& path, std::exception const& error) { mir_tracepoint(mir_server_shared_library_prober, probing_failed, path.string().c_str(), error.what()); } void mrl::SharedLibraryProberReport::loading_library(bf::path const& filename) { mir_tracepoint(mir_server_shared_library_prober, loading_library, filename.string().c_str()); } void mrl::SharedLibraryProberReport::loading_failed(bf::path const& filename, std::exception const& error) { mir_tracepoint(mir_server_shared_library_prober, loading_failed, filename.string().c_str(), error.what()); } ./src/server/report/lttng/message_processor_report_tp.h0000644000015600001650000000502212676616125023642 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER mir_server_msgproc #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "./message_processor_report_tp.h" #if !defined(MIR_LTTNG_MESSAGE_PROCESSOR_REPORT_TP_H_) || defined(TRACEPOINT_HEADER_MULTI_READ) #define MIR_LTTNG_MESSAGE_PROCESSOR_REPORT_TP_H_ #include "lttng_utils.h" TRACEPOINT_EVENT_CLASS( mir_server_msgproc, method_event, TP_ARGS(const void*, mediator, int, id, const char*, method), TP_FIELDS( ctf_integer_hex(void*, mediator, mediator) ctf_integer(int, id, id) ctf_string(method, method) ) ) TRACEPOINT_EVENT_INSTANCE( mir_server_msgproc, method_event, received_invocation, TP_ARGS(const void*, mediator, int, id, const char*, method) ) TRACEPOINT_EVENT( mir_server_msgproc, completed_invocation, TP_ARGS(const void*, mediator, int, id, int, result), TP_FIELDS( ctf_integer_hex(void*, mediator, mediator) ctf_integer(int, id, id) ctf_integer(int, result, result) ) ) TRACEPOINT_EVENT_INSTANCE( mir_server_msgproc, method_event, unknown_method, TP_ARGS(const void*, mediator, int, id, char const*, method) ) TRACEPOINT_EVENT( mir_server_msgproc, exception_handled, TP_ARGS(const void*, mediator, int, id, char const*, exception), TP_FIELDS( ctf_integer_hex(void*, mediator, mediator) ctf_integer(int, id, id) ctf_string(exception, exception) ) ) TRACEPOINT_EVENT( mir_server_msgproc, exception_handled_wo_invocation, TP_ARGS(const void*, mediator, char const*, exception), TP_FIELDS( ctf_integer_hex(void*, mediator, mediator) ctf_string(exception, exception) ) ) #endif /* MIR_LTTNG_MESSAGE_PROCESSOR_REPORT_TP_H_ */ #include ./src/server/report/lttng/session_mediator_report.h0000644000015600001650000000513112676616125022764 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_LTTNG_SESSION_MEDIATOR_REPORT_H_ #define MIR_REPORT_LTTNG_SESSION_MEDIATOR_REPORT_H_ #include "server_tracepoint_provider.h" #include "mir/frontend/session_mediator_report.h" namespace mir { namespace report { namespace lttng { // Interface for monitoring application activity class SessionMediatorReport : public frontend::SessionMediatorReport { public: void session_connect_called(std::string const& app_name) override; void session_create_surface_called(std::string const& app_name) override; void session_next_buffer_called(std::string const& app_name) override; void session_exchange_buffer_called(std::string const& app_name) override; void session_submit_buffer_called(std::string const& app_name) override; void session_allocate_buffers_called(std::string const& app_name) override; void session_release_buffers_called(std::string const& app_name) override; void session_release_surface_called(std::string const& app_name) override; void session_disconnect_called(std::string const& app_name) override; void session_configure_surface_called(std::string const& app_name) override; void session_configure_surface_cursor_called(std::string const& app_name) override; void session_configure_display_called(std::string const& app_name) override; void session_set_base_display_configuration_called(std::string const& app_name) override; void session_start_prompt_session_called(std::string const& app_name, pid_t application_process) override; void session_stop_prompt_session_called(std::string const& app_name) override; void session_create_buffer_stream_called(std::string const& app_name) override; void session_release_buffer_stream_called(std::string const& app_name) override; void session_error(std::string const& app_name, char const* method, std::string const& what) override; private: ServerTracepointProvider tp_provider; }; } } } #endif ./src/server/report/lttng/compositor_report.h0000644000015600001650000000323012676616125021611 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_LTTNG_COMPOSITOR_REPORT_H_ #define MIR_REPORT_LTTNG_COMPOSITOR_REPORT_H_ #include "server_tracepoint_provider.h" #include "mir/compositor/compositor_report.h" namespace mir { namespace report { namespace lttng { class CompositorReport : public compositor::CompositorReport { public: CompositorReport() = default; virtual ~CompositorReport() = default; void added_display(int width, int height, int x, int y, SubCompositorId id) override; void began_frame(SubCompositorId id) override; void renderables_in_frame(SubCompositorId id, graphics::RenderableList const& renderables) override; void rendered_frame(SubCompositorId id) override; void finished_frame(SubCompositorId id) override; void started() override; void stopped() override; void scheduled() override; private: ServerTracepointProvider tp_provider; }; } // namespace lttng } // namespace report } // namespace mir #endif // MIR_REPORT_LTTNG_COMPOSITOR_REPORT_H_ ./src/server/report/lttng/connector_report.cpp0000644000015600001650000000346012676616125021745 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "connector_report.h" #include "mir/report/lttng/mir_tracepoint.h" #include #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE #include "connector_report_tp.h" #define CONNECTOR_TRACE_CALL(name) MIR_LTTNG_VOID_TRACE_CALL(ConnectorReport, mir_server_connector, name) CONNECTOR_TRACE_CALL(thread_start) CONNECTOR_TRACE_CALL(thread_end) #undef CONNECTOR_TRACE_CALL void mir::report::lttng::ConnectorReport::creating_session_for(int socket_handle) { mir_tracepoint(mir_server_connector, creating_session_for, socket_handle); } void mir::report::lttng::ConnectorReport::creating_socket_pair(int server_handle, int client_handle) { mir_tracepoint(mir_server_connector, creating_socket_pair, server_handle, client_handle); } void mir::report::lttng::ConnectorReport::listening_on(std::string const& endpoint) { mir_tracepoint(mir_server_connector, listening_on, endpoint.c_str()); } void mir::report::lttng::ConnectorReport::error(std::exception const& error) { mir_tracepoint(mir_server_connector, error, boost::diagnostic_information(error).c_str()); } ./src/server/report/lttng/message_processor_report.h0000644000015600001650000000304612676616125023143 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_REPORT_LTTNG_MESSAGE_PROCESSOR_REPORT_H_ #define MIR_REPORT_LTTNG_MESSAGE_PROCESSOR_REPORT_H_ #include "server_tracepoint_provider.h" #include "mir/frontend/message_processor_report.h" namespace mir { namespace report { namespace lttng { class MessageProcessorReport : public mir::frontend::MessageProcessorReport { public: void received_invocation(void const* mediator, int id, std::string const& method); void completed_invocation(void const* mediator, int id, bool result); void unknown_method(void const* mediator, int id, std::string const& method); void exception_handled(void const* mediator, int id, std::exception const& error); void exception_handled(void const* mediator, std::exception const& error); private: ServerTracepointProvider tp_provider; }; } } } #endif /* MIR_REPORT_LTTNG_MESSAGE_PROCESSOR_REPORT_H_ */ ./src/server/report/lttng/connector_report.h0000644000015600001650000000266712676616125021422 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_LTTNG_CONNECTOR_REPORT_H_ #define MIR_REPORT_LTTNG_CONNECTOR_REPORT_H_ #include "server_tracepoint_provider.h" #include "mir/frontend/connector_report.h" #include #include namespace mir { namespace report { namespace lttng { class ConnectorReport : public frontend::ConnectorReport { public: void thread_start() override; void thread_end() override; void creating_session_for(int socket_handle) override; void creating_socket_pair(int server_handle, int client_handle) override; void listening_on(std::string const& endpoint) override; void error(std::exception const& error) override; private: ServerTracepointProvider tp_provider; }; } } } #endif // MIR_REPORT_LTTNG_CONNECTOR_REPORT_H_ ./src/server/report/lttng/scene_report.h0000644000015600001650000000255512676616125020521 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_LTTNG_SCENE_REPORT_H_ #define MIR_REPORT_LTTNG_SCENE_REPORT_H_ #include "server_tracepoint_provider.h" #include "mir/scene/scene_report.h" namespace mir { namespace report { namespace lttng { class SceneReport : public scene::SceneReport { public: void surface_created(BasicSurfaceId id, std::string const& name) override; void surface_added(BasicSurfaceId id, std::string const& name) override; void surface_removed(BasicSurfaceId id, std::string const& name) override; void surface_deleted(BasicSurfaceId id, std::string const& name) override; private: ServerTracepointProvider tp_provider; }; } } } #endif /* MIR_REPORT_LTTNG_SCENE_REPORT_H_ */ ./src/server/report/lttng/compositor_report_tp.h0000644000015600001650000000500012676616125022311 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER mir_server_compositor #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "./compositor_report_tp.h" #if !defined(MIR_LTTNG_COMPOSITOR_REPORT_TP_H_) || defined(TRACEPOINT_HEADER_MULTI_READ) #define MIR_LTTNG_COMPOSITOR_REPORT_TP_H_ #include "lttng_utils.h" MIR_LTTNG_VOID_TRACE_CLASS(mir_server_compositor) #define COMPOSITOR_TRACE_POINT(name) MIR_LTTNG_VOID_TRACE_POINT(TRACEPOINT_PROVIDER, name) COMPOSITOR_TRACE_POINT(started) COMPOSITOR_TRACE_POINT(stopped) COMPOSITOR_TRACE_POINT(scheduled) #undef COMPOSITOR_TRACE_POINT TRACEPOINT_EVENT( mir_server_compositor, added_display, TP_ARGS(int, width, int, height, int, x, int, y, void const*, id), TP_FIELDS( ctf_integer(int, width, width) ctf_integer(int, height, height) ctf_integer(int, x, x) ctf_integer(int, y, y) ctf_integer_hex(uintptr_t, id, (uintptr_t)(id)) ) ) TRACEPOINT_EVENT( mir_server_compositor, began_frame, TP_ARGS(void const*, id), TP_FIELDS( ctf_integer_hex(uintptr_t, id, (uintptr_t)(id)) ) ) TRACEPOINT_EVENT( mir_server_compositor, rendered_frame, TP_ARGS(void const*, id), TP_FIELDS( ctf_integer_hex(uintptr_t, id, (uintptr_t)(id)) ) ) TRACEPOINT_EVENT( mir_server_compositor, finished_frame, TP_ARGS(void const*, id), TP_FIELDS( ctf_integer_hex(uintptr_t, id, (uintptr_t)(id)) ) ) TRACEPOINT_EVENT( mir_server_compositor, buffers_in_frame, TP_ARGS(void const*, id, unsigned int*, buffer_ids, size_t, buffer_ids_len), TP_FIELDS( ctf_integer_hex(uintptr_t, id, (uintptr_t)(id)) ctf_sequence(unsigned int, buffer_ids, buffer_ids, size_t, buffer_ids_len) ) ) #endif /* MIR_LTTNG_COMPOSITOR_REPORT_TP_H_ */ #include ./src/server/report/lttng/session_mediator_report.cpp0000644000015600001650000000541012676616125023317 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "session_mediator_report.h" #include "mir/report/lttng/mir_tracepoint.h" #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE #include "session_mediator_report_tp.h" #define MIR_SESSION_MEDIATOR_EVENT_METHOD(event) \ void mir::report::lttng::SessionMediatorReport::event(std::string const& app_name) \ { \ mir_tracepoint(mir_server_session_mediator, event, app_name.c_str()); \ } MIR_SESSION_MEDIATOR_EVENT_METHOD(session_connect_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_create_surface_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_next_buffer_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_exchange_buffer_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_submit_buffer_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_allocate_buffers_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_release_buffers_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_release_surface_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_disconnect_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_configure_surface_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_configure_surface_cursor_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_configure_display_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_set_base_display_configuration_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_stop_prompt_session_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_create_buffer_stream_called) MIR_SESSION_MEDIATOR_EVENT_METHOD(session_release_buffer_stream_called) void mir::report::lttng::SessionMediatorReport::session_start_prompt_session_called(std::string const& app_name, pid_t application_process) { mir_tracepoint(mir_server_session_mediator, session_start_prompt_session_called, app_name.c_str(), application_process); } void mir::report::lttng::SessionMediatorReport::session_error(std::string const& app_name, char const* method, std::string const& what) { mir_tracepoint(mir_server_session_mediator, session_error, app_name.c_str(), method, what.c_str()); } ./src/server/report/lttng/server_tracepoint_provider.cpp0000644000015600001650000000170212676616125024025 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #include "server_tracepoint_provider.h" namespace { std::string const server_tp_provider_lib_name{"libmirserverlttng.so"}; } mir::report::lttng::ServerTracepointProvider::ServerTracepointProvider() : TracepointProvider{server_tp_provider_lib_name} { } ./src/server/report/lttng/tracepoints.c0000644000015600001650000000056712676616125020360 0ustar jenkinsjenkins/* The probes need to be compiled in a C file (not C++) */ #define TRACEPOINT_CREATE_PROBES #include "compositor_report_tp.h" #include "input_report_tp.h" #include "connector_report_tp.h" #include "display_report_tp.h" #include "session_mediator_report_tp.h" #include "scene_report_tp.h" #include "message_processor_report_tp.h" #include "shared_library_prober_report_tp.h" ./src/server/report/lttng/display_report_tp.h0000644000015600001650000000446012676616125021571 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER mir_server_display #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "./display_report_tp.h" #if !defined(MIR_LTTNG_DISPLAY_REPORT_TP_H_) || defined(TRACEPOINT_HEADER_MULTI_READ) #define MIR_LTTNG_DISPLAY_REPORT_TP_H_ #include "lttng_utils.h" MIR_LTTNG_VOID_TRACE_CLASS(mir_server_display) #define DISPLAY_REPORT_TRACE_POINT(name) MIR_LTTNG_VOID_TRACE_POINT(mir_server_display,name) DISPLAY_REPORT_TRACE_POINT(report_successful_setup_of_native_resources) DISPLAY_REPORT_TRACE_POINT(report_successful_egl_make_current_on_construction) DISPLAY_REPORT_TRACE_POINT(report_successful_egl_buffer_swap_on_construction) DISPLAY_REPORT_TRACE_POINT(report_successful_display_construction) DISPLAY_REPORT_TRACE_POINT(report_successful_drm_mode_set_crtc_on_construction) DISPLAY_REPORT_TRACE_POINT(report_vt_switch_away_failure) DISPLAY_REPORT_TRACE_POINT(report_vt_switch_back_failure) DISPLAY_REPORT_TRACE_POINT(report_gpu_composition_in_use) #undef DISPLAY_REPORT_TRACE_POINT TRACEPOINT_EVENT( mir_server_display, report_drm_master_failure, TP_ARGS(char const*, error_string), TP_FIELDS( ctf_string(error_string, error_string) ) ) TRACEPOINT_EVENT( mir_server_display, report_hwc_composition_in_use, TP_ARGS(int, major, int, minor), TP_FIELDS( ctf_integer(int, major, major) ctf_integer(int, minor, minor) ) ) TRACEPOINT_EVENT( mir_server_display, report_vsync, TP_ARGS(int, id), TP_FIELDS( ctf_integer(int, id, id) ) ) #endif /* MIR_LTTNG_DISPLAY_REPORT_TP_H_ */ #include ./src/server/report/lttng/lttng_utils.h0000644000015600001650000000244112676616125020373 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include #include #ifndef _MIR_LTTNG_UTILS_H_ #define _MIR_LTTNG_UTILS_H_ #define MIR_LTTNG_VOID_TRACE_CALL(klass, comp, name) \ void mir::report::lttng::klass::name() \ { \ mir_tracepoint(comp, name, 0); \ } #define MIR_LTTNG_VOID_TRACE_CLASS(comp) \ TRACEPOINT_EVENT_CLASS(comp, dummy_event, TP_ARGS(int,empty), TP_FIELDS(ctf_integer(int,empty,empty))) #define MIR_LTTNG_VOID_TRACE_POINT(comp, name) \ TRACEPOINT_EVENT_INSTANCE(comp, dummy_event, name, TP_ARGS(int,empty)) #endif ./src/server/report/lttng/CMakeLists.txt0000644000015600001650000000214412676616157020417 0ustar jenkinsjenkinsinclude_directories(${CMAKE_CURRENT_SOURCE_DIR} ${LTTNG_UST_INCLUDE_DIRS}) # Don't treat missing-field-initializers as an error, since # the LTTng macros contain code that triggers this (but it's # harmless; it concerns a padding field) add_compile_options( -Wno-error=missing-field-initializers -Wno-error=unused-function -Wno-error=unused-parameter ) # Using LTO on the lttng DSO causes a gcc ICE. # Since LTO is reasonably uninteresting for the lttng tracer, disable it. string(REPLACE "-flto" "" NO_LTO_FLAGS ${CMAKE_C_FLAGS}) set(CMAKE_C_FLAGS ${NO_LTO_FLAGS}) set( LTTNG_SOURCES compositor_report.cpp connector_report.cpp display_report.cpp input_report.cpp message_processor_report.cpp lttng_report_factory.cpp session_mediator_report.cpp scene_report.cpp server_tracepoint_provider.cpp shared_library_prober_report.cpp ) add_library( mirlttng OBJECT ${LTTNG_SOURCES} ) add_library(mirserverlttng SHARED tracepoints.c) target_link_libraries( mirserverlttng ${LTTNG_UST_LIBRARIES} ) install(TARGETS mirserverlttng LIBRARY DESTINATION ${MIR_TRACEPOINT_LIB_INSTALL_DIR} ) ./src/server/report/lttng/compositor_report.cpp0000644000015600001650000000414012676616125022145 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "compositor_report.h" #include "mir/graphics/buffer.h" #include "mir/report/lttng/mir_tracepoint.h" #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE #include "compositor_report_tp.h" #define COMPOSITOR_TRACE_CALL(name) MIR_LTTNG_VOID_TRACE_CALL(CompositorReport, mir_server_compositor, name) COMPOSITOR_TRACE_CALL(started) COMPOSITOR_TRACE_CALL(stopped) COMPOSITOR_TRACE_CALL(scheduled) #undef COMPOSITOR_TRACE_CALL void mir::report::lttng::CompositorReport::added_display(int width, int height, int x, int y, SubCompositorId id) { mir_tracepoint(mir_server_compositor, added_display, width, height, x, y, id); } void mir::report::lttng::CompositorReport::began_frame(SubCompositorId id) { mir_tracepoint(mir_server_compositor, began_frame, id); } void mir::report::lttng::CompositorReport::renderables_in_frame( SubCompositorId id, graphics::RenderableList const& list) { std::vector ids(list.size()); auto it = list.begin(); for(auto& id : ids) id = (*it++)->buffer()->id().as_value(); mir_tracepoint(mir_server_compositor, buffers_in_frame, id, ids.data(), ids.size()); } void mir::report::lttng::CompositorReport::rendered_frame(SubCompositorId id) { mir_tracepoint(mir_server_compositor, rendered_frame, id); } void mir::report::lttng::CompositorReport::finished_frame(SubCompositorId id) { mir_tracepoint(mir_server_compositor, finished_frame, id); } ./src/server/report/lttng/shared_library_prober_report_tp.h0000644000015600001650000000360112676616125024463 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER mir_server_shared_library_prober #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "./shared_library_prober_report_tp.h" #if !defined(MIR_LTTNG_SHARED_LIBRARY_PROBER_REPORT_TP_H_) || defined(TRACEPOINT_HEADER_MULTI_READ) #define MIR_LTTNG_SHARED_LIBRARY_PROBER_REPORT_TP_H_ #include "lttng_utils.h" TRACEPOINT_EVENT( mir_server_shared_library_prober, probing_path, TP_ARGS(const char*, path), TP_FIELDS( ctf_string(path, path) ) ) TRACEPOINT_EVENT( mir_server_shared_library_prober, probing_failed, TP_ARGS(const char*, path, const char*, message), TP_FIELDS( ctf_string(path, path) ctf_string(message, message) ) ) TRACEPOINT_EVENT( mir_server_shared_library_prober, loading_library, TP_ARGS(const char*, path), TP_FIELDS( ctf_string(path, path) ) ) TRACEPOINT_EVENT( mir_server_shared_library_prober, loading_failed, TP_ARGS(const char*, path, const char*, message), TP_FIELDS( ctf_string(path, path) ctf_string(message, message) ) ) #endif /* MIR_LTTNG_SHARED_LIBRARY_PROBER_REPORT_TP_H_ */ #include ./src/server/report/lttng/input_report.h0000644000015600001650000000311612676616125020555 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_LTTNG_INPUT_REPORT_H_ #define MIR_REPORT_LTTNG_INPUT_REPORT_H_ #include "server_tracepoint_provider.h" #include "mir/input/input_report.h" namespace mir { namespace report { namespace lttng { class InputReport : public input::InputReport { public: InputReport() = default; virtual ~InputReport() noexcept(true) = default; void received_event_from_kernel(int64_t when, int type, int code, int value) override; void published_key_event(int dest_fd, uint32_t seq_id, int64_t event_time) override; void published_motion_event(int dest_fd, uint32_t seq_id, int64_t event_time) override; void opened_input_device(char const* device_name, char const* input_platform) override; void failed_to_open_input_device(char const* device_name, char const* input_platform) override; private: ServerTracepointProvider tp_provider; }; } } } #endif /* MIR_REPORT_LTTNG_INPUT_REPORT_H_ */ ./src/server/report/lttng/scene_report_tp.h0000644000015600001650000000344312676616125021221 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER mir_server_scene #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "./scene_report_tp.h" #if !defined(MIR_LTTNG_SCENE_REPORT_TP_H_) || defined(TRACEPOINT_HEADER_MULTI_READ) #define MIR_LTTNG_SCENE_REPORT_TP_H_ #include #include #include "lttng_utils.h" #undef SCENE_TRACE_POINT TRACEPOINT_EVENT(TRACEPOINT_PROVIDER, surface_created, TP_ARGS(char const*, name), TP_FIELDS(ctf_string(name, name))) TRACEPOINT_EVENT(TRACEPOINT_PROVIDER, surface_added, TP_ARGS(char const*, name), TP_FIELDS(ctf_string(name, name))) TRACEPOINT_EVENT(TRACEPOINT_PROVIDER, surface_removed, TP_ARGS(char const*, name), TP_FIELDS(ctf_string(name, name))) TRACEPOINT_EVENT(TRACEPOINT_PROVIDER, surface_deleted, TP_ARGS(char const*, name), TP_FIELDS(ctf_string(name, name))) #endif /* MIR_LTTNG_SCENE_REPORT_TP_H_ */ #include ./src/server/report/logging_report_factory.h0000644000015600001650000000401012676616125021435 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_LOGGING_REPORT_FACTORY_H_ #define MIR_REPORT_LOGGING_REPORT_FACTORY_H_ #include "report_factory.h" namespace mir { namespace logging { class Logger; } namespace time { class Clock; } class DefaultServerConfiguration; namespace report { class LoggingReportFactory : public report::ReportFactory { public: LoggingReportFactory(std::shared_ptr const& logger, std::shared_ptr const& clock); std::shared_ptr create_compositor_report() override; std::shared_ptr create_display_report() override; std::shared_ptr create_scene_report() override; std::shared_ptr create_connector_report() override; std::shared_ptr create_session_mediator_report() override; std::shared_ptr create_message_processor_report() override; std::shared_ptr create_input_report() override; std::shared_ptr create_shared_library_prober_report() override; std::shared_ptr create_shell_report() override; private: std::shared_ptr const logger; std::shared_ptr const clock; }; } } #endif ./src/server/report/report_factory.h0000644000015600001650000000426712676616125017745 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_REPORT_FACTORY_H_ #define MIR_REPORT_REPORT_FACTORY_H_ #include namespace mir { class SharedLibraryProberReport; namespace compositor { class CompositorReport; } namespace frontend { class ConnectorReport; class SessionMediatorReport; class MessageProcessorReport; } namespace graphics { class DisplayReport; } namespace input { class InputReport; } namespace scene { class SceneReport; } namespace shell { class ShellReport; } namespace report { class ReportFactory { public: virtual ~ReportFactory() = default; virtual std::shared_ptr create_compositor_report() = 0; virtual std::shared_ptr create_display_report() = 0; virtual std::shared_ptr create_scene_report() = 0; virtual std::shared_ptr create_connector_report() = 0; virtual std::shared_ptr create_session_mediator_report() = 0; virtual std::shared_ptr create_message_processor_report() = 0; virtual std::shared_ptr create_input_report() = 0; virtual std::shared_ptr create_shared_library_prober_report() = 0; virtual std::shared_ptr create_shell_report() = 0; protected: ReportFactory() = default; ReportFactory(ReportFactory const&) = delete; ReportFactory& operator=(ReportFactory const &) = delete; }; } } #endif /* MIR_REPORT_REPORT_FACTORY_H_ */ ./src/server/report/lttng_report_factory.h0000644000015600001650000000322312676616125021144 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_LTTNG_REPORT_FACTORY_H_ #define MIR_REPORT_LTTNG_REPORT_FACTORY_H_ #include "report_factory.h" namespace mir { namespace report { class LttngReportFactory : public report::ReportFactory { public: std::shared_ptr create_compositor_report() override; std::shared_ptr create_display_report() override; std::shared_ptr create_scene_report() override; std::shared_ptr create_connector_report() override; std::shared_ptr create_session_mediator_report() override; std::shared_ptr create_message_processor_report() override; std::shared_ptr create_input_report() override; std::shared_ptr create_shared_library_prober_report() override; std::shared_ptr create_shell_report() override; }; } } #endif ./src/server/report/logging/0000755000015600001650000000000012676616126016150 5ustar jenkinsjenkins./src/server/report/logging/input_report.cpp0000644000015600001650000001603412676616125021411 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "input_report.h" #include "mir/report/legacy_input_report.h" #include "mir/logging/logger.h" #include "mir/logging/input_timestamp.h" #include "std/MirLog.h" #include #include #include #include #include namespace mrl = mir::report::logging; namespace ml = mir::logging; namespace mrli = mir::report::legacy_input; namespace { char const* const component = "android-input"; class LegacyInputReport; std::mutex mutex; std::shared_ptr the_legacy_input_report; class LegacyInputReport { public: LegacyInputReport(std::shared_ptr const& logger) : logger(logger) { } void log(int prio, char const* buffer) { switch (prio) { case ANDROID_LOG_UNKNOWN: case ANDROID_LOG_DEFAULT: case ANDROID_LOG_VERBOSE: case ANDROID_LOG_DEBUG: logger->log(ml::Severity::debug, buffer, component); break; case ANDROID_LOG_INFO: logger->log(ml::Severity::informational, buffer, component); break; case ANDROID_LOG_WARN: logger->log(ml::Severity::warning, buffer, component); break; case ANDROID_LOG_ERROR: logger->log(ml::Severity::error, buffer, component); }; } private: std::shared_ptr const logger; }; void my_write_to_log(int prio, char const* buffer) { std::unique_lock lock(mutex); the_legacy_input_report->log(prio, buffer); } } void mrli::initialize(std::shared_ptr const& logger) { std::unique_lock lock(mutex); ::the_legacy_input_report = std::make_shared(logger); mir::write_to_log = my_write_to_log; } mrl::InputReport::InputReport(const std::shared_ptr& logger) : logger(logger) { } const char* mrl::InputReport::component() { static const char* s = "input"; return s; } namespace { #define PRINT_EV_ENUM(value, name) if (value == name) { return # name; } std::string print_evdev_type(int type) { std::stringstream out; PRINT_EV_ENUM(type, EV_SYN); PRINT_EV_ENUM(type, EV_KEY); PRINT_EV_ENUM(type, EV_REL); PRINT_EV_ENUM(type, EV_ABS); PRINT_EV_ENUM(type, EV_MSC); PRINT_EV_ENUM(type, EV_SW); PRINT_EV_ENUM(type, EV_LED); PRINT_EV_ENUM(type, EV_SND); PRINT_EV_ENUM(type, EV_REP); PRINT_EV_ENUM(type, EV_FF); PRINT_EV_ENUM(type, EV_PWR); PRINT_EV_ENUM(type, EV_FF); PRINT_EV_ENUM(type, EV_MAX); PRINT_EV_ENUM(type, EV_CNT); return std::to_string(type); } // Currently we only support pretty printing for ABS_ events as ABS_MT is the hardest protocol to debug std::string print_evdev_code(int type, int code) { if (type == EV_ABS) { PRINT_EV_ENUM(code, ABS_X); PRINT_EV_ENUM(code, ABS_Y); PRINT_EV_ENUM(code, ABS_Z); PRINT_EV_ENUM(code, ABS_RX); PRINT_EV_ENUM(code, ABS_RY); PRINT_EV_ENUM(code, ABS_RZ); PRINT_EV_ENUM(code, ABS_THROTTLE); PRINT_EV_ENUM(code, ABS_RUDDER); PRINT_EV_ENUM(code, ABS_WHEEL); PRINT_EV_ENUM(code, ABS_GAS); PRINT_EV_ENUM(code, ABS_BRAKE); PRINT_EV_ENUM(code, ABS_HAT0X); PRINT_EV_ENUM(code, ABS_HAT0Y); PRINT_EV_ENUM(code, ABS_HAT1X); PRINT_EV_ENUM(code, ABS_HAT1Y); PRINT_EV_ENUM(code, ABS_HAT2X); PRINT_EV_ENUM(code, ABS_HAT2Y); PRINT_EV_ENUM(code, ABS_HAT3X); PRINT_EV_ENUM(code, ABS_HAT3Y); PRINT_EV_ENUM(code, ABS_PRESSURE); PRINT_EV_ENUM(code, ABS_DISTANCE); PRINT_EV_ENUM(code, ABS_TILT_X); PRINT_EV_ENUM(code, ABS_TILT_Y); PRINT_EV_ENUM(code, ABS_TOOL_WIDTH); PRINT_EV_ENUM(code, ABS_VOLUME); PRINT_EV_ENUM(code, ABS_MISC); PRINT_EV_ENUM(code, ABS_MT_SLOT); PRINT_EV_ENUM(code, ABS_MT_TOUCH_MAJOR); PRINT_EV_ENUM(code, ABS_MT_TOUCH_MINOR); PRINT_EV_ENUM(code, ABS_MT_WIDTH_MAJOR); PRINT_EV_ENUM(code, ABS_MT_WIDTH_MINOR); PRINT_EV_ENUM(code, ABS_MT_ORIENTATION); PRINT_EV_ENUM(code, ABS_MT_POSITION_X); PRINT_EV_ENUM(code, ABS_MT_POSITION_Y); PRINT_EV_ENUM(code, ABS_MT_TOOL_TYPE); PRINT_EV_ENUM(code, ABS_MT_BLOB_ID); PRINT_EV_ENUM(code, ABS_MT_TRACKING_ID); PRINT_EV_ENUM(code, ABS_MT_PRESSURE); PRINT_EV_ENUM(code, ABS_MT_DISTANCE); // input.h from android doesn't have these even though modern kernels do. // PRINT_EV_ENUM(code, ABS_MT_TOOL_X); // PRINT_EV_ENUM(code, ABS_MT_TOOL_Y); } return std::to_string(code); } } void mrl::InputReport::received_event_from_kernel(int64_t when, int type, int code, int value) { std::stringstream ss; ss << "Received event" << " time=" << ml::input_timestamp(std::chrono::nanoseconds(when)) << " type=" << print_evdev_type(type) << " code=" << print_evdev_code(type, code) << " value=" << value; logger->log(ml::Severity::informational, ss.str(), component()); } void mrl::InputReport::published_key_event(int dest_fd, uint32_t seq_id, int64_t event_time) { std::stringstream ss; ss << "Published key event" << " seq_id=" << seq_id << " time=" << ml::input_timestamp(std::chrono::nanoseconds(event_time)) << " dest_fd=" << dest_fd; logger->log(ml::Severity::informational, ss.str(), component()); } void mrl::InputReport::published_motion_event(int dest_fd, uint32_t seq_id, int64_t event_time) { std::stringstream ss; ss << "Published motion event" << " seq_id=" << seq_id << " time=" << ml::input_timestamp(std::chrono::nanoseconds(event_time)) << " dest_fd=" << dest_fd; logger->log(ml::Severity::informational, ss.str(), component()); } void mrl::InputReport::opened_input_device(char const* device_name, char const* input_platform) { std::stringstream ss; ss << "Input device opened " << " name=" << device_name << " platform=" << input_platform; logger->log(ml::Severity::informational, ss.str(), component()); } void mrl::InputReport::failed_to_open_input_device(char const* device_name, char const* input_platform) { std::stringstream ss; ss << "Failure opening input device " << " name=" << device_name << " platform=" << input_platform; logger->log(ml::Severity::informational, ss.str(), component()); } ./src/server/report/logging/message_processor_report.cpp0000644000015600001650000001161512676616125023775 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "message_processor_report.h" #include "mir/logging/logger.h" #include #include namespace ml = mir::logging; namespace mrl = mir::report::logging; namespace { char const* const component = "frontend::MessageProcessor"; } mrl::MessageProcessorReport::MessageProcessorReport( std::shared_ptr const& log, std::shared_ptr const& clock) : log(log), clock(clock) { } mrl::MessageProcessorReport::~MessageProcessorReport() noexcept(true) { if (!mediators.empty()) { std::ostringstream out; { out << "Calls outstanding on exit:\n"; std::lock_guard lock(mutex); for (auto const& med : mediators) { for (auto const & invocation: med.second.current_invocations) { out << "mediator=" << med.first << ": " << invocation.second.method << "()"; if (!invocation.second.exception.empty()) out << " ERROR=" << invocation.second.exception; out << '\n'; } } } log->log(ml::Severity::informational, out.str(), component); } } void mrl::MessageProcessorReport::received_invocation(void const* mediator, int id, std::string const& method) { std::ostringstream out; out << "mediator=" << mediator << ", method=" << method << "()"; log->log(ml::Severity::debug, out.str(), component); std::lock_guard lock(mutex); auto& invocations = mediators[mediator].current_invocations; auto& invocation = invocations[id]; invocation.start = clock->now(); invocation.method = method; } void mrl::MessageProcessorReport::completed_invocation(void const* mediator, int id, bool result) { auto const end = clock->now(); std::ostringstream out; { std::lock_guard lock(mutex); auto const pm = mediators.find(mediator); if (pm != mediators.end()) { auto& invocations = pm->second.current_invocations; auto const pi = invocations.find(id); if (pi != invocations.end()) { out << "mediator=" << mediator << ": " << pi->second.method << "(), elapsed=" << std::chrono::duration_cast(end - pi->second.start).count() << "µs"; if (!pi->second.exception.empty()) out << " ERROR=" << pi->second.exception; if (!result) out << " (disconnecting)"; } invocations.erase(pi); if (invocations.empty()) mediators.erase(mediator); } } log->log(ml::Severity::informational, out.str(), component); } void mrl::MessageProcessorReport::unknown_method(void const* mediator, int id, std::string const& method) { std::ostringstream out; out << "mediator=" << mediator << ", id=" << id << ", UNKNOWN method=\"" << method << "\""; log->log(ml::Severity::warning, out.str(), component); std::lock_guard lock(mutex); auto const pm = mediators.find(mediator); if (pm != mediators.end()) mediators.erase(mediator); } void mrl::MessageProcessorReport::exception_handled(void const* mediator, int id, std::exception const& error) { std::lock_guard lock(mutex); auto const pm = mediators.find(mediator); if (pm != mediators.end()) { auto& invocations = pm->second.current_invocations; auto const pi = invocations.find(id); if (pi != invocations.end()) { pi->second.exception = boost::diagnostic_information(error); } } } void mrl::MessageProcessorReport::exception_handled(void const* mediator, std::exception const& error) { std::ostringstream out; out << "mediator=" << mediator << ", ERROR: " << boost::diagnostic_information(error); log->log(ml::Severity::informational, out.str(), component); std::lock_guard lock(mutex); auto const pm = mediators.find(mediator); if (pm != mediators.end()) mediators.erase(mediator); } ./src/server/report/logging/logging_report_factory.cpp0000644000015600001650000000553712676616125023435 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "../logging_report_factory.h" #include "compositor_report.h" #include "connector_report.h" #include "display_report.h" #include "message_processor_report.h" #include "scene_report.h" #include "session_mediator_report.h" #include "shell_report.h" #include "input_report.h" #include "mir/logging/shared_library_prober_report.h" #include "mir/default_server_configuration.h" namespace mr = mir::report; mr::LoggingReportFactory::LoggingReportFactory(std::shared_ptr const& logger, std::shared_ptr const& clock) : logger(logger), clock(clock) { } std::shared_ptr mr::LoggingReportFactory::create_compositor_report() { return std::make_shared(logger, clock); } std::shared_ptr mr::LoggingReportFactory::create_display_report() { return std::make_shared(logger, clock); } std::shared_ptr mr::LoggingReportFactory::create_scene_report() { return std::make_shared(logger); } std::shared_ptr mr::LoggingReportFactory::create_connector_report() { return std::make_shared(logger); } std::shared_ptr mr::LoggingReportFactory::create_session_mediator_report() { return std::make_shared(logger); } std::shared_ptr mr::LoggingReportFactory::create_message_processor_report() { return std::make_shared(logger, clock); } std::shared_ptr mr::LoggingReportFactory::create_input_report() { return std::make_shared(logger); } std::shared_ptr mr::LoggingReportFactory::create_shared_library_prober_report() { return std::make_shared(logger); } std::shared_ptr mir::report::LoggingReportFactory::create_shell_report() { return std::make_shared(logger); } ./src/server/report/logging/display_report.cpp0000644000015600001650000001300112676616125021706 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "display_report.h" #include "mir/logging/logger.h" #include #include #include namespace ml=mir::logging; namespace mrl=mir::report::logging; mrl::DisplayReport::DisplayReport( std::shared_ptr const& logger, std::shared_ptr const& clock) : logger(logger), clock(clock), last_report(clock->now()) { } mrl::DisplayReport::~DisplayReport() { } const char* mrl::DisplayReport::component() { static const char* s = "graphics"; return s; } void mrl::DisplayReport::report_successful_setup_of_native_resources() { logger->log(ml::Severity::informational, "Successfully setup native resources.", component()); } void mrl::DisplayReport::report_successful_egl_make_current_on_construction() { logger->log(ml::Severity::informational, "Successfully made egl context current on construction.", component()); } void mrl::DisplayReport::report_successful_egl_buffer_swap_on_construction() { logger->log(ml::Severity::informational, "Successfully performed egl buffer swap on construction.", component()); } void mrl::DisplayReport::report_successful_drm_mode_set_crtc_on_construction() { logger->log(ml::Severity::informational, "Successfully performed drm mode setup on construction.", component()); } void mrl::DisplayReport::report_successful_display_construction() { logger->log(ml::Severity::informational, "Successfully finished construction.", component()); } void mrl::DisplayReport::report_drm_master_failure(int error) { std::stringstream ss; ss << "Failed to change ownership of DRM master (error: " << strerror(error) << ")."; if (error == EPERM || error == EACCES) ss << " Try running Mir with root privileges."; logger->log(ml::Severity::warning, ss.str(), component()); } void mrl::DisplayReport::report_vt_switch_away_failure() { logger->log(ml::Severity::warning, "Failed to switch away from Mir VT.", component()); } void mrl::DisplayReport::report_vt_switch_back_failure() { logger->log(ml::Severity::warning, "Failed to switch back to Mir VT.", component()); } void mrl::DisplayReport::report_egl_configuration(EGLDisplay disp, EGLConfig config) { #define STRMACRO(X) {#X, X} struct {std::string name; EGLint val;} egl_string_mapping [] = { STRMACRO(EGL_BUFFER_SIZE), STRMACRO(EGL_ALPHA_SIZE), STRMACRO(EGL_BLUE_SIZE), STRMACRO(EGL_GREEN_SIZE), STRMACRO(EGL_RED_SIZE), STRMACRO(EGL_DEPTH_SIZE), STRMACRO(EGL_STENCIL_SIZE), STRMACRO(EGL_CONFIG_CAVEAT), STRMACRO(EGL_CONFIG_ID), STRMACRO(EGL_LEVEL), STRMACRO(EGL_MAX_PBUFFER_HEIGHT), STRMACRO(EGL_MAX_PBUFFER_PIXELS), STRMACRO(EGL_MAX_PBUFFER_WIDTH), STRMACRO(EGL_NATIVE_RENDERABLE), STRMACRO(EGL_NATIVE_VISUAL_ID), STRMACRO(EGL_NATIVE_VISUAL_TYPE), STRMACRO(EGL_SAMPLES), STRMACRO(EGL_SAMPLE_BUFFERS), STRMACRO(EGL_SURFACE_TYPE), STRMACRO(EGL_TRANSPARENT_TYPE), STRMACRO(EGL_TRANSPARENT_BLUE_VALUE), STRMACRO(EGL_TRANSPARENT_GREEN_VALUE), STRMACRO(EGL_TRANSPARENT_RED_VALUE), STRMACRO(EGL_BIND_TO_TEXTURE_RGB), STRMACRO(EGL_BIND_TO_TEXTURE_RGBA), STRMACRO(EGL_MIN_SWAP_INTERVAL), STRMACRO(EGL_MAX_SWAP_INTERVAL), STRMACRO(EGL_LUMINANCE_SIZE), STRMACRO(EGL_ALPHA_MASK_SIZE), STRMACRO(EGL_COLOR_BUFFER_TYPE), STRMACRO(EGL_RENDERABLE_TYPE), STRMACRO(EGL_MATCH_NATIVE_PIXMAP), STRMACRO(EGL_CONFORMANT), STRMACRO(EGL_SLOW_CONFIG), STRMACRO(EGL_NON_CONFORMANT_CONFIG), STRMACRO(EGL_TRANSPARENT_RGB), STRMACRO(EGL_RGB_BUFFER), STRMACRO(EGL_LUMINANCE_BUFFER), STRMACRO(EGL_FRAMEBUFFER_TARGET_ANDROID) }; #undef STRMACRO logger->log(ml::Severity::informational, "Display EGL Configuration:", component()); for( auto &i : egl_string_mapping) { EGLint value; eglGetConfigAttrib(disp, config, i.val, &value); logger->log(ml::Severity::informational, " [" + i.name + "] : " + std::to_string(value), component()); } } void mrl::DisplayReport::report_vsync(unsigned int display_id) { using namespace std::chrono; seconds const static report_interval{1}; std::unique_lock lk(vsync_event_mutex); auto now = clock->now(); event_map[display_id]++; if (now > last_report + report_interval) { for(auto const& event : event_map) logger->log(ml::Severity::informational, std::to_string(event.second) + " vsync events on [" + std::to_string(event.first) + "] over " + std::to_string(duration_cast(now - last_report).count()) + "ms", component()); event_map.clear(); last_report = now; } } ./src/server/report/logging/display_configuration_report.cpp0000644000015600001650000001063412676616125024646 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "display_configuration_report.h" #include "mir/graphics/display_configuration.h" #include "mir/logging/logger.h" #include namespace mg = mir::graphics; namespace ml = mir::logging; namespace mrl= mir::report::logging; namespace { auto const component = MIR_LOG_COMPONENT_FALLBACK; auto const severity = ml::Severity::informational; } mrl::DisplayConfigurationReport::DisplayConfigurationReport(std::shared_ptr const& logger) : logger{logger} { } mrl::DisplayConfigurationReport::~DisplayConfigurationReport() { } void mrl::DisplayConfigurationReport::initial_configuration(mg::DisplayConfiguration const& configuration) { logger->log(component, severity, "Initial display configuration:"); log_configuration(configuration); } void mrl::DisplayConfigurationReport::new_configuration(mg::DisplayConfiguration const& configuration) { logger->log(component, severity, "New display configuration:"); log_configuration(configuration); } void mrl::DisplayConfigurationReport::log_configuration(mg::DisplayConfiguration const& configuration) const { configuration.for_each_output([this](mg::DisplayConfigurationOutput const& out) { static const char* const type_str[] = {"Unknown", "VGA", "DVI-I", "DVI-D", "DVI-A", "Composite", "S-Video", "LVDS", "Component", "9-pin-DIN", "DisplayPort", "HDMI-A", "HDMI-B", "TV", "eDP"}; auto type = type_str[static_cast(out.type)]; int out_id = out.id.as_value(); int card_id = out.card_id.as_value(); const char prefix[] = " "; if (out.connected) { int width_mm = out.physical_size_mm.width.as_int(); int height_mm = out.physical_size_mm.height.as_int(); float inches = sqrtf(width_mm * width_mm + height_mm * height_mm) / 25.4; int indent = 0; logger->log(component, severity, "%s%d.%d: %n%s %.1f\" %dx%dmm", prefix, card_id, out_id, &indent, type, inches, width_mm, height_mm); if (out.used) { if (out.current_mode_index < out.modes.size()) { auto const& mode = out.modes[out.current_mode_index]; logger->log(component, severity, "%*cCurrent mode %dx%d %.2fHz", indent, ' ', mode.size.width.as_int(), mode.size.height.as_int(), mode.vrefresh_hz); } if (out.preferred_mode_index < out.modes.size()) { auto const& mode = out.modes[out.preferred_mode_index]; logger->log(component, severity, "%*cPreferred mode %dx%d %.2fHz", indent, ' ', mode.size.width.as_int(), mode.size.height.as_int(), mode.vrefresh_hz); } logger->log(component, severity, "%*cLogical position %+d%+d", indent, ' ', out.top_left.x.as_int(), out.top_left.y.as_int()); } else { logger->log(component, severity, "%*cDisabled", indent, ' '); } } else { logger->log(component, severity, "%s%d.%d: unused %s", prefix, card_id, out_id, type); } }); } ./src/server/report/logging/display_report.h0000644000015600001650000000456212676616125021367 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_LOGGING_DISPLAY_REPORTER_H_ #define MIR_REPORT_LOGGING_DISPLAY_REPORTER_H_ #include "mir/graphics/display_report.h" #include "mir/time/clock.h" #include #include #include namespace mir { namespace logging { class Logger; } namespace report { namespace logging { class DisplayReport : public graphics::DisplayReport { public: static const char* component(); DisplayReport( std::shared_ptr const& logger, std::shared_ptr const& clock); virtual ~DisplayReport(); virtual void report_successful_setup_of_native_resources() override; virtual void report_successful_egl_make_current_on_construction() override; virtual void report_successful_egl_buffer_swap_on_construction() override; virtual void report_successful_drm_mode_set_crtc_on_construction() override; virtual void report_successful_display_construction() override; virtual void report_vsync(unsigned int display_id) override; virtual void report_drm_master_failure(int error) override; virtual void report_vt_switch_away_failure() override; virtual void report_vt_switch_back_failure() override; virtual void report_egl_configuration(EGLDisplay disp, EGLConfig cfg) override; protected: DisplayReport(DisplayReport const&) = delete; DisplayReport& operator=(DisplayReport const&) = delete; private: std::shared_ptr const logger; std::shared_ptr const clock; std::mutex vsync_event_mutex; mir::time::Timestamp last_report; std::unordered_map event_map; }; } } } #endif /* MIR_REPORT_LOGGING_DISPLAY_REPORTER_H_ */ ./src/server/report/logging/shell_report.h0000644000015600001650000000502212676616125021021 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_NULL_SHELL_REPORT_H #define MIR_REPORT_NULL_SHELL_REPORT_H #include "mir/shell/shell_report.h" namespace mir { namespace logging { class Logger; class ShellReport : public shell::ShellReport { public: ShellReport(std::shared_ptr const& log); void opened_session(scene::Session const& /*session*/) override; void closing_session(scene::Session const& /*session*/) override; void created_surface( scene::Session const& /*session*/, frontend::SurfaceId /*surface_id*/) override; void update_surface( scene::Session const& /*session*/, scene::Surface const& /*surface*/, shell::SurfaceSpecification const& /*modifications*/) override; void update_surface( scene::Session const& /*session*/, scene::Surface const& /*surface*/, MirSurfaceAttrib /*attrib*/, int /*value*/) override; void destroying_surface( scene::Session const& /*session*/, frontend::SurfaceId /*surface*/) override; void started_prompt_session( scene::PromptSession const& /*prompt_session*/, scene::Session const& /*session*/) override; void added_prompt_provider( scene::PromptSession const& /*prompt_session*/, scene::Session const& /*session*/) override; void stopping_prompt_session( scene::PromptSession const& /*prompt_session*/) override; void adding_display(geometry::Rectangle const& /*area*/) override; void removing_display(geometry::Rectangle const& /*area*/) override; void input_focus_set_to( scene::Session const* /*focus_session*/, scene::Surface const* /*focus_surface*/) override; void surfaces_raised(shell::SurfaceSet const& /*surfaces*/) override; private: std::shared_ptr const log; }; } } #endif //MIR_REPORT_NULL_SHELL_REPORT_H ./src/server/report/logging/scene_report.cpp0000644000015600001650000000611312676616125021344 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "scene_report.h" #include "mir/logging/logger.h" #include namespace ml = mir::logging; namespace mrl = mir::report::logging; namespace { char const* const component = "scene"; } mrl::SceneReport::SceneReport(std::shared_ptr const& logger) : logger(logger) { } void mrl::SceneReport::surface_created(BasicSurfaceId id, std::string const& name) { std::lock_guard lock(mutex); surfaces[id] = name; std::stringstream ss; ss << "surface_created(" << id << " [\"" << name << "\"])"; logger->log(ml::Severity::informational, ss.str(), component); } void mrl::SceneReport::surface_added(BasicSurfaceId id, std::string const& name) { std::lock_guard lock(mutex); auto const i = surfaces.find(id); std::stringstream ss; ss << "surface_added(" << id << " [\"" << name << "\"])"; if (i == surfaces.end()) { ss << " - ERROR not reported to surface_created()"; } else if (name != i->second) { ss << " - WARNING name changed from " << i->second; } ss << " - INFO surface count=" << surfaces.size(); logger->log(ml::Severity::informational, ss.str(), component); } void mrl::SceneReport::surface_removed(BasicSurfaceId id, std::string const& name) { std::lock_guard lock(mutex); auto const i = surfaces.find(id); std::stringstream ss; ss << "surface_removed(" << id << " [\"" << name << "\"])"; if (i == surfaces.end()) { ss << " - ERROR not reported to surface_created()"; } else if (name != i->second) { ss << " - WARNING name changed from " << i->second; } ss << " - INFO surface count=" << surfaces.size(); logger->log(ml::Severity::informational, ss.str(), component); } void mrl::SceneReport::surface_deleted(BasicSurfaceId id, std::string const& name) { std::lock_guard lock(mutex); auto const i = surfaces.find(id); std::stringstream ss; ss << "surface_deleted(" << id << " [\"" << name << "\"])"; if (i == surfaces.end()) { ss << " - ERROR not reported to surface_created()"; } else if (name != i->second) { ss << " - WARNING name changed from " << i->second; } if (i != surfaces.end()) surfaces.erase(i); ss << " - INFO surface count=" << surfaces.size() << std::endl; logger->log(ml::Severity::informational, ss.str(), component); } ./src/server/report/logging/session_mediator_report.h0000644000015600001650000000555412676616125023273 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_LOGGING_SESSION_MEDIATOR_REPORT_H_ #define MIR_REPORT_LOGGING_SESSION_MEDIATOR_REPORT_H_ #include "mir/frontend/session_mediator_report.h" #include namespace mir { namespace logging { class Logger; } namespace report { namespace logging { class SessionMediatorReport : public frontend::SessionMediatorReport { public: SessionMediatorReport(std::shared_ptr const& log); virtual void session_connect_called(std::string const& app_name) override; virtual void session_create_surface_called(std::string const& app_name) override; virtual void session_next_buffer_called(std::string const& app_name) override; virtual void session_exchange_buffer_called(std::string const& app_name) override; virtual void session_submit_buffer_called(std::string const& app_name) override; virtual void session_allocate_buffers_called(std::string const& app_name) override; virtual void session_release_buffers_called(std::string const& app_name) override; virtual void session_release_surface_called(std::string const& app_name) override; virtual void session_disconnect_called(std::string const& app_name) override; virtual void session_configure_surface_called(std::string const& app_name) override; virtual void session_configure_surface_cursor_called(std::string const& app_name) override; virtual void session_configure_display_called(std::string const& app_name) override; virtual void session_set_base_display_configuration_called(std::string const& app_name) override; virtual void session_start_prompt_session_called(std::string const& app_name, pid_t application_process) override; virtual void session_stop_prompt_session_called(std::string const& app_name) override; void session_create_buffer_stream_called(std::string const& app_name) override; void session_release_buffer_stream_called(std::string const& app_name) override; virtual void session_error( std::string const& app_name, char const* method, std::string const& what) override; private: std::shared_ptr const log; }; } } } #endif /* MIR_REPORT_LOGGING_SESSION_MEDIATOR_REPORT_H_ */ ./src/server/report/logging/compositor_report.h0000644000015600001650000000533012676616125022112 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_REPORT_LOGGING_COMPOSITOR_REPORT_H_ #define MIR_REPORT_LOGGING_COMPOSITOR_REPORT_H_ #include "mir/compositor/compositor_report.h" #include "mir/time/clock.h" #include #include #include #include namespace mir { namespace logging { class Logger; } namespace report { namespace logging { class CompositorReport : public mir::compositor::CompositorReport { public: CompositorReport(std::shared_ptr const& logger, std::shared_ptr const& clock); void added_display(int width, int height, int x, int y, SubCompositorId id) override; void began_frame(SubCompositorId id) override; void renderables_in_frame(SubCompositorId id, graphics::RenderableList const& renderables) override; void rendered_frame(SubCompositorId id) override; void finished_frame(SubCompositorId id) override; void started() override; void stopped() override; void scheduled() override; private: std::shared_ptr const logger; std::shared_ptr const clock; typedef time::Timestamp TimePoint; TimePoint now() const; struct Instance { TimePoint start_of_frame; TimePoint end_of_frame; TimePoint total_time_sum; TimePoint render_time_sum; TimePoint latency_sum; long nframes = 0; long nbypassed = 0; bool bypassed = true; bool prev_bypassed = false; TimePoint last_reported_total_time_sum; TimePoint last_reported_render_time_sum; TimePoint last_reported_latency_sum; long last_reported_nframes = 0; long last_reported_bypassed = 0; void log(mir::logging::Logger& logger, SubCompositorId id); }; std::mutex mutex; // Protects the following... std::unordered_map instance; TimePoint last_scheduled; TimePoint last_report; }; } // namespace logging } // namespace report } // namespace mir #endif // MIR_REPORT_LOGGING_COMPOSITOR_REPORT_H_ ./src/server/report/logging/shell_report.cpp0000644000015600001650000001016212676616125021355 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "shell_report.h" #include "mir/logging/logger.h" #include "mir/scene/session.h" #include "mir/scene/surface.h" #include namespace { char const* const component = "shell::Shell"; } namespace mrl = mir::logging; mrl::ShellReport::ShellReport(std::shared_ptr const& log) : log(log) { } void mrl::ShellReport::opened_session(scene::Session const& session) { log->log(Severity::informational, "session \"" + session.name() + "\" opened", component); } void mrl::ShellReport::closing_session(scene::Session const& session) { log->log(Severity::informational, "session \"" + session.name() + "\" closing", component); } void mrl::ShellReport::created_surface( scene::Session const& session, frontend::SurfaceId surface_id) { auto const surface = session.surface(surface_id); std::ostringstream out; out << "session \"" << session.name() << "\" created surface: \"" << surface->name() << "\" @"; out << surface->input_bounds(); log->log(Severity::informational, out.str(), component); } void mrl::ShellReport::update_surface( scene::Session const& session, scene::Surface const& surface, shell::SurfaceSpecification const& /*modifications*/) { std::ostringstream out; out << "session \"" << session.name() << "\" update surface: \"" << surface.name() << "\" @"; out << surface.input_bounds(); log->log(Severity::informational, out.str(), component); } void mrl::ShellReport::update_surface( scene::Session const& session, scene::Surface const& surface, MirSurfaceAttrib /*attrib*/, int /*value*/) { std::ostringstream out; out << "session \"" << session.name() << "\" update surface: \"" << surface.name() << "\" @"; out << surface.input_bounds(); log->log(Severity::informational, out.str(), component); } void mrl::ShellReport::destroying_surface( scene::Session const& session, frontend::SurfaceId surface_id) { log->log(Severity::informational, "session \"" + session.name() + "\" destroying surface: \"" + session.surface(surface_id)->name() + "\"", component); } void mrl::ShellReport::started_prompt_session( scene::PromptSession const& /*prompt_session*/, scene::Session const& /*session*/) { } void mrl::ShellReport::added_prompt_provider( scene::PromptSession const& /*prompt_session*/, scene::Session const& /*session*/) { } void mrl::ShellReport::stopping_prompt_session( scene::PromptSession const& /*prompt_session*/) { } void mrl::ShellReport::adding_display(geometry::Rectangle const& area) { log->log(Severity::informational, "Adding display area: " + boost::lexical_cast(area), component); } void mrl::ShellReport::removing_display(geometry::Rectangle const& area) { log->log(Severity::informational, "Removing display area: " + boost::lexical_cast(area), component); } void mrl::ShellReport::input_focus_set_to( scene::Session const* focus_session, scene::Surface const* focus_surface) { auto const session = focus_session ? focus_session->name() : "(none)"; auto const surface = focus_surface ? focus_surface->name() : "(none)"; log->log(Severity::informational, "Input focus = session: \"" + session + "\" surface: \"" + surface + "\"", component); } void mrl::ShellReport::surfaces_raised(shell::SurfaceSet const& surfaces) { log->log(Severity::informational, "Raising " + boost::lexical_cast(surfaces.size()) + " surfaces", component); } ./src/server/report/logging/connector_report.cpp0000644000015600001650000000501312676616125022237 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "connector_report.h" #include "mir/logging/logger.h" #include #include #include namespace ml = mir::logging; namespace mrl = mir::report::logging; namespace { char const* const component = "frontend::Connector"; } mrl::ConnectorReport::ConnectorReport(std::shared_ptr const& log) : logger(log) { } void mrl::ConnectorReport::thread_start() { std::stringstream ss; ss << "thread (" << std::this_thread::get_id() << ") started."; logger->log(ml::Severity::informational, ss.str(), component); } void mrl::ConnectorReport::thread_end() { std::stringstream ss; ss << "thread (" << std::this_thread::get_id() << ") ended."; logger->log(ml::Severity::informational, ss.str(), component); } void mrl::ConnectorReport::creating_session_for(int socket_handle) { std::stringstream ss; ss << "thread (" << std::this_thread::get_id() << ") Creating session for socket " << socket_handle; logger->log(ml::Severity::informational, ss.str(), component); } void mrl::ConnectorReport::creating_socket_pair(int server_handle, int client_handle) { std::stringstream ss; ss << "thread (" << std::this_thread::get_id() << ") Creating socket pair (server=" << server_handle << ", client=" << client_handle << ")."; logger->log(ml::Severity::informational, ss.str(), component); } void mrl::ConnectorReport::listening_on(std::string const& endpoint) { std::stringstream ss; ss << "Listening on endpoint: " << endpoint; logger->log(ml::Severity::informational, ss.str(), component); } void mrl::ConnectorReport::error(std::exception const& error) { std::stringstream ss; ss << "thread (" << std::this_thread::get_id() << ") Error: " << boost::diagnostic_information(error); logger->log(ml::Severity::warning, ss.str(), component); } ./src/server/report/logging/message_processor_report.h0000644000015600001650000000434512676616125023444 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_LOGGING_MESSAGE_PROCESSOR_REPORT_H_ #define MIR_REPORT_LOGGING_MESSAGE_PROCESSOR_REPORT_H_ #include "mir/frontend/message_processor_report.h" #include "mir/time/clock.h" #include #include #include namespace mir { namespace logging { class Logger; } namespace report { namespace logging { namespace detail { struct InvocationDetails { time::Timestamp start; std::string method; std::string exception; }; typedef std::unordered_map Invocations; struct MediatorDetails { Invocations current_invocations; }; typedef std::unordered_map Mediators; } class MessageProcessorReport : public mir::frontend::MessageProcessorReport { public: MessageProcessorReport( std::shared_ptr const& log, std::shared_ptr const& clock); void received_invocation(void const* mediator, int id, std::string const& method); void completed_invocation(void const* mediator, int id, bool result); void unknown_method(void const* mediator, int id, std::string const& method); void exception_handled(void const* mediator, int id, std::exception const& error); void exception_handled(void const* mediator, std::exception const& error); ~MessageProcessorReport() noexcept(true); private: std::shared_ptr const log; std::shared_ptr const clock; std::mutex mutex; detail::Mediators mediators; }; } } } #endif /* MIR_REPORT_LOGGING_MESSAGE_PROCESSOR_REPORT_H_ */ ./src/server/report/logging/connector_report.h0000644000015600001650000000276412676616125021716 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_LOGGING_CONNECTOR_REPORT_H_ #define MIR_REPORT_LOGGING_CONNECTOR_REPORT_H_ #include "mir/frontend/connector_report.h" #include namespace mir { namespace logging { class Logger; } namespace report { namespace logging { class ConnectorReport : public frontend::ConnectorReport { public: ConnectorReport(std::shared_ptr const& log); void thread_start() override; void thread_end() override; void creating_session_for(int socket_handle) override; void creating_socket_pair(int server_handle, int client_handle) override; void listening_on(std::string const& endpoint) override; void error(std::exception const& error) override; private: std::shared_ptr const logger; }; } } } #endif /* MIR_REPORT_LOGGING_CONNECTOR_REPORT_H_ */ ./src/server/report/logging/scene_report.h0000644000015600001650000000302212676616125021005 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_LOGGING_SCENE_REPORT_H_ #define MIR_REPORT_LOGGING_SCENE_REPORT_H_ #include "mir/scene/scene_report.h" #include #include #include namespace mir { namespace logging { class Logger; } namespace report { namespace logging { class SceneReport : public scene::SceneReport { public: SceneReport(std::shared_ptr const& log); void surface_created(BasicSurfaceId id, std::string const& name); void surface_added(BasicSurfaceId id, std::string const& name); void surface_removed(BasicSurfaceId id, std::string const& name); void surface_deleted(BasicSurfaceId id, std::string const& name); private: std::shared_ptr const logger; std::mutex mutex; std::map surfaces; }; } } } #endif /* MIR_REPORT_LOGGING_SCENE_REPORT_H_ */ ./src/server/report/logging/session_mediator_report.cpp0000644000015600001650000001150012676616125023612 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "session_mediator_report.h" #include "mir/logging/logger.h" namespace { char const* const component = "frontend::SessionMediator"; } namespace ml = mir::logging; namespace mrl = mir::report::logging; mrl::SessionMediatorReport::SessionMediatorReport(std::shared_ptr const& log) : log(log) { } void mrl::SessionMediatorReport::session_connect_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_connect(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_create_surface_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_create_surface(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_next_buffer_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_next_buffer_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_exchange_buffer_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_exchange_buffer_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_submit_buffer_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_submit_buffer_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_allocate_buffers_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_allocate_buffers_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_release_buffers_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_release_buffers_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_release_surface_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_release_surface_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_disconnect_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_disconnect_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_configure_surface_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_configure_surface_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_configure_surface_cursor_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_configure_surface_cursor_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_configure_display_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_configure_display_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_set_base_display_configuration_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_set_base_display_configuration_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_start_prompt_session_called(std::string const& app_name, pid_t application_process) { log->log(ml::Severity::informational, "session_start_prompt_session_called(\"" + app_name + ", " + std::to_string(application_process) + ")", component); } void mrl::SessionMediatorReport::session_stop_prompt_session_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_stop_prompt_session_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_create_buffer_stream_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_create_buffer_stream_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_release_buffer_stream_called(std::string const& app_name) { log->log(ml::Severity::informational, "session_release_buffer_stream_called(\"" + app_name + "\")", component); } void mrl::SessionMediatorReport::session_error( std::string const& app_name, char const* method, std::string const& what) { log->log(ml::Severity::error, std::string(method) + " - session_error(\"" + app_name + "\"):\n" + what, component); } ./src/server/report/logging/CMakeLists.txt0000644000015600001650000000072212676616125020710 0ustar jenkinsjenkinsinclude_directories(${MIR_3RD_PARTY_INCLUDE_DIRECTORIES}) include_directories(${MIR_ANDROID_INCLUDE_DIRECTORIES}) set( LOGGING_SOURCES connector_report.cpp session_mediator_report.cpp message_processor_report.cpp display_report.cpp input_report.cpp compositor_report.cpp scene_report.cpp shell_report.cpp shell_report.h logging_report_factory.cpp display_configuration_report.cpp ) add_library( mirlogging OBJECT ${LOGGING_SOURCES} ) ./src/server/report/logging/compositor_report.cpp0000644000015600001650000001322712676616125022451 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #include "compositor_report.h" #include "mir/logging/logger.h" using namespace mir::time; namespace ml = mir::logging; namespace mrl = mir::report::logging; namespace { const char * const component = "compositor"; const auto min_report_interval = std::chrono::seconds(1); } mrl::CompositorReport::CompositorReport( std::shared_ptr const& logger, std::shared_ptr const& clock) : logger(logger), clock(clock), last_report(now()) { } mrl::CompositorReport::TimePoint mrl::CompositorReport::now() const { return clock->now(); } void mrl::CompositorReport::added_display(int width, int height, int x, int y, SubCompositorId id) { char msg[128]; snprintf(msg, sizeof msg, "Added display %p: %dx%d %+d%+d", id, width, height, x, y); logger->log(ml::Severity::informational, msg, component); } void mrl::CompositorReport::began_frame(SubCompositorId id) { std::lock_guard lock(mutex); auto& inst = instance[id]; auto t = now(); inst.start_of_frame = t; inst.latency_sum += t - last_scheduled; inst.bypassed = true; } void mrl::CompositorReport::renderables_in_frame(SubCompositorId, mir::graphics::RenderableList const&) { } void mrl::CompositorReport::rendered_frame(SubCompositorId id) { std::lock_guard lock(mutex); auto& inst = instance[id]; inst.render_time_sum += now() - inst.start_of_frame; inst.bypassed = false; } void mrl::CompositorReport::Instance::log(ml::Logger& logger, SubCompositorId id) { // The first report is a valid sample, but don't log anything because // we need at least two samples for valid deltas. if (last_reported_total_time_sum > TimePoint()) { long long dt = std::chrono::duration_cast( total_time_sum - last_reported_total_time_sum ).count(); auto dn = nframes - last_reported_nframes; long long dr = std::chrono::duration_cast( render_time_sum - last_reported_render_time_sum ).count(); long long dl = std::chrono::duration_cast( latency_sum - last_reported_latency_sum ).count(); long bypass_percent = dn ? (nbypassed - last_reported_bypassed) * 100L / dn : 0; // Keep everything premultiplied by 1000 to guarantee accuracy // and avoid floating point. long frames_per_1000sec = dt ? dn * 1000000000LL / dt : 0; long avg_render_time_usec = dn ? dr / dn : 0; long avg_latency_usec = dn ? dl / dn : 0; long dt_msec = dt / 1000L; char msg[128]; snprintf(msg, sizeof msg, "Display %p averaged %ld.%03ld FPS, " "%ld.%03ld ms/frame, " "latency %ld.%03ld ms, " "%ld frames over %ld.%03ld sec, " "%ld%% bypassed", id, frames_per_1000sec / 1000, frames_per_1000sec % 1000, avg_render_time_usec / 1000, avg_render_time_usec % 1000, avg_latency_usec / 1000, avg_latency_usec % 1000, dn, dt_msec / 1000, dt_msec % 1000, bypass_percent ); logger.log(ml::Severity::informational, msg, component); } last_reported_total_time_sum = total_time_sum; last_reported_render_time_sum = render_time_sum; last_reported_latency_sum = latency_sum; last_reported_nframes = nframes; last_reported_bypassed = nbypassed; } void mrl::CompositorReport::finished_frame(SubCompositorId id) { std::lock_guard lock(mutex); auto& inst = instance[id]; auto t = now(); inst.total_time_sum += t - inst.end_of_frame; inst.end_of_frame = t; inst.nframes++; if (inst.bypassed) ++inst.nbypassed; /* * The exact reporting interval doesn't matter because we count everything * as a Reimann sum. Results will simply be the average over the interval. */ if ((t - last_report) >= min_report_interval) { last_report = t; for (auto& i : instance) i.second.log(*logger, i.first); } if (inst.bypassed != inst.prev_bypassed || inst.nframes == 1) { char msg[128]; snprintf(msg, sizeof msg, "Display %p bypass %s", id, inst.bypassed ? "ON" : "OFF"); logger->log(ml::Severity::informational, msg, component); } inst.prev_bypassed = inst.bypassed; } void mrl::CompositorReport::started() { logger->log(ml::Severity::informational, "Started", component); } void mrl::CompositorReport::stopped() { logger->log(ml::Severity::informational, "Stopped", component); std::lock_guard lock(mutex); instance.clear(); } void mrl::CompositorReport::scheduled() { std::lock_guard lock(mutex); last_scheduled = now(); } ./src/server/report/logging/input_report.h0000644000015600001650000000327012676616125021054 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_REPORT_LOGGING_INPUT_REPORT_H_ #define MIR_REPORT_LOGGING_INPUT_REPORT_H_ #include "mir/input/input_report.h" #include namespace mir { namespace logging { class Logger; } namespace report { namespace logging { class InputReport : public input::InputReport { public: InputReport(std::shared_ptr const& logger); virtual ~InputReport() noexcept(true) = default; void received_event_from_kernel(int64_t when, int type, int code, int value) override; void published_key_event(int dest_fd, uint32_t seq_id, int64_t event_time) override; void published_motion_event(int dest_fd, uint32_t seq_id, int64_t event_time) override; void opened_input_device(char const* device_name, char const* input_platform) override; void failed_to_open_input_device(char const* device_name, char const* input_platform) override; private: char const* component(); std::shared_ptr const logger; }; } } } #endif /* MIR_REPORT_LOGGING_INPUT_REPORT_H_ */ ./src/server/report/logging/display_configuration_report.h0000644000015600001650000000302412676616125024306 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_DISPLAYCONFIGURATIONREPORT_H #define MIR_DISPLAYCONFIGURATIONREPORT_H #include "mir/graphics/display_configuration_report.h" #include namespace mir { namespace logging { class Logger; } namespace report { namespace logging { class DisplayConfigurationReport : public mir::graphics::DisplayConfigurationReport { public: DisplayConfigurationReport(std::shared_ptr const& logger); ~DisplayConfigurationReport(); virtual void initial_configuration(graphics::DisplayConfiguration const& configuration) override; virtual void new_configuration(graphics::DisplayConfiguration const& configuration) override; private: void log_configuration(graphics::DisplayConfiguration const& configuration) const; std::shared_ptr const logger; }; } } } #endif //MIR_DISPLAYCONFIGURATIONREPORT_H ./src/server/report/CMakeLists.txt0000644000015600001650000000022712676616125017262 0ustar jenkinsjenkinsadd_subdirectory(logging) add_subdirectory(lttng) add_subdirectory(null) add_library( mirreport OBJECT default_server_configuration.cpp ) ./src/server/report/null_report_factory.h0000644000015600001650000000432212676616125020767 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_NULL_REPORT_FACTORY_H_ #define MIR_REPORT_NULL_REPORT_FACTORY_H_ #include "report_factory.h" namespace mir { namespace report { class NullReportFactory : public mir::report::ReportFactory { public: std::shared_ptr create_compositor_report() override; std::shared_ptr create_display_report() override; std::shared_ptr create_scene_report() override; std::shared_ptr create_connector_report() override; std::shared_ptr create_session_mediator_report() override; std::shared_ptr create_message_processor_report() override; std::shared_ptr create_input_report() override; std::shared_ptr create_shared_library_prober_report() override; std::shared_ptr create_shell_report() override; }; std::shared_ptr null_compositor_report(); std::shared_ptr null_display_report(); std::shared_ptr null_scene_report(); std::shared_ptr null_connector_report(); std::shared_ptr null_session_mediator_report(); std::shared_ptr null_message_processor_report(); std::shared_ptr null_input_report(); std::shared_ptr null_shared_library_prober_report(); } } #endif ./src/server/report/default_server_configuration.cpp0000644000015600001650000001201312676616125023163 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #include "mir/default_server_configuration.h" #include "mir/options/configuration.h" #include "lttng_report_factory.h" #include "logging_report_factory.h" #include "null_report_factory.h" #include "mir/abnormal_exit.h" #include "logging/display_configuration_report.h" namespace mg = mir::graphics; namespace mf = mir::frontend; namespace mc = mir::compositor; namespace mi = mir::input; namespace ms = mir::scene; std::unique_ptr mir::DefaultServerConfiguration::report_factory(char const* report_opt) { auto opt = the_options()->get(report_opt); if (opt == options::log_opt_value) { return std::make_unique(the_logger(), the_clock()); } else if (opt == options::lttng_opt_value) { return std::make_unique(); } else if (opt == options::off_opt_value) { return std::make_unique(); } else { throw AbnormalExit(std::string("Invalid ") + report_opt + " option: " + opt + " (valid options are: \"" + options::off_opt_value + "\" and \"" + options::log_opt_value + "\" and \"" + options::lttng_opt_value + "\")"); } } auto mir::DefaultServerConfiguration::the_compositor_report() -> std::shared_ptr { return compositor_report( [this]()->std::shared_ptr { return report_factory(options::compositor_report_opt)->create_compositor_report(); }); } auto mir::DefaultServerConfiguration::the_connector_report() -> std::shared_ptr { return connector_report( [this]()->std::shared_ptr { return report_factory(options::connector_report_opt)->create_connector_report(); }); } auto mir::DefaultServerConfiguration::the_session_mediator_report() -> std::shared_ptr { return session_mediator_report( [this]()->std::shared_ptr { return report_factory(options::session_mediator_report_opt)->create_session_mediator_report(); }); } auto mir::DefaultServerConfiguration::the_message_processor_report() -> std::shared_ptr { return message_processor_report( [this]()->std::shared_ptr { return report_factory(options::msg_processor_report_opt)->create_message_processor_report(); }); } auto mir::DefaultServerConfiguration::the_display_report() -> std::shared_ptr { return display_report( [this]()->std::shared_ptr { return report_factory(options::display_report_opt)->create_display_report(); }); } auto mir::DefaultServerConfiguration::the_display_configuration_report() -> std::shared_ptr { return display_configuration_report([this]() -> std::shared_ptr { return std::make_shared(the_logger()); }); } auto mir::DefaultServerConfiguration::the_input_report() -> std::shared_ptr { return input_report( [this]()->std::shared_ptr { return report_factory(options::input_report_opt)->create_input_report(); }); } auto mir::DefaultServerConfiguration::the_scene_report() -> std::shared_ptr { return scene_report( [this]()->std::shared_ptr { return report_factory(options::scene_report_opt)->create_scene_report(); }); } auto mir::DefaultServerConfiguration::the_shared_library_prober_report() -> std::shared_ptr { return shared_library_prober_report( [this]()->std::shared_ptr { return report_factory(options::shared_library_prober_report_opt)->create_shared_library_prober_report(); }); } auto mir::DefaultServerConfiguration::the_shell_report() -> std::shared_ptr { return shell_report( [this]()->std::shared_ptr { return report_factory(options::shell_report_opt)->create_shell_report(); }); } ./src/server/report_exception.cpp0000644000015600001650000000244112676616125017304 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/report_exception.h" #include "mir/abnormal_exit.h" #include #include void mir::report_exception(std::ostream& out) { try { throw; } catch (mir::AbnormalExit const& error) { out << error.what() << std::endl; } catch (std::exception const& error) { out << "ERROR: " << boost::diagnostic_information(error) << std::endl; } catch (...) { out << "ERROR: unrecognised exception. (This is weird!)" << std::endl; } } void mir::report_exception() { report_exception(std::cerr); } ./src/server/default_server_configuration.cpp0000644000015600001650000001377212676616157021672 0ustar jenkinsjenkins/* * Copyright © 2012-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #include "mir/default_server_configuration.h" #include "mir/fatal.h" #include "mir/options/default_configuration.h" #include "mir/abnormal_exit.h" #include "mir/glib_main_loop.h" #include "mir/default_server_status_listener.h" #include "mir/emergency_cleanup.h" #include "mir/default_configuration.h" #include "mir/cookie/authority.h" #include "mir/logging/dumb_console_logger.h" #include "mir/options/program_option.h" #include "mir/frontend/session_credentials.h" #include "mir/frontend/session_authorizer.h" #include "mir/graphics/cursor.h" #include "mir/scene/null_session_listener.h" #include "mir/graphics/display.h" #include "mir/input/cursor_listener.h" #include "mir/input/vt_filter.h" #include "mir/input/input_manager.h" #include "mir/time/steady_clock.h" #include "mir/geometry/rectangles.h" #include "mir/default_configuration.h" #include "mir/scene/null_prompt_session_listener.h" #include "default_emergency_cleanup.h" #include namespace mc = mir::compositor; namespace geom = mir::geometry; namespace mf = mir::frontend; namespace mg = mir::graphics; namespace ml = mir::logging; namespace mo = mir::options; namespace ms = mir::scene; namespace msh = mir::shell; namespace mi = mir::input; namespace { unsigned const secret_size{64}; } mir::DefaultServerConfiguration::DefaultServerConfiguration(int argc, char const* argv[]) : DefaultServerConfiguration(std::make_shared(argc, argv)) { } mir::DefaultServerConfiguration::DefaultServerConfiguration(std::shared_ptr const& configuration_options) : configuration_options(configuration_options), default_filter(std::make_shared()) { } auto mir::DefaultServerConfiguration::the_options() const ->std::shared_ptr { return configuration_options->the_options(); } std::string mir::DefaultServerConfiguration::the_socket_file() const { auto socket_file = the_options()->get(options::server_socket_opt); return socket_file; } std::shared_ptr mir::DefaultServerConfiguration::the_session_listener() { return session_listener( [this] { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_prompt_session_listener() { return prompt_session_listener( [this] { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_session_authorizer() { struct DefaultSessionAuthorizer : public mf::SessionAuthorizer { bool connection_is_allowed(mf::SessionCredentials const& /* creds */) override { return true; } bool configure_display_is_allowed(mf::SessionCredentials const& /* creds */) override { return true; } bool set_base_display_configuration_is_allowed(mf::SessionCredentials const& /* creds */) override { return true; } bool screencast_is_allowed(mf::SessionCredentials const& /* creds */) override { return true; } bool prompt_session_is_allowed(mf::SessionCredentials const& /* creds */) override { return true; } }; return session_authorizer( [&]() { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_clock() { return clock( []() { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_main_loop() { return main_loop( [this]() -> std::shared_ptr { return std::make_shared(the_clock()); }); } std::shared_ptr mir::DefaultServerConfiguration::the_server_action_queue() { return the_main_loop(); } std::shared_ptr mir::DefaultServerConfiguration::the_server_status_listener() { return server_status_listener( []() { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_emergency_cleanup() { return emergency_cleanup( []() { return std::make_shared(); }); } std::shared_ptr mir::DefaultServerConfiguration::the_cookie_authority() { return cookie_authority( []() { static_assert(secret_size >= mir::cookie::Authority::minimum_secret_size, "Secret size is smaller then the minimum secret size"); return mir::cookie::Authority::create(); }); } auto mir::DefaultServerConfiguration::the_fatal_error_strategy() -> void (*)(char const* reason, ...) { if (the_options()->is_set(options::fatal_abort_opt)) return &fatal_error_abort; else return fatal_error; } auto mir::DefaultServerConfiguration::the_logger() -> std::shared_ptr { return logger( [this]() -> std::shared_ptr { return std::make_shared(); }); } ./src/include/0000755000015600001650000000000012676616124013322 5ustar jenkinsjenkins./src/include/gl/0000755000015600001650000000000012676616124013724 5ustar jenkinsjenkins./src/include/gl/mir/0000755000015600001650000000000012676616124014513 5ustar jenkinsjenkins./src/include/gl/mir/gl/0000755000015600001650000000000012676616126015117 5ustar jenkinsjenkins./src/include/gl/mir/gl/program.h0000644000015600001650000000316512676616125016743 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_GL_PROGRAM_H_ #define MIR_GL_PROGRAM_H_ #include namespace mir { namespace gl { class Shader { public: Shader(GLchar const* shader_src, GLuint type); ~Shader(); operator GLuint() const; private: Shader(Shader const&) = delete; Shader& operator=(Shader const&) = delete; GLuint const shader; }; class Program { public: virtual ~Program() = default; virtual operator GLuint() const = 0; protected: Program() = default; private: Program(Program const&) = delete; Program& operator=(Program const&) = delete; }; class SimpleProgram : public Program { public: SimpleProgram( GLchar const* vertex_shader_src, GLchar const* fragment_shader_src); ~SimpleProgram(); operator GLuint() const override; private: Shader const vertex_shader; Shader const fragment_shader; GLuint const program; }; } } #endif /* MIR_GL_PROGRAM_H_ */ ./src/include/gl/mir/gl/default_program_factory.h0000644000015600001650000000267612676616125022204 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GL_DEFAULT_PROGRAM_FACTORY_H_ #define MIR_GL_DEFAULT_PROGRAM_FACTORY_H_ #include "program_factory.h" #include namespace mir { namespace gl { class DefaultProgramFactory : public ProgramFactory { public: std::unique_ptr create_gl_program(std::string const&, std::string const&) const override; std::unique_ptr create_texture_cache() const override; private: /* * We need to serialize renderer creation because some GL calls used * during renderer construction that create unique resource ids * (e.g. glCreateProgram) are not thread-safe when the threads are * have the same or shared EGL contexts. */ std::mutex mutable mutex; }; } } #endif /* MIR_GL_DEFAULT_PROGRAM_FACTORY_H_ */ ./src/include/gl/mir/gl/program_factory.h0000644000015600001650000000251512676616125020470 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_GL_PROGRAM_FACTORY_H_ #define MIR_GL_PROGRAM_FACTORY_H_ #include "program.h" #include "texture_cache.h" #include namespace mir { namespace gl { class ProgramFactory { public: virtual ~ProgramFactory() = default; virtual std::unique_ptr create_gl_program(std::string const&, std::string const&) const = 0; virtual std::unique_ptr create_texture_cache() const = 0; protected: ProgramFactory() = default; private: ProgramFactory(ProgramFactory const&) = delete; ProgramFactory& operator=(ProgramFactory const&) = delete; }; } } #endif /* MIR_GL_PROGRAM_FACTORY_H_ */ ./src/include/gl/mir/gl/primitive.h0000644000015600001650000000256312676616125017305 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt * Kevin DuBois */ #ifndef MIR_GL_PRIMITIVE_H_ #define MIR_GL_PRIMITIVE_H_ #include namespace mir { namespace gl { struct Vertex { GLfloat position[3]; GLfloat texcoord[2]; }; struct Primitive { enum {max_vertices = 4}; Primitive() : type(GL_TRIANGLE_FAN), nvertices(4) { // Default is a quad. Just need to assign vertices[] and tex_id. } GLenum type; // GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES etc GLuint tex_id; // GL texture ID (or 0 to represent the surface itself) int nvertices; Vertex vertices[max_vertices]; }; } } #endif /* MIR_GL_PRIMITIVE_H_ */ ./src/include/gl/mir/gl/tessellation_helpers.h0000644000015600001650000000212612676616125021520 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_GL_TESSELLATION_HELPERS_H_ #define MIR_GL_TESSELLATION_HELPERS_H_ #include "mir/gl/primitive.h" #include "mir/geometry/displacement.h" namespace mir { namespace graphics { class Renderable; } namespace gl { Primitive tessellate_renderable_into_rectangle( graphics::Renderable const& renderable, geometry::Displacement const& offset); } } #endif /* MIR_GL_TESSELLATION_HELPERS_H_ */ ./src/include/gl/mir/gl/texture.h0000644000015600001650000000207412676616125016772 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_GL_TEXTURE_H_ #define MIR_GL_TEXTURE_H_ #include namespace mir { namespace gl { class Texture { public: Texture(); ~Texture(); void bind() const; private: Texture(Texture const&) = delete; Texture& operator=(Texture const&) = delete; GLuint generate_id() const; GLuint const id; }; } } #endif /* MIR_GL_TEXTURE_H_ */ ./src/include/gl/mir/gl/texture_cache.h0000644000015600001650000000400012676616125020104 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt * Kevin DuBois */ #ifndef MIR_GL_TEXTURE_CACHE_H_ #define MIR_GL_TEXTURE_CACHE_H_ #include namespace mir { namespace graphics { class Renderable; } namespace gl { class Texture; class TextureCache { public: virtual ~TextureCache() = default; /** * Loads texture from the renderable. Must be called with a current GL * context. * \param [in] renderable * The Renderable that needs to be used as a texture * \returns * The texture that represents the renderable. */ virtual std::shared_ptr load(graphics::Renderable const&) = 0; /** * Mark all entries in the cache as out-of-date to ensure fresh textures * are loaded next time. This function _must_ be implemented in a way that * does not require a GL context, as it will typically be called without * one. */ virtual void invalidate() = 0; /** * Free textures that were not used (loaded) since the last * drop/invalidate. Must be called with a current GL context. */ virtual void drop_unused() = 0; protected: TextureCache() = default; private: TextureCache(TextureCache const&) = delete; TextureCache& operator=(TextureCache const&) = delete; }; } } #endif /* MIR_GL_TEXTURE_CACHE_H_ */ ./src/include/server/0000755000015600001650000000000012676616124014630 5ustar jenkinsjenkins./src/include/server/mir/0000755000015600001650000000000012676616160015417 5ustar jenkinsjenkins./src/include/server/mir/display_changer.h0000644000015600001650000000272512676616125020733 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_DISPLAY_CHANGER_H_ #define MIR_DISPLAY_CHANGER_H_ #include namespace mir { namespace graphics { class DisplayConfiguration; } class DisplayChanger { public: virtual ~DisplayChanger() = default; enum SystemStateHandling : bool { RetainSystemState, PauseResumeSystem }; virtual void configure_for_hardware_change( std::shared_ptr const& conf, SystemStateHandling pause_resume_system) = 0; virtual void pause_display_config_processing() = 0; virtual void resume_display_config_processing() = 0; protected: DisplayChanger() = default; DisplayChanger(DisplayChanger const&) = delete; DisplayChanger& operator=(DisplayChanger const&) = delete; }; } #endif /* MIR_DISPLAY_CHANGER_H_ */ ./src/include/server/mir/emergency_cleanup.h0000644000015600001650000000173212676616125021261 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_EMERGENCY_CLEANUP_H_ #define MIR_EMERGENCY_CLEANUP_H_ #include "mir/emergency_cleanup_registry.h" namespace mir { class EmergencyCleanup : public EmergencyCleanupRegistry { public: virtual void operator()() const = 0; }; } #endif /* MIR_EMERGENCY_CLEANUP_H_ */ ./src/include/server/mir/thread/0000755000015600001650000000000012676616125016667 5ustar jenkinsjenkins./src/include/server/mir/thread/basic_thread_pool.h0000644000015600001650000000314312676616125022502 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #ifndef MIR_THREAD_BASIC_THREAD_POOL_H_ #define MIR_THREAD_BASIC_THREAD_POOL_H_ #include #include #include #include #include namespace mir { namespace thread { class WorkerThread; class BasicThreadPool { public: BasicThreadPool(int min_threads); ~BasicThreadPool(); std::future run(std::function const& task); typedef void const* TaskId; std::future run(std::function const& task, TaskId id); void shrink(); private: BasicThreadPool(BasicThreadPool const&) = delete; BasicThreadPool& operator=(BasicThreadPool const&) = delete; std::future run(WorkerThread* t, std::function const& task, TaskId id); WorkerThread *find_thread_by(TaskId id); WorkerThread *find_idle_thread(); std::mutex mutex; int const min_threads; std::vector> threads; }; } } #endif ./src/include/server/mir/scene/0000755000015600001650000000000012676616160016514 5ustar jenkinsjenkins./src/include/server/mir/scene/null_prompt_session_listener.h0000644000015600001650000000271512676616125024716 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Nick Dedekind */ #ifndef MIR_SCENE_NULL_PROMPT_SESSION_LISTENER_H_ #define MIR_SCENE_NULL_PROMPT_SESSION_LISTENER_H_ #include "mir/scene/prompt_session_listener.h" namespace mir { namespace scene { class NullPromptSessionListener : public PromptSessionListener { public: void starting(std::shared_ptr const&) override {} void stopping(std::shared_ptr const&) override {} void suspending(std::shared_ptr const&) override {} void resuming(std::shared_ptr const&) override {} void prompt_provider_added(PromptSession const&, std::shared_ptr const&) override {} void prompt_provider_removed(PromptSession const&, std::shared_ptr const&) override {} }; } } #endif // MIR_SHELL_NULL_PROMPT_SESSION_LISTENER_H_ ./src/include/server/mir/scene/scene_report.h0000644000015600001650000000262312676616125021361 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SCENE_SCENE_REPORT_H_ #define MIR_SCENE_SCENE_REPORT_H_ #include namespace mir { namespace scene { class SceneReport { public: typedef void* BasicSurfaceId; virtual void surface_created(BasicSurfaceId id, std::string const& name) = 0; virtual void surface_added(BasicSurfaceId id, std::string const& name) = 0; virtual void surface_removed(BasicSurfaceId id, std::string const& name) = 0; virtual void surface_deleted(BasicSurfaceId id, std::string const& name) = 0; protected: SceneReport() = default; virtual ~SceneReport() = default; SceneReport(SceneReport const&) = delete; SceneReport& operator=(SceneReport const&) = delete; }; } } #endif /* MIR_SCENE_SCENE_REPORT_H_ */ ./src/include/server/mir/scene/null_observer.h0000644000015600001650000000302512676616125021547 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_SCENE_NULL_OBSERVER_H_ #define MIR_SCENE_NULL_OBSERVER_H_ #include "mir/scene/observer.h" namespace mir { namespace scene { class NullObserver : public Observer { public: NullObserver() = default; virtual ~NullObserver() = default; void surface_added(Surface* surface); void surface_removed(Surface* surface); void surfaces_reordered(); // Called at observer registration to notify of already existing surfaces. void surface_exists(Surface* surface); // Called when observer is unregistered, for example, to provide a place to // unregister SurfaceObservers which may have been added in surface_added/exists void end_observation(); protected: NullObserver(NullObserver const&) = delete; NullObserver& operator=(NullObserver const&) = delete; }; } } // namespace mir #endif // MIR_SCENE_NULL_OBSERVER_H_ ./src/include/server/mir/scene/surface_event_source.h0000644000015600001650000000400312676616125023074 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SCENE_SURFACE_EVENT_SOURCE_H_ #define MIR_SCENE_SURFACE_EVENT_SOURCE_H_ #include "mir/scene/null_surface_observer.h" #include "mir/frontend/surface_id.h" #include "mir/frontend/event_sink.h" #include namespace mir { namespace scene { class Surface; class OutputProperties; class OutputPropertiesCache; class SurfaceEventSource : public NullSurfaceObserver { public: SurfaceEventSource( frontend::SurfaceId id, Surface const& surface, OutputPropertiesCache const& outputs, std::shared_ptr const& event_sink); void attrib_changed(MirSurfaceAttrib attrib, int value) override; void resized_to(geometry::Size const& size) override; void moved_to(geometry::Point const& top_left) override; void orientation_set_to(MirOrientation orientation) override; void client_surface_close_requested() override; void keymap_changed(MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) override; private: frontend::SurfaceId const id; Surface const& surface; OutputPropertiesCache const& outputs; std::weak_ptr last_output; std::shared_ptr const event_sink; }; } } #endif // MIR_SCENE_SURFACE_EVENT_SOURCE_H_ ./src/include/server/mir/scene/surface_observers.h0000644000015600001650000000412412676616157022416 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_SCENE_SURFACE_OBSERVERS_H_ #define MIR_SCENE_SURFACE_OBSERVERS_H_ #include "mir/basic_observers.h" #include "mir/scene/surface_observer.h" namespace mir { namespace scene { class SurfaceObservers : public SurfaceObserver, BasicObservers { public: using BasicObservers::add; using BasicObservers::remove; using BasicObservers::for_each; void attrib_changed(MirSurfaceAttrib attrib, int value) override; void resized_to(geometry::Size const& size) override; void moved_to(geometry::Point const& top_left) override; void hidden_set_to(bool hide) override; void frame_posted(int frames_available, geometry::Size const& size) override; void alpha_set_to(float alpha) override; void orientation_set_to(MirOrientation orientation) override; void transformation_set_to(glm::mat4 const& t) override; void reception_mode_set_to(input::InputReceptionMode mode) override; void cursor_image_set_to(graphics::CursorImage const& image) override; void client_surface_close_requested() override; void keymap_changed(MirInputDeviceId id, std::string const& model, std::string const& layout, std::string const& variant, std::string const& options) override; void renamed(char const*) override; void cursor_image_removed() override; }; } } #endif /* MIR_SCENE_SURFACE_OBSERVERS_H_ */ ./src/include/server/mir/scene/legacy_scene_change_notification.h0000644000015600001650000000446412676616125025372 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_SCENE_SIMPLE_OBSERVER_H_ #define MIR_SCENE_SIMPLE_OBSERVER_H_ #include "mir/scene/observer.h" #include #include #include namespace mir { namespace geometry { class Rectangle; } namespace scene { class SurfaceObserver; // A simple implementation of surface observer which forwards all changes to a provided callback. // Also installs surface observers on each added surface which in turn forward each change to // said callback. class LegacySceneChangeNotification : public Observer { public: LegacySceneChangeNotification( std::function const& scene_notify_change, std::function const& buffer_notify_change); LegacySceneChangeNotification( std::function const& scene_notify_change, std::function const& damage_notify_change); ~LegacySceneChangeNotification(); void surface_added(Surface* surface) override; void surface_removed(Surface* surface) override; void surfaces_reordered() override; void scene_changed() override; void surface_exists(Surface* surface) override; void end_observation() override; private: std::function const scene_notify_change; std::function const buffer_notify_change; std::function const damage_notify_change; std::mutex surface_observers_guard; std::map> surface_observers; void add_surface_observer(Surface* surface); }; } } // namespace mir #endif // MIR_SCENE_SIMPLE_OBSERVER_H_ ./src/include/server/mir/glib_main_loop_sources.h0000644000015600001650000000656412676616125022321 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GLIB_MAIN_LOOP_SOURCES_H_ #define MIR_GLIB_MAIN_LOOP_SOURCES_H_ #include "mir/time/clock.h" #include "mir/thread_safe_list.h" #include "mir/fd.h" #include #include #include #include #include #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-register" #endif #include #if defined(__clang__) #pragma clang diagnostic pop #endif namespace mir { class LockableCallback; namespace detail { class GSourceHandle { public: GSourceHandle(); GSourceHandle(GSource* gsource, std::function const& terminate_dispatch); GSourceHandle(GSourceHandle&& other); GSourceHandle& operator=(GSourceHandle other); ~GSourceHandle(); void ensure_no_further_dispatch(); operator GSource*() const; private: GSource* gsource; std::function terminate_dispatch; }; void add_idle_gsource( GMainContext* main_context, int priority, std::function const& callback); void add_server_action_gsource( GMainContext* main_context, void const* owner, std::function const& action, std::function const& should_dispatch); GSourceHandle add_timer_gsource( GMainContext* main_context, std::shared_ptr const& clock, std::shared_ptr const& handler, std::function const& exception_handler, time::Timestamp target_time); class FdSources { public: FdSources(GMainContext* main_context); ~FdSources(); void add(int fd, void const* owner, std::function const& handler); void remove_all_owned_by(void const* owner); private: struct FdContext; struct FdSource; GMainContext* const main_context; std::mutex sources_mutex; std::vector> sources; }; class SignalSources { public: SignalSources(FdSources& fd_sources); ~SignalSources(); void add(std::vector const& sigs, std::function const& handler); private: class SourceRegistration; struct HandlerElement { operator bool() const { return !!handler; } std::vector sigs; std::function handler; }; void dispatch_pending_signal(); void ensure_signal_is_handled(int sig); int read_pending_signal(); void dispatch_signal(int sig); FdSources& fd_sources; mir::Fd signal_read_fd; mir::Fd signal_write_fd; mir::ThreadSafeList handlers; std::mutex handled_signals_mutex; std::unordered_map handled_signals; std::unique_ptr source_registration; }; } } #endif ./src/include/server/mir/shell/0000755000015600001650000000000012676616160016526 5ustar jenkinsjenkins./src/include/server/mir/shell/canonical_window_manager.h0000644000015600001650000001127212676616157023720 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SHELL_CANONICAL_WINDOW_MANAGER_H_ #define MIR_SHELL_CANONICAL_WINDOW_MANAGER_H_ #include "basic_window_manager.h" #include "mir/geometry/displacement.h" namespace mir { namespace shell { class DisplayLayout; // standard window management algorithm: // o Switch apps: tap or click on the corresponding tile // o Move window: Alt-leftmousebutton drag (three finger drag) // o Resize window: Alt-middle_button drag (two finger drag) // o Maximize/restore current window (to display size): Alt-F11 // o Maximize/restore current window (to display height): Shift-F11 // o Maximize/restore current window (to display width): Ctrl-F11 // o client requests to maximize, vertically maximize & restore class CanonicalWindowManagerPolicy: public WindowManagementPolicy { public: explicit CanonicalWindowManagerPolicy( WindowManagerTools* const tools, std::shared_ptr const& display_layout); void handle_session_info_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays) override; void handle_displays_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays) override; auto handle_place_new_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& request_parameters) -> scene::SurfaceCreationParameters override; void handle_new_surface(std::shared_ptr const& session, std::shared_ptr const& surface) override; void handle_modify_surface( std::shared_ptr const& session, std::shared_ptr const& surface, SurfaceSpecification const& modifications) override; void handle_delete_surface(std::shared_ptr const& session, std::weak_ptr const& surface) override; int handle_set_state(std::shared_ptr const& surface, MirSurfaceState value) override; bool handle_keyboard_event(MirKeyboardEvent const* event) override; bool handle_touch_event(MirTouchEvent const* event) override; bool handle_pointer_event(MirPointerEvent const* event) override; void handle_raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface) override; private: static const int modifier_mask = mir_input_event_modifier_alt | mir_input_event_modifier_shift | mir_input_event_modifier_sym | mir_input_event_modifier_ctrl | mir_input_event_modifier_meta; void toggle(MirSurfaceState state); void click(geometry::Point cursor); void resize(geometry::Point cursor); void drag(geometry::Point cursor); // "Mir and Unity: Surfaces, input, and displays (v0.3)" talks about active // *window*,but Mir really only understands surfaces void select_active_surface(std::shared_ptr const& surface); auto active_surface() const -> std::shared_ptr; bool resize(std::shared_ptr const& surface, geometry::Point cursor, geometry::Point old_cursor, geometry::Rectangle bounds); bool drag(std::shared_ptr surface, geometry::Point to, geometry::Point from, geometry::Rectangle bounds); void move_tree(std::shared_ptr const& root, geometry::Displacement movement) const; void apply_resize( std::shared_ptr const& surface, geometry::Point const& new_pos, geometry::Size const& new_size) const; WindowManagerTools* const tools; std::shared_ptr const display_layout; geometry::Rectangle display_area; geometry::Point old_cursor{}; std::weak_ptr active_surface_; using FullscreenSurfaces = std::set, std::owner_less>>; FullscreenSurfaces fullscreen_surfaces; }; using CanonicalWindowManager = WindowManagerConstructor; } } #endif /* MIR_SHELL_CANONICAL_WINDOW_MANAGER_H_ */ ./src/include/server/mir/shell/window_management_info.h0000644000015600001650000000605212676616157023426 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SHELL_WINDOW_MANAGEMENT_INFO_H #define MIR_SHELL_WINDOW_MANAGEMENT_INFO_H #include "mir/geometry/rectangles.h" #include "mir/optional_value.h" #include "mir/shell/surface_specification.h" #include namespace mir { namespace scene { class Session; class Surface; class SurfaceCreationParameters; } namespace shell { struct SurfaceInfo { SurfaceInfo( std::shared_ptr const& session, std::shared_ptr const& surface, scene::SurfaceCreationParameters const& params); bool can_be_active() const; bool can_morph_to(MirSurfaceType new_type) const; bool must_have_parent() const; bool must_not_have_parent() const; bool is_visible() const; static bool needs_titlebar(MirSurfaceType type); void constrain_resize( std::shared_ptr const& surface, geometry::Point& requested_pos, geometry::Size& requested_size, const bool left_resize, const bool top_resize, geometry::Rectangle const& bounds) const; MirSurfaceType type; MirSurfaceState state; geometry::Rectangle restore_rect; std::weak_ptr session; std::weak_ptr parent; std::vector > children; std::shared_ptr titlebar; frontend::SurfaceId titlebar_id; bool is_titlebar = false; geometry::Width min_width; geometry::Height min_height; geometry::Width max_width; geometry::Height max_height; mir::optional_value width_inc; mir::optional_value height_inc; mir::optional_value min_aspect; mir::optional_value max_aspect; mir::optional_value output_id; void init_titlebar(std::shared_ptr const& surface); void paint_titlebar(int intensity); private: struct StreamPainter; struct AllocatingPainter; struct SwappingPainter; std::shared_ptr stream_painter; }; struct SessionInfo { std::vector> surfaces; // This is only used by the TilingWindowManagerPolicy, // perhaps we need a more extensible mechanism. (std::experimental::any?) geometry::Rectangle tile; }; } } #endif //MIR_SHELL_WINDOW_MANAGEMENT_INFO_H ./src/include/server/mir/shell/basic_window_manager.h0000644000015600001650000002153412676616157023054 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_SHELL_BASIC_WINDOW_MANAGER_H_ #define MIR_SHELL_BASIC_WINDOW_MANAGER_H_ #include "mir/geometry/rectangles.h" #include "mir/shell/abstract_shell.h" #include "mir/shell/window_manager.h" #include "mir/shell/window_management_info.h" #include #include namespace mir { namespace shell { /// The interface through which the policy instructs the controller. /// These functions assume that the BasicWindowManager data structures can be accessed freely. /// I.e. should only be invoked by the policy handle_... methods (where any necessary locks are held). class WindowManagerTools { public: using SurfaceInfoMap = std::map, SurfaceInfo, std::owner_less>>; using SessionInfoMap = std::map, SessionInfo, std::owner_less>>; virtual auto find_session(std::function const& predicate) -> std::shared_ptr = 0; virtual auto info_for(std::weak_ptr const& session) const -> SessionInfo& = 0; virtual auto info_for(std::weak_ptr const& surface) const -> SurfaceInfo& = 0; virtual std::shared_ptr focused_session() const = 0; virtual std::shared_ptr focused_surface() const = 0; virtual void focus_next_session() = 0; virtual void set_focus_to( std::shared_ptr const& focus, std::shared_ptr const& surface) = 0; virtual auto surface_at(geometry::Point cursor) const -> std::shared_ptr = 0; virtual auto active_display() -> geometry::Rectangle const = 0; virtual void forget(std::weak_ptr const& surface) = 0; virtual void raise_tree(std::shared_ptr const& root) = 0; virtual ~WindowManagerTools() = default; WindowManagerTools() = default; WindowManagerTools(WindowManagerTools const&) = delete; WindowManagerTools& operator=(WindowManagerTools const&) = delete; }; class WindowManagementPolicy { public: using SessionInfoMap = typename WindowManagerTools::SessionInfoMap; using SurfaceInfoMap = typename WindowManagerTools::SurfaceInfoMap; virtual void handle_session_info_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays) = 0; virtual void handle_displays_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays) = 0; virtual auto handle_place_new_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& request_parameters) -> scene::SurfaceCreationParameters = 0; virtual void handle_new_surface(std::shared_ptr const& session, std::shared_ptr const& surface) = 0; virtual void handle_modify_surface( std::shared_ptr const& session, std::shared_ptr const& surface, shell::SurfaceSpecification const& modifications) = 0; virtual void handle_delete_surface(std::shared_ptr const& session, std::weak_ptr const& surface) = 0; virtual int handle_set_state(std::shared_ptr const& surface, MirSurfaceState value) = 0; virtual bool handle_keyboard_event(MirKeyboardEvent const* event) = 0; virtual bool handle_touch_event(MirTouchEvent const* event) = 0; virtual bool handle_pointer_event(MirPointerEvent const* event) = 0; virtual void handle_raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface) = 0; virtual ~WindowManagementPolicy() = default; WindowManagementPolicy() = default; WindowManagementPolicy(WindowManagementPolicy const&) = delete; WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete; }; /// A policy based window manager. /// This takes care of the management of any meta implementation held for the sessions and surfaces. class BasicWindowManager : public virtual shell::WindowManager, protected WindowManagerTools { protected: BasicWindowManager( shell::FocusController* focus_controller, std::unique_ptr policy); public: using typename WindowManagerTools::SurfaceInfoMap; using typename WindowManagerTools::SessionInfoMap; void add_session(std::shared_ptr const& session) override; void remove_session(std::shared_ptr const& session) override; auto add_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::function const& session, scene::SurfaceCreationParameters const& params)> const& build) -> frontend::SurfaceId override; void modify_surface( std::shared_ptr const& session, std::shared_ptr const& surface, shell::SurfaceSpecification const& modifications) override; void remove_surface( std::shared_ptr const& session, std::weak_ptr const& surface) override; void forget(std::weak_ptr const& surface) override; void add_display(geometry::Rectangle const& area) override; void remove_display(geometry::Rectangle const& area) override; bool handle_keyboard_event(MirKeyboardEvent const* event) override; bool handle_touch_event(MirTouchEvent const* event) override; bool handle_pointer_event(MirPointerEvent const* event) override; void handle_raise_surface( std::shared_ptr const& session, std::shared_ptr const& surface, uint64_t timestamp) override; int set_surface_attribute( std::shared_ptr const& /*session*/, std::shared_ptr const& surface, MirSurfaceAttrib attrib, int value) override; auto find_session(std::function const& predicate) -> std::shared_ptr override; auto info_for(std::weak_ptr const& session) const -> SessionInfo& override; auto info_for(std::weak_ptr const& surface) const -> SurfaceInfo& override; std::shared_ptr focused_session() const override; std::shared_ptr focused_surface() const override; void focus_next_session() override; void set_focus_to( std::shared_ptr const& focus, std::shared_ptr const& surface) override; auto surface_at(geometry::Point cursor) const -> std::shared_ptr override; auto active_display() -> geometry::Rectangle const override; void raise_tree(std::shared_ptr const& root) override; private: shell::FocusController* const focus_controller; std::unique_ptr const policy; std::mutex mutex; SessionInfoMap session_info; SurfaceInfoMap surface_info; geometry::Rectangles displays; geometry::Point cursor; uint64_t last_input_event_timestamp{0}; void update_event_timestamp(MirKeyboardEvent const* kev); void update_event_timestamp(MirPointerEvent const* pev); void update_event_timestamp(MirTouchEvent const* tev); }; /// A policy based window manager. This exists to initialize BasicWindowManager and /// the WMPolicy (in an awkward manner). /// TODO revisit this initialization sequence. template class WindowManagerConstructor : public BasicWindowManager { public: template WindowManagerConstructor( shell::FocusController* focus_controller, PolicyArgs&&... policy_args) : BasicWindowManager( focus_controller, build_policy(std::forward(policy_args)...)) { } private: template auto build_policy(PolicyArgs&&... policy_args) -> std::unique_ptr { return std::unique_ptr( new WMPolicy(this, std::forward(policy_args)...)); } }; } } #endif /* MIR_SHELL_BASIC_WINDOW_MANAGER_H_ */ ./src/include/server/mir/compositor/0000755000015600001650000000000012676616125017616 5ustar jenkinsjenkins./src/include/server/mir/compositor/frame_dropping_policy.h0000644000015600001650000000400712676616125024343 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_COMPOSITOR_FRAME_DROPPING_POLICY_H_ #define MIR_COMPOSITOR_FRAME_DROPPING_POLICY_H_ #include namespace mir { namespace compositor { /** * \brief Policy to determine when to drop a frame from a client * * The FrameDroppingPolicy objects are constructed from a * \ref FrameDroppingPolicyFactory * * The frame dropping mechanism is provided as the * \a drop_frames argument of \ref FrameDroppingPolicyFactory::create_policy * * The policy may decide to drop a frame any time that there is an outstanding * swap - namely, when there have been more calls to \ref swap_now_blocking * than to \ref swap_unblocked */ class FrameDroppingPolicy { public: virtual ~FrameDroppingPolicy() = default; FrameDroppingPolicy(FrameDroppingPolicy const&) = delete; FrameDroppingPolicy& operator=(FrameDroppingPolicy const&) = delete; /** * \brief Notify that a swap is now blocking */ virtual void swap_now_blocking() = 0; /** * \brief Notify that previous swap is no longer blocking */ virtual void swap_unblocked() = 0; protected: /** * \note FrameDroppingPolicies should not be constructed directly; * use a \ref FrameDroppingPolicyFactory */ FrameDroppingPolicy() = default; }; } } #endif // MIR_COMPOSITOR_FRAME_DROPPING_POLICY_H_ ./src/include/server/mir/compositor/frame_dropping_policy_factory.h0000644000015600001650000000533612676616125026100 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers * Alberto Aguirre */ #ifndef MIR_COMPOSITOR_FRAME_DROPPING_POLICY_FACTORY_H_ #define MIR_COMPOSITOR_FRAME_DROPPING_POLICY_FACTORY_H_ #include namespace mir { class LockableCallback; namespace compositor { class FrameDroppingPolicy; /** * \brief Creator of FrameDroppingPolicies * * The FrameDroppingPolicyFactory is how you go from a means of dropping frames - * the \a drop_frames parameter of \ref create_policy - * to a \ref FrameDroppingPolicy */ class FrameDroppingPolicyFactory { public: FrameDroppingPolicyFactory() = default; virtual ~FrameDroppingPolicyFactory() = default; FrameDroppingPolicyFactory(FrameDroppingPolicyFactory const&) = delete; FrameDroppingPolicyFactory& operator=(FrameDroppingPolicyFactory const&) = delete; /** * \brief Create a FrameDroppingPolicy that will call \a drop_frame when it * decides to drop a frame * * A LockableCallback allows the user to preserve lock ordering * in situations where FrameDroppingPolicy methods need to be called under * external lock and the callback implementation needs to run code protected * by the same lock. A FrameDroppingPolicy implementation may have internal * locks of its own, which maybe acquired during callback dispatching; * to preserve lock ordering LockableCallback::lock will be invoked during * callback dispatch before any internal locks are acquired. * * \param drop_frame Function to call when a frame needs to be dropped * \param lock Function called within the callback dispatcher context * before any internal locks are acquired. * \param unlock Function called within the callback dispatcher context * after any internal locks are released. */ virtual std::unique_ptr create_policy( std::shared_ptr const& drop_frame) const = 0; }; } } #endif // MIR_COMPOSITOR_FRAME_DROPPING_POLICY_FACTORY_H_ ./src/include/server/mir/compositor/renderer.h0000644000015600001650000000256612676616125021606 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #ifndef MIR_COMPOSITOR_RENDERER_H_ #define MIR_COMPOSITOR_RENDERER_H_ #include "mir/geometry/rectangle.h" #include "mir/graphics/renderable.h" namespace mir { namespace graphics { class Buffer; } namespace compositor { class Renderer { public: virtual ~Renderer() = default; virtual void set_viewport(geometry::Rectangle const& rect) = 0; virtual void set_rotation(float degrees) = 0; virtual void render(graphics::RenderableList const&) const = 0; virtual void suspend() = 0; // called when render() is skipped protected: Renderer() = default; Renderer(const Renderer&) = delete; Renderer& operator=(const Renderer&) = delete; }; } } #endif // MIR_COMPOSITOR_RENDERER_H_ ./src/include/server/mir/compositor/decoration.h0000644000015600001650000000220312676616125022113 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_COMPOSITOR_DECORATION_H_ #define MIR_COMPOSITOR_DECORATION_H_ #include namespace mir { namespace compositor { struct Decoration { enum class Type {none, surface} type; std::string name; Decoration() : type{Type::none} {} Decoration(Type t, std::string const& n) : type{t}, name{n} {} operator bool() const { return type != Type::none; } }; } } // namespace mir::compositor #endif // MIR_COMPOSITOR_DECORATION_H_ ./src/include/server/mir/compositor/renderer_factory.h0000644000015600001650000000243612676616125023331 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_COMPOSITOR_RENDERER_FACTORY_H_ #define MIR_COMPOSITOR_RENDERER_FACTORY_H_ #include namespace mir { namespace graphics { struct DisplayBuffer; } namespace compositor { class Renderer; class RendererFactory { public: virtual ~RendererFactory() = default; virtual std::unique_ptr create_renderer_for(graphics::DisplayBuffer& display_buffer) = 0; protected: RendererFactory() = default; RendererFactory(RendererFactory const&) = delete; RendererFactory& operator=(RendererFactory const&) = delete; }; } } #endif /* MIR_COMPOSITOR_RENDERER_FACTORY_H_ */ ./src/include/server/mir/compositor/buffer_stream.h0000644000015600001650000000307512676616125022620 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Kevin DuBois */ #ifndef MIR_COMPOSITOR_BUFFER_STREAM_H_ #define MIR_COMPOSITOR_BUFFER_STREAM_H_ #include "mir/geometry/size.h" #include "mir/frontend/buffer_stream.h" #include "mir_toolkit/common.h" #include "mir/graphics/buffer_id.h" #include namespace mir { namespace graphics { class Buffer; } namespace compositor { class BufferStream : public frontend::BufferStream { public: virtual ~BufferStream() = default; virtual std::shared_ptr lock_compositor_buffer(void const* user_id) = 0; virtual geometry::Size stream_size() = 0; virtual void resize(geometry::Size const& size) = 0; virtual void force_requests_to_complete() = 0; virtual int buffers_ready_for_compositor(void const* user_id) const = 0; virtual void drop_old_buffers() = 0; virtual bool has_submitted_buffer() const = 0; }; } } #endif /* MIR_COMPOSITOR_BUFFER_STREAM_H_ */ ./src/include/server/mir/frontend/0000755000015600001650000000000012676616126017240 5ustar jenkinsjenkins./src/include/server/mir/frontend/display_changer.h0000644000015600001650000000274212676616125022551 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_FRONTEND_DISPLAY_CHANGER_H_ #define MIR_FRONTEND_DISPLAY_CHANGER_H_ #include #include namespace mir { namespace graphics { class DisplayConfiguration; } namespace frontend { class Session; class DisplayChanger { public: virtual ~DisplayChanger() = default; virtual std::shared_ptr base_configuration() = 0; virtual void configure(std::shared_ptr const&, std::shared_ptr const&) = 0; virtual void set_base_configuration(std::shared_ptr const&) = 0; protected: DisplayChanger() = default; DisplayChanger(DisplayChanger const&) = delete; DisplayChanger& operator=(DisplayChanger const&) = delete; }; } } #endif /* MIR_FRONTEND_DISPLAY_CHANGER_H_ */ ./src/include/server/mir/frontend/connection_context.h0000644000015600001650000000312412676616125023313 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_CONNECTION_CONTEXT_H_ #define MIR_FRONTEND_CONNECTION_CONTEXT_H_ #include #include namespace mir { namespace frontend { class Connector; class Session; class ConnectionContext { public: ConnectionContext(Connector const* connector) : ConnectionContext([](std::shared_ptr const&){}, connector) {} ConnectionContext( std::function const& session)> const connect_handler, Connector const* connector); int fd_for_new_client(std::function const& session)> const& connect_handler) const; void handle_client_connect(std::shared_ptr const& session) const { connect_handler(session); } private: std::function const& session)> const connect_handler; Connector const* const connector; }; } } #endif /* MIR_FRONTEND_CONNECTION_CONTEXT_H_ */ ./src/include/server/mir/frontend/screencast.h0000644000015600001650000000317212676616125021545 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_FRONTEND_SCREENCAST_H_ #define MIR_FRONTEND_SCREENCAST_H_ #include "mir/int_wrapper.h" #include "mir/graphics/display_configuration.h" #include namespace mir { namespace graphics { class Buffer; } namespace frontend { namespace detail { struct ScreencastSessionIdTag; } typedef IntWrapper ScreencastSessionId; class Screencast { public: virtual ~Screencast() = default; virtual ScreencastSessionId create_session( mir::geometry::Rectangle const& region, mir::geometry::Size const& size, MirPixelFormat pixel_format) = 0; virtual void destroy_session(ScreencastSessionId id) = 0; virtual std::shared_ptr capture(ScreencastSessionId id) = 0; protected: Screencast() = default; Screencast(Screencast const&) = delete; Screencast& operator=(Screencast const&) = delete; }; } } #endif /* MIR_FRONTEND_SCREENCAST_H_ */ ./src/include/server/mir/frontend/connector.h0000644000015600001650000000253212676616125021404 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #ifndef MIR_FRONTEND_CONNECTOR_H_ #define MIR_FRONTEND_CONNECTOR_H_ #include #include namespace mir { namespace frontend { class Session; /// Handle client process connections class Connector { public: virtual void start() = 0; virtual void stop() = 0; virtual int client_socket_fd() const = 0; virtual int client_socket_fd(std::function const& session)> const& connect_handler) const = 0; protected: Connector() = default; virtual ~Connector() = default; Connector(const Connector&) = delete; Connector& operator=(const Connector&) = delete; }; } } #endif // MIR_FRONTEND_CONNECTOR_H_ ./src/include/server/mir/frontend/fd_sets.h0000644000015600001650000000167612676616125021051 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_FRONTEND_FD_SETS_H_ #define MIR_FRONTEND_FD_SETS_H_ #include #include #include "mir/fd.h" namespace mir { namespace frontend { typedef std::vector> FdSets; } } // namespace mir #endif // MIR_FRONTEND_FD_SETS_H_ ./src/include/server/mir/frontend/message_processor_report.h0000644000015600001650000000322712676616125024532 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_MESSAGE_PROCESSOR_REPORT_H_ #define MIR_FRONTEND_MESSAGE_PROCESSOR_REPORT_H_ #include "mir_toolkit/event.h" #include namespace mir { namespace frontend { class MessageProcessorReport { public: MessageProcessorReport() {} virtual ~MessageProcessorReport() = default; virtual void received_invocation(void const* mediator, int id, std::string const& method) = 0; virtual void completed_invocation(void const* mediator, int id, bool result) = 0; virtual void unknown_method(void const* mediator, int id, std::string const& method) = 0; virtual void exception_handled(void const* mediator, int id, std::exception const& error) = 0; virtual void exception_handled(void const* mediator, std::exception const& error) = 0; private: MessageProcessorReport(MessageProcessorReport const&) = delete; MessageProcessorReport& operator=(MessageProcessorReport const&) = delete; }; } } #endif /* MIR_FRONTEND_MESSAGE_PROCESSOR_REPORT_H_ */ ./src/include/server/mir/frontend/connector_report.h0000644000015600001650000000270312676616125022777 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths #include namespace mir { namespace frontend { class ConnectorReport { public: virtual void thread_start() = 0; virtual void thread_end() = 0; virtual void creating_session_for(int socket_handle) = 0; virtual void creating_socket_pair(int server_handle, int client_handle) = 0; virtual void listening_on(std::string const& endpoint) = 0; virtual void error(std::exception const& error) = 0; protected: virtual ~ConnectorReport() = default; ConnectorReport() = default; ConnectorReport(const ConnectorReport&) = delete; ConnectorReport& operator=(const ConnectorReport&) = delete; }; } } #endif // MIR_FRONTEND_CONNECTOR_REPORT_H_ ./src/include/server/mir/frontend/connections.h0000644000015600001650000000336312676616125021737 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_CONNECTIONS_H_ #define MIR_FRONTEND_CONNECTIONS_H_ #include #include #include namespace mir { namespace frontend { namespace detail { template class Connections { public: Connections() {} ~Connections() { clear(); } void add(std::shared_ptr const& connection) { std::unique_lock lock(mutex); connections[connection->id()] = connection; } void remove(int id) { std::unique_lock lock(mutex); connections.erase(id); } bool includes(int id) const { std::unique_lock lock(mutex); return connections.find(id) != connections.end(); } void clear() { std::unique_lock lock(mutex); connections.clear(); } private: Connections(Connections const&) = delete; Connections& operator =(Connections const&) = delete; std::mutex mutex; std::map> connections; }; } } } #endif // MIR_FRONTEND_CONNECTIONS_H_ ./src/include/server/mir/frontend/shell.h0000644000015600001650000000626212676616125020525 0ustar jenkinsjenkins/* * Copyright © 2012-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Thomas Voss */ #ifndef MIR_FRONTEND_SHELL_H_ #define MIR_FRONTEND_SHELL_H_ #include "mir/frontend/surface_id.h" #include "mir_toolkit/common.h" #include #include #include namespace mir { namespace scene { struct SurfaceCreationParameters; struct PromptSessionCreationParameters; class Surface; } namespace shell { class SurfaceSpecification; } namespace frontend { class EventSink; class Session; class PromptSession; class Shell { public: virtual ~Shell() = default; virtual std::shared_ptr open_session( pid_t client_pid, std::string const& name, std::shared_ptr const& sink) = 0; virtual void close_session(std::shared_ptr const& session) = 0; virtual std::shared_ptr start_prompt_session_for(std::shared_ptr const& session, scene::PromptSessionCreationParameters const& params) = 0; virtual void add_prompt_provider_for(std::shared_ptr const& prompt_session, std::shared_ptr const& session) = 0; virtual void stop_prompt_session(std::shared_ptr const& prompt_session) = 0; virtual SurfaceId create_surface( std::shared_ptr const& session, scene::SurfaceCreationParameters const& params, std::shared_ptr const& sink) = 0; virtual void modify_surface(std::shared_ptr const& session, SurfaceId surface, shell::SurfaceSpecification const& modifications) = 0; virtual void destroy_surface(std::shared_ptr const& session, SurfaceId surface) = 0; virtual std::string persistent_id_for(std::shared_ptr const& session, SurfaceId surface) = 0; virtual std::shared_ptr surface_for_id(std::string const& serialised_id) = 0; virtual int set_surface_attribute( std::shared_ptr const& session, SurfaceId surface_id, MirSurfaceAttrib attrib, int value) = 0; virtual int get_surface_attribute( std::shared_ptr const& session, SurfaceId surface_id, MirSurfaceAttrib attrib) = 0; virtual void raise_surface( std::shared_ptr const& session, SurfaceId surface_id, uint64_t timestamp) = 0; protected: Shell() = default; Shell(const Shell&) = delete; Shell& operator=(const Shell&) = delete; }; } } #endif // MIR_FRONTEND_SHELL_H_ ./src/include/server/mir/frontend/client_buffers.h0000644000015600001650000000307312676616125022405 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Kevin DuBois */ #ifndef MIR_FRONTEND_CLIENT_BUFFERS_H_ #define MIR_FRONTEND_CLIENT_BUFFERS_H_ #include "mir/graphics/buffer_id.h" #include namespace mir { namespace graphics { class Buffer; class BufferProperties; } namespace frontend { class ClientBuffers { public: virtual graphics::BufferID add_buffer(graphics::BufferProperties const& properties) = 0; virtual void remove_buffer(graphics::BufferID id) = 0; virtual std::shared_ptr& operator[](graphics::BufferID) = 0; virtual void send_buffer(graphics::BufferID id) = 0; virtual void receive_buffer(graphics::BufferID id) = 0; virtual size_t client_owned_buffer_count() const = 0; ClientBuffers(ClientBuffers const&) = delete; ClientBuffers& operator=(ClientBuffers const&) = delete; virtual ~ClientBuffers() = default; ClientBuffers() = default; }; } } #endif /* MIR_FRONTEND_CLIENT_BUFFERS_H_ */ ./src/include/server/mir/frontend/message_processor.h0000644000015600001650000000332612676616125023137 0ustar jenkinsjenkins/* * Copyright © 2012, 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_MESSAGE_PROCESSOR_H_ #define MIR_FRONTEND_MESSAGE_PROCESSOR_H_ #include #include #include namespace mir { namespace protobuf { namespace wire { class Invocation; } } namespace frontend { namespace detail { class Invocation { public: Invocation(mir::protobuf::wire::Invocation const& invocation) : invocation(invocation) {} const ::std::string& method_name() const; const ::std::string& parameters() const; google::protobuf::uint32 id() const; private: mir::protobuf::wire::Invocation const& invocation; }; class MessageProcessor { public: virtual bool dispatch(Invocation const& invocation, std::vector const& side_channel_fds) = 0; virtual void client_pid(int pid) = 0; protected: MessageProcessor() = default; virtual ~MessageProcessor() = default; MessageProcessor(MessageProcessor const&) = delete; MessageProcessor& operator=(MessageProcessor const&) = delete; }; } } } #endif /* PROTOBUF_MESSAGE_PROCESSOR_H_ */ ./src/include/server/mir/frontend/event_sink.h0000644000015600001650000000316612676616125021563 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Daniel van Vugt */ #ifndef MIR_FRONTEND_EVENT_SINK_H_ #define MIR_FRONTEND_EVENT_SINK_H_ #include "mir_toolkit/event.h" #include "mir/frontend/buffer_sink.h" #include namespace mir { namespace input { class Device; } namespace graphics { class DisplayConfiguration; class Buffer; } namespace frontend { class EventSink : public BufferSink { public: virtual ~EventSink() = default; virtual void handle_event(MirEvent const& e) = 0; virtual void handle_lifecycle_event(MirLifecycleState state) = 0; virtual void handle_display_config_change(graphics::DisplayConfiguration const& config) = 0; virtual void send_ping(int32_t serial) = 0; virtual void handle_input_device_change(std::vector> const& devices) = 0; protected: EventSink() = default; EventSink(EventSink const&) = delete; EventSink& operator=(EventSink const&) = delete; }; } } // namespace mir #endif // MIR_FRONTEND_EVENT_SINK_H_ ./src/include/server/mir/frontend/protobuf_connection_creator.h0000644000015600001650000000475512676616125025221 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_PROTOBUF_CONNECTION_CREATOR_H_ #define MIR_FRONTEND_PROTOBUF_CONNECTION_CREATOR_H_ #include "mir/frontend/connection_creator.h" #include "mir/frontend/connections.h" #include namespace mir { namespace graphics { class PlatformIpcOperations; } namespace frontend { class MessageProcessorReport; class ProtobufIpcFactory; class SessionAuthorizer; namespace detail { class DisplayServer; class SocketConnection; class MessageProcessor; class ProtobufMessageSender; } class ProtobufConnectionCreator : public ConnectionCreator { public: ProtobufConnectionCreator( std::shared_ptr const& ipc_factory, std::shared_ptr const& session_authorizer, std::shared_ptr const& operations, std::shared_ptr const& report); ~ProtobufConnectionCreator() noexcept; void create_connection_for( std::shared_ptr const& socket, ConnectionContext const& connection_context) override; virtual std::shared_ptr create_processor( std::shared_ptr const& sender, std::shared_ptr const& display_server, std::shared_ptr const& report) const; private: int next_id(); std::shared_ptr const ipc_factory; std::shared_ptr const session_authorizer; std::shared_ptr const operations; std::shared_ptr const report; std::atomic next_session_id; std::shared_ptr> const connections; }; } } #endif /* MIR_FRONTEND_PROTOBUF_CONNECTION_CREATOR_H_ */ ./src/include/server/mir/frontend/protobuf_message_sender.h0000644000015600001650000000270512676616125024320 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_PROTOBUF_MESSAGE_SENDER_H_ #define MIR_FRONTEND_PROTOBUF_MESSAGE_SENDER_H_ #include "mir/frontend/fd_sets.h" #include namespace google { namespace protobuf { class MessageLite; } } namespace mir { namespace frontend { namespace detail { class ProtobufMessageSender { public: virtual void send_response( google::protobuf::uint32 call_id, google::protobuf::MessageLite* message, FdSets const& fd_sets) = 0; protected: ProtobufMessageSender() = default; virtual ~ProtobufMessageSender() = default; private: ProtobufMessageSender(ProtobufMessageSender const&) = delete; ProtobufMessageSender& operator=(ProtobufMessageSender const&) = delete; }; } } } #endif /* MIR_FRONTEND_PROTOBUF_MESSAGE_SENDER_H_ */ ./src/include/server/mir/frontend/unsupported_feature_exception.h0000644000015600001650000000214112676616125025567 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Christopher James Halse Rogers */ #ifndef MIR_FRONTEND_UNSUPPORTED_FEATURE_EXCEPTION_H_ #define MIR_FRONTEND_UNSUPPORTED_FEATURE_EXCEPTION_H_ #include namespace mir { namespace frontend { class unsupported_feature : public std::runtime_error { public: unsupported_feature() : std::runtime_error{"Unsupported feature requested"} { } }; } } #endif /* MIR_FRONTEND_UNSUPPORTED_FEATURE_EXCEPTION_H_ */ ./src/include/server/mir/frontend/connection_creator.h0000644000015600001650000000255112676616125023271 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_CONNECTION_CREATOR_H_ #define MIR_FRONTEND_CONNECTION_CREATOR_H_ #include #include namespace mir { namespace frontend { class ConnectionContext; class ConnectionCreator { public: virtual void create_connection_for( std::shared_ptr const& socket, ConnectionContext const& connection_context) = 0; protected: ConnectionCreator() = default; virtual ~ConnectionCreator() noexcept = default; ConnectionCreator(ConnectionCreator const&) = delete; ConnectionCreator& operator=(ConnectionCreator const&) = delete; }; } } #endif /* MIR_FRONTEND_CONNECTION_CREATOR_H_ */ ./src/include/server/mir/frontend/template_protobuf_message_processor.h0000644000015600001650000000622712676616125026755 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_FRONTEND_TEMPLATE_PROTOBUF_MESSAGE_PROCESSOR_H_ #define MIR_FRONTEND_TEMPLATE_PROTOBUF_MESSAGE_PROCESSOR_H_ #include "mir/frontend/message_processor.h" #include "mir/cookie/authority.h" #include #include #include #include namespace mir { namespace frontend { namespace detail { // Utility metafunction result_ptr_t<> allows invoke() to pick the right // send_response() overload. The base template resolves to the prototype // "send_response(::google::protobuf::uint32 id, ::google::protobuf::Message* response)" // Client code may specialize result_ptr_t to resolve to another overload. template struct result_ptr_t { typedef ::google::protobuf::MessageLite* type; }; // Boiler plate for unpacking a parameter message, invoking a server function, and // sending the result message. Assumes the existence of Self::send_response(). template void invoke( Self* self, Server* server, void (ServerX::*function)( ParameterMessage const* request, ResultMessage* response, ::google::protobuf::Closure* done), Invocation const& invocation) { ParameterMessage parameter_message; if (!parameter_message.ParseFromString(invocation.parameters())) BOOST_THROW_EXCEPTION(std::runtime_error("Failed to parse message parameters!")); ResultMessage result_message; try { std::unique_ptr callback( google::protobuf::NewPermanentCallback< Self, ::google::protobuf::uint32, typename result_ptr_t::type>( self, &Self::send_response, invocation.id(), &result_message)); (server->*function)( ¶meter_message, &result_message, callback.get()); } catch (mir::cookie::SecurityCheckError const& /*err*/) { throw; } catch (std::exception const& x) { using namespace std::literals::string_literals; result_message.set_error("Error processing request: "s + x.what() + "\nInternal error details: " + boost::diagnostic_information(x)); self->send_response(invocation.id(), &result_message); } } } } } #endif /* MIR_FRONTEND_TEMPLATE_PROTOBUF_MESSAGE_PROCESSOR_H_ */ ./src/include/server/mir/lockable_callback_wrapper.h0000644000015600001650000000302712676616125022723 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #ifndef MIR_LOCKABLE_CALLBACK_WRAPPER_H_ #define MIR_LOCKABLE_CALLBACK_WRAPPER_H_ #include "mir/lockable_callback.h" #include #include namespace mir { class LockableCallbackWrapper : public LockableCallback { public: LockableCallbackWrapper(std::shared_ptr const& wrapped, std::function const& precall_hook); LockableCallbackWrapper(std::shared_ptr const& wrapped, std::function const& precall_hook, std::function const& postcall_hook); void operator()() override; void lock() override; void unlock() override; private: std::shared_ptr wrapped_callback; std::function precall_hook; std::function postcall_hook; }; } #endif ./src/include/server/mir/glib_main_loop.h0000644000015600001650000000562512676616125020553 0ustar jenkinsjenkins/* * Copyright © 2014-2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GLIB_MAIN_LOOP_H_ #define MIR_GLIB_MAIN_LOOP_H_ #include "mir/main_loop.h" #include "mir/glib_main_loop_sources.h" #include #include #include #include #include namespace mir { namespace detail { class GMainContextHandle { public: GMainContextHandle(); ~GMainContextHandle(); operator GMainContext*() const; private: GMainContext* const main_context; }; } class GLibMainLoop : public MainLoop { public: GLibMainLoop(std::shared_ptr const& clock); void run() override; void stop() override; void register_signal_handler( std::initializer_list signals, std::function const& handler) override; void register_signal_handler( std::initializer_list signals, mir::UniqueModulePtr> handler) override; void register_fd_handler( std::initializer_list fds, void const* owner, std::function const& handler) override; void register_fd_handler( std::initializer_list fds, void const* owner, mir::UniqueModulePtr> handler) override; void unregister_fd_handler(void const* owner) override; void enqueue(void const* owner, ServerAction const& action) override; void pause_processing_for(void const* owner) override; void resume_processing_for(void const* owner) override; std::unique_ptr create_alarm( std::function const& callback) override; std::unique_ptr create_alarm( std::shared_ptr const& callback) override; void reprocess_all_sources(); private: bool should_process_actions_for(void const* owner); void handle_exception(std::exception_ptr const& e); std::shared_ptr const clock; detail::GMainContextHandle const main_context; std::atomic running; detail::FdSources fd_sources; detail::SignalSources signal_sources; std::mutex do_not_process_mutex; std::vector do_not_process; std::function before_iteration_hook; std::exception_ptr main_loop_exception; }; } #endif ./src/include/server/mir/input/0000755000015600001650000000000012676616160016556 5ustar jenkinsjenkins./src/include/server/mir/input/validator.h0000644000015600001650000000274012676616125020720 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_VALIDATOR_H_ #define MIR_INPUT_VALIDATOR_H_ #include "mir/events/event_builders.h" #include #include #include namespace mir { namespace input { class Validator { public: Validator(std::function const& dispatch_valid_event); void validate_and_dispatch(MirEvent const& event); private: std::mutex state_guard; std::function const dispatch_valid_event; std::unordered_map last_event_by_device; void handle_touch_event(MirInputDeviceId, MirTouchEvent const* event); void ensure_stream_validity_locked(std::lock_guard const& lg, MirTouchEvent const* ev, MirTouchEvent const* last_ev); }; } } #endif // MIR_INPUT_VALIDATOR_H_ ./src/include/server/mir/input/input_probe.h0000644000015600001650000000341312676616157021264 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_PROBE_H_ #define MIR_INPUT_PROBE_H_ #include "mir/module_deleter.h" namespace mir { namespace graphics { class Platform; } namespace options { class Option; } class EmergencyCleanupRegistry; class SharedLibraryProberReport; namespace input { class InputReport; class Platform; class InputDeviceRegistry; mir::UniqueModulePtr probe_input_platforms( options::Option const& options, std::shared_ptr const& emergency_cleanup, std::shared_ptr const& device_registry, std::shared_ptr const& input_report, SharedLibraryProberReport & prober_report); /// Tries to create an input platform from the graphics module, otherwise returns a null pointer auto input_platform_from_graphics_module( graphics::Platform const& graphics_platform, options::Option const& options, std::shared_ptr const& emergency_cleanup, std::shared_ptr const& device_registry, std::shared_ptr const& input_report) -> mir::UniqueModulePtr; } } #endif ./src/include/server/mir/input/input_region.h0000644000015600001650000000327612676616125021442 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_INPUT_INPUT_REGION_H_ #define MIR_INPUT_INPUT_REGION_H_ namespace mir { namespace geometry { struct Rectangle; struct Rectangles; struct Point; } namespace input { /** * Interface to the region of valid input coordinates. */ class InputRegion { public: virtual ~InputRegion() = default; /** The bounding rectangle of the input region */ virtual geometry::Rectangle bounding_rectangle() = 0; /** * Confines a point to the input region. * * If the point is within input region it remains unchanged, * otherwise it is replaced by the region point that is closest to * it. * * @param [in,out] point the point to confine */ virtual void confine(geometry::Point& point) = 0; virtual void set_input_rectangles(geometry::Rectangles const& rectangles) = 0; protected: InputRegion() = default; InputRegion(InputRegion const&) = delete; InputRegion& operator=(InputRegion const&) = delete; }; } } #endif /* MIR_INPUT_INPUT_REGION_H_ */ ./src/include/server/mir/input/input_sender.h0000644000015600001650000000225612676616125021434 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_INPUT_SENDER_H_ #define MIR_INPUT_INPUT_SENDER_H_ #include "mir_toolkit/event.h" #include namespace mir { namespace input { class InputChannel; class InputSender { public: InputSender() = default; virtual ~InputSender() = default; virtual void send_event(MirEvent const& event, std::shared_ptr const& channel) = 0; protected: InputSender& operator=(InputSender const&) = delete; InputSender(InputSender const&) = delete; }; } } #endif ./src/include/server/mir/input/android/0000755000015600001650000000000012676616124020176 5ustar jenkinsjenkins./src/include/server/mir/input/input_channel_factory.h0000644000015600001650000000233712676616125023313 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr */ #ifndef MIR_INPUT_INPUT_CHANNEL_FACTORY_H_ #define MIR_INPUT_INPUT_CHANNEL_FACTORY_H_ #include namespace mir { namespace input { class InputChannel; class InputChannelFactory { public: virtual ~InputChannelFactory() {} virtual std::shared_ptr make_input_channel() = 0; protected: InputChannelFactory() = default; InputChannelFactory(InputChannelFactory const&) = delete; InputChannelFactory& operator=(InputChannelFactory const&) = delete; }; } } // namespace mir #endif // MIR_INPUT_INPUT_CHANNEL_FACTORY_H_ ./src/include/server/mir/input/scene.h0000644000015600001650000000441212676616125020026 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Carr * Daniel d'Andradra */ #ifndef MIR_INPUT_INPUT_SCENE_H_ #define MIR_INPUT_INPUT_SCENE_H_ #include "mir/input/input_channel_factory.h" #include namespace mir { namespace scene { class Observer; } namespace graphics { class Renderable; } namespace input { class Surface; class Scene { public: virtual ~Scene() = default; virtual void for_each(std::function const&)> const& callback) = 0; virtual void add_observer(std::shared_ptr const& observer) = 0; virtual void remove_observer(std::weak_ptr const& observer) = 0; // An interface which the input stack can use to add certain non interactive input visualizations // in to the scene (i.e. cursors, touchspots). Overlay renderables will be rendered above all surfaces. // Within the set of overlay renderables, rendering order is undefined. virtual void add_input_visualization(std::shared_ptr const& overlay) = 0; virtual void remove_input_visualization(std::weak_ptr const& overlay) = 0; // As input visualizations added through the overlay system will not use the standard SurfaceObserver // mechanism, we require this method to trigger recomposition. // TODO: How can something like SurfaceObserver be adapted to work with non surface renderables? virtual void emit_scene_changed() = 0; protected: Scene() = default; Scene(Scene const&) = delete; Scene& operator=(Scene const&) = delete; }; } } #endif // MIR_INPUT_INPUT_SCENE ./src/include/server/mir/input/vt_filter.h0000644000015600001650000000170412676616125020730 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Ancell */ #ifndef MIR_INPUT_VT_FILTER_H_ #define MIR_INPUT_VT_FILTER_H_ #include "mir/input/event_filter.h" namespace mir { namespace input { class VTFilter : public EventFilter { public: bool handle(MirEvent const& event) override; }; } } #endif // MIR_INPUT_VT_FILTER_H_ ./src/include/server/mir/input/seat.h0000644000015600001650000000243312676616157017673 0ustar jenkinsjenkins/* * Copyright © 2015-2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Andreas Pokorny */ #ifndef MIR_INPUT_SEAT_H_ #define MIR_INPUT_SEAT_H_ #include "mir/geometry/rectangle.h" #include "mir_toolkit/event.h" #include namespace mir { namespace input { class Device; class Seat { public: Seat()=default; virtual ~Seat() = default; virtual void add_device(Device const& device) = 0; virtual void remove_device(Device const& device) = 0; virtual void dispatch_event(MirEvent& event) = 0; virtual geometry::Rectangle get_rectangle_for(Device const& dev) = 0; private: Seat(Seat const&) = delete; Seat& operator=(Seat const&) = delete; }; } } #endif ./src/include/server/mir/input/legacy_input_dispatchable.h0000644000015600001650000000171412676616125024121 0ustar jenkinsjenkins /* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_INPUT_LEGACY_INPUT_DISPATCHABLE_H_ #define MIR_INPUT_LEGACY_INPUT_DISPATCHABLE_H_ #include "mir/dispatch/dispatchable.h" namespace mir { namespace input { struct LegacyInputDispatchable : mir::dispatch::Dispatchable { virtual void start() = 0; }; } } #endif ./src/include/server/mir/display_server.h0000644000015600001650000000265112676616125020630 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: * Alan Griffiths * Thomas Voss */ #ifndef MIR_DISPLAY_SERVER_H_ #define MIR_DISPLAY_SERVER_H_ #include #include /// All things Mir namespace mir { class ServerConfiguration; class DisplayServer { public: explicit DisplayServer(ServerConfiguration& config); ~DisplayServer(); void run(); void stop(); private: struct Private; // This gets dereferenced in stop(), called across thread boundaries, // so gets (correctly, if harmlessly) detected as a data race if it's not atomic. std::atomic const p; DisplayServer() = delete; DisplayServer(const DisplayServer&) = delete; DisplayServer& operator=(const DisplayServer&) = delete; }; } #endif /* MIR_DISPLAY_SERVER_H_ */ ./src/include/server/mir/run_mir.h0000644000015600001650000000335012676616125017245 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_RUN_MIR_H_ #define MIR_RUN_MIR_H_ #include #include namespace mir { class ServerConfiguration; class DisplayServer; /** * Run a DisplayServer with the supplied configuration. * init will be called after constructing the server, but before invoking DisplayServer::run() * The server will be stopped on receipt of SIGTERM or SIGINT * This function does not return until the server has stopped. */ void run_mir( ServerConfiguration& config, std::function init); /** * Run a DisplayServer with the supplied configuration. * init will be called after constructing the server, but before invoking DisplayServer::run() * The terminator will be called following receipt of SIGTERM or SIGINT * (but not in a signal handler - so arbitrary functions may be invoked). * This function does not return until the server has stopped. */ void run_mir( ServerConfiguration& config, std::function init, std::function const& terminator); } #endif /* MIR_RUN_MIR_H_ */ ./src/include/server/mir/basic_callback.h0000644000015600001650000000211312676616125020463 0ustar jenkinsjenkins/* * Copyright © 2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alberto Aguirre */ #ifndef MIR_BASIC_CALLBACK_H_ #define MIR_BASIC_CALLBACK_H_ #include "mir/lockable_callback.h" #include namespace mir { class BasicCallback : public mir::LockableCallback { public: BasicCallback(std::function const& callback); void operator()() override; void lock() override; void unlock() override; private: std::function callback; }; } #endif ./src/include/server/mir/graphics/0000755000015600001650000000000012676616126017221 5ustar jenkinsjenkins./src/include/server/mir/graphics/surfaceless_egl_context.h0000644000015600001650000000351012676616125024302 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_SURFACELESS_EGL_CONTEXT_H_ #define MIR_GRAPHICS_SURFACELESS_EGL_CONTEXT_H_ #include "mir/graphics/egl_resources.h" #include "mir/graphics/gl_context.h" #include namespace mir { namespace graphics { class SurfacelessEGLContext : public GLContext { public: SurfacelessEGLContext(EGLDisplay egl_display, EGLContext shared_context); SurfacelessEGLContext(EGLDisplay egl_display, EGLint const* attribs, EGLContext shared_context); /* We have to explicitly define this, as GLContext has a deleted copy constructor */ SurfacelessEGLContext(SurfacelessEGLContext&& move); virtual ~SurfacelessEGLContext() noexcept; void make_current() const override; void release_current() const override; operator EGLContext() const; private: SurfacelessEGLContext(SurfacelessEGLContext const&) = delete; SurfacelessEGLContext& operator=(SurfacelessEGLContext const&) = delete; EGLDisplay egl_display; bool surfaceless; EGLConfig egl_config; graphics::EGLSurfaceStore egl_surface; graphics::EGLContextStore egl_context; }; } } #endif /* MIR_GRAPHICS_SURFACELESS_EGL_SURFACE_H_ */ ./src/include/server/mir/graphics/gl_extensions_base.h0000644000015600001650000000222712676616125023247 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_GRAPHICS_GL_EXTENSIONS_BASE_H_ #define MIR_GRAPHICS_GL_EXTENSIONS_BASE_H_ namespace mir { namespace graphics { class GLExtensionsBase { public: GLExtensionsBase(char const* extensions); bool support(char const* ext) const; private: GLExtensionsBase(GLExtensionsBase const&) = delete; GLExtensionsBase& operator=(GLExtensionsBase const&) = delete; char const* const extensions; }; } } #endif /* MIR_GRAPHICS_GL_EXTENSIONS_BASE_H_ */ ./src/include/server/mir/time/0000755000015600001650000000000012676616124016355 5ustar jenkinsjenkins./src/include/server/mir/default_server_status_listener.h0000644000015600001650000000211312676616125024110 0ustar jenkinsjenkins/* * Copyright © 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Robert Ancell */ #ifndef MIR_DEFAULT_SERVER_STATUS_LISTENER_H_ #define MIR_DEFAULT_SERVER_STATUS_LISTENER_H_ #include "mir/server_status_listener.h" namespace mir { class DefaultServerStatusListener : public virtual ServerStatusListener { public: virtual void paused() { } virtual void resumed() { } virtual void started() { } }; } #endif /* MIR_DEFAULT_SERVER_STATUS_LISTENER_H_ */ ./src/include/server/mir/server_configuration.h0000644000015600001650000000542712676616125022036 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_SERVER_CONFIGURATION_H_ #define MIR_SERVER_CONFIGURATION_H_ #include namespace mir { namespace cookie { class Authority; } namespace compositor { class Compositor; } namespace frontend { class Connector; class Shell; } namespace shell { class SessionContainer; } namespace graphics { class Display; class DisplayConfigurationPolicy; class Platform; } namespace input { class InputManager; class InputDispatcher; class EventFilter; class InputConfiguration; } namespace scene { class ApplicationNotRespondingDetector; } class MainLoop; class ServerStatusListener; class DisplayChanger; class EmergencyCleanup; class ServerConfiguration { public: // TODO most of these interfaces are wider DisplayServer needs... // TODO ...some or all of them need narrowing virtual std::shared_ptr the_connector() = 0; virtual std::shared_ptr the_prompt_connector() = 0; virtual std::shared_ptr the_display() = 0; virtual std::shared_ptr the_compositor() = 0; virtual std::shared_ptr the_input_manager() = 0; virtual std::shared_ptr the_input_dispatcher() = 0; virtual std::shared_ptr the_main_loop() = 0; virtual std::shared_ptr the_server_status_listener() = 0; virtual std::shared_ptr the_display_changer() = 0; virtual std::shared_ptr the_graphics_platform() = 0; virtual std::shared_ptr the_emergency_cleanup() = 0; virtual std::shared_ptr the_cookie_authority() = 0; virtual auto the_fatal_error_strategy() -> void (*)(char const* reason, ...) = 0; virtual std::shared_ptr the_application_not_responding_detector() = 0; protected: ServerConfiguration() = default; virtual ~ServerConfiguration() = default; ServerConfiguration(ServerConfiguration const&) = delete; ServerConfiguration& operator=(ServerConfiguration const&) = delete; }; } #endif /* MIR_SERVER_CONFIGURATION_H_ */ ./src/include/server/mir/report/0000755000015600001650000000000012676616125016733 5ustar jenkinsjenkins./src/include/server/mir/report/legacy_input_report.h0000644000015600001650000000177112676616125023170 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Andreas Pokorny */ #ifndef MIR_REPORT_LEGACY_INPUT_REPORT_H_ #define MIR_REPORT_LEGACY_INPUT_REPORT_H_ #include namespace mir { namespace logging { class Logger; } namespace report { namespace legacy_input { void initialize(std::shared_ptr const& logger); } } } #endif // MIR_REPORT_LEGACY_INPUT_REPORT_H_ ./src/include/server/mir/default_server_configuration.h0000644000015600001650000004441612676616157023550 0ustar jenkinsjenkins/* * Copyright © 2012-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_DEFAULT_SERVER_CONFIGURATION_H_ #define MIR_DEFAULT_SERVER_CONFIGURATION_H_ #include "mir/cached_ptr.h" #include "mir/server_configuration.h" #include "mir/shell/window_manager_builder.h" #include #include namespace mir { class ServerActionQueue; class SharedLibrary; class SharedLibraryProberReport; namespace cookie { class Authority; } namespace dispatch { class MultiplexingDispatchable; } namespace compositor { class Renderer; class BufferStreamFactory; class Scene; class Drawer; class DisplayBufferCompositorFactory; class Compositor; class RendererFactory; class CompositorReport; class FrameDroppingPolicyFactory; } namespace frontend { class Shell; class Connector; class ConnectorReport; class ProtobufIpcFactory; class ConnectionCreator; class SessionMediatorReport; class MessageProcessorReport; class SessionAuthorizer; class EventSink; class DisplayChanger; class Screencast; } namespace shell { class DisplayConfigurationController; class InputTargeter; class FocusSetter; class FocusController; class DisplayLayout; class HostLifecycleEventListener; class Shell; class ShellReport; class SurfaceStack; class PersistentSurfaceStore; namespace detail { class FrontendShell; } } namespace time { class Clock; } namespace scene { class SurfaceFactory; class BroadcastingSessionEventSink; class BufferStreamFactory; class MediatingDisplayChanger; class PixelBuffer; class SessionContainer; class SessionEventSink; class SessionEventHandlerRegister; class SessionListener; class SessionCoordinator; class SnapshotStrategy; class SurfaceStack; class SceneReport; class PromptSessionListener; class PromptSessionManager; class CoordinateTranslator; } namespace graphics { class Platform; class Display; class DisplayReport; class DisplayConfigurationReport; class GraphicBufferAllocator; class Cursor; class CursorImage; class GLConfig; namespace nested { class HostConnection; class MirClientHostConnection; } } namespace input { class InputReport; class Scene; class InputManager; class SurfaceInputDispatcher; class InputDeviceRegistry; class InputDeviceHub; class DefaultInputDeviceHub; class CompositeEventFilter; class EventFilterChainDispatcher; class InputChannelFactory; class CursorListener; class TouchVisualizer; class InputRegion; class InputSender; class CursorImages; class Seat; } namespace logging { class Logger; } namespace options { class Option; class Configuration; } namespace report { class ReportFactory; } class DefaultServerConfiguration : public virtual ServerConfiguration { public: DefaultServerConfiguration(int argc, char const* argv[]); explicit DefaultServerConfiguration(std::shared_ptr const& configuration_options); /** @name DisplayServer dependencies * dependencies of DisplayServer on the rest of the Mir * @{ */ std::shared_ptr the_connector() override; std::shared_ptr the_prompt_connector() override; std::shared_ptr the_display() override; std::shared_ptr the_compositor() override; std::shared_ptr the_input_manager() override; std::shared_ptr the_main_loop() override; std::shared_ptr the_server_status_listener() override; std::shared_ptr the_display_changer() override; std::shared_ptr the_graphics_platform() override; std::shared_ptr the_input_dispatcher() override; std::shared_ptr the_emergency_cleanup() override; std::shared_ptr the_cookie_authority() override; /** * Function to call when a "fatal" error occurs. This implementation allows * the default strategy to be overridden by --on-fatal-error-abort to force a * core. (This behavior is useful for diagnostic purposes during development.) * To change the default strategy used FatalErrorStrategy. See acceptance test * ServerShutdown.fatal_error_default_can_be_changed_to_abort * for an example. */ auto the_fatal_error_strategy() -> void (*)(char const* reason, ...) override final; std::shared_ptr the_application_not_responding_detector() override; /** @} */ /** @name graphics configuration - customization * configurable interfaces for modifying graphics * @{ */ virtual std::shared_ptr the_renderer_factory(); virtual std::shared_ptr the_display_configuration_controller(); virtual std::shared_ptr the_display_configuration_policy(); virtual std::shared_ptr the_host_connection(); virtual std::shared_ptr the_gl_config(); /** @} */ /** @name graphics configuration - dependencies * dependencies of graphics on the rest of the Mir * @{ */ virtual std::shared_ptr the_display_report(); virtual std::shared_ptr the_cursor(); virtual std::shared_ptr wrap_cursor(std::shared_ptr const& wrapped); virtual std::shared_ptr the_default_cursor_image(); virtual std::shared_ptr the_cursor_images(); virtual std::shared_ptr the_display_configuration_report(); /** @} */ /** @name compositor configuration - customization * configurable interfaces for modifying compositor * @{ */ virtual std::shared_ptr the_compositor_report(); virtual std::shared_ptr the_display_buffer_compositor_factory(); virtual std::shared_ptr wrap_display_buffer_compositor_factory( std::shared_ptr const& wrapped); /** @} */ /** @name compositor configuration - dependencies * dependencies of compositor on the rest of the Mir * @{ */ virtual std::shared_ptr the_buffer_allocator(); virtual std::shared_ptr the_scene(); virtual std::shared_ptr the_frame_dropping_policy_factory(); /** @} */ /** @name frontend configuration - dependencies * dependencies of frontend on the rest of the Mir * @{ */ virtual std::shared_ptr the_session_mediator_report(); virtual std::shared_ptr the_message_processor_report(); virtual std::shared_ptr the_session_authorizer(); // the_frontend_shell() is an adapter for the_shell(). // To customize this behaviour it is recommended you override wrap_shell(). std::shared_ptr the_frontend_shell(); virtual std::shared_ptr the_global_event_sink(); virtual std::shared_ptr the_frontend_display_changer(); virtual std::shared_ptr the_screencast(); /** @name frontend configuration - internal dependencies * internal dependencies of frontend * @{ */ virtual std::shared_ptr the_connection_creator(); virtual std::shared_ptr the_prompt_connection_creator(); virtual std::shared_ptr the_connector_report(); /** @} */ /** @} */ // the_focus_controller() is an interface for the_shell(). std::shared_ptr the_focus_controller(); /** @name shell configuration - customization * configurable interfaces for modifying shell * @{ */ virtual auto the_shell() -> std::shared_ptr; virtual auto the_window_manager_builder() -> shell::WindowManagerBuilder; virtual std::shared_ptr the_session_listener(); virtual std::shared_ptr the_shell_display_layout(); virtual std::shared_ptr the_prompt_session_listener(); virtual std::shared_ptr the_prompt_session_manager(); virtual std::shared_ptr the_host_lifecycle_event_listener(); virtual std::shared_ptr the_persistent_surface_store(); virtual std::shared_ptr the_shell_report(); /** @} */ /** @name internal scene configuration * builder functions used in the default implementation. * The interfaces returned are not published, so the functions are only useful in tests * @{ */ virtual std::shared_ptr the_pixel_buffer(); virtual std::shared_ptr the_snapshot_strategy(); virtual std::shared_ptr the_session_container(); virtual std::shared_ptr the_session_event_sink(); virtual std::shared_ptr the_session_event_handler_register(); virtual std::shared_ptr the_surface_factory(); virtual std::shared_ptr the_surface_stack(); virtual std::shared_ptr wrap_surface_stack(std::shared_ptr const& wrapped); /** @} */ /** @name scene configuration - dependencies * dependencies of scene on the rest of the Mir * @{ */ virtual std::shared_ptr the_buffer_stream_factory(); virtual std::shared_ptr the_scene_report(); /** @} */ /** @name scene configuration - services * services provided by scene for the rest of Mir * @{ */ virtual std::shared_ptr the_session_coordinator(); virtual std::shared_ptr the_coordinate_translator(); /** @} */ /** @name input configuration * @{ */ virtual std::shared_ptr the_input_report(); virtual std::shared_ptr the_composite_event_filter(); virtual std::shared_ptr the_event_filter_chain_dispatcher(); virtual std::shared_ptr the_input_targeter(); virtual std::shared_ptr the_input_scene(); virtual std::shared_ptr the_cursor_listener(); virtual std::shared_ptr the_touch_visualizer(); virtual std::shared_ptr the_input_region(); virtual std::shared_ptr the_input_sender(); virtual std::shared_ptr the_seat(); // new input reading related parts: virtual std::shared_ptr the_input_reading_multiplexer(); virtual std::shared_ptr the_input_device_registry(); virtual std::shared_ptr the_input_device_hub(); virtual std::shared_ptr the_surface_input_dispatcher(); /** @} */ /** @name logging configuration - customization * configurable interfaces for modifying logging * @{ */ virtual std::shared_ptr the_logger(); /** @} */ virtual std::shared_ptr the_clock(); virtual std::shared_ptr the_server_action_queue(); virtual std::shared_ptr the_shared_library_prober_report(); private: // We need to ensure the platform library is destroyed last as the // DisplayConfiguration can hold weak_ptrs to objects created from the library // TODO: We need a better way to manage the lifetimes of platform libraries std::shared_ptr platform_library; protected: std::shared_ptr the_options() const; std::shared_ptr the_mir_client_host_connection(); virtual std::shared_ptr the_input_channel_factory(); virtual std::shared_ptr the_mediating_display_changer(); virtual std::shared_ptr new_ipc_factory( std::shared_ptr const& session_authorizer); /** @} */ /** @Convenience wrapper functions * @{ */ virtual std::shared_ptr wrap_display_configuration_policy( std::shared_ptr const& wrapped); virtual std::shared_ptr wrap_shell( std::shared_ptr const& wrapped); virtual std::shared_ptr wrap_cursor_listener( std::shared_ptr const& wrapped); /** @} */ CachedPtr connector; CachedPtr prompt_connector; CachedPtr input_report; CachedPtr event_filter_chain_dispatcher; CachedPtr composite_event_filter; CachedPtr input_manager; CachedPtr surface_input_dispatcher; CachedPtr default_input_device_hub; CachedPtr input_reading_multiplexer; CachedPtr input_dispatcher; CachedPtr input_sender; CachedPtr input_region; CachedPtr input_targeter; CachedPtr cursor_listener; CachedPtr touch_visualizer; CachedPtr seat; CachedPtr graphics_platform; CachedPtr buffer_allocator; CachedPtr display; CachedPtr cursor; CachedPtr default_cursor_image; CachedPtr cursor_images; CachedPtr connector_report; CachedPtr session_mediator_report; CachedPtr message_processor_report; CachedPtr session_authorizer; CachedPtr global_event_sink; CachedPtr connection_creator; CachedPtr prompt_connection_creator; CachedPtr screencast; CachedPtr renderer_factory; CachedPtr buffer_stream_factory; CachedPtr frame_dropping_policy_factory; CachedPtr scene_surface_stack; CachedPtr surface_stack; CachedPtr scene_report; CachedPtr surface_factory; CachedPtr session_container; CachedPtr session_listener; CachedPtr pixel_buffer; CachedPtr snapshot_strategy; CachedPtr shell_display_layout; CachedPtr display_buffer_compositor_factory; CachedPtr compositor; CachedPtr compositor_report; CachedPtr logger; CachedPtr display_report; CachedPtr display_configuration_report; CachedPtr clock; CachedPtr main_loop; CachedPtr server_status_listener; CachedPtr display_configuration_policy; CachedPtr host_connection; CachedPtr mediating_display_changer; CachedPtr gl_config; CachedPtr prompt_session_listener; CachedPtr prompt_session_manager; CachedPtr session_coordinator; CachedPtr coordinate_translator; CachedPtr emergency_cleanup; CachedPtr host_lifecycle_event_listener; CachedPtr surface_store; CachedPtr shared_library_prober_report; CachedPtr shell; CachedPtr shell_report; CachedPtr application_not_responding_detector; CachedPtr cookie_authority; private: std::shared_ptr const configuration_options; std::shared_ptr const default_filter; virtual std::string the_socket_file() const; // The following caches and factory functions are internal to the // default implementations of corresponding the Mir components CachedPtr broadcasting_session_event_sink; std::shared_ptr the_broadcasting_session_event_sink(); auto report_factory(char const* report_opt) -> std::unique_ptr; CachedPtr frontend_shell; }; } #endif /* MIR_DEFAULT_SERVER_CONFIGURATION_H_ */ ./src/include/platform/0000755000015600001650000000000012676616124015146 5ustar jenkinsjenkins./src/include/platform/mir/0000755000015600001650000000000012676616125015736 5ustar jenkinsjenkins./src/include/platform/mir/emergency_cleanup_registry.h0000644000015600001650000000274512676616125023534 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alexandros Frantzis */ #ifndef MIR_EMERGENCY_CLEANUP_REGISTRY_H_ #define MIR_EMERGENCY_CLEANUP_REGISTRY_H_ #include #include "mir/module_deleter.h" namespace mir { using EmergencyCleanupHandler = std::function; using ModuleEmergencyCleanupHandler = mir::UniqueModulePtr>; class EmergencyCleanupRegistry { public: virtual ~EmergencyCleanupRegistry() = default; virtual void add(EmergencyCleanupHandler const& handler) = 0; virtual void add(ModuleEmergencyCleanupHandler handler) = 0; protected: EmergencyCleanupRegistry() = default; EmergencyCleanupRegistry(EmergencyCleanupRegistry const&) = delete; EmergencyCleanupRegistry& operator=(EmergencyCleanupRegistry const&) = delete; }; } #endif /* MIR_EMERGENCY_CLEANUP_REGISTRY_H_ */ ./src/include/platform/mir/options/0000755000015600001650000000000012676616125017431 5ustar jenkinsjenkins./src/include/platform/mir/options/program_option.h0000644000015600001650000000370012676616125022641 0ustar jenkinsjenkins/* * Copyright © 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_OPTIONS_PROGRAM_OPTION_H_ #define MIR_OPTIONS_PROGRAM_OPTION_H_ #include "mir/options/option.h" #include #include namespace mir { namespace options { class ProgramOption : public Option { public: ProgramOption(); void parse_arguments( boost::program_options::options_description const& description, int argc, char const* argv[]); void parse_environment( boost::program_options::options_description const& description, char const* prefix); void parse_file( boost::program_options::options_description const& description, std::string const& filename); bool is_set(char const* name) const override; bool get(char const* name, bool default_) const override; std::string get(char const*, char const* default_) const override; int get(char const* name, int default_) const override; boost::any const& get(char const* name) const override; using Option::get; std::vector unparsed_command_line() const; private: boost::program_options::variables_map options; std::vector unparsed_tokens; }; } } #endif /* MIR_OPTIONS_PROGRAM_OPTION_H_ */ ./src/include/platform/mir/options/configuration.h0000644000015600001650000000512712676616125022456 0ustar jenkinsjenkins/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored By: Alan Griffiths */ #ifndef MIR_OPTIONS_CONFIGURATION_H_ #define MIR_OPTIONS_CONFIGURATION_H_ #include "mir/options/option.h" #include namespace mir { namespace options { extern char const* const server_socket_opt; extern char const* const prompt_socket_opt; extern char const* const no_server_socket_opt; extern char const* const arw_server_socket_opt; extern char const* const enable_input_opt; extern char const* const session_mediator_report_opt; extern char const* const msg_processor_report_opt; extern char const* const shared_library_prober_report_opt; extern char const* const shell_report_opt; extern char const* const compositor_report_opt; extern char const* const display_report_opt; extern char const* const legacy_input_report_opt; extern char const* const connector_report_opt; extern char const* const scene_report_opt; extern char const* const input_report_opt; extern char const* const host_socket_opt; extern char const* const frontend_threads_opt; extern char const* const touchspots_opt; extern char const* const fatal_abort_opt; extern char const* const debug_opt; extern char const* const nbuffers_opt; extern char const* const composite_delay_opt; extern char const* const enable_key_repeat_opt; extern char const* const name_opt; extern char const* const offscreen_opt; extern char const* const enable_key_repeat_opt; extern char const* const off_opt_value; extern char const* const log_opt_value; extern char const* const lttng_opt_value; extern char const* const platform_graphics_lib; extern char const* const platform_input_lib; extern char const* const platform_path; class Configuration { public: virtual std::shared_ptr the_options() const = 0; protected: Configuration() = default; virtual ~Configuration() = default; Configuration(Configuration const&) = delete; Configuration& operator=(Configuration const&) = delete; }; } } #endif /* MIR_OPTIONS_CONFIGURATION_H_ */ ./src/include/platform/mir/options/default_configuration.h0000644000015600001650000000572012676616125024161 0ustar jenkinsjenkins/* * Copyright © 2013-2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authored by: Alan Griffiths */ #ifndef MIR_OPTIONS_DEFAULT_CONFIGURATION_H_ #define MIR_OPTIONS_DEFAULT_CONFIGURATION_H_ #include "mir/options/configuration.h" #include "mir/options/program_option.h" #include namespace mir { class SharedLibrary; namespace options { class DefaultConfiguration : public Configuration { public: DefaultConfiguration(int argc, char const* argv[]); DefaultConfiguration(int argc, char const* argv[], std::string const& config_file); DefaultConfiguration( int argc, char const* argv[], std::function const& handler); DefaultConfiguration( int argc, char const* argv[], std::function const& handler, std::string const& config_file); virtual ~DefaultConfiguration() = default; // add_options() allows users to add their own options. This MUST be called // before the first invocation of the_options() - typically during initialization. boost::program_options::options_description_easy_init add_options(); private: // MUST be the first member to ensure it's destroyed last, lest we attempt to // call destructors in DSOs we've unloaded. std::shared_ptr platform_graphics_library; std::string const config_file; void add_platform_options(); // accessed via the base interface, when access to add_options() has been "lost" std::shared_ptr the_options() const override; virtual void parse_arguments( boost::program_options::options_description desc, ProgramOption& options, int argc, char const* argv[]) const; virtual void parse_environment( boost::program_options::options_description& desc, ProgramOption& options) const; virtual void parse_config_file( boost::program_options::options_description& desc, ProgramOption& options) const; int const argc; char const** const argv; std::function const unparsed_arguments_handler; std::shared_ptr const program_options; std::shared_ptr