diff --git a/BUILD.gn b/BUILD.gn
new file mode 100755
index 0000000000000000000000000000000000000000..f2453a7c7454800c18abfd1e172fb87a8a434fae
--- /dev/null
+++ b/BUILD.gn
@@ -0,0 +1,541 @@
+import("//build/ohos.gni")
+import("//third_party/wayland_standard/wayland_protocol.gni")
+
+
+this is test for ljqc 2021-07-21
+
+
+## Generate Wayland Protocols {{{
+wayland_protocol("text_cursor_position_protocol") {
+ sources = [ "protocol/text-cursor-position.xml" ]
+}
+wayland_protocol("weston_content_protection_protocol") {
+ sources = [ "protocol/weston-content-protection.xml" ]
+}
+wayland_protocol("weston_touch_calibration_protocol") {
+ sources = [ "protocol/weston-touch-calibration.xml" ]
+}
+wayland_protocol("weston_direct_display_protocol") {
+ sources = [ "protocol/weston-direct-display.xml" ]
+}
+wayland_protocol("weston_screenshooter_protocol") {
+ sources = [ "protocol/weston-screenshooter.xml" ]
+}
+wayland_protocol("ivi_application_protocol") {
+ sources = [ "protocol/ivi-application.xml" ]
+}
+wayland_protocol("drm_auth_protocol") {
+ sources = [ "protocol/drm-auth.xml" ]
+}
+
+## Generate Wayland Protocols }}}
+
+## Build libshared.a {{{
+config("libshared_config") {
+ visibility = [ ":*" ]
+
+ include_dirs = [
+ "include",
+ "//third_party/wayland_standard/src",
+ "//third_party/wayland-ivi-extension_standard/include",
+ ]
+
+ cflags = [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ]
+}
+
+config("libshared_public_config") {
+ include_dirs = [ "shared" ]
+
+ cflags = []
+}
+
+ohos_static_library("libshared") {
+ sources = [
+ "shared/config-parser.c",
+ "shared/file-util.c",
+ "shared/matrix.c",
+ "shared/option-parser.c",
+ "shared/os-compatibility.c",
+ ]
+
+ configs = [ ":libshared_config" ]
+
+ public_configs = [ ":libshared_public_config" ]
+
+ deps = []
+
+ public_deps = [ "//third_party/wayland_standard:libwayland_server" ]
+}
+
+## Build libshared.a }}}
+
+## Build libweston.so {{{
+config("libweston_config") {
+ visibility = [ ":*" ]
+
+ include_dirs = [
+ "//third_party/weston/include",
+ "libweston",
+ "//third_party/wayland_standard/src",
+ "//third_party/wayland-ivi-extension_standard/include",
+ "//third_party/weston",
+ ]
+
+ cflags = [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wno-unused-function",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wno-implicit-function-declaration",
+ ]
+}
+
+config("libweston_public_config") {
+ include_dirs = [
+ "include",
+ "libweston/backend-drm",
+ "//third_party/bounds_checking_function/include",
+ ]
+
+ cflags = []
+}
+
+ohos_shared_library("libweston") {
+ sources = [
+ "libweston/animation.c",
+ "libweston/bindings.c",
+ "libweston/clipboard.c",
+ "libweston/compositor.c",
+ "libweston/content-protection.c",
+ "libweston/data-device.c",
+ "libweston/input.c",
+ "libweston/launcher-direct.c",
+ "libweston/launcher-util.c",
+ "libweston/launcher-weston-launch.c",
+ "libweston/linux-dmabuf.c",
+ "libweston/linux-explicit-synchronization.c",
+ "libweston/linux-sync-file.c",
+ "libweston/pixel-formats.c",
+ "libweston/pixman-renderer.c",
+ "libweston/plugin-registry.c",
+ "libweston/screenshooter.c",
+ "libweston/tde-render-part.c",
+ "libweston/touch-calibration.c",
+ "libweston/vertex-clipping.c",
+ "libweston/weston-direct-display.c",
+ "libweston/zoom.c",
+ ]
+
+ configs = [
+ ":libweston_config",
+ ":display_gfx_public_config",
+ ]
+
+ public_configs = [
+ ":libweston_public_config",
+ ":drm-backend_public_config",
+ ]
+
+ deps = [
+ ":libdisplay_gfx",
+ "//foundation/graphic/standard/prebuilts/librarys/display_gralloc:libdisplay_gralloc",
+ "//third_party/libdrm:libdrm",
+ "//third_party/libinput:libinput-third",
+ "//utils/native/base:utils",
+ ]
+
+ public_deps = [
+ ":libshared",
+ ":text_cursor_position_protocol",
+ ":weston_content_protection_protocol",
+ ":weston_direct_display_protocol",
+ ":weston_touch_calibration_protocol",
+ "//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog",
+ "//third_party/libxkbcommon:libxkbcommon",
+ "//third_party/pixman:libpixman",
+ "//third_party/wayland-protocols_standard:input_timestamps_unstable_v1_protocol",
+ "//third_party/wayland-protocols_standard:linux_dmabuf_unstable_v1_protocol",
+ "//third_party/wayland-protocols_standard:linux_explicit_synchronization_unstable_v1_protocol",
+ "//third_party/wayland-protocols_standard:pointer_constraints_unstable_v1_protocol",
+ "//third_party/wayland-protocols_standard:presentation_time_protocol",
+ "//third_party/wayland-protocols_standard:relative_pointer_unstable_v1_protocol",
+ "//third_party/wayland-protocols_standard:viewporter_protocol",
+ "//third_party/wayland-protocols_standard:xdg_output_unstable_v1_protocol",
+ ]
+
+ part_name = "graphic_standard"
+ subsystem_name = "graphic"
+}
+
+## Build libweston.so }}}
+
+## Build libexec_weston.a {{{
+config("libexec_weston_config") {
+ visibility = [ ":*" ]
+
+ include_dirs = [
+ "libweston",
+ ".",
+ ]
+
+ cflags = [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ ]
+}
+
+config("libexec_weston_public_config") {
+ include_dirs = []
+
+ cflags = []
+}
+
+ohos_static_library("libexec_weston") {
+ sources = [
+ "compositor/main.c",
+ "compositor/text-backend.c",
+ "compositor/weston-screenshooter.c",
+ ]
+
+ configs = [ ":libexec_weston_config" ]
+
+ public_configs = [ ":libexec_weston_public_config" ]
+
+ deps = [
+ ":weston_screenshooter_protocol",
+ "//third_party/libinput:libinput-third",
+ "//third_party/wayland-protocols_standard:input_method_unstable_v1_protocol",
+ "//third_party/wayland-protocols_standard:text_input_unstable_v1_protocol",
+ ]
+
+ public_deps = [
+ ":libweston",
+ "//third_party/wayland_standard:wayland_core_protocol",
+ ]
+}
+
+## Build libexec_weston.a }}}
+
+## Build weston {{{
+config("weston_config") {
+ visibility = [ ":*" ]
+
+ include_dirs = []
+
+ cflags = [
+ "-Wall",
+ "-Werror",
+ ]
+}
+
+config("weston_public_config") {
+ include_dirs = []
+
+ cflags = []
+}
+
+ohos_executable("weston") {
+ install_enable = true
+
+ sources = [ "compositor/executable.c" ]
+
+ configs = [ ":weston_config" ]
+
+ public_configs = [ ":weston_public_config" ]
+
+ deps = [ ":libexec_weston" ]
+
+ public_deps = []
+
+ subsystem_name = "graphic"
+ part_name = "graphic_standard"
+}
+
+## Build weston }}}
+
+## Build libweston-desktop.so {{{
+config("libweston-desktop_config") {
+ visibility = [ ":*" ]
+
+ include_dirs = [ "." ]
+
+ cflags = [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-function",
+ ]
+}
+
+config("libweston-desktop_public_config") {
+ include_dirs = [ "include/libweston-desktop" ]
+
+ cflags = []
+}
+
+ohos_shared_library("libweston-desktop") {
+ sources = [
+ "libweston-desktop/client.c",
+ "libweston-desktop/libweston-desktop.c",
+ "libweston-desktop/seat.c",
+ "libweston-desktop/surface.c",
+ "libweston-desktop/wl-shell.c",
+ "libweston-desktop/xdg-shell-v6.c",
+ "libweston-desktop/xdg-shell.c",
+ ]
+
+ configs = [ ":libweston-desktop_config" ]
+
+ public_configs = [ ":libweston-desktop_public_config" ]
+
+ deps = []
+
+ public_deps = [
+ ":libexec_weston",
+ "//third_party/wayland-protocols_standard:xdg_shell_protocol",
+ "//third_party/wayland-protocols_standard:xdg_shell_unstable_v6_protocol",
+ ]
+
+ part_name = "graphic_standard"
+ subsystem_name = "graphic"
+}
+
+## Build libweston-desktop.so }}}
+
+## Build ivi-shell.so {{{
+config("ivi-shell_config") {
+ visibility = [ ":*" ]
+
+ include_dirs = [ "." ]
+
+ cflags = [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-function",
+ ]
+}
+
+config("ivi-shell_public_config") {
+ include_dirs = []
+
+ cflags = []
+}
+
+ohos_shared_library("ivi-shell") {
+ sources = [
+ "ivi-shell/ivi-layout-transition.c",
+ "ivi-shell/ivi-layout.c",
+ "ivi-shell/ivi-shell.c",
+ ]
+
+ configs = [ ":ivi-shell_config" ]
+
+ public_configs = [ ":ivi-shell_public_config" ]
+
+ deps = [
+ ":ivi_application_protocol",
+ ":libweston-desktop",
+ "//third_party/wayland_standard:wayland_core_protocol",
+ ]
+
+ public_deps = []
+
+ subsystem_name = "graphic"
+ part_name = "graphic_standard"
+}
+
+## Build ivi-shell.so }}}
+
+## Build drm-backend.so {{{
+config("drm-backend_config") {
+ visibility = [ ":*" ]
+
+ include_dirs = [
+ "include",
+ "libweston",
+ "libweston/backend-drm/auth",
+ ".",
+ ]
+
+ cflags = [
+ "-Wall",
+ "-Wno-error",
+ "-Wno-unused-parameter",
+ "-Wno-missing-field-initializers",
+ "-Wno-implicit-function-declaration",
+ "-Wno-unused-variable",
+ "-Wno-unused-function",
+ "-Wno-sometimes-uninitialized",
+ ]
+}
+
+config("drm-backend_public_config") {
+ include_dirs = []
+
+ cflags = []
+}
+
+ohos_executable("simple-preasenation") {
+ sources = [
+ "clients/presentation-shm.c",
+ # "libweston/backend-drm/drm-gbm.c",
+ ]
+
+ configs = [ ":drm-backend_config" ]
+
+ public_configs = [ ":drm-backend_public_config" ]
+
+ deps = [
+ "//third_party/wayland-protocols_standard:presentation_time_protocol",
+ "//third_party/wayland_standard:libwayland_client",
+ "//third_party/wayland_standard:libwayland_cursor",
+ "//third_party/wayland_standard:wayland_core_protocol",
+ "//third_party/weston:ivi_application_protocol",
+ ]
+
+ part_name = "graphic_standard"
+ subsystem_name = "graphic"
+}
+
+ohos_shared_library("drm-backend") {
+ sources = [
+ "libweston/backend-drm/auth/wayland_drm_auth_server.c",
+ "libweston/backend-drm/drm.c",
+ "libweston/backend-drm/fb.c",
+ "libweston/backend-drm/kms.c",
+ "libweston/backend-drm/libbacklight.c",
+ "libweston/backend-drm/modes.c",
+ "libweston/backend-drm/state-helpers.c",
+ "libweston/backend-drm/state-propose.c",
+ "libweston/libinput-device.c",
+ "libweston/libinput-seat.c",
+
+ # "libweston/backend-drm/drm-gbm.c",
+ ]
+
+ configs = [ ":drm-backend_config" ]
+
+ public_configs = [ ":drm-backend_public_config" ]
+
+ deps = [
+ ":drm_auth_protocol",
+ ":libweston",
+ "//third_party/libdrm:libdrm",
+ "//third_party/libinput:libinput-third",
+ "//third_party/wayland-protocols_standard:linux_dmabuf_unstable_v1_protocol",
+ "//third_party/wayland-protocols_standard:presentation_time_protocol",
+ "//third_party/wayland-protocols_standard:relative_pointer_unstable_v1_protocol",
+ "//third_party/wayland_standard:wayland_core_protocol",
+ ]
+
+ public_deps = []
+
+ part_name = "graphic_standard"
+ subsystem_name = "graphic"
+}
+
+## Build drm-backend.so }}}
+
+## Install weston.ini to /system/etc/weston.ini {{{
+ohos_prebuilt_etc("weston.ini") {
+ source = "weston.ini"
+ part_name = "graphic_standard"
+ subsystem_name = "graphic"
+}
+
+## Install weston.ini to /system/etc/weston.ini }}}
+
+## Install weston.rc to /system/etc/init/weston.rc {{{
+ohos_prebuilt_etc("weston.rc") {
+ source = "weston.rc"
+ relative_install_dir = "init"
+ part_name = "graphic_standard"
+ subsystem_name = "graphic"
+}
+
+## Install weston.rc to /system/etc/init/weston.rc }}}
+
+# gl-renderer.so waiting for libEGL & libGLESv2
+### Build gl-renderer.so {{{
+#config("gl-renderer_config") {
+# visibility = [ ":*" ]
+#
+# include_dirs = [
+# "//third_party/weston",
+# "//third_party/wayland_standard/egl",
+# "//third_party/weston/libweston",
+# "//third_party/wayland-ivi-extension_standard/include",
+# ]
+#
+# cflags = [
+# "-Wno-return-type",
+# "-Wno-visibility",
+# "-Wno-unused-function",
+# ]
+#}
+#
+#config("gl-renderer_public_config") {
+# include_dirs = [
+# ]
+#
+# cflags = [
+# ]
+#}
+#
+#ohos_shared_library("gl-renderer") {
+# sources = [
+# "libweston/renderer-gl/egl-glue.c",
+# "libweston/renderer-gl/gl-renderer.c",
+# "libweston/vertex-clipping.c",
+# ]
+#
+# configs = [
+# ":gl-renderer_config",
+# ]
+#
+# public_configs = [
+# ":gl-renderer_public_config",
+# ]
+#
+# deps = [
+# ":libweston",
+# "//third_party/wayland-protocols_standard:linux_dmabuf_unstable_v1_protocol",
+# ]
+#
+# asdk_deps = [
+# "shared_library:libEGL",
+# "shared_library:libGLESv2",
+# ]
+#
+# public_deps = [
+# ]
+#
+# subsystem_name = "graphic"
+# part_name = "graphic_standard"
+#}
+### Build gl-renderer.so }}}
+
+## Prebuilt libdisplay_gfx.so {{{
+config("display_gfx_public_config") {
+ include_dirs = [ "//drivers/peripheral/display/interfaces/include" ]
+
+ libs = [ "//device/hisilicon/hardware/display/libs/hispark_taurus/ext/libdisplay_gfx.z.so" ]
+}
+
+ohos_prebuilt_shared_library("libdisplay_gfx") {
+ source = "//device/hisilicon/hardware/display/libs/hispark_taurus/ext/libdisplay_gfx.z.so"
+
+ public_configs = [ ":display_gfx_public_config" ]
+
+ part_name = "graphic_standard"
+ subsystem_name = "graphic"
+}
+## Prebuilt libdisplay_gfx.so }}}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000000000000000000000000000000000000..98fa565d439b44ad21815b3fa9317563aafde906
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,372 @@
+Contributing to Weston
+=======================
+
+Finding something to work on
+----------------------------
+
+Weston's development is [tracked on GitLab](https://gitlab.freedesktop.org/wayland/weston).
+In addition to reviewing code submissions (see below), we use the issue tracker
+to discuss both bugfixes and development of new features.
+
+The '[good for new contributors](https://gitlab.freedesktop.org/wayland/weston/issues?label_name%5B%5D=Good+for+new+contributors)'
+label is used for issues the development team thinks are a good place to begin
+working on Weston. These issues cover features or bugfixes which are small,
+self-contained, don't require much specific background knowledge, and aren't
+blocked by more complex work.
+
+If you have picked an issue you would like to work on, you may want to mention
+in the issue tracker that you would like to pick it up. You can also discuss
+it with the developers in the issue tracker, or on the
+[mailing list](https://lists.freedesktop.org/mailman/listinfo/wayland-devel).
+Many developers also use IRC through [Freenode](https://freenode.net)'s
+`#wayland` channel; however you may need to wait some time for a response on
+IRC, which requires keeping your client connected. If you cannot stay for a
+long time (potentially some hours due to timezone differences), then you
+may want to send your question to the list or issue tracker instead.
+
+
+Sending patches
+---------------
+
+Patches should be sent via
+[GitLab merge requests](https://docs.gitlab.com/ce/gitlab-basics/add-merge-request.html).
+Weston is
+[hosted on freedesktop.org's GitLab](https://gitlab.freedesktop.org/wayland/weston/):
+in order to submit code, you should create an account on this GitLab instance,
+fork the core Weston repository, push your changes to a branch in your new
+repository, and then submit these patches for review through a merge request.
+
+Weston formerly accepted patches via `git-send-email`, sent to
+**wayland-devel\@lists.freedesktop.org**; these were
+[tracked using Patchwork](https://patchwork.freedesktop.org/projects/wayland/).
+Some old patches continue to be sent this way, and we may accept small new
+patches sent to the list, but please send all new patches through GitLab merge
+requests.
+
+Formatting and separating commits
+---------------------------------
+
+Unlike many projects using GitHub and GitLab, Weston has a
+[linear, 'recipe' style history](http://www.bitsnbites.eu/git-history-work-log-vs-recipe/).
+This means that every commit should be small, digestible, stand-alone, and
+functional. Rather than a purely chronological commit history like this:
+
+ doc: final docs for view transforms
+ fix tests when disabled, redo broken doc formatting
+ better transformed-view iteration (thanks Hannah!)
+ try to catch more cases in tests
+ tests: add new spline test
+ fix compilation on splines
+ doc: notes on reticulating splines
+ compositor: add spline reticulation for view transforms
+
+we aim to have a clean history which only reflects the final state, broken up
+into functional groupings:
+
+ compositor: add spline reticulation for view transforms
+ compositor: new iterator for view transforms
+ tests: add view-transform correctness tests
+ doc: fix Doxygen formatting for view transforms
+
+This ensures that the final patch series only contains the final state,
+without the changes and missteps taken along the development process.
+
+The first line of a commit message should contain a prefix indicating
+what part is affected by the patch followed by one sentence that
+describes the change. For examples:
+
+ compositor-drm: Support modifiers for drm_fb
+
+and
+
+ input: do not forward unmatched touch-ups
+
+If in doubt what prefix to use, look at other commits that change the
+same file(s) as the patch being sent.
+
+The body of the commit message should describe what the patch changes
+and why, and also note any particular side effects. This shouldn't be
+empty on most of the cases. It shouldn't take a lot of effort to write
+a commit message for an obvious change, so an empty commit message
+body is only acceptable if the questions "What?" and "Why?" are already
+answered on the one-line summary.
+
+The lines of the commit message should have at most 76 characters, to
+cope with the way git log presents them.
+
+See [notes on commit messages] for a recommended reading on writing commit
+messages.
+
+Your patches should also include a Signed-off-by line with your name and
+email address which indicates that you agree to the
+[Developer's Certificate of Origin 1.1](DCO-1.1.txt).
+If you're not the patch's original author, you should
+also gather S-o-b's by them (and/or whomever gave the patch to you.) The
+significance of this is that it certifies that you created the patch,
+that it was created under an appropriate open source license, or
+provided to you under those terms. This lets us indicate a chain of
+responsibility for the copyright status of the code.
+
+We won't reject patches that lack S-o-b, but it is strongly recommended.
+
+When you re-send patches, revised or not, it would be very good to document the
+changes compared to the previous revision in the commit message and/or the
+merge request. If you have already received Reviewed-by or Acked-by tags, you
+should evaluate whether they still apply and include them in the respective
+commit messages. Otherwise the tags may be lost, reviewers miss the credit they
+deserve, and the patches may cause redundant review effort.
+
+
+Tracking patches and following up
+---------------------------------
+
+Once submitted to GitLab, your patches will be reviewed by the Weston
+development team on GitLab. Review may be entirely positive and result in your
+code landing instantly, in which case, great! You're done. However, we may ask
+you to make some revisions: fixing some bugs we've noticed, working to a
+slightly different design, or adding documentation and tests.
+
+If you do get asked to revise the patches, please bear in mind the notes above.
+You should use `git rebase -i` to make revisions, so that your patches follow
+the clear linear split documented above. Following that split makes it easier
+for reviewers to understand your work, and to verify that the code you're
+submitting is correct.
+
+A common request is to split single large patch into multiple patches. This can
+happen, for example, if when adding a new feature you notice a bug in Weston's
+core which you need to fix to progress. Separating these changes into separate
+commits will allow us to verify and land the bugfix quickly, pushing part of
+your work for the good of everyone, whilst revision and discussion continues on
+the larger feature part. It also allows us to direct you towards reviewers who
+best understand the different areas you are working on.
+
+When you have made any requested changes, please rebase the commits, verify
+that they still individually look good, then force-push your new branch to
+GitLab. This will update the merge request and notify everyone subscribed to
+your merge request, so they can review it again.
+
+There are also
+[many GitLab CLI clients](https://about.gitlab.com/applications/#cli-clients),
+if you prefer to avoid the web interface. It may be difficult to follow review
+comments without using the web interface though, so we do recommend using this
+to go through the review process, even if you use other clients to track the
+list of available patches.
+
+
+Coding style
+------------
+
+You should follow the style of the file you're editing. In general, we
+try to follow the rules below.
+
+**Note: this file uses spaces due to markdown rendering issues for tabs.
+ Code must be indented using tabs.**
+
+- indent with tabs, and a tab is always 8 characters wide
+- opening braces are on the same line as the if statement;
+- no braces in an if-body with just one statement;
+- if one of the branches of an if-else condition has braces, then the
+ other branch should also have braces;
+- there is always an empty line between variable declarations and the
+ code;
+
+```c
+static int
+my_function(void)
+{
+ int a = 0;
+
+ if (a)
+ b();
+ else
+ c();
+
+ if (a) {
+ b();
+ c();
+ } else {
+ d();
+ }
+}
+```
+
+- lines should be less than 80 characters wide;
+- when breaking lines with functions calls, the parameters are aligned
+ with the opening parentheses;
+- when assigning a variable with the result of a function call, if the
+ line would be longer we break it around the equal '=' sign if it makes
+ sense;
+
+```c
+ long_variable_name =
+ function_with_a_really_long_name(parameter1, parameter2,
+ parameter3, parameter4);
+
+ x = function_with_a_really_long_name(parameter1, parameter2,
+ parameter3, parameter4);
+```
+
+Conduct
+=======
+
+As a freedesktop.org project, Wayland follows the Contributor Covenant,
+found at:
+https://www.freedesktop.org/wiki/CodeOfConduct
+
+Please conduct yourself in a respectful and civilised manner when
+interacting with community members on mailing lists, IRC, or bug
+trackers. The community represents the project as a whole, and abusive
+or bullying behaviour is not tolerated by the project.
+
+
+Licensing
+=========
+
+Weston is licensed with the intention to be usable anywhere X.org is.
+Originally, X.org was covered under the MIT X11 license, but changed to
+the MIT Expat license. Similarly, Weston was covered initially as MIT
+X11 licensed, but changed to the MIT Expat license, following in X.org's
+footsteps. Other than wording, the two licenses are substantially the
+same, with the exception of a no-advertising clause in X11 not included
+in Expat.
+
+New source code files should specify the MIT Expat license in their
+boilerplate, as part of the copyright statement.
+
+
+Review
+======
+
+All patches, even trivial ones, require at least one positive review
+(Reviewed-by). Additionally, if no Reviewed-by's have been given by
+people with commit access, there needs to be at least one Acked-by from
+someone with commit access. A person with commit access is expected to be
+able to evaluate the patch with respect to the project scope and architecture.
+
+The below review guidelines are intended to be interpreted in spirit, not by
+the letter. There may be circumstances where some guidelines are better
+ignored. We rely very much on the judgement of reviewers and commit rights
+holders.
+
+During review, the following matters should be checked:
+
+- The commit message explains why the change is being made.
+
+- The code fits the project's scope.
+
+- The code license is the same MIT licence the project generally uses.
+
+- Stable ABI or API is not broken.
+
+- Stable ABI or API additions must be justified by actual use cases, not only
+by speculation. They must also be documented, and it is strongly recommended to
+include tests exercising the additions in the test suite.
+
+- The code fits the existing software architecture, e.g. no layering
+violations.
+
+- The code is correct and does not introduce new failures for existing users,
+does not add new corner-case bugs, and does not introduce new compiler
+warnings.
+
+- The patch does what it says in the commit message and changes nothing else.
+
+- The patch is a single logical change. If the commit message addresses
+multiple points, it is a hint that the commit might need splitting up.
+
+- A bug fix should target the underlying root cause instead of hiding symptoms.
+If a complete fix is not practical, partial fixes are acceptable if they come
+with code comments and filed Gitlab issues for the remaining bugs.
+
+- The bug root cause rule applies to external software components as well, e.g.
+do not work around kernel driver issues in userspace.
+
+- The test suite passes.
+
+- The code does not depend on API or ABI which has no working free open source
+implementation.
+
+- The code is not dead or untestable. E.g. if there are no free open source
+software users for it then it is effectively dead code.
+
+- The code is written to be easy to understand, or if code cannot be clear
+enough on its own there are code comments to explain it.
+
+- The code is minimal, i.e. prefer refactor and re-use when possible unless
+clarity suffers.
+
+- The code adheres to the style guidelines.
+
+- In a patch series, every intermediate step adheres to the above guidelines.
+
+
+Commit rights
+=============
+
+Commit rights will be granted to anyone who requests them and fulfills the
+below criteria:
+
+- Submitted some (10 as a rule of thumb) non-trivial (not just simple
+ spelling fixes and whitespace adjustment) patches that have been merged
+ already.
+
+- Are actively participating in public discussions about their work (on the
+ mailing list or IRC). This should not be interpreted as a requirement to
+ review other peoples patches but just make sure that patch submission isn't
+ one-way communication. Cross-review is still highly encouraged.
+
+- Will be regularly contributing further patches. This includes regular
+ contributors to other parts of the open source graphics stack who only
+ do the occasional development in this project.
+
+- Agrees to use their commit rights in accordance with the documented merge
+ criteria, tools, and processes.
+
+To apply for commit rights, create a new issue in gitlab for the respective
+project and give it the "accounts" label.
+
+Committers are encouraged to request their commit rights get removed when they
+no longer contribute to the project. Commit rights will be reinstated when they
+come back to the project.
+
+Maintainers and committers should encourage contributors to request commit
+rights, especially junior contributors tend to underestimate their skills.
+
+
+Stabilising for releases
+========================
+
+A release cycle ends with a stable release which also starts a new cycle and
+lifts any code freezes. Gradual code freezing towards a stable release starts
+with an alpha release. The release stages of a cycle are:
+
+- **Alpha release**:
+ Signified by version number #.#.91.
+ Major features must have landed before this. Major features include
+ invasive code motion and refactoring, high risk changes, and new stable
+ library ABI.
+
+- **Beta release**:
+ Signified by version number #.#.92.
+ Minor features must have landed before this. Minor features include all
+ new features that are not major, low risk changes, clean-ups, and
+ documentation. Stable ABI that was new in the alpha release can be removed
+ before a beta release if necessary.
+
+- **Release candidates (RC)**:
+ Signified by version number #.#.93 and up to #.#.99.
+ Bug fixes that are not release critical must have landed before this.
+ Release critical bug fixes can still be landed after this, but they may
+ call for another RC.
+
+- **Stable release**:
+ Signified by version number #.#.0.
+ Ideally no changes since the last RC.
+
+Mind that version #.#.90 is never released. It is used during development when
+no code freeze is in effect. Stable branches and point releases are not covered
+by the above.
+
+
+[git documentation]: http://git-scm.com/documentation
+[notes on commit messages]: http://who-t.blogspot.de/2009/12/on-commit-messages.html
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000000000000000000000000000000000000..fa4a8c48e13fbeccb02a740bff2ee11df553773d
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,26 @@
+
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+---
+
+The above is the version of the MIT "Expat" License used by X.org:
+
+ http://cgit.freedesktop.org/xorg/xserver/tree/COPYING
diff --git a/DCO-1.1.txt b/DCO-1.1.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1ffe05c3e20022e9aa7dca0499567b2d5aaecfb0
--- /dev/null
+++ b/DCO-1.1.txt
@@ -0,0 +1,38 @@
+test
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+1 Letterman Drive
+Suite D4700
+San Francisco, CA, 94129
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ license and I have the right under that license to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source license (unless I am
+ permitted to submit under a different license), as indicated
+ in the file; or
+
+(c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+(d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source license(s) involved.
diff --git a/OAT.xml b/OAT.xml
new file mode 100644
index 0000000000000000000000000000000000000000..90cd95bd7bf389554b50bd03e71b5a3c5f32ff5e
--- /dev/null
+++ b/OAT.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+ COPYING
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/README.OpenSource b/README.OpenSource
new file mode 100644
index 0000000000000000000000000000000000000000..9d1c806e7a8b8b47b9555b361be02b769111952f
--- /dev/null
+++ b/README.OpenSource
@@ -0,0 +1,11 @@
+[
+ {
+ "Name": "weston",
+ "License": "MIT license",
+ "License File": "COPYING",
+ "Version Number": "9.0.0",
+ "Owner": "lizheng2@huawei.com",
+ "Upstream URL": "https://wayland.freedesktop.org/releases/weston-9.0.0.tar.xz",
+ "Description": "Weston is the reference implementation of a Wayland compositor, as well as a useful environment in and of itself."
+ }
+]
diff --git a/README.md b/README.md
index 25dc9639cb9d50c73819a7faa7993d45dbb6882a..30da9afa413d6161774f3b154f5c1b87f0e4e821 100644
--- a/README.md
+++ b/README.md
@@ -1,53 +1,361 @@
-# python
+测试
+Weston
+======
-# Python for OHOS
+
-#### 介绍
-这个仓库是为了能够在OpenHarmony设备上使用 Python 进行应用程序开发而创建。
-[1. 使用Python开发OpenHarmony设备程序(0-初体验)](https://harmonyos.51cto.com/posts/1887)
+Weston is the reference implementation of a Wayland compositor, as well as a
+useful environment in and of itself.
+Out of the box, Weston provides a very basic desktop, or a full-featured
+environment for non-desktop uses such as automotive, embedded, in-flight,
+industrial, kiosks, set-top boxes and TVs. It also provides a library allowing
+other projects to build their own full-featured environments on top of Weston's
+core.
-## 以下说明针对lite-python文件夹下的代码
-#### 软件架构
-这个仓库下的 Baseline 是 [MicroPython v1.13](https://github.com/micropython/micropython/tree/v1.13),在 MicroPython 的基础上进行了必要的剪裁以满足 OHOS 上的应用开发需求。
+The core focus of Weston is correctness and reliability. Weston aims to be lean
+and fast, but more importantly, to be predictable. Whilst Weston does have known
+bugs and shortcomings, we avoid unknown or variable behaviour as much as
+possible, including variable performance such as occasional spikes in frame
+display time.
-#### 编译说明
-1. 编译环境:
- 1)OS - Ubuntu 16+
- 2)Make - 3.81+
- 3)Python - 3.8+
-2. 配置交叉编译器:
- 1)打开源码根目录中的 Makefile
- 2)对变量 CROSS_COMPILE 进行赋值,如:CROSS_COMPILE ?= /home/harmony/gcc_riscv32/bin/riscv32-unknown-elf-
-3. 在源码根目录中执行 make
+A small suite of example or demo clients are also provided: though they can be
+useful in themselves, their main purpose is to be an example or test case for
+others building compositors or clients.
-#### 使用说明
-1. 将编译得到的库文件 //build/libdtpython.a 拷贝到 //vendor/hisi/hi3861/hi3861/build/libs 目录下,如图:
-
-2. 在设备应用中加载 Python 并执行代码
+If you are after a more mainline desktop experience, the
+[GNOME](https://www.gnome.org) and [KDE](https://www.kde.org) projects provide
+full-featured desktop environments built on the Wayland protocol. Many other
+projects also exist providing Wayland clients and desktop environments: you are
+not limited to just what you can find in Weston.
-```
-#include "dtpython.h"
+Reporting issues and contributing
+=================================
-extern const char* c_test_py; // test.py
-extern const char* c_another_py; // another.py
+Weston's development is
+[hosted on freedesktop.org GitLab](https://gitlab.freedesktop.org/wayland/weston/).
+Please also see [the contributing document](CONTRIBUTING.md), which details how
+to make code or non-technical contributions to Weston.
-static void DTPython_Demo_Entry(void)
-{
- printf("[DTPython_Demo] DTPython_Demo_Entry()\n");
+Building Weston
+===============
- DTPython_Init(); // 初始化Python环境
+Weston is built using [Meson](https://mesonbuild.com/). Weston often depends
+on the current release versions of
+[Wayland](https://gitlab.freedesktop.org/wayland/wayland) and
+[wayland-protocols](https://cgit.freedesktop.org/wayland/wayland-protocols).
- DTPython_RunCode("print(\'Python Code Begin\')"); // 执行Python语句:print('Python Code Begin')
-
- DTPython_RunCode("s = \'HOS Device Development\'"); // 执行Python语句:s = 'HOS Device Development'
- DTPython_RunCode("print(s)"); // 执行Python语句:print(s)
+If necessary, the latest Meson can be installed as a user with:
- DTPython_RunCode(c_test_py); // 模拟执行Python文件:DTPython_RunFile("test.py");
- DTPython_RunCode(c_another_py); // 模拟执行Python文件:DTPython_RunFile("another.py");
+ $ pip3 install --user meson
- DTPython_RunCode("print(\'Python Code End\')"); // 执行Python语句:print('Python Code End')
+Weston's Meson build does not do autodetection and it defaults to all
+features enabled, which means you likely hit missing dependencies on the first
+try. If a dependency is avoidable through a build option, the error message
+should tell you what option can be used to avoid it. You may need to disable
+several features if you want to avoid certain dependencies.
- DTPython_Deinit(); // 清理Python环境
-}
-```
+ $ git clone https://gitlab.freedesktop.org/wayland/weston.git
+ $ cd weston
+ $ meson build/ --prefix=...
+ $ ninja -C build/ install
+ $ cd ..
+
+The `meson` command populates the build directory. This step can
+fail due to missing dependencies. Any build options you want can be added on
+that line, e.g. `meson build/ --prefix=... -Ddemo-clients=false`. All the build
+options can be found in the file [meson_options.txt](meson_options.txt).
+
+Once the build directory has been successfully populated, you can inspect the
+configuration with `meson configure build/`. If you need to change an
+option, you can do e.g. `meson configure build/ -Ddemo-clients=false`.
+
+Every push to the Weston master repository and its forks is built using GitLab
+CI. [Reading the configuration](.gitlab-ci.yml) may provide a useful example of
+how to build and install Weston.
+
+More [detailed documentation on building Weston](https://wayland.freedesktop.org/building.html)
+is available on the Wayland site. There are also more details on
+[how to run and write tests](https://wayland.freedesktop.org/testing.html).
+
+For building the documentation see [weston-doc](#weston-doc).
+
+Running Weston
+==============
+
+Once Weston is installed, most users can simply run it by typing `weston`. This
+will launch Weston inside whatever environment you launch it from: when launched
+from a text console, it will take over that console. When launched from inside
+an existing Wayland or X11 session, it will start a 'nested' instance of Weston
+inside a window in that session.
+
+Help is available by running `weston --help`, or `man weston`, which will list
+the available configuration options and display backends. It can also be
+configured through a file on disk; more information on this can be found through
+`man weston.ini`.
+
+In some special cases, such as when running remotely or without logind's session
+control, Weston may not be able to run directly from a text console. In these
+situations, you can instead execute the `weston-launch` helper, which will gain
+privileged access to input and output devices by running as root, then granting
+access to the main Weston binary running as your user. Running Weston this way
+is not recommended unless necessary.
+
+Weston-doc
+==========
+
+For documenting weston we use [sphinx](http://www.sphinx-doc.org/en/master/)
+together with [breathe](https://breathe.readthedocs.io/en/latest/) that
+understands XMLs databases generated by doxygen. So far, this is a compromise
+until better tools are available in order to remove the doxygen
+dependency. You should be able to install both sphinx and breathe extension
+using pip3 command, or your package manager.
+Doxygen should be available using your distribution package manager.
+
+Once those are set-up, run `meson` with `-Ddoc=true` option in order to enable
+building the documentation. Installation will place the documentation in the
+prefix's path under datadir (i.e., `share/doc`).
+
+Adding and improving documentation
+----------------------------------
+
+For re-generating the documentation a special `docs` target has been added.
+Although first time you build (and subsequently install) weston, you'll see the
+documentation being built, updates to the spinx documentation files or to the
+source files will only be updated when using `docs` target!
+
+Example:
+
+~~~~
+$ ninja install # generates and installs the documentation
+# time passes, hack hack, add doc in sources or rST files
+$ ninja install # not sufficient, docs will not be updated
+$ ninja docs && ninja install # run 'docs' then install
+~~~~
+
+Improving/adding documentation can be done by modifying rST files under
+`doc/sphinx/` directory or by modifying the source code using doxygen
+directives.
+
+Libweston
+=========
+
+Libweston is an effort to separate the re-usable parts of Weston into
+a library. Libweston provides most of the boring and tedious bits of
+correctly implementing core Wayland protocols and interfacing with
+input and output systems, so that people who just want to write a new
+"Wayland window manager" (WM) or a small desktop environment (DE) can
+focus on the WM part.
+
+Libweston was first introduced in Weston 1.12, and is expected to
+continue evolving through many Weston releases before it achieves a
+stable API and feature completeness.
+
+Libweston's primary purpose is exporting an API for creating Wayland
+compositors. Libweston's secondary purpose is to export the weston_config API
+so that third party plugins and helper programs can read `weston.ini` if they
+want to. However, these two scopes are orthogonal and independent. At no point
+will the compositor functionality use or depend on the weston_config
+functionality.
+
+
+API/ABI (in)stability and parallel installability
+-------------------------------------------------
+
+As libweston's API surface is huge, it is impossible to get it right
+in one go. Therefore developers reserve the right to break the API/ABI and bump
+the major version to signify that. For git snapshots of the master branch, the
+API/ABI can break any time without warning.
+
+Libweston major can be bumped only once during a development cycle. This should
+happen on the first patch that breaks the API or ABI. Further breaks before the
+next Weston major.0.0 release do not cause a bump. This means that libweston
+API and ABI are allowed to break also after an alpha release, up to the final
+release. However, breaks after alpha should be judged by the usual practices
+for allowing minor features, fixes only, or critical fixes only.
+
+To make things tolerable for libweston users despite API/ABI breakages,
+different libweston major versions are designed to be perfectly
+parallel-installable. This way external projects can easily depend on a
+particular API/ABI-version. Thus they do not have to fight over which
+ABI-version is installed in a user's system. This allows a user to install many
+different compositors each requiring a different libweston ABI-version without
+tricks or conflicts.
+
+Note, that versions of Weston itself will not be parallel-installable,
+only libweston is.
+
+For more information about parallel installability, see
+http://ometer.com/parallel.html
+
+
+Versioning scheme
+-----------------
+
+In order to provide consistent, easy to use versioning, libweston
+follows the rules in the Apache Portable Runtime Project
+http://apr.apache.org/versioning.html.
+
+The document provides the full details, with the gist summed below:
+ - Major - backward incompatible changes.
+ - Minor - new backward compatible features.
+ - Patch - internal (implementation specific) fixes.
+
+Weston and libweston have separate version numbers in meson.build. All
+releases are made by the Weston version number. Libweston version number
+matches the Weston version number in all releases except maybe pre-releases.
+Pre-releases have the Weston micro version 91 or greater.
+
+A pre-release is allowed to install a libweston version greater than the Weston
+version in case libweston major was bumped. In that case, the libweston version
+must be Weston major + 1.
+
+Pkg-config files are named after libweston major, but carry the Weston version
+number. This means that Weston pre-release 2.1.91 may install libweston-3.pc
+for the future libweston 3.0.0, but the .pc file says the version is still
+2.1.91. When a libweston user wants to depend on the fully stable API and ABI
+of a libweston major, he should use (e.g. for major 3):
+
+ PKG_CHECK_MODULES(LIBWESTON, [libweston-3 >= 3.0.0])
+
+Depending only on libweston-3 without a specific version number still allows
+pre-releases which might have different API or ABI.
+
+
+Forward compatibility
+---------------------
+
+Inspired by ATK, Qt and KDE programs/libraries, libjpeg-turbo, GDK,
+NetworkManager, js17, lz4 and many others, libweston uses a macro to restrict
+the API visible to the developer - REQUIRE_LIBWESTON_API_VERSION.
+
+Note that different projects focus on different aspects - upper and/or lower
+version check, default to visible/hidden old/new symbols and so on.
+
+libweston aims to guard all newly introduced API, in order to prevent subtle
+breaks that a simple recompile (against a newer version) might cause.
+
+The macro is of the format 0x$MAJOR$MINOR and does not include PATCH version.
+As mentioned in the Versioning scheme section, the latter does not reflect any
+user visible API changes, thus should be not considered part of the API version.
+
+All new symbols should be guarded by the macro like the example given below:
+
+~~~~
+#if REQUIRE_LIBWESTON_API_VERSION >= 0x0101
+
+bool
+weston_ham_sandwich(void);
+
+#endif
+~~~~
+
+In order to use the said symbol, the one will have a similar code in their
+configure.ac:
+
+~~~~
+PKG_CHECK_MODULES(LIBWESTON, [libweston-1 >= 1.1])
+AC_DEFINE(REQUIRE_LIBWESTON_API_VERSION, [0x0101])
+~~~~
+
+If the user is _not_ interested in forward compatibility, they can use 0xffff
+or similar high value. Yet doing so is not recommended.
+
+
+Libweston design goals
+----------------------
+
+The high-level goal of libweston is to decouple the compositor from
+the shell implementation (what used to be shell plugins).
+
+Thus, instead of launching 'weston' with various arguments to choose the
+shell, one would launch the shell itself, e.g. 'weston-desktop',
+'weston-ivi', 'orbital', etc. The main executable (the hosting program)
+will implement the shell, while libweston will be used for a fundamental
+compositor implementation.
+
+Libweston is also intended for use by other project developers who want
+to create new "Wayland WMs".
+
+Details:
+
+- All configuration and user interfaces will be outside of libweston.
+ This includes command line parsing, configuration files, and runtime
+ (graphical) UI.
+
+- The hosting program (main executable) will be in full control of all
+ libweston options. Libweston should not have user settable options
+ that would work behind the hosting program's back, except perhaps
+ debugging features and such.
+
+- Signal handling will be outside of libweston.
+
+- Child process execution and management will be outside of libweston.
+
+- The different backends (drm, fbdev, x11, etc) will be an internal
+ detail of libweston. Libweston will not support third party
+ backends. However, hosting programs need to handle
+ backend-specific configuration due to differences in behaviour and
+ available features.
+
+- Renderers will be libweston internal details too, though again the
+ hosting program may affect the choice of renderer if the backend
+ allows, and maybe set renderer-specific options.
+
+- plugin design ???
+
+- xwayland ???
+
+- weston-launch is still with libweston even though it can only launch
+ Weston and nothing else. We would like to allow it to launch any compositor,
+ but since it gives by design root access to input devices and DRM, how can
+ we restrict it to intended programs?
+
+There are still many more details to be decided.
+
+
+For packagers
+-------------
+
+Always build Weston with --with-cairo=image.
+
+The Weston project is (will be) intended to be split into several
+binary packages, each with its own dependencies. The maximal split
+would be roughly like this:
+
+- libweston (minimal dependencies):
+ + headless backend
+ + wayland backend
+
+- gl-renderer (depends on GL libs etc.)
+
+- drm-backend (depends on libdrm, libgbm, libudev, libinput, ...)
+
+- x11-backend (depends of X11/xcb libs)
+
+- xwayland (depends on X11/xcb libs)
+
+- fbdev-backend (depends on libudev...)
+
+- rdp-backend (depends on freerdp)
+
+- weston (the executable, not parallel-installable):
+ + desktop shell
+ + ivi-shell
+ + fullscreen shell
+ + weston-info (deprecated), weston-terminal, etc. we install by default
+ + screen-share
+
+- weston demos (not parallel-installable)
+ + weston-simple-* programs
+ + possibly all the programs we build but do not install by
+ default
+
+- and possibly more...
+
+Everything should be parallel-installable across libweston major
+ABI-versions (libweston-1.so, libweston-2.so, etc.), except those
+explicitly mentioned.
+
+Weston's build may not sanely allow this yet, but this is the
+intention.
diff --git a/clients/calibrator.c b/clients/calibrator.c
new file mode 100644
index 0000000000000000000000000000000000000000..21ca876f7224adfeac65e40f628b9f78f3ac8838
--- /dev/null
+++ b/clients/calibrator.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "window.h"
+#include "shared/helpers.h"
+#include
+
+/* Our points for the calibration must be not be on a line */
+static const struct {
+ float x_ratio, y_ratio;
+} test_ratios[] = {
+ { 0.20, 0.40 },
+ { 0.80, 0.60 },
+ { 0.40, 0.80 }
+};
+
+struct calibrator {
+ struct tests {
+ int32_t drawn_x, drawn_y;
+ int32_t clicked_x, clicked_y;
+ } tests[ARRAY_LENGTH(test_ratios)];
+ int current_test;
+
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+};
+
+/*
+ * Calibration algorithm:
+ *
+ * The equation we want to apply at event time where x' and y' are the
+ * calibrated co-ordinates.
+ *
+ * x' = Ax + By + C
+ * y' = Dx + Ey + F
+ *
+ * For example "zero calibration" would be A=1.0 B=0.0 C=0.0, D=0.0, E=1.0,
+ * and F=0.0.
+ *
+ * With 6 unknowns we need 6 equations to find the constants:
+ *
+ * x1' = Ax1 + By1 + C
+ * y1' = Dx1 + Ey1 + F
+ * ...
+ * x3' = Ax3 + By3 + C
+ * y3' = Dx3 + Ey3 + F
+ *
+ * In matrix form:
+ *
+ * x1' x1 y1 1 A
+ * x2' = x2 y2 1 x B
+ * x3' x3 y3 1 C
+ *
+ * So making the matrix M we can find the constants with:
+ *
+ * A x1'
+ * B = M^-1 x x2'
+ * C x3'
+ *
+ * (and similarly for D, E and F)
+ *
+ * For the calibration the desired values x, y are the same values at which
+ * we've drawn at.
+ *
+ */
+static void
+finish_calibration (struct calibrator *calibrator)
+{
+ struct weston_matrix m;
+ struct weston_matrix inverse;
+ struct weston_vector x_calib, y_calib;
+ int i;
+
+
+ /*
+ * x1 y1 1 0
+ * x2 y2 1 0
+ * x3 y3 1 0
+ * 0 0 0 1
+ */
+ memset(&m, 0, sizeof(m));
+ for (i = 0; i < (int)ARRAY_LENGTH(test_ratios); i++) {
+ m.d[i] = calibrator->tests[i].clicked_x;
+ m.d[i + 4] = calibrator->tests[i].clicked_y;
+ m.d[i + 8] = 1;
+ }
+ m.d[15] = 1;
+
+ weston_matrix_invert(&inverse, &m);
+
+ memset(&x_calib, 0, sizeof(x_calib));
+ memset(&y_calib, 0, sizeof(y_calib));
+
+ for (i = 0; i < (int)ARRAY_LENGTH(test_ratios); i++) {
+ x_calib.f[i] = calibrator->tests[i].drawn_x;
+ y_calib.f[i] = calibrator->tests[i].drawn_y;
+ }
+
+ /* Multiples into the vector */
+ weston_matrix_transform(&inverse, &x_calib);
+ weston_matrix_transform(&inverse, &y_calib);
+
+ printf ("Calibration values: %f %f %f %f %f %f\n",
+ x_calib.f[0], x_calib.f[1], x_calib.f[2],
+ y_calib.f[0], y_calib.f[1], y_calib.f[2]);
+
+ exit(0);
+}
+
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct calibrator *calibrator = data;
+ int32_t x, y;
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED && button == BTN_LEFT) {
+ input_get_position(input, &x, &y);
+ calibrator->tests[calibrator->current_test].clicked_x = x;
+ calibrator->tests[calibrator->current_test].clicked_y = y;
+
+ calibrator->current_test--;
+ if (calibrator->current_test < 0)
+ finish_calibration(calibrator);
+ }
+
+ widget_schedule_redraw(widget);
+}
+
+static void
+touch_handler(struct widget *widget, struct input *input, uint32_t serial,
+ uint32_t time, int32_t id, float x, float y, void *data)
+{
+ struct calibrator *calibrator = data;
+
+ calibrator->tests[calibrator->current_test].clicked_x = x;
+ calibrator->tests[calibrator->current_test].clicked_y = y;
+ calibrator->current_test--;
+
+ if (calibrator->current_test < 0)
+ finish_calibration(calibrator);
+
+ widget_schedule_redraw(widget);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct calibrator *calibrator = data;
+ struct rectangle allocation;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ int32_t drawn_x, drawn_y;
+
+ widget_get_allocation(calibrator->widget, &allocation);
+ surface = window_get_surface(calibrator->window);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
+ cairo_paint(cr);
+
+ drawn_x = test_ratios[calibrator->current_test].x_ratio * allocation.width;
+ drawn_y = test_ratios[calibrator->current_test].y_ratio * allocation.height;
+
+ calibrator->tests[calibrator->current_test].drawn_x = drawn_x;
+ calibrator->tests[calibrator->current_test].drawn_y = drawn_y;
+
+ cairo_translate(cr, drawn_x, drawn_y);
+ cairo_set_line_width(cr, 2.0);
+ cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
+ cairo_move_to(cr, 0, -10.0);
+ cairo_line_to(cr, 0, 10.0);
+ cairo_stroke(cr);
+ cairo_move_to(cr, -10.0, 0);
+ cairo_line_to(cr, 10.0, 0.0);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static struct calibrator *
+calibrator_create(struct display *display, bool enable_button)
+{
+ struct calibrator *calibrator;
+
+ calibrator = malloc(sizeof *calibrator);
+ if (calibrator == NULL)
+ return NULL;
+
+ calibrator->window = window_create(display);
+ calibrator->widget = window_add_widget(calibrator->window, calibrator);
+ window_set_title(calibrator->window, "Wayland calibrator");
+ calibrator->display = display;
+
+ calibrator->current_test = ARRAY_LENGTH(test_ratios) - 1;
+
+ if (enable_button)
+ widget_set_button_handler(calibrator->widget, button_handler);
+ widget_set_touch_down_handler(calibrator->widget, touch_handler);
+ widget_set_redraw_handler(calibrator->widget, redraw_handler);
+
+ window_set_fullscreen(calibrator->window, 1);
+
+ return calibrator;
+}
+
+static void
+calibrator_destroy(struct calibrator *calibrator)
+{
+ widget_destroy(calibrator->widget);
+ window_destroy(calibrator->window);
+ free(calibrator);
+}
+
+static void
+help(const char *name)
+{
+ fprintf(stderr, "Usage: %s [args...]\n", name);
+ fprintf(stderr, " -m, --enable-mouse Enable mouse for testing the touchscreen\n");
+ fprintf(stderr, " -h, --help Display this help message\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *display;
+ struct calibrator *calibrator;
+ int c;
+ bool enable_mouse = 0;
+ struct option opts[] = {
+ { "enable-mouse", no_argument, NULL, 'm' },
+ { "help", no_argument, NULL, 'h' },
+ { 0, 0, NULL, 0 }
+ };
+
+ while ((c = getopt_long(argc, argv, "mh", opts, NULL)) != -1) {
+ switch (c) {
+ case 'm':
+ enable_mouse = 1;
+ break;
+ case 'h':
+ help(argv[0]);
+ exit(EXIT_FAILURE);
+ default:
+ break;
+ }
+ }
+
+ display = display_create(&argc, argv);
+
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ calibrator = calibrator_create(display, enable_mouse);
+
+ if (!calibrator)
+ return -1;
+
+ display_run(display);
+
+ calibrator_destroy(calibrator);
+ display_destroy(display);
+
+ return 0;
+}
+
diff --git a/clients/clickdot.c b/clients/clickdot.c
new file mode 100644
index 0000000000000000000000000000000000000000..4e8a945e7c055a94959ed909ee6a51752ed2f666
--- /dev/null
+++ b/clients/clickdot.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2012 Collabora, Ltd.
+ * Copyright © 2012 Jonas Ådahl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "window.h"
+#include "shared/helpers.h"
+#include "shared/xalloc.h"
+
+struct clickdot {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+
+ cairo_surface_t *buffer;
+
+ struct {
+ int32_t x, y;
+ } dot;
+
+ struct {
+ int32_t x, y;
+ int32_t old_x, old_y;
+ } line;
+
+ int reset;
+
+ struct input *cursor_timeout_input;
+ struct toytimer cursor_timeout;
+};
+
+static void
+draw_line(struct clickdot *clickdot, cairo_t *cr,
+ struct rectangle *allocation)
+{
+ cairo_t *bcr;
+ cairo_surface_t *tmp_buffer = NULL;
+
+ if (clickdot->reset) {
+ tmp_buffer = clickdot->buffer;
+ clickdot->buffer = NULL;
+ clickdot->line.x = -1;
+ clickdot->line.y = -1;
+ clickdot->line.old_x = -1;
+ clickdot->line.old_y = -1;
+ clickdot->reset = 0;
+ }
+
+ if (clickdot->buffer == NULL) {
+ clickdot->buffer =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ allocation->width,
+ allocation->height);
+ bcr = cairo_create(clickdot->buffer);
+ cairo_set_source_rgba(bcr, 0, 0, 0, 0);
+ cairo_rectangle(bcr,
+ 0, 0,
+ allocation->width, allocation->height);
+ cairo_fill(bcr);
+ }
+ else
+ bcr = cairo_create(clickdot->buffer);
+
+ if (tmp_buffer) {
+ cairo_set_source_surface(bcr, tmp_buffer, 0, 0);
+ cairo_rectangle(bcr, 0, 0,
+ allocation->width, allocation->height);
+ cairo_clip(bcr);
+ cairo_paint(bcr);
+
+ cairo_surface_destroy(tmp_buffer);
+ }
+
+ if (clickdot->line.x != -1 && clickdot->line.y != -1) {
+ if (clickdot->line.old_x != -1 &&
+ clickdot->line.old_y != -1) {
+ cairo_set_line_width(bcr, 2.0);
+ cairo_set_source_rgb(bcr, 1, 1, 1);
+ cairo_translate(bcr,
+ -allocation->x, -allocation->y);
+
+ cairo_move_to(bcr,
+ clickdot->line.old_x,
+ clickdot->line.old_y);
+ cairo_line_to(bcr,
+ clickdot->line.x,
+ clickdot->line.y);
+
+ cairo_stroke(bcr);
+ }
+
+ clickdot->line.old_x = clickdot->line.x;
+ clickdot->line.old_y = clickdot->line.y;
+ }
+ cairo_destroy(bcr);
+
+ cairo_set_source_surface(cr, clickdot->buffer,
+ allocation->x, allocation->y);
+ cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
+ cairo_rectangle(cr,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+ cairo_clip(cr);
+ cairo_paint(cr);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ static const double r = 10.0;
+ struct clickdot *clickdot = data;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ struct rectangle allocation;
+
+ widget_get_allocation(clickdot->widget, &allocation);
+
+ surface = window_get_surface(clickdot->window);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle(cr,
+ allocation.x,
+ allocation.y,
+ allocation.width,
+ allocation.height);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_fill(cr);
+
+ draw_line(clickdot, cr, &allocation);
+
+ cairo_translate(cr, clickdot->dot.x + 0.5, clickdot->dot.y + 0.5);
+ cairo_set_line_width(cr, 1.0);
+ cairo_set_source_rgb(cr, 0.1, 0.9, 0.9);
+ cairo_move_to(cr, 0.0, -r);
+ cairo_line_to(cr, 0.0, r);
+ cairo_move_to(cr, -r, 0.0);
+ cairo_line_to(cr, r, 0.0);
+ cairo_arc(cr, 0.0, 0.0, r, 0.0, 2.0 * M_PI);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct clickdot *clickdot = data;
+
+ window_schedule_redraw(clickdot->window);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym,
+ enum wl_keyboard_key_state state, void *data)
+{
+ struct clickdot *clickdot = data;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_Escape:
+ display_exit(clickdot->display);
+ break;
+ }
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct clickdot *clickdot = data;
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED && button == BTN_LEFT)
+ input_get_position(input, &clickdot->dot.x, &clickdot->dot.y);
+
+ widget_schedule_redraw(widget);
+}
+
+static void
+cursor_timeout_reset(struct clickdot *clickdot)
+{
+ toytimer_arm_once_usec(&clickdot->cursor_timeout, 500 * 1000);
+}
+
+static int
+motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct clickdot *clickdot = data;
+ clickdot->line.x = x;
+ clickdot->line.y = y;
+
+ window_schedule_redraw(clickdot->window);
+
+ cursor_timeout_reset(clickdot);
+ clickdot->cursor_timeout_input = input;
+
+ return CURSOR_BLANK;
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height,
+ void *data)
+{
+ struct clickdot *clickdot = data;
+
+ clickdot->reset = 1;
+}
+
+static void
+leave_handler(struct widget *widget,
+ struct input *input, void *data)
+{
+ struct clickdot *clickdot = data;
+
+ clickdot->reset = 1;
+}
+
+static void
+cursor_timeout_func(struct toytimer *tt)
+{
+ struct clickdot *clickdot =
+ container_of(tt, struct clickdot, cursor_timeout);
+
+ input_set_pointer_image(clickdot->cursor_timeout_input,
+ CURSOR_LEFT_PTR);
+}
+
+static struct clickdot *
+clickdot_create(struct display *display)
+{
+ struct clickdot *clickdot;
+
+ clickdot = xzalloc(sizeof *clickdot);
+ clickdot->window = window_create(display);
+ clickdot->widget = window_frame_create(clickdot->window, clickdot);
+ window_set_title(clickdot->window, "Wayland ClickDot");
+ clickdot->display = display;
+ clickdot->buffer = NULL;
+
+ window_set_key_handler(clickdot->window, key_handler);
+ window_set_user_data(clickdot->window, clickdot);
+ window_set_keyboard_focus_handler(clickdot->window,
+ keyboard_focus_handler);
+
+ widget_set_redraw_handler(clickdot->widget, redraw_handler);
+ widget_set_button_handler(clickdot->widget, button_handler);
+ widget_set_motion_handler(clickdot->widget, motion_handler);
+ widget_set_resize_handler(clickdot->widget, resize_handler);
+ widget_set_leave_handler(clickdot->widget, leave_handler);
+
+ widget_schedule_resize(clickdot->widget, 500, 400);
+ clickdot->dot.x = 250;
+ clickdot->dot.y = 200;
+ clickdot->line.x = -1;
+ clickdot->line.y = -1;
+ clickdot->line.old_x = -1;
+ clickdot->line.old_y = -1;
+ clickdot->reset = 0;
+
+ toytimer_init(&clickdot->cursor_timeout, CLOCK_MONOTONIC,
+ display, cursor_timeout_func);
+
+ return clickdot;
+}
+
+static void
+clickdot_destroy(struct clickdot *clickdot)
+{
+ toytimer_fini(&clickdot->cursor_timeout);
+ if (clickdot->buffer)
+ cairo_surface_destroy(clickdot->buffer);
+ widget_destroy(clickdot->widget);
+ window_destroy(clickdot->window);
+ free(clickdot);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *display;
+ struct clickdot *clickdot;
+
+ display = display_create(&argc, argv);
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ clickdot = clickdot_create(display);
+
+ display_run(display);
+
+ clickdot_destroy(clickdot);
+ display_destroy(display);
+
+ return 0;
+}
diff --git a/clients/cliptest.c b/clients/cliptest.c
new file mode 100644
index 0000000000000000000000000000000000000000..89983850a3e708567aad88eedb1719c7c9214b9c
--- /dev/null
+++ b/clients/cliptest.c
@@ -0,0 +1,637 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ * Copyright © 2012 Rob Clark
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/* cliptest: for debugging calculate_edges() function.
+ * controls:
+ * clip box position: mouse left drag, keys: w a s d
+ * clip box size: mouse right drag, keys: i j k l
+ * surface orientation: mouse wheel, keys: n m
+ * surface transform disable key: r
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "libweston/vertex-clipping.h"
+#include "shared/xalloc.h"
+#include "window.h"
+
+typedef float GLfloat;
+
+struct geometry {
+ pixman_box32_t clip;
+
+ pixman_box32_t surf;
+ float s; /* sin phi */
+ float c; /* cos phi */
+ float phi;
+};
+
+struct weston_view {
+ struct {
+ int enabled;
+ } transform;
+
+ struct geometry *geometry;
+};
+
+static void
+weston_view_to_global_float(struct weston_view *view,
+ float sx, float sy, float *x, float *y)
+{
+ struct geometry *g = view->geometry;
+
+ /* pure rotation around origin by sine and cosine */
+ *x = g->c * sx + g->s * sy;
+ *y = -g->s * sx + g->c * sy;
+}
+
+/* ---------------------- copied begins -----------------------*/
+/* Keep this in sync with what is in gl-renderer.c! */
+
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+#define min(a, b) (((a) > (b)) ? (b) : (a))
+
+/*
+ * Compute the boundary vertices of the intersection of the global coordinate
+ * aligned rectangle 'rect', and an arbitrary quadrilateral produced from
+ * 'surf_rect' when transformed from surface coordinates into global coordinates.
+ * The vertices are written to 'ex' and 'ey', and the return value is the
+ * number of vertices. Vertices are produced in clockwise winding order.
+ * Guarantees to produce either zero vertices, or 3-8 vertices with non-zero
+ * polygon area.
+ */
+static int
+calculate_edges(struct weston_view *ev, pixman_box32_t *rect,
+ pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey)
+{
+
+ struct clip_context ctx;
+ int i, n;
+ GLfloat min_x, max_x, min_y, max_y;
+ struct polygon8 surf = {
+ { surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 },
+ { surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 },
+ 4
+ };
+
+ ctx.clip.x1 = rect->x1;
+ ctx.clip.y1 = rect->y1;
+ ctx.clip.x2 = rect->x2;
+ ctx.clip.y2 = rect->y2;
+
+ /* transform surface to screen space: */
+ for (i = 0; i < surf.n; i++)
+ weston_view_to_global_float(ev, surf.x[i], surf.y[i],
+ &surf.x[i], &surf.y[i]);
+
+ /* find bounding box: */
+ min_x = max_x = surf.x[0];
+ min_y = max_y = surf.y[0];
+
+ for (i = 1; i < surf.n; i++) {
+ min_x = min(min_x, surf.x[i]);
+ max_x = max(max_x, surf.x[i]);
+ min_y = min(min_y, surf.y[i]);
+ max_y = max(max_y, surf.y[i]);
+ }
+
+ /* First, simple bounding box check to discard early transformed
+ * surface rects that do not intersect with the clip region:
+ */
+ if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) ||
+ (min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1))
+ return 0;
+
+ /* Simple case, bounding box edges are parallel to surface edges,
+ * there will be only four edges. We just need to clip the surface
+ * vertices to the clip rect bounds:
+ */
+ if (!ev->transform.enabled)
+ return clip_simple(&ctx, &surf, ex, ey);
+
+ /* Transformed case: use a general polygon clipping algorithm to
+ * clip the surface rectangle with each side of 'rect'.
+ * The algorithm is Sutherland-Hodgman, as explained in
+ * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm
+ * but without looking at any of that code.
+ */
+ n = clip_transformed(&ctx, &surf, ex, ey);
+
+ if (n < 3)
+ return 0;
+
+ return n;
+}
+
+
+/* ---------------------- copied ends -----------------------*/
+
+static void
+geometry_set_phi(struct geometry *g, float phi)
+{
+ g->phi = phi;
+ g->s = sin(phi);
+ g->c = cos(phi);
+}
+
+static void
+geometry_init(struct geometry *g)
+{
+ g->clip.x1 = -50;
+ g->clip.y1 = -50;
+ g->clip.x2 = -10;
+ g->clip.y2 = -10;
+
+ g->surf.x1 = -20;
+ g->surf.y1 = -20;
+ g->surf.x2 = 20;
+ g->surf.y2 = 20;
+
+ geometry_set_phi(g, 0.0);
+}
+
+struct ui_state {
+ uint32_t button;
+ int down;
+
+ int down_pos[2];
+ struct geometry geometry;
+};
+
+struct cliptest {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+ int fullscreen;
+
+ struct ui_state ui;
+
+ struct geometry geometry;
+ struct weston_view view;
+};
+
+static void
+draw_polygon_closed(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
+{
+ int i;
+
+ cairo_move_to(cr, x[0], y[0]);
+ for (i = 1; i < n; i++)
+ cairo_line_to(cr, x[i], y[i]);
+ cairo_line_to(cr, x[0], y[0]);
+}
+
+static void
+draw_polygon_labels(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
+{
+ char str[16];
+ int i;
+
+ for (i = 0; i < n; i++) {
+ snprintf(str, 16, "%d", i);
+ cairo_move_to(cr, x[i], y[i]);
+ cairo_show_text(cr, str);
+ }
+}
+
+static void
+draw_coordinates(cairo_t *cr, double ox, double oy, GLfloat *x, GLfloat *y, int n)
+{
+ char str[64];
+ int i;
+ cairo_font_extents_t ext;
+
+ cairo_font_extents(cr, &ext);
+ for (i = 0; i < n; i++) {
+ snprintf(str, 64, "%d: %14.9f, %14.9f", i, x[i], y[i]);
+ cairo_move_to(cr, ox, oy + ext.height * (i + 1));
+ cairo_show_text(cr, str);
+ }
+}
+
+static void
+draw_box(cairo_t *cr, pixman_box32_t *box, struct weston_view *view)
+{
+ GLfloat x[4], y[4];
+
+ if (view) {
+ weston_view_to_global_float(view, box->x1, box->y1, &x[0], &y[0]);
+ weston_view_to_global_float(view, box->x2, box->y1, &x[1], &y[1]);
+ weston_view_to_global_float(view, box->x2, box->y2, &x[2], &y[2]);
+ weston_view_to_global_float(view, box->x1, box->y2, &x[3], &y[3]);
+ } else {
+ x[0] = box->x1; y[0] = box->y1;
+ x[1] = box->x2; y[1] = box->y1;
+ x[2] = box->x2; y[2] = box->y2;
+ x[3] = box->x1; y[3] = box->y2;
+ }
+
+ draw_polygon_closed(cr, x, y, 4);
+}
+
+static void
+draw_geometry(cairo_t *cr, struct weston_view *view,
+ GLfloat *ex, GLfloat *ey, int n)
+{
+ struct geometry *g = view->geometry;
+ float cx, cy;
+
+ draw_box(cr, &g->surf, view);
+ cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.4);
+ cairo_fill(cr);
+ weston_view_to_global_float(view, g->surf.x1 - 4, g->surf.y1 - 4, &cx, &cy);
+ cairo_arc(cr, cx, cy, 1.5, 0.0, 2.0 * M_PI);
+ if (view->transform.enabled == 0)
+ cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.8);
+ cairo_fill(cr);
+
+ draw_box(cr, &g->clip, NULL);
+ cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.4);
+ cairo_fill(cr);
+
+ if (n) {
+ draw_polygon_closed(cr, ex, ey, n);
+ cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
+ cairo_stroke(cr);
+
+ cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.5);
+ draw_polygon_labels(cr, ex, ey, n);
+ }
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct cliptest *cliptest = data;
+ struct geometry *g = cliptest->view.geometry;
+ struct rectangle allocation;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ GLfloat ex[8];
+ GLfloat ey[8];
+ int n;
+
+ n = calculate_edges(&cliptest->view, &g->clip, &g->surf, ex, ey);
+
+ widget_get_allocation(cliptest->widget, &allocation);
+
+ surface = window_get_surface(cliptest->window);
+ cr = cairo_create(surface);
+ widget_get_allocation(cliptest->widget, &allocation);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_clip(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+ cairo_paint(cr);
+
+ cairo_translate(cr, allocation.x, allocation.y);
+ cairo_set_line_width(cr, 1.0);
+ cairo_move_to(cr, allocation.width / 2.0, 0.0);
+ cairo_line_to(cr, allocation.width / 2.0, allocation.height);
+ cairo_move_to(cr, 0.0, allocation.height / 2.0);
+ cairo_line_to(cr, allocation.width, allocation.height / 2.0);
+ cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1.0);
+ cairo_stroke(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_push_group(cr);
+ cairo_translate(cr, allocation.width / 2.0,
+ allocation.height / 2.0);
+ cairo_scale(cr, 4.0, 4.0);
+ cairo_set_line_width(cr, 0.5);
+ cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
+ cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size(cr, 5.0);
+ draw_geometry(cr, &cliptest->view, ex, ey, n);
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+
+ cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0);
+ cairo_select_font_face(cr, "monospace", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 12.0);
+ draw_coordinates(cr, 10.0, 10.0, ex, ey, n);
+
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+}
+
+static int
+motion_handler(struct widget *widget, struct input *input,
+ uint32_t time, float x, float y, void *data)
+{
+ struct cliptest *cliptest = data;
+ struct ui_state *ui = &cliptest->ui;
+ struct geometry *ref = &ui->geometry;
+ struct geometry *geom = &cliptest->geometry;
+ float dx, dy;
+
+ if (!ui->down)
+ return CURSOR_LEFT_PTR;
+
+ dx = (x - ui->down_pos[0]) * 0.25;
+ dy = (y - ui->down_pos[1]) * 0.25;
+
+ switch (ui->button) {
+ case BTN_LEFT:
+ geom->clip.x1 = ref->clip.x1 + dx;
+ geom->clip.y1 = ref->clip.y1 + dy;
+ /* fall through */
+ case BTN_RIGHT:
+ geom->clip.x2 = ref->clip.x2 + dx;
+ geom->clip.y2 = ref->clip.y2 + dy;
+ break;
+ default:
+ return CURSOR_LEFT_PTR;
+ }
+
+ widget_schedule_redraw(cliptest->widget);
+ return CURSOR_BLANK;
+}
+
+static void
+button_handler(struct widget *widget, struct input *input,
+ uint32_t time, uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct cliptest *cliptest = data;
+ struct ui_state *ui = &cliptest->ui;
+
+ ui->button = button;
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ ui->down = 1;
+ input_get_position(input, &ui->down_pos[0], &ui->down_pos[1]);
+ } else {
+ ui->down = 0;
+ ui->geometry = cliptest->geometry;
+ }
+}
+
+static void
+axis_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t axis, wl_fixed_t value, void *data)
+{
+ struct cliptest *cliptest = data;
+ struct geometry *geom = &cliptest->geometry;
+
+ if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
+ return;
+
+ geometry_set_phi(geom, geom->phi +
+ (M_PI / 12.0) * wl_fixed_to_double(value));
+ cliptest->view.transform.enabled = 1;
+
+ widget_schedule_redraw(cliptest->widget);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym,
+ enum wl_keyboard_key_state state, void *data)
+{
+ struct cliptest *cliptest = data;
+ struct geometry *g = &cliptest->geometry;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_Escape:
+ display_exit(cliptest->display);
+ return;
+ case XKB_KEY_w:
+ g->clip.y1 -= 1;
+ g->clip.y2 -= 1;
+ break;
+ case XKB_KEY_a:
+ g->clip.x1 -= 1;
+ g->clip.x2 -= 1;
+ break;
+ case XKB_KEY_s:
+ g->clip.y1 += 1;
+ g->clip.y2 += 1;
+ break;
+ case XKB_KEY_d:
+ g->clip.x1 += 1;
+ g->clip.x2 += 1;
+ break;
+ case XKB_KEY_i:
+ g->clip.y2 -= 1;
+ break;
+ case XKB_KEY_j:
+ g->clip.x2 -= 1;
+ break;
+ case XKB_KEY_k:
+ g->clip.y2 += 1;
+ break;
+ case XKB_KEY_l:
+ g->clip.x2 += 1;
+ break;
+ case XKB_KEY_n:
+ geometry_set_phi(g, g->phi + (M_PI / 24.0));
+ cliptest->view.transform.enabled = 1;
+ break;
+ case XKB_KEY_m:
+ geometry_set_phi(g, g->phi - (M_PI / 24.0));
+ cliptest->view.transform.enabled = 1;
+ break;
+ case XKB_KEY_r:
+ geometry_set_phi(g, 0.0);
+ cliptest->view.transform.enabled = 0;
+ break;
+ default:
+ return;
+ }
+
+ widget_schedule_redraw(cliptest->widget);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct cliptest *cliptest = data;
+
+ window_schedule_redraw(cliptest->window);
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct cliptest *cliptest = data;
+
+ cliptest->fullscreen ^= 1;
+ window_set_fullscreen(window, cliptest->fullscreen);
+}
+
+static struct cliptest *
+cliptest_create(struct display *display)
+{
+ struct cliptest *cliptest;
+
+ cliptest = xzalloc(sizeof *cliptest);
+ cliptest->view.geometry = &cliptest->geometry;
+ cliptest->view.transform.enabled = 0;
+ geometry_init(&cliptest->geometry);
+ geometry_init(&cliptest->ui.geometry);
+
+ cliptest->window = window_create(display);
+ cliptest->widget = window_frame_create(cliptest->window, cliptest);
+ window_set_title(cliptest->window, "cliptest");
+ cliptest->display = display;
+
+ window_set_user_data(cliptest->window, cliptest);
+ widget_set_redraw_handler(cliptest->widget, redraw_handler);
+ widget_set_button_handler(cliptest->widget, button_handler);
+ widget_set_motion_handler(cliptest->widget, motion_handler);
+ widget_set_axis_handler(cliptest->widget, axis_handler);
+
+ window_set_keyboard_focus_handler(cliptest->window,
+ keyboard_focus_handler);
+ window_set_key_handler(cliptest->window, key_handler);
+ window_set_fullscreen_handler(cliptest->window, fullscreen_handler);
+
+ /* set minimum size */
+ widget_schedule_resize(cliptest->widget, 200, 100);
+
+ /* set current size */
+ widget_schedule_resize(cliptest->widget, 500, 400);
+
+ return cliptest;
+}
+
+static struct timespec begin_time;
+
+static void
+reset_timer(void)
+{
+ clock_gettime(CLOCK_MONOTONIC, &begin_time);
+}
+
+static double
+read_timer(void)
+{
+ struct timespec t;
+
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return (double)(t.tv_sec - begin_time.tv_sec) +
+ 1e-9 * (t.tv_nsec - begin_time.tv_nsec);
+}
+
+static int
+benchmark(void)
+{
+ struct weston_view view;
+ struct geometry geom;
+ GLfloat ex[8], ey[8];
+ int i;
+ double t;
+ const int N = 1000000;
+
+ geom.clip.x1 = -19;
+ geom.clip.y1 = -19;
+ geom.clip.x2 = 19;
+ geom.clip.y2 = 19;
+
+ geom.surf.x1 = -20;
+ geom.surf.y1 = -20;
+ geom.surf.x2 = 20;
+ geom.surf.y2 = 20;
+
+ geometry_set_phi(&geom, 0.0);
+
+ view.transform.enabled = 1;
+ view.geometry = &geom;
+
+ reset_timer();
+ for (i = 0; i < N; i++) {
+ geometry_set_phi(&geom, (float)i / 360.0f);
+ calculate_edges(&view, &geom.clip, &geom.surf, ex, ey);
+ }
+ t = read_timer();
+
+ printf("%d calls took %g s, average %g us/call\n", N, t, t / N * 1e6);
+
+ return 0;
+}
+
+static void
+cliptest_destroy(struct cliptest *cliptest)
+{
+ widget_destroy(cliptest->widget);
+ window_destroy(cliptest->window);
+ free(cliptest);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *d;
+ struct cliptest *cliptest;
+
+ if (argc > 1) {
+ if (argc == 2 && !strcmp(argv[1], "-b"))
+ return benchmark();
+ printf("Usage: %s [OPTIONS]\n -b run benchmark\n", argv[0]);
+ return 1;
+ }
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ cliptest = cliptest_create(d);
+ display_run(d);
+
+ cliptest_destroy(cliptest);
+ display_destroy(d);
+
+ return 0;
+}
diff --git a/clients/confine.c b/clients/confine.c
new file mode 100644
index 0000000000000000000000000000000000000000..6f3845712c49469a991618b1806518cfd23e57bf
--- /dev/null
+++ b/clients/confine.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2012 Collabora, Ltd.
+ * Copyright © 2012 Jonas Ådahl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "window.h"
+#include "shared/helpers.h"
+#include "shared/xalloc.h"
+
+#define NUM_COMPLEX_REGION_RECTS 9
+
+static bool option_complex_confine_region;
+static bool option_help;
+
+struct confine {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+
+ cairo_surface_t *buffer;
+
+ struct {
+ int32_t x, y;
+ int32_t old_x, old_y;
+ } line;
+
+ int reset;
+
+ struct input *cursor_timeout_input;
+ struct toytimer cursor_timeout;
+
+ bool pointer_confined;
+
+ bool complex_confine_region_enabled;
+ bool complex_confine_region_dirty;
+ struct rectangle complex_confine_region[NUM_COMPLEX_REGION_RECTS];
+};
+
+static void
+draw_line(struct confine *confine, cairo_t *cr,
+ struct rectangle *allocation)
+{
+ cairo_t *bcr;
+ cairo_surface_t *tmp_buffer = NULL;
+
+ if (confine->reset) {
+ tmp_buffer = confine->buffer;
+ confine->buffer = NULL;
+ confine->line.x = -1;
+ confine->line.y = -1;
+ confine->line.old_x = -1;
+ confine->line.old_y = -1;
+ confine->reset = 0;
+ }
+
+ if (confine->buffer == NULL) {
+ confine->buffer =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ allocation->width,
+ allocation->height);
+ bcr = cairo_create(confine->buffer);
+ cairo_set_source_rgba(bcr, 0, 0, 0, 0);
+ cairo_rectangle(bcr,
+ 0, 0,
+ allocation->width, allocation->height);
+ cairo_fill(bcr);
+ }
+ else
+ bcr = cairo_create(confine->buffer);
+
+ if (tmp_buffer) {
+ cairo_set_source_surface(bcr, tmp_buffer, 0, 0);
+ cairo_rectangle(bcr, 0, 0,
+ allocation->width, allocation->height);
+ cairo_clip(bcr);
+ cairo_paint(bcr);
+
+ cairo_surface_destroy(tmp_buffer);
+ }
+
+ if (confine->line.x != -1 && confine->line.y != -1) {
+ if (confine->line.old_x != -1 &&
+ confine->line.old_y != -1) {
+ cairo_set_line_width(bcr, 2.0);
+ cairo_set_source_rgb(bcr, 1, 1, 1);
+ cairo_translate(bcr,
+ -allocation->x, -allocation->y);
+
+ cairo_move_to(bcr,
+ confine->line.old_x,
+ confine->line.old_y);
+ cairo_line_to(bcr,
+ confine->line.x,
+ confine->line.y);
+
+ cairo_stroke(bcr);
+ }
+
+ confine->line.old_x = confine->line.x;
+ confine->line.old_y = confine->line.y;
+ }
+ cairo_destroy(bcr);
+
+ cairo_set_source_surface(cr, confine->buffer,
+ allocation->x, allocation->y);
+ cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
+ cairo_rectangle(cr,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+ cairo_clip(cr);
+ cairo_paint(cr);
+}
+
+static void
+calculate_complex_confine_region(struct confine *confine)
+{
+ struct rectangle allocation;
+ int32_t x, y, w, h;
+ struct rectangle *rs = confine->complex_confine_region;
+
+ if (!confine->complex_confine_region_dirty)
+ return;
+
+ widget_get_allocation(confine->widget, &allocation);
+ x = allocation.x;
+ y = allocation.y;
+ w = allocation.width;
+ h = allocation.height;
+
+ /*
+ * The code below constructs a region made up of rectangles that
+ * is then used to set up both an illustrative shaded region in the
+ * widget and a confine region used when confining the pointer.
+ */
+
+ rs[0].x = x + (int)round(w * 0.05);
+ rs[0].y = y + (int)round(h * 0.15);
+ rs[0].width = (int)round(w * 0.35);
+ rs[0].height = (int)round(h * 0.7);
+
+ rs[1].x = rs[0].x + rs[0].width;
+ rs[1].y = y + (int)round(h * 0.45);
+ rs[1].width = (int)round(w * 0.09);
+ rs[1].height = (int)round(h * 0.1);
+
+ rs[2].x = rs[1].x + rs[1].width;
+ rs[2].y = y + (int)round(h * 0.48);
+ rs[2].width = (int)round(w * 0.02);
+ rs[2].height = (int)round(h * 0.04);
+
+ rs[3].x = rs[2].x + rs[2].width;
+ rs[3].y = y + (int)round(h * 0.45);
+ rs[3].width = (int)round(w * 0.09);
+ rs[3].height = (int)round(h * 0.1);
+
+ rs[4].x = rs[3].x + rs[3].width;
+ rs[4].y = y + (int)round(h * 0.15);
+ rs[4].width = (int)round(w * 0.35);
+ rs[4].height = (int)round(h * 0.7);
+
+ rs[5].x = x + (int)round(w * 0.05);
+ rs[5].y = y + (int)round(h * 0.05);
+ rs[5].width = rs[0].width + rs[1].width + rs[2].width +
+ rs[3].width + rs[4].width;
+ rs[5].height = (int)round(h * 0.10);
+
+ rs[6].x = x + (int)round(w * 0.1);
+ rs[6].y = rs[4].y + rs[4].height + (int)round(h * 0.02);
+ rs[6].width = (int)round(w * 0.8);
+ rs[6].height = (int)round(h * 0.03);
+
+ rs[7].x = x + (int)round(w * 0.05);
+ rs[7].y = rs[6].y + rs[6].height;
+ rs[7].width = (int)round(w * 0.9);
+ rs[7].height = (int)round(h * 0.03);
+
+ rs[8].x = x + (int)round(w * 0.1);
+ rs[8].y = rs[7].y + rs[7].height;
+ rs[8].width = (int)round(w * 0.8);
+ rs[8].height = (int)round(h * 0.03);
+
+ confine->complex_confine_region_dirty = false;
+}
+
+static void
+draw_complex_confine_region_mask(struct confine *confine, cairo_t *cr)
+{
+ int i;
+
+ calculate_complex_confine_region(confine);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ for (i = 0; i < NUM_COMPLEX_REGION_RECTS; i++) {
+ cairo_rectangle(cr,
+ confine->complex_confine_region[i].x,
+ confine->complex_confine_region[i].y,
+ confine->complex_confine_region[i].width,
+ confine->complex_confine_region[i].height);
+ cairo_set_source_rgba(cr, 0.14, 0.14, 0.14, 0.9);
+ cairo_fill(cr);
+ }
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct confine *confine = data;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ struct rectangle allocation;
+
+ widget_get_allocation(confine->widget, &allocation);
+
+ surface = window_get_surface(confine->window);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle(cr,
+ allocation.x,
+ allocation.y,
+ allocation.width,
+ allocation.height);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_fill(cr);
+
+ if (confine->complex_confine_region_enabled) {
+ draw_complex_confine_region_mask(confine, cr);
+ }
+
+ draw_line(confine, cr, &allocation);
+
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct confine *confine = data;
+
+ window_schedule_redraw(confine->window);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym,
+ enum wl_keyboard_key_state state, void *data)
+{
+ struct confine *confine = data;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_Escape:
+ display_exit(confine->display);
+ break;
+ case XKB_KEY_BackSpace:
+ cairo_surface_destroy(confine->buffer);
+ confine->buffer = NULL;
+ window_schedule_redraw(confine->window);
+ break;
+ case XKB_KEY_m:
+ window_set_maximized(confine->window,
+ !window_is_maximized(window));
+ break;
+ }
+}
+
+static void
+toggle_pointer_confine(struct confine *confine, struct input *input)
+{
+ if (confine->pointer_confined) {
+ window_unconfine_pointer(confine->window);
+ } else if (confine->complex_confine_region_enabled) {
+ calculate_complex_confine_region(confine);
+ window_confine_pointer_to_rectangles(
+ confine->window,
+ input,
+ confine->complex_confine_region,
+ NUM_COMPLEX_REGION_RECTS);
+
+ } else {
+ window_confine_pointer_to_widget(confine->window,
+ confine->widget,
+ input);
+ }
+
+ confine->pointer_confined = !confine->pointer_confined;
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct confine *confine = data;
+ bool is_pressed = state == WL_POINTER_BUTTON_STATE_PRESSED;
+
+ if (is_pressed && button == BTN_LEFT)
+ toggle_pointer_confine(confine, input);
+ widget_schedule_redraw(widget);
+}
+
+static void
+cursor_timeout_reset(struct confine *confine)
+{
+ toytimer_arm_once_usec(&confine->cursor_timeout, 500 * 1000);
+}
+
+static int
+motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct confine *confine = data;
+ confine->line.x = x;
+ confine->line.y = y;
+
+ window_schedule_redraw(confine->window);
+
+ cursor_timeout_reset(confine);
+ confine->cursor_timeout_input = input;
+
+ return CURSOR_BLANK;
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height,
+ void *data)
+{
+ struct confine *confine = data;
+
+ confine->reset = 1;
+
+ if (confine->complex_confine_region_enabled) {
+ confine->complex_confine_region_dirty = true;
+
+ if (confine->pointer_confined) {
+ calculate_complex_confine_region(confine);
+ window_update_confine_rectangles(
+ confine->window,
+ confine->complex_confine_region,
+ NUM_COMPLEX_REGION_RECTS);
+ }
+ }
+}
+
+static void
+leave_handler(struct widget *widget,
+ struct input *input, void *data)
+{
+ struct confine *confine = data;
+
+ confine->reset = 1;
+}
+
+static void
+cursor_timeout_func(struct toytimer *tt)
+{
+ struct confine *confine =
+ container_of(tt, struct confine, cursor_timeout);
+
+ input_set_pointer_image(confine->cursor_timeout_input,
+ CURSOR_LEFT_PTR);
+}
+
+static void
+pointer_unconfined(struct window *window, struct input *input, void *data)
+{
+ struct confine *confine = data;
+
+ confine->pointer_confined = false;
+}
+
+static struct confine *
+confine_create(struct display *display)
+{
+ struct confine *confine;
+
+ confine = xzalloc(sizeof *confine);
+ confine->window = window_create(display);
+ confine->widget = window_frame_create(confine->window, confine);
+ window_set_title(confine->window, "Wayland Confine");
+ confine->display = display;
+ confine->buffer = NULL;
+
+ window_set_key_handler(confine->window, key_handler);
+ window_set_user_data(confine->window, confine);
+ window_set_keyboard_focus_handler(confine->window,
+ keyboard_focus_handler);
+ window_set_pointer_confined_handler(confine->window,
+ NULL,
+ pointer_unconfined);
+
+ widget_set_redraw_handler(confine->widget, redraw_handler);
+ widget_set_button_handler(confine->widget, button_handler);
+ widget_set_motion_handler(confine->widget, motion_handler);
+ widget_set_resize_handler(confine->widget, resize_handler);
+ widget_set_leave_handler(confine->widget, leave_handler);
+
+ widget_schedule_resize(confine->widget, 500, 400);
+ confine->line.x = -1;
+ confine->line.y = -1;
+ confine->line.old_x = -1;
+ confine->line.old_y = -1;
+ confine->reset = 0;
+
+ toytimer_init(&confine->cursor_timeout, CLOCK_MONOTONIC,
+ display, cursor_timeout_func);
+
+ return confine;
+}
+
+static void
+confine_destroy(struct confine *confine)
+{
+ toytimer_fini(&confine->cursor_timeout);
+ if (confine->buffer)
+ cairo_surface_destroy(confine->buffer);
+ widget_destroy(confine->widget);
+ window_destroy(confine->window);
+ free(confine);
+}
+
+static const struct weston_option confine_options[] = {
+ { WESTON_OPTION_BOOLEAN, "complex-confine-region", 0, &option_complex_confine_region },
+ { WESTON_OPTION_BOOLEAN, "help", 0, &option_help },
+};
+
+static void
+print_help(const char *argv0)
+{
+ printf("Usage: %s [--complex-confine-region]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *display;
+ struct confine *confine;
+
+ if (parse_options(confine_options,
+ ARRAY_LENGTH(confine_options),
+ &argc, argv) > 1 ||
+ option_help) {
+ print_help(argv[0]);
+ return 0;
+ }
+
+ display = display_create(&argc, argv);
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ confine = confine_create(display);
+
+ if (option_complex_confine_region) {
+ confine->complex_confine_region_dirty = true;
+ confine->complex_confine_region_enabled = true;
+ }
+
+ display_run(display);
+
+ confine_destroy(confine);
+ display_destroy(display);
+
+ return 0;
+}
diff --git a/clients/content_protection.c b/clients/content_protection.c
new file mode 100644
index 0000000000000000000000000000000000000000..27e2b8a9daa71ac1c019e652036ac0c7f393b362
--- /dev/null
+++ b/clients/content_protection.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright © 2018 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include "weston-content-protection-client-protocol.h"
+#include "window.h"
+#include
+
+#define WIDTH 500
+#define HEIGHT 400
+#define FRAME_H 18
+#define FRAME_W 5
+#define BUTTON_WIDTH 65
+#define BUTTON_HEIGHT 20
+
+enum protection_mode {
+ RELAXED,
+ ENFORCED
+};
+
+struct protected_content_player {
+ struct weston_content_protection *protection;
+ struct weston_protected_surface *psurface;
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ struct button_t *b0, *b1, *off, *enforced, *relaxed;
+ int width, height, x, y;
+ enum weston_protected_surface_type protection_type;
+ enum protection_mode mode;
+};
+
+struct button_t {
+ struct window *window;
+ struct widget *widget;
+ struct protected_content_player *pc_player;
+ const char *name;
+};
+/**
+ * An event to tell the client that there is a change in protection status
+ *
+ * This event is sent whenever there is a change in content
+ * protection. The content protection status can be ON or OFF. ON
+ * in case of the desired protection type is accepted on all
+ * connectors, and Off in case of any of the connector
+ * content-protection property is changed from "enabled"
+ */
+static void
+handle_status_changed(void *data, struct weston_protected_surface *psurface,
+ uint32_t status)
+{
+ struct protected_content_player *pc_player = data;
+ enum weston_protected_surface_type event_status = status;
+
+ switch (event_status) {
+ case WESTON_PROTECTED_SURFACE_TYPE_HDCP_0:
+ pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_HDCP_0;
+ break;
+ case WESTON_PROTECTED_SURFACE_TYPE_HDCP_1:
+ pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_HDCP_1;
+ break;
+ case WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED:
+ default:
+ pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED;
+ }
+ window_schedule_redraw(pc_player->window);
+}
+
+static const struct weston_protected_surface_listener pc_player_listener = {
+ handle_status_changed,
+};
+
+static void
+draw_content(cairo_surface_t *surface, int x, int y, int width, int height,
+ enum weston_protected_surface_type type, enum protection_mode mode)
+{
+ cairo_t *cr;
+ cairo_text_extents_t extents;
+ const char *content_text;
+ const char *mode_text;
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle(cr, x, y, width, height);
+ if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_0)
+ cairo_set_source_rgba(cr, 0, 1.0, 0, 1.0);
+ else if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_1)
+ cairo_set_source_rgba(cr, 0, 0, 1.0, 1.0);
+ else
+ cairo_set_source_rgba(cr, 1.0, 0, 0, 1.0);
+ cairo_fill(cr);
+
+ cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
+ cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 15);
+ if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_0)
+ content_text = "Content-Type : Type-0";
+ else if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_1)
+ content_text = "Content-Type : Type-1";
+ else
+ content_text = "Content-Type : Unprotected";
+ cairo_text_extents(cr, content_text, &extents);
+ cairo_move_to(cr, width/2 - (extents.width/2),
+ height/2 - (extents.height/2));
+ cairo_show_text(cr, content_text);
+
+ if (mode == ENFORCED)
+ mode_text = "Mode : Enforced";
+ else
+ mode_text = "Mode : Relaxed";
+ cairo_text_extents(cr, mode_text, &extents);
+ cairo_move_to(cr, width / 2 - (extents.width / 2),
+ 2 * height / 3 - (2 * extents.height / 3));
+ cairo_show_text(cr, mode_text);
+
+ cairo_fill(cr);
+ cairo_destroy(cr);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct protected_content_player *pc_player = data;
+ cairo_surface_t *surface;
+ struct rectangle rect;
+
+ widget_get_allocation(pc_player->widget, &rect);
+ surface = window_get_surface(pc_player->window);
+ if (surface == NULL ||
+ cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "failed to create cairo egl surface\n");
+ return;
+ }
+ draw_content(surface, rect.x, rect.y, rect.width, rect.height,
+ pc_player->protection_type, pc_player->mode);
+ cairo_surface_destroy(surface);
+}
+
+static void
+resize_handler(struct widget *widget, int32_t width, int32_t height, void *data)
+{
+ struct rectangle allocation;
+ struct protected_content_player *pc_player = data;
+
+ widget_get_allocation(pc_player->widget, &allocation);
+ widget_set_allocation(pc_player->b0->widget,
+ allocation.x + 20, allocation.y + 30,
+ BUTTON_WIDTH, BUTTON_HEIGHT);
+ widget_set_allocation(pc_player->b1->widget,
+ allocation.x + 20 + BUTTON_WIDTH + 5,
+ allocation.y + 30,
+ BUTTON_WIDTH, BUTTON_HEIGHT);
+ widget_set_allocation(pc_player->off->widget,
+ allocation.x + 20 + 2 * (BUTTON_WIDTH + 5),
+ allocation.y + 30,
+ BUTTON_WIDTH, BUTTON_HEIGHT);
+ widget_set_allocation(pc_player->enforced->widget,
+ allocation.x + 20 + 3 * (BUTTON_WIDTH + 5),
+ allocation.y + 30,
+ BUTTON_WIDTH, BUTTON_HEIGHT);
+ widget_set_allocation(pc_player->relaxed->widget,
+ allocation.x + 20 + 4 * (BUTTON_WIDTH + 5),
+ allocation.y + 30,
+ BUTTON_WIDTH, BUTTON_HEIGHT);
+}
+
+static void
+buttons_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state, void *data)
+{
+ struct button_t *b = data;
+ struct protected_content_player *pc_player = b->pc_player;
+ struct wl_surface *surface;
+
+ if (strcmp(b->name, "ENFORCED") == 0) {
+ weston_protected_surface_enforce(pc_player->psurface);
+ pc_player->mode = ENFORCED;
+ window_schedule_redraw(pc_player->window);
+ }
+ else if (strcmp(b->name, "RELAXED") == 0) {
+ weston_protected_surface_relax(pc_player->psurface);
+ pc_player->mode = RELAXED;
+ window_schedule_redraw(pc_player->window);
+ }
+ else if (strcmp(b->name, "TYPE-0") == 0)
+ weston_protected_surface_set_type(pc_player->psurface,
+ WESTON_PROTECTED_SURFACE_TYPE_HDCP_0);
+ else if (strcmp(b->name, "TYPE-1") == 0)
+ weston_protected_surface_set_type(pc_player->psurface,
+ WESTON_PROTECTED_SURFACE_TYPE_HDCP_1);
+ else
+ weston_protected_surface_set_type(pc_player->psurface,
+ WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED);
+
+ surface = window_get_wl_surface(pc_player->window);
+ wl_surface_commit(surface);
+}
+
+static void
+handle_global(struct display *display, uint32_t name, const char *interface,
+ uint32_t version, void *data)
+{
+ struct protected_content_player *pc_player = data;
+
+ if (strcmp(interface, "weston_content_protection") == 0) {
+ pc_player->protection = display_bind(display, name,
+ &weston_content_protection_interface,
+ 1);
+ }
+}
+
+static void
+buttons_redraw_handler(struct widget *widget, void *data)
+{
+ struct button_t *b = data;
+ cairo_surface_t *surface;
+ struct rectangle allocation;
+ cairo_t *cr;
+
+ surface = window_get_surface(b->window);
+ widget_get_allocation(b->widget, &allocation);
+
+ cr = cairo_create(surface);
+ cairo_rectangle(cr, allocation.x, allocation.y, allocation.width,
+ allocation.height);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ cairo_set_source_rgba(cr, 1, 1, 1, 1);
+ cairo_fill(cr);
+
+ cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
+ cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 10);
+ cairo_move_to(cr, allocation.x + 5, allocation.y + 15);
+ cairo_show_text(cr, b->name);
+ cairo_fill(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static struct button_t*
+create_button(struct protected_content_player *pc_player, const char *name)
+{
+ struct button_t *b;
+
+ b = zalloc(sizeof(struct button_t));
+ if (b == NULL) {
+ fprintf(stderr, "Failed to allocate memory for button.\n");
+ exit(0);
+ }
+ b->widget = widget_add_widget(pc_player->widget, b);
+ b->window = pc_player->window;
+ b->pc_player = pc_player;
+ b->name = name;
+ widget_set_redraw_handler(b->widget, buttons_redraw_handler);
+ widget_set_button_handler(b->widget, buttons_handler);
+ return b;
+}
+
+static void
+destroy_button(struct button_t *b)
+{
+ if (!b)
+ return;
+ widget_destroy(b->widget);
+ free(b);
+}
+
+static void free_pc_player(struct protected_content_player *pc_player)
+{
+ if (!pc_player)
+ return;
+
+ destroy_button(pc_player->b0);
+ destroy_button(pc_player->b1);
+ destroy_button(pc_player->off);
+ destroy_button(pc_player->enforced);
+ destroy_button(pc_player->relaxed);
+ widget_destroy(pc_player->widget);
+ window_destroy(pc_player->window);
+ free(pc_player);
+}
+
+int main(int argc, char *argv[])
+{
+ struct protected_content_player *pc_player;
+ struct display *d;
+ static const char str_type_0[] = "TYPE-0";
+ static const char str_type_1[] = "TYPE-1";
+ static const char str_type_off[] = "OFF";
+ static const char str_type_enforced[] = "ENFORCED";
+ static const char str_type_relaxed[] = "RELAXED";
+ struct wl_surface *surface;
+
+ pc_player = zalloc(sizeof(struct protected_content_player));
+ if (pc_player == NULL) {
+ fprintf(stderr, "failed to allocate memory: %m\n");
+ return -1;
+ }
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+ pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED;
+ pc_player->mode = RELAXED;
+ pc_player->width = WIDTH * 2.0/4.0;
+ pc_player->height = HEIGHT * 2.0/4.0;
+ pc_player->x = WIDTH * 1.0/4.0;
+ pc_player->y = HEIGHT * 1.0/4.0;
+ pc_player->window = window_create(d);
+ pc_player->widget = window_frame_create(pc_player->window, pc_player);
+ pc_player->display = d;
+ display_set_user_data(d, pc_player);
+
+ display_set_global_handler(d, handle_global);
+ surface = window_get_wl_surface(pc_player->window);
+ if (pc_player->protection == NULL) {
+ printf("The content-protection object is NULL\n");
+ return -1;
+ }
+ pc_player->psurface = weston_content_protection_get_protection(pc_player->protection,
+ surface);
+ weston_protected_surface_add_listener(pc_player->psurface,
+ &pc_player_listener,
+ pc_player);
+
+ pc_player->b0 = create_button(pc_player, str_type_0);
+ pc_player->b1 = create_button(pc_player, str_type_1);
+ pc_player->off = create_button(pc_player, str_type_off);
+ pc_player->enforced = create_button(pc_player, str_type_enforced);
+ pc_player->relaxed = create_button(pc_player, str_type_relaxed);
+
+ window_set_title(pc_player->window, "Player");
+ widget_set_redraw_handler(pc_player->widget, redraw_handler);
+ widget_set_resize_handler(pc_player->widget, resize_handler);
+ window_schedule_resize(pc_player->window, WIDTH, HEIGHT);
+ widget_schedule_redraw(pc_player->b0->widget);
+ widget_schedule_redraw(pc_player->b1->widget);
+ widget_schedule_redraw(pc_player->off->widget);
+
+ display_run(d);
+ weston_protected_surface_destroy(pc_player->psurface);
+ weston_content_protection_destroy(pc_player->protection);
+ free_pc_player(pc_player);
+ display_destroy(d);
+ return 0;
+}
diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c
new file mode 100644
index 0000000000000000000000000000000000000000..bde5dc82073ae7a359f3b8184173e120e0065149
--- /dev/null
+++ b/clients/desktop-shell.c
@@ -0,0 +1,1559 @@
+/*
+ * Copyright © 2011 Kristian Høgsberg
+ * Copyright © 2011 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include "window.h"
+#include "shared/cairo-util.h"
+#include
+#include "shared/helpers.h"
+#include "shared/xalloc.h"
+#include
+#include "shared/file-util.h"
+
+#include "weston-desktop-shell-client-protocol.h"
+
+#define DEFAULT_CLOCK_FORMAT CLOCK_FORMAT_MINUTES
+#define DEFAULT_SPACING 10
+
+extern char **environ; /* defined by libc */
+
+enum clock_format {
+ CLOCK_FORMAT_MINUTES,
+ CLOCK_FORMAT_SECONDS,
+ CLOCK_FORMAT_NONE
+};
+
+struct desktop {
+ struct display *display;
+ struct weston_desktop_shell *shell;
+ struct unlock_dialog *unlock_dialog;
+ struct task unlock_task;
+ struct wl_list outputs;
+
+ int want_panel;
+ enum weston_desktop_shell_panel_position panel_position;
+ enum clock_format clock_format;
+
+ struct window *grab_window;
+ struct widget *grab_widget;
+
+ struct weston_config *config;
+ bool locking;
+
+ enum cursor_type grab_cursor;
+
+ int painted;
+};
+
+struct surface {
+ void (*configure)(void *data,
+ struct weston_desktop_shell *desktop_shell,
+ uint32_t edges, struct window *window,
+ int32_t width, int32_t height);
+};
+
+struct output;
+
+struct panel {
+ struct surface base;
+
+ struct output *owner;
+
+ struct window *window;
+ struct widget *widget;
+ struct wl_list launcher_list;
+ struct panel_clock *clock;
+ int painted;
+ enum weston_desktop_shell_panel_position panel_position;
+ enum clock_format clock_format;
+ uint32_t color;
+};
+
+struct background {
+ struct surface base;
+
+ struct output *owner;
+
+ struct window *window;
+ struct widget *widget;
+ int painted;
+
+ char *image;
+ int type;
+ uint32_t color;
+};
+
+struct output {
+ struct wl_output *output;
+ uint32_t server_output_id;
+ struct wl_list link;
+
+ int x;
+ int y;
+ struct panel *panel;
+ struct background *background;
+};
+
+struct panel_launcher {
+ struct widget *widget;
+ struct panel *panel;
+ cairo_surface_t *icon;
+ int focused, pressed;
+ char *path;
+ struct wl_list link;
+ struct wl_array envp;
+ struct wl_array argv;
+};
+
+struct panel_clock {
+ struct widget *widget;
+ struct panel *panel;
+ struct toytimer timer;
+ char *format_string;
+ time_t refresh_timer;
+};
+
+struct unlock_dialog {
+ struct window *window;
+ struct widget *widget;
+ struct widget *button;
+ int button_focused;
+ int closing;
+ struct desktop *desktop;
+};
+
+static void
+panel_add_launchers(struct panel *panel, struct desktop *desktop);
+
+static void
+sigchild_handler(int s)
+{
+ int status;
+ pid_t pid;
+
+ while (pid = waitpid(-1, &status, WNOHANG), pid > 0)
+ fprintf(stderr, "child %d exited\n", pid);
+}
+
+static int
+is_desktop_painted(struct desktop *desktop)
+{
+ struct output *output;
+
+ wl_list_for_each(output, &desktop->outputs, link) {
+ if (output->panel && !output->panel->painted)
+ return 0;
+ if (output->background && !output->background->painted)
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+check_desktop_ready(struct window *window)
+{
+ struct display *display;
+ struct desktop *desktop;
+
+ display = window_get_display(window);
+ desktop = display_get_user_data(display);
+
+ if (!desktop->painted && is_desktop_painted(desktop)) {
+ desktop->painted = 1;
+
+ weston_desktop_shell_desktop_ready(desktop->shell);
+ }
+}
+
+static void
+panel_launcher_activate(struct panel_launcher *widget)
+{
+ char **argv;
+ pid_t pid;
+
+ pid = fork();
+ if (pid < 0) {
+ fprintf(stderr, "fork failed: %s\n", strerror(errno));
+ return;
+ }
+
+ if (pid)
+ return;
+
+ argv = widget->argv.data;
+
+ if (setsid() == -1)
+ exit(EXIT_FAILURE);
+
+ if (execve(argv[0], argv, widget->envp.data) < 0) {
+ fprintf(stderr, "execl '%s' failed: %s\n", argv[0],
+ strerror(errno));
+ exit(1);
+ }
+}
+
+static void
+panel_launcher_redraw_handler(struct widget *widget, void *data)
+{
+ struct panel_launcher *launcher = data;
+ struct rectangle allocation;
+ cairo_t *cr;
+
+ cr = widget_cairo_create(launcher->panel->widget);
+
+ widget_get_allocation(widget, &allocation);
+ allocation.x += allocation.width / 2 -
+ cairo_image_surface_get_width(launcher->icon) / 2;
+ if (allocation.width > allocation.height)
+ allocation.x += allocation.width / 2 - allocation.height / 2;
+ allocation.y += allocation.height / 2 -
+ cairo_image_surface_get_height(launcher->icon) / 2;
+ if (allocation.height > allocation.width)
+ allocation.y += allocation.height / 2 - allocation.width / 2;
+ if (launcher->pressed) {
+ allocation.x++;
+ allocation.y++;
+ }
+
+ cairo_set_source_surface(cr, launcher->icon,
+ allocation.x, allocation.y);
+ cairo_paint(cr);
+
+ if (launcher->focused) {
+ cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.4);
+ cairo_mask_surface(cr, launcher->icon,
+ allocation.x, allocation.y);
+ }
+
+ cairo_destroy(cr);
+}
+
+static int
+panel_launcher_motion_handler(struct widget *widget, struct input *input,
+ uint32_t time, float x, float y, void *data)
+{
+ struct panel_launcher *launcher = data;
+
+ widget_set_tooltip(widget, basename((char *)launcher->path), x, y);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+set_hex_color(cairo_t *cr, uint32_t color)
+{
+ cairo_set_source_rgba(cr,
+ ((color >> 16) & 0xff) / 255.0,
+ ((color >> 8) & 0xff) / 255.0,
+ ((color >> 0) & 0xff) / 255.0,
+ ((color >> 24) & 0xff) / 255.0);
+}
+
+static void
+panel_redraw_handler(struct widget *widget, void *data)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ struct panel *panel = data;
+
+ cr = widget_cairo_create(panel->widget);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ set_hex_color(cr, panel->color);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+ surface = window_get_surface(panel->window);
+ cairo_surface_destroy(surface);
+ panel->painted = 1;
+ check_desktop_ready(panel->window);
+}
+
+static int
+panel_launcher_enter_handler(struct widget *widget, struct input *input,
+ float x, float y, void *data)
+{
+ struct panel_launcher *launcher = data;
+
+ launcher->focused = 1;
+ widget_schedule_redraw(widget);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+panel_launcher_leave_handler(struct widget *widget,
+ struct input *input, void *data)
+{
+ struct panel_launcher *launcher = data;
+
+ launcher->focused = 0;
+ widget_destroy_tooltip(widget);
+ widget_schedule_redraw(widget);
+}
+
+static void
+panel_launcher_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct panel_launcher *launcher;
+
+ launcher = widget_get_user_data(widget);
+ widget_schedule_redraw(widget);
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED)
+ panel_launcher_activate(launcher);
+
+}
+
+static void
+panel_launcher_touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct panel_launcher *launcher;
+
+ launcher = widget_get_user_data(widget);
+ launcher->focused = 1;
+ widget_schedule_redraw(widget);
+}
+
+static void
+panel_launcher_touch_up_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ void *data)
+{
+ struct panel_launcher *launcher;
+
+ launcher = widget_get_user_data(widget);
+ launcher->focused = 0;
+ widget_schedule_redraw(widget);
+ panel_launcher_activate(launcher);
+}
+
+static void
+clock_func(struct toytimer *tt)
+{
+ struct panel_clock *clock = container_of(tt, struct panel_clock, timer);
+
+ widget_schedule_redraw(clock->widget);
+}
+
+static void
+panel_clock_redraw_handler(struct widget *widget, void *data)
+{
+ struct panel_clock *clock = data;
+ cairo_t *cr;
+ struct rectangle allocation;
+ cairo_text_extents_t extents;
+ time_t rawtime;
+ struct tm * timeinfo;
+ char string[128];
+
+ time(&rawtime);
+ timeinfo = localtime(&rawtime);
+ strftime(string, sizeof string, clock->format_string, timeinfo);
+
+ widget_get_allocation(widget, &allocation);
+ if (allocation.width == 0)
+ return;
+
+ cr = widget_cairo_create(clock->panel->widget);
+ cairo_set_font_size(cr, 14);
+ cairo_text_extents(cr, string, &extents);
+ if (allocation.x > 0)
+ allocation.x +=
+ allocation.width - DEFAULT_SPACING * 1.5 - extents.width;
+ else
+ allocation.x +=
+ allocation.width / 2 - extents.width / 2;
+ allocation.y += allocation.height / 2 - 1 + extents.height / 2;
+ cairo_move_to(cr, allocation.x + 1, allocation.y + 1);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.85);
+ cairo_show_text(cr, string);
+ cairo_move_to(cr, allocation.x, allocation.y);
+ cairo_set_source_rgba(cr, 1, 1, 1, 0.85);
+ cairo_show_text(cr, string);
+ cairo_destroy(cr);
+}
+
+static int
+clock_timer_reset(struct panel_clock *clock)
+{
+ struct itimerspec its;
+
+ its.it_interval.tv_sec = clock->refresh_timer;
+ its.it_interval.tv_nsec = 0;
+ its.it_value.tv_sec = clock->refresh_timer;
+ its.it_value.tv_nsec = 0;
+ toytimer_arm(&clock->timer, &its);
+
+ return 0;
+}
+
+static void
+panel_destroy_clock(struct panel_clock *clock)
+{
+ widget_destroy(clock->widget);
+ toytimer_fini(&clock->timer);
+ free(clock);
+}
+
+static void
+panel_add_clock(struct panel *panel)
+{
+ struct panel_clock *clock;
+
+ clock = xzalloc(sizeof *clock);
+ clock->panel = panel;
+ panel->clock = clock;
+
+ switch (panel->clock_format) {
+ case CLOCK_FORMAT_MINUTES:
+ clock->format_string = "%a %b %d, %I:%M %p";
+ clock->refresh_timer = 60;
+ break;
+ case CLOCK_FORMAT_SECONDS:
+ clock->format_string = "%a %b %d, %I:%M:%S %p";
+ clock->refresh_timer = 1;
+ break;
+ case CLOCK_FORMAT_NONE:
+ assert(!"not reached");
+ }
+
+ toytimer_init(&clock->timer, CLOCK_MONOTONIC,
+ window_get_display(panel->window), clock_func);
+ clock_timer_reset(clock);
+
+ clock->widget = widget_add_widget(panel->widget, clock);
+ widget_set_redraw_handler(clock->widget, panel_clock_redraw_handler);
+}
+
+static void
+panel_resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct panel_launcher *launcher;
+ struct panel *panel = data;
+ int x = 0;
+ int y = 0;
+ int w = height > width ? width : height;
+ int h = w;
+ int horizontal = panel->panel_position == WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP || panel->panel_position == WESTON_DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
+ int first_pad_h = horizontal ? 0 : DEFAULT_SPACING / 2;
+ int first_pad_w = horizontal ? DEFAULT_SPACING / 2 : 0;
+
+ wl_list_for_each(launcher, &panel->launcher_list, link) {
+ widget_set_allocation(launcher->widget, x, y,
+ w + first_pad_w + 1, h + first_pad_h + 1);
+ if (horizontal)
+ x += w + first_pad_w;
+ else
+ y += h + first_pad_h;
+ first_pad_h = first_pad_w = 0;
+ }
+
+ if (panel->clock_format == CLOCK_FORMAT_SECONDS)
+ w = 170;
+ else /* CLOCK_FORMAT_MINUTES */
+ w = 150;
+
+ if (horizontal)
+ x = width - w;
+ else
+ y = height - (h = DEFAULT_SPACING * 3);
+
+ if (panel->clock)
+ widget_set_allocation(panel->clock->widget,
+ x, y, w + 1, h + 1);
+}
+
+static void
+panel_destroy(struct panel *panel);
+
+static void
+panel_configure(void *data,
+ struct weston_desktop_shell *desktop_shell,
+ uint32_t edges, struct window *window,
+ int32_t width, int32_t height)
+{
+ struct desktop *desktop = data;
+ struct surface *surface = window_get_user_data(window);
+ struct panel *panel = container_of(surface, struct panel, base);
+ struct output *owner;
+
+ if (width < 1 || height < 1) {
+ /* Shell plugin configures 0x0 for redundant panel. */
+ owner = panel->owner;
+ panel_destroy(panel);
+ owner->panel = NULL;
+ return;
+ }
+
+ switch (desktop->panel_position) {
+ case WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP:
+ case WESTON_DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
+ height = 32;
+ break;
+ case WESTON_DESKTOP_SHELL_PANEL_POSITION_LEFT:
+ case WESTON_DESKTOP_SHELL_PANEL_POSITION_RIGHT:
+ switch (desktop->clock_format) {
+ case CLOCK_FORMAT_NONE:
+ width = 32;
+ break;
+ case CLOCK_FORMAT_MINUTES:
+ width = 150;
+ break;
+ case CLOCK_FORMAT_SECONDS:
+ width = 170;
+ break;
+ }
+ break;
+ }
+ window_schedule_resize(panel->window, width, height);
+}
+
+static void
+panel_destroy_launcher(struct panel_launcher *launcher)
+{
+ wl_array_release(&launcher->argv);
+ wl_array_release(&launcher->envp);
+
+ free(launcher->path);
+
+ cairo_surface_destroy(launcher->icon);
+
+ widget_destroy(launcher->widget);
+ wl_list_remove(&launcher->link);
+
+ free(launcher);
+}
+
+static void
+panel_destroy(struct panel *panel)
+{
+ struct panel_launcher *tmp;
+ struct panel_launcher *launcher;
+
+ if (panel->clock)
+ panel_destroy_clock(panel->clock);
+
+ wl_list_for_each_safe(launcher, tmp, &panel->launcher_list, link)
+ panel_destroy_launcher(launcher);
+
+ widget_destroy(panel->widget);
+ window_destroy(panel->window);
+
+ free(panel);
+}
+
+static struct panel *
+panel_create(struct desktop *desktop, struct output *output)
+{
+ struct panel *panel;
+ struct weston_config_section *s;
+
+ panel = xzalloc(sizeof *panel);
+
+ panel->owner = output;
+ panel->base.configure = panel_configure;
+ panel->window = window_create_custom(desktop->display);
+ panel->widget = window_add_widget(panel->window, panel);
+ wl_list_init(&panel->launcher_list);
+
+ window_set_title(panel->window, "panel");
+ window_set_user_data(panel->window, panel);
+
+ widget_set_redraw_handler(panel->widget, panel_redraw_handler);
+ widget_set_resize_handler(panel->widget, panel_resize_handler);
+
+ panel->panel_position = desktop->panel_position;
+ panel->clock_format = desktop->clock_format;
+ if (panel->clock_format != CLOCK_FORMAT_NONE)
+ panel_add_clock(panel);
+
+ s = weston_config_get_section(desktop->config, "shell", NULL, NULL);
+ weston_config_section_get_color(s, "panel-color",
+ &panel->color, 0xaa000000);
+
+ panel_add_launchers(panel, desktop);
+
+ return panel;
+}
+
+static cairo_surface_t *
+load_icon_or_fallback(const char *icon)
+{
+ cairo_surface_t *surface = cairo_image_surface_create_from_png(icon);
+ cairo_status_t status;
+ cairo_t *cr;
+
+ status = cairo_surface_status(surface);
+ if (status == CAIRO_STATUS_SUCCESS)
+ return surface;
+
+ cairo_surface_destroy(surface);
+ fprintf(stderr, "ERROR loading icon from file '%s', error: '%s'\n",
+ icon, cairo_status_to_string(status));
+
+ /* draw fallback icon */
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ 20, 20);
+ cr = cairo_create(surface);
+
+ cairo_set_source_rgba(cr, 0.8, 0.8, 0.8, 1);
+ cairo_paint(cr);
+
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+ cairo_rectangle(cr, 0, 0, 20, 20);
+ cairo_move_to(cr, 4, 4);
+ cairo_line_to(cr, 16, 16);
+ cairo_move_to(cr, 4, 16);
+ cairo_line_to(cr, 16, 4);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return surface;
+}
+
+static void
+panel_add_launcher(struct panel *panel, const char *icon, const char *path)
+{
+ struct panel_launcher *launcher;
+ char *start, *p, *eq, **ps;
+ int i, j, k;
+
+ launcher = xzalloc(sizeof *launcher);
+ launcher->icon = load_icon_or_fallback(icon);
+ launcher->path = xstrdup(path);
+
+ wl_array_init(&launcher->envp);
+ wl_array_init(&launcher->argv);
+ for (i = 0; environ[i]; i++) {
+ ps = wl_array_add(&launcher->envp, sizeof *ps);
+ *ps = environ[i];
+ }
+ j = 0;
+
+ start = launcher->path;
+ while (*start) {
+ for (p = start, eq = NULL; *p && !isspace(*p); p++)
+ if (*p == '=')
+ eq = p;
+
+ if (eq && j == 0) {
+ ps = launcher->envp.data;
+ for (k = 0; k < i; k++)
+ if (strncmp(ps[k], start, eq - start) == 0) {
+ ps[k] = start;
+ break;
+ }
+ if (k == i) {
+ ps = wl_array_add(&launcher->envp, sizeof *ps);
+ *ps = start;
+ i++;
+ }
+ } else {
+ ps = wl_array_add(&launcher->argv, sizeof *ps);
+ *ps = start;
+ j++;
+ }
+
+ while (*p && isspace(*p))
+ *p++ = '\0';
+
+ start = p;
+ }
+
+ ps = wl_array_add(&launcher->envp, sizeof *ps);
+ *ps = NULL;
+ ps = wl_array_add(&launcher->argv, sizeof *ps);
+ *ps = NULL;
+
+ launcher->panel = panel;
+ wl_list_insert(panel->launcher_list.prev, &launcher->link);
+
+ launcher->widget = widget_add_widget(panel->widget, launcher);
+ widget_set_enter_handler(launcher->widget,
+ panel_launcher_enter_handler);
+ widget_set_leave_handler(launcher->widget,
+ panel_launcher_leave_handler);
+ widget_set_button_handler(launcher->widget,
+ panel_launcher_button_handler);
+ widget_set_touch_down_handler(launcher->widget,
+ panel_launcher_touch_down_handler);
+ widget_set_touch_up_handler(launcher->widget,
+ panel_launcher_touch_up_handler);
+ widget_set_redraw_handler(launcher->widget,
+ panel_launcher_redraw_handler);
+ widget_set_motion_handler(launcher->widget,
+ panel_launcher_motion_handler);
+}
+
+enum {
+ BACKGROUND_SCALE,
+ BACKGROUND_SCALE_CROP,
+ BACKGROUND_TILE,
+ BACKGROUND_CENTERED
+};
+
+static void
+background_draw(struct widget *widget, void *data)
+{
+ struct background *background = data;
+ cairo_surface_t *surface, *image;
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+ cairo_t *cr;
+ double im_w, im_h;
+ double sx, sy, s;
+ double tx, ty;
+ struct rectangle allocation;
+
+ surface = window_get_surface(background->window);
+
+ cr = widget_cairo_create(background->widget);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ if (background->color == 0)
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.2, 1.0);
+ else
+ set_hex_color(cr, background->color);
+ cairo_paint(cr);
+
+ widget_get_allocation(widget, &allocation);
+ image = NULL;
+ if (background->image)
+ image = load_cairo_surface(background->image);
+ else if (background->color == 0) {
+ char *name = file_name_with_datadir("pattern.png");
+
+ image = load_cairo_surface(name);
+ free(name);
+ }
+
+ if (image && background->type != -1) {
+ im_w = cairo_image_surface_get_width(image);
+ im_h = cairo_image_surface_get_height(image);
+ sx = im_w / allocation.width;
+ sy = im_h / allocation.height;
+
+ pattern = cairo_pattern_create_for_surface(image);
+
+ switch (background->type) {
+ case BACKGROUND_SCALE:
+ cairo_matrix_init_scale(&matrix, sx, sy);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
+ break;
+ case BACKGROUND_SCALE_CROP:
+ s = (sx < sy) ? sx : sy;
+ /* align center */
+ tx = (im_w - s * allocation.width) * 0.5;
+ ty = (im_h - s * allocation.height) * 0.5;
+ cairo_matrix_init_translate(&matrix, tx, ty);
+ cairo_matrix_scale(&matrix, s, s);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
+ break;
+ case BACKGROUND_TILE:
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+ break;
+ case BACKGROUND_CENTERED:
+ s = (sx < sy) ? sx : sy;
+ if (s < 1.0)
+ s = 1.0;
+
+ /* align center */
+ tx = (im_w - s * allocation.width) * 0.5;
+ ty = (im_h - s * allocation.height) * 0.5;
+
+ cairo_matrix_init_translate(&matrix, tx, ty);
+ cairo_matrix_scale(&matrix, s, s);
+ cairo_pattern_set_matrix(pattern, &matrix);
+ break;
+ }
+
+ cairo_set_source(cr, pattern);
+ cairo_pattern_destroy (pattern);
+ cairo_surface_destroy(image);
+ cairo_mask(cr, pattern);
+ }
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+
+ background->painted = 1;
+ check_desktop_ready(background->window);
+}
+
+static void
+background_destroy(struct background *background);
+
+static void
+background_configure(void *data,
+ struct weston_desktop_shell *desktop_shell,
+ uint32_t edges, struct window *window,
+ int32_t width, int32_t height)
+{
+ struct output *owner;
+ struct background *background =
+ (struct background *) window_get_user_data(window);
+
+ if (width < 1 || height < 1) {
+ /* Shell plugin configures 0x0 for redundant background. */
+ owner = background->owner;
+ background_destroy(background);
+ owner->background = NULL;
+ return;
+ }
+
+ if (!background->image) {
+ widget_set_viewport_destination(background->widget, width, height);
+ width = 1;
+ height = 1;
+ }
+
+ widget_schedule_resize(background->widget, width, height);
+}
+
+static void
+unlock_dialog_redraw_handler(struct widget *widget, void *data)
+{
+ struct unlock_dialog *dialog = data;
+ struct rectangle allocation;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ cairo_pattern_t *pat;
+ double cx, cy, r, f;
+
+ cr = widget_cairo_create(widget);
+
+ widget_get_allocation(dialog->widget, &allocation);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.6);
+ cairo_fill(cr);
+
+ cairo_translate(cr, allocation.x, allocation.y);
+ if (dialog->button_focused)
+ f = 1.0;
+ else
+ f = 0.7;
+
+ cx = allocation.width / 2.0;
+ cy = allocation.height / 2.0;
+ r = (cx < cy ? cx : cy) * 0.4;
+ pat = cairo_pattern_create_radial(cx, cy, r * 0.7, cx, cy, r);
+ cairo_pattern_add_color_stop_rgb(pat, 0.0, 0, 0.86 * f, 0);
+ cairo_pattern_add_color_stop_rgb(pat, 0.85, 0.2 * f, f, 0.2 * f);
+ cairo_pattern_add_color_stop_rgb(pat, 1.0, 0, 0.86 * f, 0);
+ cairo_set_source(cr, pat);
+ cairo_pattern_destroy(pat);
+ cairo_arc(cr, cx, cy, r, 0.0, 2.0 * M_PI);
+ cairo_fill(cr);
+
+ widget_set_allocation(dialog->button,
+ allocation.x + cx - r,
+ allocation.y + cy - r, 2 * r, 2 * r);
+
+ cairo_destroy(cr);
+
+ surface = window_get_surface(dialog->window);
+ cairo_surface_destroy(surface);
+}
+
+static void
+unlock_dialog_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct unlock_dialog *dialog = data;
+ struct desktop *desktop = dialog->desktop;
+
+ if (button == BTN_LEFT) {
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED &&
+ !dialog->closing) {
+ display_defer(desktop->display, &desktop->unlock_task);
+ dialog->closing = 1;
+ }
+ }
+}
+
+static void
+unlock_dialog_touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct unlock_dialog *dialog = data;
+
+ dialog->button_focused = 1;
+ widget_schedule_redraw(widget);
+}
+
+static void
+unlock_dialog_touch_up_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ void *data)
+{
+ struct unlock_dialog *dialog = data;
+ struct desktop *desktop = dialog->desktop;
+
+ dialog->button_focused = 0;
+ widget_schedule_redraw(widget);
+ display_defer(desktop->display, &desktop->unlock_task);
+ dialog->closing = 1;
+}
+
+static void
+unlock_dialog_keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ window_schedule_redraw(window);
+}
+
+static int
+unlock_dialog_widget_enter_handler(struct widget *widget,
+ struct input *input,
+ float x, float y, void *data)
+{
+ struct unlock_dialog *dialog = data;
+
+ dialog->button_focused = 1;
+ widget_schedule_redraw(widget);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+unlock_dialog_widget_leave_handler(struct widget *widget,
+ struct input *input, void *data)
+{
+ struct unlock_dialog *dialog = data;
+
+ dialog->button_focused = 0;
+ widget_schedule_redraw(widget);
+}
+
+static struct unlock_dialog *
+unlock_dialog_create(struct desktop *desktop)
+{
+ struct display *display = desktop->display;
+ struct unlock_dialog *dialog;
+ struct wl_surface *surface;
+
+ dialog = xzalloc(sizeof *dialog);
+
+ dialog->window = window_create_custom(display);
+ dialog->widget = window_frame_create(dialog->window, dialog);
+ window_set_title(dialog->window, "Unlock your desktop");
+
+ window_set_user_data(dialog->window, dialog);
+ window_set_keyboard_focus_handler(dialog->window,
+ unlock_dialog_keyboard_focus_handler);
+ dialog->button = widget_add_widget(dialog->widget, dialog);
+ widget_set_redraw_handler(dialog->widget,
+ unlock_dialog_redraw_handler);
+ widget_set_enter_handler(dialog->button,
+ unlock_dialog_widget_enter_handler);
+ widget_set_leave_handler(dialog->button,
+ unlock_dialog_widget_leave_handler);
+ widget_set_button_handler(dialog->button,
+ unlock_dialog_button_handler);
+ widget_set_touch_down_handler(dialog->button,
+ unlock_dialog_touch_down_handler);
+ widget_set_touch_up_handler(dialog->button,
+ unlock_dialog_touch_up_handler);
+
+ surface = window_get_wl_surface(dialog->window);
+ weston_desktop_shell_set_lock_surface(desktop->shell, surface);
+
+ window_schedule_resize(dialog->window, 260, 230);
+
+ return dialog;
+}
+
+static void
+unlock_dialog_destroy(struct unlock_dialog *dialog)
+{
+ window_destroy(dialog->window);
+ free(dialog);
+}
+
+static void
+unlock_dialog_finish(struct task *task, uint32_t events)
+{
+ struct desktop *desktop =
+ container_of(task, struct desktop, unlock_task);
+
+ weston_desktop_shell_unlock(desktop->shell);
+ unlock_dialog_destroy(desktop->unlock_dialog);
+ desktop->unlock_dialog = NULL;
+}
+
+static void
+desktop_shell_configure(void *data,
+ struct weston_desktop_shell *desktop_shell,
+ uint32_t edges,
+ struct wl_surface *surface,
+ int32_t width, int32_t height)
+{
+ struct window *window = wl_surface_get_user_data(surface);
+ struct surface *s = window_get_user_data(window);
+
+ s->configure(data, desktop_shell, edges, window, width, height);
+}
+
+static void
+desktop_shell_prepare_lock_surface(void *data,
+ struct weston_desktop_shell *desktop_shell)
+{
+ struct desktop *desktop = data;
+
+ if (!desktop->locking) {
+ weston_desktop_shell_unlock(desktop->shell);
+ return;
+ }
+
+ if (!desktop->unlock_dialog) {
+ desktop->unlock_dialog = unlock_dialog_create(desktop);
+ desktop->unlock_dialog->desktop = desktop;
+ }
+}
+
+static void
+desktop_shell_grab_cursor(void *data,
+ struct weston_desktop_shell *desktop_shell,
+ uint32_t cursor)
+{
+ struct desktop *desktop = data;
+
+ switch (cursor) {
+ case WESTON_DESKTOP_SHELL_CURSOR_NONE:
+ desktop->grab_cursor = CURSOR_BLANK;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_BUSY:
+ desktop->grab_cursor = CURSOR_WATCH;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_MOVE:
+ desktop->grab_cursor = CURSOR_DRAGGING;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_RESIZE_TOP:
+ desktop->grab_cursor = CURSOR_TOP;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_RESIZE_BOTTOM:
+ desktop->grab_cursor = CURSOR_BOTTOM;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_RESIZE_LEFT:
+ desktop->grab_cursor = CURSOR_LEFT;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_RESIZE_RIGHT:
+ desktop->grab_cursor = CURSOR_RIGHT;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_RESIZE_TOP_LEFT:
+ desktop->grab_cursor = CURSOR_TOP_LEFT;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_RESIZE_TOP_RIGHT:
+ desktop->grab_cursor = CURSOR_TOP_RIGHT;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_RESIZE_BOTTOM_LEFT:
+ desktop->grab_cursor = CURSOR_BOTTOM_LEFT;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_RESIZE_BOTTOM_RIGHT:
+ desktop->grab_cursor = CURSOR_BOTTOM_RIGHT;
+ break;
+ case WESTON_DESKTOP_SHELL_CURSOR_ARROW:
+ default:
+ desktop->grab_cursor = CURSOR_LEFT_PTR;
+ }
+}
+
+static const struct weston_desktop_shell_listener listener = {
+ desktop_shell_configure,
+ desktop_shell_prepare_lock_surface,
+ desktop_shell_grab_cursor
+};
+
+static void
+background_destroy(struct background *background)
+{
+ widget_destroy(background->widget);
+ window_destroy(background->window);
+
+ free(background->image);
+ free(background);
+}
+
+static struct background *
+background_create(struct desktop *desktop, struct output *output)
+{
+ struct background *background;
+ struct weston_config_section *s;
+ char *type;
+
+ background = xzalloc(sizeof *background);
+ background->owner = output;
+ background->base.configure = background_configure;
+ background->window = window_create_custom(desktop->display);
+ background->widget = window_add_widget(background->window, background);
+ window_set_user_data(background->window, background);
+ widget_set_redraw_handler(background->widget, background_draw);
+ widget_set_transparent(background->widget, 0);
+
+ s = weston_config_get_section(desktop->config, "shell", NULL, NULL);
+ weston_config_section_get_string(s, "background-image",
+ &background->image, NULL);
+ weston_config_section_get_color(s, "background-color",
+ &background->color, 0x00000000);
+
+ weston_config_section_get_string(s, "background-type",
+ &type, "tile");
+ if (type == NULL) {
+ fprintf(stderr, "%s: out of memory\n", program_invocation_short_name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (strcmp(type, "scale") == 0) {
+ background->type = BACKGROUND_SCALE;
+ } else if (strcmp(type, "scale-crop") == 0) {
+ background->type = BACKGROUND_SCALE_CROP;
+ } else if (strcmp(type, "tile") == 0) {
+ background->type = BACKGROUND_TILE;
+ } else if (strcmp(type, "centered") == 0) {
+ background->type = BACKGROUND_CENTERED;
+ } else {
+ background->type = -1;
+ fprintf(stderr, "invalid background-type: %s\n",
+ type);
+ }
+
+ free(type);
+
+ return background;
+}
+
+static int
+grab_surface_enter_handler(struct widget *widget, struct input *input,
+ float x, float y, void *data)
+{
+ struct desktop *desktop = data;
+
+ return desktop->grab_cursor;
+}
+
+static void
+grab_surface_destroy(struct desktop *desktop)
+{
+ widget_destroy(desktop->grab_widget);
+ window_destroy(desktop->grab_window);
+}
+
+static void
+grab_surface_create(struct desktop *desktop)
+{
+ struct wl_surface *s;
+
+ desktop->grab_window = window_create_custom(desktop->display);
+ window_set_user_data(desktop->grab_window, desktop);
+
+ s = window_get_wl_surface(desktop->grab_window);
+ weston_desktop_shell_set_grab_surface(desktop->shell, s);
+
+ desktop->grab_widget =
+ window_add_widget(desktop->grab_window, desktop);
+ /* We set the allocation to 1x1 at 0,0 so the fake enter event
+ * at 0,0 will go to this widget. */
+ widget_set_allocation(desktop->grab_widget, 0, 0, 1, 1);
+
+ widget_set_enter_handler(desktop->grab_widget,
+ grab_surface_enter_handler);
+}
+
+static void
+output_destroy(struct output *output)
+{
+ if (output->background)
+ background_destroy(output->background);
+ if (output->panel)
+ panel_destroy(output->panel);
+ wl_output_destroy(output->output);
+ wl_list_remove(&output->link);
+
+ free(output);
+}
+
+static void
+desktop_destroy_outputs(struct desktop *desktop)
+{
+ struct output *tmp;
+ struct output *output;
+
+ wl_list_for_each_safe(output, tmp, &desktop->outputs, link)
+ output_destroy(output);
+}
+
+static void
+output_handle_geometry(void *data,
+ struct wl_output *wl_output,
+ int x, int y,
+ int physical_width,
+ int physical_height,
+ int subpixel,
+ const char *make,
+ const char *model,
+ int transform)
+{
+ struct output *output = data;
+
+ output->x = x;
+ output->y = y;
+
+ if (output->panel)
+ window_set_buffer_transform(output->panel->window, transform);
+ if (output->background)
+ window_set_buffer_transform(output->background->window, transform);
+}
+
+static void
+output_handle_mode(void *data,
+ struct wl_output *wl_output,
+ uint32_t flags,
+ int width,
+ int height,
+ int refresh)
+{
+}
+
+static void
+output_handle_done(void *data,
+ struct wl_output *wl_output)
+{
+}
+
+static void
+output_handle_scale(void *data,
+ struct wl_output *wl_output,
+ int32_t scale)
+{
+ struct output *output = data;
+
+ if (output->panel)
+ window_set_buffer_scale(output->panel->window, scale);
+ if (output->background)
+ window_set_buffer_scale(output->background->window, scale);
+}
+
+static const struct wl_output_listener output_listener = {
+ output_handle_geometry,
+ output_handle_mode,
+ output_handle_done,
+ output_handle_scale
+};
+
+static void
+output_init(struct output *output, struct desktop *desktop)
+{
+ struct wl_surface *surface;
+
+ if (desktop->want_panel) {
+ output->panel = panel_create(desktop, output);
+ surface = window_get_wl_surface(output->panel->window);
+ weston_desktop_shell_set_panel(desktop->shell,
+ output->output, surface);
+ }
+
+ output->background = background_create(desktop, output);
+ surface = window_get_wl_surface(output->background->window);
+ weston_desktop_shell_set_background(desktop->shell,
+ output->output, surface);
+}
+
+static void
+create_output(struct desktop *desktop, uint32_t id)
+{
+ struct output *output;
+
+ output = zalloc(sizeof *output);
+ if (!output)
+ return;
+
+ output->output =
+ display_bind(desktop->display, id, &wl_output_interface, 2);
+ output->server_output_id = id;
+
+ wl_output_add_listener(output->output, &output_listener, output);
+
+ wl_list_insert(&desktop->outputs, &output->link);
+
+ /* On start up we may process an output global before the shell global
+ * in which case we can't create the panel and background just yet */
+ if (desktop->shell)
+ output_init(output, desktop);
+}
+
+static void
+output_remove(struct desktop *desktop, struct output *output)
+{
+ struct output *cur;
+ struct output *rep = NULL;
+
+ if (!output->background) {
+ output_destroy(output);
+ return;
+ }
+
+ /* Find a wl_output that is a clone of the removed wl_output.
+ * We don't want to leave the clone without a background or panel. */
+ wl_list_for_each(cur, &desktop->outputs, link) {
+ if (cur == output)
+ continue;
+
+ /* XXX: Assumes size matches. */
+ if (cur->x == output->x && cur->y == output->y) {
+ rep = cur;
+ break;
+ }
+ }
+
+ if (rep) {
+ /* If found and it does not already have a background or panel,
+ * hand over the background and panel so they don't get
+ * destroyed.
+ *
+ * We never create multiple backgrounds or panels for clones,
+ * but if the compositor moves outputs, a pair of wl_outputs
+ * might become "clones". This may happen temporarily when
+ * an output is about to be removed and the rest are reflowed.
+ * In this case it is correct to let the background/panel be
+ * destroyed.
+ */
+
+ if (!rep->background) {
+ rep->background = output->background;
+ output->background = NULL;
+ rep->background->owner = rep;
+ }
+
+ if (!rep->panel) {
+ rep->panel = output->panel;
+ output->panel = NULL;
+ if (rep->panel)
+ rep->panel->owner = rep;
+ }
+ }
+
+ output_destroy(output);
+}
+
+static void
+global_handler(struct display *display, uint32_t id,
+ const char *interface, uint32_t version, void *data)
+{
+ struct desktop *desktop = data;
+
+ if (!strcmp(interface, "weston_desktop_shell")) {
+ desktop->shell = display_bind(desktop->display,
+ id,
+ &weston_desktop_shell_interface,
+ 1);
+ weston_desktop_shell_add_listener(desktop->shell,
+ &listener,
+ desktop);
+ } else if (!strcmp(interface, "wl_output")) {
+ create_output(desktop, id);
+ }
+}
+
+static void
+global_handler_remove(struct display *display, uint32_t id,
+ const char *interface, uint32_t version, void *data)
+{
+ struct desktop *desktop = data;
+ struct output *output;
+
+ if (!strcmp(interface, "wl_output")) {
+ wl_list_for_each(output, &desktop->outputs, link) {
+ if (output->server_output_id == id) {
+ output_remove(desktop, output);
+ break;
+ }
+ }
+ }
+}
+
+static void
+panel_add_launchers(struct panel *panel, struct desktop *desktop)
+{
+ struct weston_config_section *s;
+ char *icon, *path;
+ const char *name;
+ int count;
+
+ count = 0;
+ s = NULL;
+ while (weston_config_next_section(desktop->config, &s, &name)) {
+ if (strcmp(name, "launcher") != 0)
+ continue;
+
+ weston_config_section_get_string(s, "icon", &icon, NULL);
+ weston_config_section_get_string(s, "path", &path, NULL);
+
+ if (icon != NULL && path != NULL) {
+ panel_add_launcher(panel, icon, path);
+ count++;
+ } else {
+ fprintf(stderr, "invalid launcher section\n");
+ }
+
+ free(icon);
+ free(path);
+ }
+
+ if (count == 0) {
+ char *name = file_name_with_datadir("terminal.png");
+
+ /* add default launcher */
+ panel_add_launcher(panel,
+ name,
+ BINDIR "/weston-terminal");
+ free(name);
+ }
+}
+
+static void
+parse_panel_position(struct desktop *desktop, struct weston_config_section *s)
+{
+ char *position;
+
+ desktop->want_panel = 1;
+
+ weston_config_section_get_string(s, "panel-position", &position, "top");
+ if (strcmp(position, "top") == 0) {
+ desktop->panel_position = WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP;
+ } else if (strcmp(position, "bottom") == 0) {
+ desktop->panel_position = WESTON_DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
+ } else if (strcmp(position, "left") == 0) {
+ desktop->panel_position = WESTON_DESKTOP_SHELL_PANEL_POSITION_LEFT;
+ } else if (strcmp(position, "right") == 0) {
+ desktop->panel_position = WESTON_DESKTOP_SHELL_PANEL_POSITION_RIGHT;
+ } else {
+ /* 'none' is valid here */
+ if (strcmp(position, "none") != 0)
+ fprintf(stderr, "Wrong panel position: %s\n", position);
+ desktop->want_panel = 0;
+ }
+ free(position);
+}
+
+static void
+parse_clock_format(struct desktop *desktop, struct weston_config_section *s)
+{
+ char *clock_format;
+
+ weston_config_section_get_string(s, "clock-format", &clock_format, "");
+ if (strcmp(clock_format, "minutes") == 0)
+ desktop->clock_format = CLOCK_FORMAT_MINUTES;
+ else if (strcmp(clock_format, "seconds") == 0)
+ desktop->clock_format = CLOCK_FORMAT_SECONDS;
+ else if (strcmp(clock_format, "none") == 0)
+ desktop->clock_format = CLOCK_FORMAT_NONE;
+ else
+ desktop->clock_format = DEFAULT_CLOCK_FORMAT;
+ free(clock_format);
+}
+
+int main(int argc, char *argv[])
+{
+ struct desktop desktop = { 0 };
+ struct output *output;
+ struct weston_config_section *s;
+ const char *config_file;
+
+ desktop.unlock_task.run = unlock_dialog_finish;
+ wl_list_init(&desktop.outputs);
+
+ config_file = weston_config_get_name_from_env();
+ desktop.config = weston_config_parse(config_file);
+ s = weston_config_get_section(desktop.config, "shell", NULL, NULL);
+ weston_config_section_get_bool(s, "locking", &desktop.locking, true);
+ parse_panel_position(&desktop, s);
+ parse_clock_format(&desktop, s);
+
+ desktop.display = display_create(&argc, argv);
+ if (desktop.display == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ display_set_user_data(desktop.display, &desktop);
+ display_set_global_handler(desktop.display, global_handler);
+ display_set_global_handler_remove(desktop.display, global_handler_remove);
+
+ /* Create panel and background for outputs processed before the shell
+ * global interface was processed */
+ if (desktop.want_panel)
+ weston_desktop_shell_set_panel_position(desktop.shell, desktop.panel_position);
+ wl_list_for_each(output, &desktop.outputs, link)
+ if (!output->panel)
+ output_init(output, &desktop);
+
+ grab_surface_create(&desktop);
+
+ signal(SIGCHLD, sigchild_handler);
+
+ display_run(desktop.display);
+
+ /* Cleanup */
+ grab_surface_destroy(&desktop);
+ desktop_destroy_outputs(&desktop);
+ if (desktop.unlock_dialog)
+ unlock_dialog_destroy(desktop.unlock_dialog);
+ weston_desktop_shell_destroy(desktop.shell);
+ display_destroy(desktop.display);
+
+ return 0;
+}
diff --git a/clients/dnd.c b/clients/dnd.c
new file mode 100644
index 0000000000000000000000000000000000000000..8323f4fdccb598c61bee26a0caa56a6eda42f214
--- /dev/null
+++ b/clients/dnd.c
@@ -0,0 +1,867 @@
+/*
+ * Copyright © 2010 Kristian Høgsberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "window.h"
+#include "shared/cairo-util.h"
+#include "shared/helpers.h"
+#include "shared/xalloc.h"
+
+struct dnd_drag;
+
+struct pointer {
+ struct input *input;
+ bool dragging;
+ struct wl_list link;
+};
+
+struct dnd {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+ uint32_t key;
+ struct item *items[16];
+ int self_only;
+ struct dnd_drag *current_drag;
+ struct wl_list pointers;
+};
+
+struct dnd_drag {
+ cairo_surface_t *translucent;
+ cairo_surface_t *opaque;
+ int hotspot_x, hotspot_y;
+ struct dnd *dnd;
+ struct input *input;
+ uint32_t time;
+ struct item *item;
+ int x_offset, y_offset;
+ int width, height;
+ uint32_t dnd_action;
+ const char *mime_type;
+
+ struct wl_surface *drag_surface;
+ struct wl_data_source *data_source;
+};
+
+struct item {
+ cairo_surface_t *surface;
+ int seed;
+ int x, y;
+};
+
+struct dnd_flower_message {
+ int seed, x_offset, y_offset;
+};
+
+
+static const int item_width = 64;
+static const int item_height = 64;
+static const int item_padding = 16;
+
+static const char flower_mime_type[] = "application/x-wayland-dnd-flower";
+static const char text_mime_type[] = "text/plain;charset=utf-8";
+
+static struct item *
+item_create(struct display *display, int x, int y, int seed)
+{
+ struct item *item;
+ struct timeval tv;
+
+ item = malloc(sizeof *item);
+ if (item == NULL)
+ return NULL;
+
+
+ gettimeofday(&tv, NULL);
+ item->seed = seed ? seed : tv.tv_usec;
+ srandom(item->seed);
+
+ const int petal_count = 3 + random() % 5;
+ const double r1 = 20 + random() % 10;
+ const double r2 = 5 + random() % 12;
+ const double u = (10 + random() % 90) / 100.0;
+ const double v = (random() % 90) / 100.0;
+
+ cairo_t *cr;
+ int i;
+ double t, dt = 2 * M_PI / (petal_count * 2);
+ double x1, y1, x2, y2, x3, y3;
+ struct rectangle rect;
+
+
+ rect.width = item_width;
+ rect.height = item_height;
+ item->surface =
+ display_create_surface(display, NULL, &rect, SURFACE_SHM);
+
+ item->x = x;
+ item->y = y;
+
+ cr = cairo_create(item->surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0);
+ cairo_paint(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_translate(cr, item_width / 2, item_height / 2);
+ t = random();
+ cairo_move_to(cr, cos(t) * r1, sin(t) * r1);
+ for (i = 0; i < petal_count; i++, t += dt * 2) {
+ x1 = cos(t) * r1;
+ y1 = sin(t) * r1;
+ x2 = cos(t + dt) * r2;
+ y2 = sin(t + dt) * r2;
+ x3 = cos(t + 2 * dt) * r1;
+ y3 = sin(t + 2 * dt) * r1;
+
+ cairo_curve_to(cr,
+ x1 - y1 * u, y1 + x1 * u,
+ x2 + y2 * v, y2 - x2 * v,
+ x2, y2);
+
+ cairo_curve_to(cr,
+ x2 - y2 * v, y2 + x2 * v,
+ x3 + y3 * u, y3 - x3 * u,
+ x3, y3);
+ }
+
+ cairo_close_path(cr);
+
+ cairo_set_source_rgba(cr,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 100) / 99.0);
+
+ cairo_fill_preserve(cr);
+
+ cairo_set_line_width(cr, 1);
+ cairo_set_source_rgba(cr,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 100) / 99.0);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return item;
+}
+
+static void
+dnd_redraw_handler(struct widget *widget, void *data)
+{
+ struct dnd *dnd = data;
+ struct rectangle allocation;
+ cairo_t *cr;
+ cairo_surface_t *surface, *item_surface;
+ unsigned int i;
+
+ surface = window_get_surface(dnd->window);
+ cr = cairo_create(surface);
+ widget_get_allocation(dnd->widget, &allocation);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_fill(cr);
+
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_clip(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ if (!dnd->items[i])
+ continue;
+
+ if (dnd->current_drag && dnd->items[i] == dnd->current_drag->item)
+ item_surface = dnd->current_drag->translucent;
+ else
+ item_surface = dnd->items[i]->surface;
+
+ cairo_set_source_surface(cr, item_surface,
+ dnd->items[i]->x + allocation.x,
+ dnd->items[i]->y + allocation.y);
+ cairo_paint(cr);
+ }
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct dnd *dnd = data;
+
+ window_schedule_redraw(dnd->window);
+}
+
+static int
+dnd_add_item(struct dnd *dnd, struct item *item)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ if (dnd->items[i] == 0) {
+ dnd->items[i] = item;
+ return i;
+ }
+ }
+ return -1;
+}
+
+static struct item *
+dnd_get_item(struct dnd *dnd, int32_t x, int32_t y)
+{
+ struct item *item;
+ struct rectangle allocation;
+ unsigned int i;
+
+ widget_get_allocation(dnd->widget, &allocation);
+
+ x -= allocation.x;
+ y -= allocation.y;
+
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ item = dnd->items[i];
+ if (item &&
+ item->x <= x && x < item->x + item_width &&
+ item->y <= y && y < item->y + item_height)
+ return item;
+ }
+
+ return NULL;
+}
+
+static int
+lookup_dnd_cursor(uint32_t dnd_action)
+{
+ if (dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
+ return CURSOR_DND_MOVE;
+ else if (dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
+ return CURSOR_DND_COPY;
+
+ return CURSOR_DND_FORBIDDEN;
+}
+
+static void
+dnd_drag_update_cursor(struct dnd_drag *dnd_drag)
+{
+ int cursor;
+
+ if (dnd_drag->mime_type == NULL)
+ cursor = CURSOR_DND_FORBIDDEN;
+ else
+ cursor = lookup_dnd_cursor(dnd_drag->dnd_action);
+
+ input_set_pointer_image(dnd_drag->input, cursor);
+}
+
+static void
+dnd_drag_update_surface(struct dnd_drag *dnd_drag)
+{
+ struct dnd *dnd = dnd_drag->dnd;
+ cairo_surface_t *surface;
+ struct wl_buffer *buffer;
+
+ if (dnd_drag->mime_type && dnd_drag->dnd_action)
+ surface = dnd_drag->opaque;
+ else
+ surface = dnd_drag->translucent;
+
+ buffer = display_get_buffer_for_surface(dnd->display, surface);
+ wl_surface_attach(dnd_drag->drag_surface, buffer, 0, 0);
+ wl_surface_damage(dnd_drag->drag_surface, 0, 0,
+ dnd_drag->width, dnd_drag->height);
+ wl_surface_commit(dnd_drag->drag_surface);
+}
+
+static void
+data_source_target(void *data,
+ struct wl_data_source *source, const char *mime_type)
+{
+ struct dnd_drag *dnd_drag = data;
+
+ dnd_drag->mime_type = mime_type;
+ dnd_drag_update_surface(dnd_drag);
+ dnd_drag_update_cursor(dnd_drag);
+}
+
+static void
+data_source_send(void *data, struct wl_data_source *source,
+ const char *mime_type, int32_t fd)
+{
+ struct dnd_flower_message dnd_flower_message;
+ struct dnd_drag *dnd_drag = data;
+ char buffer[128];
+ int n;
+
+ if (strcmp(mime_type, flower_mime_type) == 0) {
+ dnd_flower_message.seed = dnd_drag->item->seed;
+ dnd_flower_message.x_offset = dnd_drag->x_offset;
+ dnd_flower_message.y_offset = dnd_drag->y_offset;
+
+ if (write(fd, &dnd_flower_message,
+ sizeof dnd_flower_message) < 0)
+ abort();
+ } else if (strcmp(mime_type, text_mime_type) == 0) {
+ n = snprintf(buffer, sizeof buffer, "seed=%d x=%d y=%d\n",
+ dnd_drag->item->seed,
+ dnd_drag->x_offset,
+ dnd_drag->y_offset);
+
+ if (write(fd, buffer, n) < 0)
+ abort();
+ }
+
+ close(fd);
+}
+
+static void
+dnd_drag_destroy(struct dnd_drag *dnd_drag, bool delete_item)
+{
+ struct dnd *dnd = dnd_drag->dnd;
+ unsigned int i;
+
+ wl_data_source_destroy(dnd_drag->data_source);
+
+ if (delete_item) {
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ if (dnd_drag->item == dnd->items[i]) {
+ dnd->items[i] = NULL;
+ break;
+ }
+ }
+
+ /* Destroy the item that has been dragged out */
+ cairo_surface_destroy(dnd_drag->item->surface);
+ free(dnd_drag->item);
+ }
+
+ dnd->current_drag = NULL;
+
+ wl_surface_destroy(dnd_drag->drag_surface);
+
+ cairo_surface_destroy(dnd_drag->translucent);
+ cairo_surface_destroy(dnd_drag->opaque);
+ free(dnd_drag);
+}
+
+static void
+data_source_cancelled(void *data, struct wl_data_source *source)
+{
+ struct dnd_drag *dnd_drag = data;
+ struct dnd *dnd = dnd_drag->dnd;
+
+ /* The 'cancelled' event means that the source is no longer in
+ * use by the drag (or current selection). We need to clean
+ * up the drag object created and the local state. */
+ dnd_drag_destroy(dnd_drag, false);
+ window_schedule_redraw(dnd->window);
+}
+
+static void
+data_source_dnd_drop_performed(void *data, struct wl_data_source *source)
+{
+}
+
+static void
+data_source_dnd_finished(void *data, struct wl_data_source *source)
+{
+ struct dnd_drag *dnd_drag = data;
+ struct dnd *dnd = dnd_drag->dnd;
+ bool delete_item;
+
+ delete_item =
+ dnd_drag->dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+
+ /* The operation is already finished, we can destroy all
+ * related data.
+ */
+ dnd_drag_destroy(dnd_drag, delete_item);
+ window_schedule_redraw(dnd->window);
+}
+
+static void
+data_source_action(void *data, struct wl_data_source *source, uint32_t dnd_action)
+{
+ struct dnd_drag *dnd_drag = data;
+
+ dnd_drag->dnd_action = dnd_action;
+ dnd_drag_update_surface(dnd_drag);
+ dnd_drag_update_cursor(dnd_drag);
+}
+
+static const struct wl_data_source_listener data_source_listener = {
+ data_source_target,
+ data_source_send,
+ data_source_cancelled,
+ data_source_dnd_drop_performed,
+ data_source_dnd_finished,
+ data_source_action,
+};
+
+static cairo_surface_t *
+create_drag_icon(struct dnd_drag *dnd_drag,
+ struct item *item, int32_t x, int32_t y, double opacity)
+{
+ struct dnd *dnd = dnd_drag->dnd;
+ cairo_surface_t *surface;
+ struct rectangle rectangle;
+ cairo_pattern_t *pattern;
+ cairo_t *cr;
+
+ rectangle.width = item_width;
+ rectangle.height = item_height;
+ surface = display_create_surface(dnd->display, NULL, &rectangle,
+ SURFACE_SHM);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_surface(cr, item->surface, 0, 0);
+ pattern = cairo_pattern_create_rgba(0, 0, 0, opacity);
+ cairo_mask(cr, pattern);
+ cairo_pattern_destroy(pattern);
+
+ cairo_destroy(cr);
+
+ dnd_drag->hotspot_x = x - item->x;
+ dnd_drag->hotspot_y = y - item->y;
+ dnd_drag->width = rectangle.width;
+ dnd_drag->height = rectangle.height;
+
+ return surface;
+}
+
+static int
+create_drag_source(struct dnd *dnd,
+ struct input *input, uint32_t time,
+ int32_t x, int32_t y)
+{
+ struct item *item;
+ struct rectangle allocation;
+ struct dnd_drag *dnd_drag;
+ struct display *display;
+ struct wl_compositor *compositor;
+ struct wl_buffer *buffer;
+ unsigned int i;
+ uint32_t serial;
+ cairo_surface_t *icon;
+ uint32_t actions;
+
+ widget_get_allocation(dnd->widget, &allocation);
+ item = dnd_get_item(dnd, x, y);
+ x -= allocation.x;
+ y -= allocation.y;
+
+ if (item) {
+ dnd_drag = xmalloc(sizeof *dnd_drag);
+ dnd_drag->dnd = dnd;
+ dnd_drag->input = input;
+ dnd_drag->time = time;
+ dnd_drag->item = item;
+ dnd_drag->x_offset = x - item->x;
+ dnd_drag->y_offset = y - item->y;
+ dnd_drag->dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+ dnd_drag->mime_type = NULL;
+
+ actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE |
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+
+ display = window_get_display(dnd->window);
+ compositor = display_get_compositor(display);
+ serial = display_get_serial(display);
+ dnd_drag->drag_surface =
+ wl_compositor_create_surface(compositor);
+
+ if (display_get_data_device_manager_version(display) <
+ WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
+ /* Data sources version < 3 will not get action
+ * nor dnd_finished events, as we can't honor
+ * the "move" action at the time of finishing
+ * drag-and-drop, do it preemptively here.
+ */
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ if (item == dnd->items[i]){
+ dnd->items[i] = NULL;
+ break;
+ }
+ }
+ }
+
+ if (dnd->self_only) {
+ dnd_drag->data_source = NULL;
+ } else {
+ dnd_drag->data_source =
+ display_create_data_source(dnd->display);
+ if (!dnd_drag->data_source) {
+ fprintf(stderr, "No data device manager\n");
+ abort();
+ }
+ wl_data_source_add_listener(dnd_drag->data_source,
+ &data_source_listener,
+ dnd_drag);
+ wl_data_source_offer(dnd_drag->data_source,
+ flower_mime_type);
+ wl_data_source_offer(dnd_drag->data_source,
+ text_mime_type);
+ }
+
+ if (display_get_data_device_manager_version(display) >=
+ WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
+ wl_data_source_set_actions(dnd_drag->data_source, actions);
+ }
+
+ wl_data_device_start_drag(input_get_data_device(input),
+ dnd_drag->data_source,
+ window_get_wl_surface(dnd->window),
+ dnd_drag->drag_surface,
+ serial);
+
+ dnd_drag->opaque =
+ create_drag_icon(dnd_drag, item, x, y, 1);
+ dnd_drag->translucent =
+ create_drag_icon(dnd_drag, item, x, y, 0.2);
+
+ if (dnd->self_only)
+ icon = dnd_drag->opaque;
+ else
+ icon = dnd_drag->translucent;
+
+ buffer = display_get_buffer_for_surface(dnd->display, icon);
+ wl_surface_attach(dnd_drag->drag_surface, buffer,
+ -dnd_drag->hotspot_x, -dnd_drag->hotspot_y);
+ wl_surface_damage(dnd_drag->drag_surface, 0, 0,
+ dnd_drag->width, dnd_drag->height);
+ wl_surface_commit(dnd_drag->drag_surface);
+
+ dnd->current_drag = dnd_drag;
+ window_schedule_redraw(dnd->window);
+
+ return 0;
+ } else
+ return -1;
+}
+
+static int
+lookup_cursor(struct dnd *dnd, int x, int y)
+{
+ struct item *item;
+
+ item = dnd_get_item(dnd, x, y);
+ if (item)
+ return CURSOR_HAND1;
+ else
+ return CURSOR_LEFT_PTR;
+}
+
+/* Update all the mouse pointers in the window appropriately.
+ * Optionally, skip one (which will be the current pointer just
+ * about to start a drag). This is done here to save a scan
+ * through the pointer list.
+ */
+static void
+update_pointer_images_except(struct dnd *dnd, struct input *except)
+{
+ struct pointer *pointer;
+ int32_t x, y;
+
+ wl_list_for_each(pointer, &dnd->pointers, link) {
+ if (pointer->input == except) {
+ pointer->dragging = true;
+ continue;
+ }
+ input_get_position(pointer->input, &x, &y);
+ input_set_pointer_image(pointer->input,
+ lookup_cursor(dnd, x, y));
+ }
+}
+
+static void
+dnd_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state,
+ void *data)
+{
+ struct dnd *dnd = data;
+ int32_t x, y;
+
+ input_get_position(input, &x, &y);
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ input_ungrab(input);
+ if (create_drag_source(dnd, input, time, x, y) == 0) {
+ input_set_pointer_image(input, CURSOR_DRAGGING);
+ update_pointer_images_except(dnd, input);
+ }
+ }
+}
+
+static void
+dnd_touch_down_handler(struct widget *widget,
+ struct input *input, uint32_t serial,
+ uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct dnd *dnd = data;
+ int32_t int_x, int_y;
+
+ if (id > 0)
+ return;
+
+ int_x = (int32_t)x;
+ int_y = (int32_t)y;
+ if (create_drag_source(dnd, input, time, int_x, int_y) == 0)
+ touch_grab(input, 0);
+}
+
+static int
+dnd_enter_handler(struct widget *widget,
+ struct input *input, float x, float y, void *data)
+{
+ struct dnd *dnd = data;
+ struct pointer *new_pointer = malloc(sizeof *new_pointer);
+
+ if (new_pointer) {
+ new_pointer->input = input;
+ new_pointer->dragging = false;
+ wl_list_insert(dnd->pointers.prev, &new_pointer->link);
+ }
+
+ return lookup_cursor(dnd, x, y);
+}
+
+static void
+dnd_leave_handler(struct widget *widget,
+ struct input *input, void *data)
+{
+ struct dnd *dnd = data;
+ struct pointer *pointer, *tmp;
+
+ wl_list_for_each_safe(pointer, tmp, &dnd->pointers, link)
+ if (pointer->input == input) {
+ wl_list_remove(&pointer->link);
+ free(pointer);
+ }
+}
+
+static int
+dnd_motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct dnd *dnd = data;
+ struct pointer *pointer;
+
+ wl_list_for_each(pointer, &dnd->pointers, link)
+ if (pointer->input == input) {
+ if (pointer->dragging)
+ return CURSOR_DRAGGING;
+ break;
+ }
+
+ return lookup_cursor(data, x, y);
+}
+
+static void
+dnd_data_handler(struct window *window,
+ struct input *input,
+ float x, float y, const char **types, void *data)
+{
+ struct dnd *dnd = data;
+ int i, has_flower = 0;
+
+ if (!types)
+ return;
+ for (i = 0; types[i]; i++)
+ if (strcmp(types[i], flower_mime_type) == 0)
+ has_flower = 1;
+
+ if (dnd_get_item(dnd, x, y) || dnd->self_only || !has_flower) {
+ input_accept(input, NULL);
+ } else {
+ input_accept(input, flower_mime_type);
+ }
+}
+
+static void
+dnd_receive_func(void *data, size_t len, int32_t x, int32_t y, void *user_data)
+{
+ struct dnd *dnd = user_data;
+ struct dnd_flower_message *message = data;
+ struct item *item;
+ struct rectangle allocation;
+
+ if (len == 0) {
+ return;
+ } else if (len != sizeof *message) {
+ fprintf(stderr, "odd message length %zu, expected %zu\n",
+ len, sizeof *message);
+ return;
+ }
+
+ widget_get_allocation(dnd->widget, &allocation);
+ item = item_create(dnd->display,
+ x - message->x_offset - allocation.x,
+ y - message->y_offset - allocation.y,
+ message->seed);
+
+ dnd_add_item(dnd, item);
+ update_pointer_images_except(dnd, NULL);
+ window_schedule_redraw(dnd->window);
+}
+
+static void
+dnd_drop_handler(struct window *window, struct input *input,
+ int32_t x, int32_t y, void *data)
+{
+ struct dnd *dnd = data;
+ struct dnd_flower_message message;
+
+ if (dnd_get_item(dnd, x, y)) {
+ fprintf(stderr, "got 'drop', but no target\n");
+ return;
+ }
+
+ if (!dnd->self_only) {
+ input_receive_drag_data(input,
+ flower_mime_type,
+ dnd_receive_func, dnd);
+ } else if (dnd->current_drag) {
+ message.seed = dnd->current_drag->item->seed;
+ message.x_offset = dnd->current_drag->x_offset;
+ message.y_offset = dnd->current_drag->y_offset;
+ dnd_receive_func(&message, sizeof message, x, y, dnd);
+ } else {
+ fprintf(stderr, "ignoring drop from another client\n");
+ }
+}
+
+static struct dnd *
+dnd_create(struct display *display)
+{
+ struct dnd *dnd;
+ int x, y;
+ int32_t width, height;
+ unsigned int i;
+
+ dnd = xzalloc(sizeof *dnd);
+ dnd->window = window_create(display);
+ dnd->widget = window_frame_create(dnd->window, dnd);
+ window_set_title(dnd->window, "Wayland Drag and Drop Demo");
+
+ dnd->display = display;
+ dnd->key = 100;
+
+ wl_list_init(&dnd->pointers);
+
+ for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
+ x = (i % 4) * (item_width + item_padding) + item_padding;
+ y = (i / 4) * (item_height + item_padding) + item_padding;
+ if ((i ^ (i >> 2)) & 1)
+ dnd->items[i] = item_create(display, x, y, 0);
+ else
+ dnd->items[i] = NULL;
+ }
+
+ window_set_user_data(dnd->window, dnd);
+ window_set_keyboard_focus_handler(dnd->window,
+ keyboard_focus_handler);
+ window_set_data_handler(dnd->window, dnd_data_handler);
+ window_set_drop_handler(dnd->window, dnd_drop_handler);
+
+ widget_set_redraw_handler(dnd->widget, dnd_redraw_handler);
+ widget_set_enter_handler(dnd->widget, dnd_enter_handler);
+ widget_set_leave_handler(dnd->widget, dnd_leave_handler);
+ widget_set_motion_handler(dnd->widget, dnd_motion_handler);
+ widget_set_button_handler(dnd->widget, dnd_button_handler);
+ widget_set_touch_down_handler(dnd->widget, dnd_touch_down_handler);
+
+ width = 4 * (item_width + item_padding) + item_padding;
+ height = 4 * (item_height + item_padding) + item_padding;
+
+ window_frame_set_child_size(dnd->widget, width, height);
+
+ return dnd;
+}
+
+static void
+dnd_destroy(struct dnd *dnd)
+{
+ widget_destroy(dnd->widget);
+ window_destroy(dnd->window);
+ free(dnd);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *d;
+ struct dnd *dnd;
+ int self_only = 0;
+
+ if (argc == 2 && !strcmp(argv[1], "--self-only"))
+ self_only = 1;
+ else if (argc > 1) {
+ printf("Usage: %s [OPTIONS]\n --self-only\n", argv[0]);
+ return 1;
+ }
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ dnd = dnd_create(d);
+ if (self_only)
+ dnd->self_only = 1;
+
+ display_run(d);
+
+ dnd_destroy(dnd);
+ display_destroy(d);
+
+ return 0;
+}
diff --git a/clients/editor.c b/clients/editor.c
new file mode 100644
index 0000000000000000000000000000000000000000..a59f9679a682715b93495c9b181bd17e67027d69
--- /dev/null
+++ b/clients/editor.c
@@ -0,0 +1,1681 @@
+/*
+ * Copyright © 2012 Openismus GmbH
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include
+#include "shared/helpers.h"
+#include "shared/xalloc.h"
+#include "window.h"
+#include "text-input-unstable-v1-client-protocol.h"
+
+struct text_entry {
+ struct widget *widget;
+ struct window *window;
+ char *text;
+ int active;
+ bool panel_visible;
+ uint32_t cursor;
+ uint32_t anchor;
+ struct {
+ char *text;
+ int32_t cursor;
+ char *commit;
+ PangoAttrList *attr_list;
+ } preedit;
+ struct {
+ PangoAttrList *attr_list;
+ int32_t cursor;
+ } preedit_info;
+ struct {
+ int32_t cursor;
+ int32_t anchor;
+ uint32_t delete_index;
+ uint32_t delete_length;
+ bool invalid_delete;
+ } pending_commit;
+ struct zwp_text_input_v1 *text_input;
+ PangoLayout *layout;
+ struct {
+ xkb_mod_mask_t shift_mask;
+ } keysym;
+ uint32_t serial;
+ uint32_t reset_serial;
+ uint32_t content_purpose;
+ bool click_to_show;
+ char *preferred_language;
+ bool button_pressed;
+};
+
+struct editor {
+ struct zwp_text_input_manager_v1 *text_input_manager;
+ struct wl_data_source *selection;
+ char *selected_text;
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ struct text_entry *entry;
+ struct text_entry *editor;
+ struct text_entry *active_entry;
+};
+
+static const char *
+utf8_end_char(const char *p)
+{
+ while ((*p & 0xc0) == 0x80)
+ p++;
+ return p;
+}
+
+static const char *
+utf8_prev_char(const char *s, const char *p)
+{
+ for (--p; p >= s; --p) {
+ if ((*p & 0xc0) != 0x80)
+ return p;
+ }
+ return NULL;
+}
+
+static const char *
+utf8_next_char(const char *p)
+{
+ if (*p != 0)
+ return utf8_end_char(++p);
+ return NULL;
+}
+
+static void
+move_up(const char *p, uint32_t *cursor)
+{
+ const char *posr, *posr_i;
+ char text[16];
+
+ xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
+
+ posr = strstr(p, text);
+ while (posr) {
+ if (*cursor > (unsigned)(posr-p)) {
+ posr_i = strstr(posr+1, text);
+ if (!posr_i || !(*cursor > (unsigned)(posr_i-p))) {
+ *cursor = posr-p;
+ break;
+ }
+ posr = posr_i;
+ } else {
+ break;
+ }
+ }
+}
+
+static void
+move_down(const char *p, uint32_t *cursor)
+{
+ const char *posr;
+ char text[16];
+
+ xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
+
+ posr = strstr(p, text);
+ while (posr) {
+ if (*cursor <= (unsigned)(posr-p)) {
+ *cursor = posr-p + 1;
+ break;
+ }
+ posr = strstr(posr+1, text);
+ }
+}
+
+static void text_entry_redraw_handler(struct widget *widget, void *data);
+static void text_entry_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data);
+static void text_entry_touch_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float tx, float ty, void *data);
+static int text_entry_motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data);
+static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
+ int32_t cursor, int32_t anchor);
+static void text_entry_set_preedit(struct text_entry *entry,
+ const char *preedit_text,
+ int preedit_cursor);
+static void text_entry_delete_text(struct text_entry *entry,
+ uint32_t index, uint32_t length);
+static void text_entry_delete_selected_text(struct text_entry *entry);
+static void text_entry_reset_preedit(struct text_entry *entry);
+static void text_entry_commit_and_reset(struct text_entry *entry);
+static void text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle);
+static void text_entry_update(struct text_entry *entry);
+
+static void
+text_input_commit_string(void *data,
+ struct zwp_text_input_v1 *text_input,
+ uint32_t serial,
+ const char *text)
+{
+ struct text_entry *entry = data;
+
+ if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
+ fprintf(stderr, "Ignore commit. Serial: %u, Current: %u, Reset: %u\n",
+ serial, entry->serial, entry->reset_serial);
+ return;
+ }
+
+ if (entry->pending_commit.invalid_delete) {
+ fprintf(stderr, "Ignore commit. Invalid previous delete_surrounding event.\n");
+ memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
+ return;
+ }
+
+ text_entry_reset_preedit(entry);
+
+ if (entry->pending_commit.delete_length) {
+ text_entry_delete_text(entry,
+ entry->pending_commit.delete_index,
+ entry->pending_commit.delete_length);
+ } else {
+ text_entry_delete_selected_text(entry);
+ }
+
+ text_entry_insert_at_cursor(entry, text,
+ entry->pending_commit.cursor,
+ entry->pending_commit.anchor);
+
+ memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+clear_pending_preedit(struct text_entry *entry)
+{
+ memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
+
+ pango_attr_list_unref(entry->preedit_info.attr_list);
+
+ entry->preedit_info.cursor = 0;
+ entry->preedit_info.attr_list = NULL;
+
+ memset(&entry->preedit_info, 0, sizeof entry->preedit_info);
+}
+
+static void
+text_input_preedit_string(void *data,
+ struct zwp_text_input_v1 *text_input,
+ uint32_t serial,
+ const char *text,
+ const char *commit)
+{
+ struct text_entry *entry = data;
+
+ if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
+ fprintf(stderr, "Ignore preedit_string. Serial: %u, Current: %u, Reset: %u\n",
+ serial, entry->serial, entry->reset_serial);
+ clear_pending_preedit(entry);
+ return;
+ }
+
+ if (entry->pending_commit.invalid_delete) {
+ fprintf(stderr, "Ignore preedit_string. Invalid previous delete_surrounding event.\n");
+ clear_pending_preedit(entry);
+ return;
+ }
+
+ if (entry->pending_commit.delete_length) {
+ text_entry_delete_text(entry,
+ entry->pending_commit.delete_index,
+ entry->pending_commit.delete_length);
+ } else {
+ text_entry_delete_selected_text(entry);
+ }
+
+ text_entry_set_preedit(entry, text, entry->preedit_info.cursor);
+ entry->preedit.commit = strdup(commit);
+ entry->preedit.attr_list = pango_attr_list_ref(entry->preedit_info.attr_list);
+
+ clear_pending_preedit(entry);
+
+ text_entry_update(entry);
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+text_input_delete_surrounding_text(void *data,
+ struct zwp_text_input_v1 *text_input,
+ int32_t index,
+ uint32_t length)
+{
+ struct text_entry *entry = data;
+ uint32_t text_length;
+
+ entry->pending_commit.delete_index = entry->cursor + index;
+ entry->pending_commit.delete_length = length;
+ entry->pending_commit.invalid_delete = false;
+
+ text_length = strlen(entry->text);
+
+ if (entry->pending_commit.delete_index > text_length ||
+ length > text_length ||
+ entry->pending_commit.delete_index + length > text_length) {
+ fprintf(stderr, "delete_surrounding_text: Invalid index: %d," \
+ "length %u'; cursor: %u text length: %u\n", index, length, entry->cursor, text_length);
+ entry->pending_commit.invalid_delete = true;
+ return;
+ }
+}
+
+static void
+text_input_cursor_position(void *data,
+ struct zwp_text_input_v1 *text_input,
+ int32_t index,
+ int32_t anchor)
+{
+ struct text_entry *entry = data;
+
+ entry->pending_commit.cursor = index;
+ entry->pending_commit.anchor = anchor;
+}
+
+static void
+text_input_preedit_styling(void *data,
+ struct zwp_text_input_v1 *text_input,
+ uint32_t index,
+ uint32_t length,
+ uint32_t style)
+{
+ struct text_entry *entry = data;
+ PangoAttribute *attr1 = NULL;
+ PangoAttribute *attr2 = NULL;
+
+ if (!entry->preedit_info.attr_list)
+ entry->preedit_info.attr_list = pango_attr_list_new();
+
+ switch (style) {
+ case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_DEFAULT:
+ case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE:
+ attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+ break;
+ case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INCORRECT:
+ attr1 = pango_attr_underline_new(PANGO_UNDERLINE_ERROR);
+ attr2 = pango_attr_underline_color_new(65535, 0, 0);
+ break;
+ case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_SELECTION:
+ attr1 = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
+ attr2 = pango_attr_foreground_new(65535, 65535, 65535);
+ break;
+ case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT:
+ case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_ACTIVE:
+ attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+ attr2 = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
+ break;
+ case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INACTIVE:
+ attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+ attr2 = pango_attr_foreground_new(0.3 * 65535, 0.3 * 65535, 0.3 * 65535);
+ break;
+ }
+
+ if (attr1) {
+ attr1->start_index = entry->cursor + index;
+ attr1->end_index = entry->cursor + index + length;
+ pango_attr_list_insert(entry->preedit_info.attr_list, attr1);
+ }
+
+ if (attr2) {
+ attr2->start_index = entry->cursor + index;
+ attr2->end_index = entry->cursor + index + length;
+ pango_attr_list_insert(entry->preedit_info.attr_list, attr2);
+ }
+}
+
+static void
+text_input_preedit_cursor(void *data,
+ struct zwp_text_input_v1 *text_input,
+ int32_t index)
+{
+ struct text_entry *entry = data;
+
+ entry->preedit_info.cursor = index;
+}
+
+static void
+text_input_modifiers_map(void *data,
+ struct zwp_text_input_v1 *text_input,
+ struct wl_array *map)
+{
+ struct text_entry *entry = data;
+
+ entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
+}
+
+static void
+text_input_keysym(void *data,
+ struct zwp_text_input_v1 *text_input,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ uint32_t state,
+ uint32_t modifiers)
+{
+ struct text_entry *entry = data;
+ const char *new_char;
+
+ if (key == XKB_KEY_Left ||
+ key == XKB_KEY_Right) {
+ if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ if (key == XKB_KEY_Left)
+ new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
+ else
+ new_char = utf8_next_char(entry->text + entry->cursor);
+
+ if (new_char != NULL) {
+ entry->cursor = new_char - entry->text;
+ }
+
+ if (!(modifiers & entry->keysym.shift_mask))
+ entry->anchor = entry->cursor;
+ widget_schedule_redraw(entry->widget);
+
+ return;
+ }
+
+ if (key == XKB_KEY_Up ||
+ key == XKB_KEY_Down) {
+ if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ if (key == XKB_KEY_Up)
+ move_up(entry->text, &entry->cursor);
+ else
+ move_down(entry->text, &entry->cursor);
+
+ if (!(modifiers & entry->keysym.shift_mask))
+ entry->anchor = entry->cursor;
+ widget_schedule_redraw(entry->widget);
+
+ return;
+ }
+
+ if (key == XKB_KEY_BackSpace) {
+ const char *start, *end;
+
+ if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ text_entry_commit_and_reset(entry);
+
+ start = utf8_prev_char(entry->text, entry->text + entry->cursor);
+ if (start == NULL)
+ return;
+
+ end = utf8_next_char(start);
+
+ text_entry_delete_text(entry,
+ start - entry->text,
+ end - start);
+
+ return;
+ }
+
+ if (key == XKB_KEY_Tab ||
+ key == XKB_KEY_KP_Enter ||
+ key == XKB_KEY_Return) {
+ char text[16];
+
+ if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ xkb_keysym_to_utf8(key, text, sizeof(text));
+
+ text_entry_insert_at_cursor(entry, text, 0, 0);
+
+ return;
+ }
+}
+
+static void
+text_input_enter(void *data,
+ struct zwp_text_input_v1 *text_input,
+ struct wl_surface *surface)
+{
+ struct text_entry *entry = data;
+
+ if (surface != window_get_wl_surface(entry->window))
+ return;
+
+ entry->active++;
+
+ text_entry_update(entry);
+ entry->reset_serial = entry->serial;
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+text_input_leave(void *data,
+ struct zwp_text_input_v1 *text_input)
+{
+ struct text_entry *entry = data;
+
+ text_entry_commit_and_reset(entry);
+ entry->active--;
+
+ if (!entry->active) {
+ zwp_text_input_v1_hide_input_panel(text_input);
+ entry->panel_visible = false;
+ }
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+text_input_input_panel_state(void *data,
+ struct zwp_text_input_v1 *text_input,
+ uint32_t state)
+{
+}
+
+static void
+text_input_language(void *data,
+ struct zwp_text_input_v1 *text_input,
+ uint32_t serial,
+ const char *language)
+{
+ fprintf(stderr, "input language is %s \n", language);
+}
+
+static void
+text_input_text_direction(void *data,
+ struct zwp_text_input_v1 *text_input,
+ uint32_t serial,
+ uint32_t direction)
+{
+ struct text_entry *entry = data;
+ PangoContext *context = pango_layout_get_context(entry->layout);
+ PangoDirection pango_direction;
+
+
+ switch (direction) {
+ case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR:
+ pango_direction = PANGO_DIRECTION_LTR;
+ break;
+ case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL:
+ pango_direction = PANGO_DIRECTION_RTL;
+ break;
+ case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_AUTO:
+ default:
+ pango_direction = PANGO_DIRECTION_NEUTRAL;
+ }
+
+ pango_context_set_base_dir(context, pango_direction);
+}
+
+static const struct zwp_text_input_v1_listener text_input_listener = {
+ text_input_enter,
+ text_input_leave,
+ text_input_modifiers_map,
+ text_input_input_panel_state,
+ text_input_preedit_string,
+ text_input_preedit_styling,
+ text_input_preedit_cursor,
+ text_input_commit_string,
+ text_input_cursor_position,
+ text_input_delete_surrounding_text,
+ text_input_keysym,
+ text_input_language,
+ text_input_text_direction
+};
+
+static void
+data_source_target(void *data,
+ struct wl_data_source *source, const char *mime_type)
+{
+}
+
+static void
+data_source_send(void *data,
+ struct wl_data_source *source,
+ const char *mime_type, int32_t fd)
+{
+ struct editor *editor = data;
+
+ if (write(fd, editor->selected_text, strlen(editor->selected_text) + 1) < 0)
+ fprintf(stderr, "write failed: %s\n", strerror(errno));
+
+ close(fd);
+}
+
+static void
+data_source_cancelled(void *data, struct wl_data_source *source)
+{
+ wl_data_source_destroy(source);
+}
+
+static const struct wl_data_source_listener data_source_listener = {
+ data_source_target,
+ data_source_send,
+ data_source_cancelled
+};
+
+static void
+paste_func(void *buffer, size_t len,
+ int32_t x, int32_t y, void *data)
+{
+ struct editor *editor = data;
+ struct text_entry *entry = editor->active_entry;
+ char *pasted_text;
+
+ if (!entry)
+ return;
+
+ pasted_text = malloc(len + 1);
+ strncpy(pasted_text, buffer, len);
+ pasted_text[len] = '\0';
+
+ text_entry_insert_at_cursor(entry, pasted_text, 0, 0);
+
+ free(pasted_text);
+}
+
+static void
+editor_copy_cut(struct editor *editor, struct input *input, bool cut)
+{
+ struct text_entry *entry = editor->active_entry;
+
+ if (!entry)
+ return;
+
+ if (entry->cursor != entry->anchor) {
+ int start_index = MIN(entry->cursor, entry->anchor);
+ int end_index = MAX(entry->cursor, entry->anchor);
+ int len = end_index - start_index;
+
+ editor->selected_text = realloc(editor->selected_text, len + 1);
+ strncpy(editor->selected_text, &entry->text[start_index], len);
+ editor->selected_text[len] = '\0';
+
+ if (cut)
+ text_entry_delete_text(entry, start_index, len);
+
+ editor->selection =
+ display_create_data_source(editor->display);
+ if (!editor->selection)
+ return;
+
+ wl_data_source_offer(editor->selection,
+ "text/plain;charset=utf-8");
+ wl_data_source_add_listener(editor->selection,
+ &data_source_listener, editor);
+ input_set_selection(input, editor->selection,
+ display_get_serial(editor->display));
+ }
+}
+
+static void
+editor_paste(struct editor *editor, struct input *input)
+{
+ input_receive_selection_data(input,
+ "text/plain;charset=utf-8",
+ paste_func, editor);
+}
+
+static void
+menu_func(void *data, struct input *input, int index)
+{
+ struct window *window = data;
+ struct editor *editor = window_get_user_data(window);
+
+ fprintf(stderr, "picked entry %d\n", index);
+
+ switch (index) {
+ case 0:
+ editor_copy_cut(editor, input, true);
+ break;
+ case 1:
+ editor_copy_cut(editor, input, false);
+ break;
+ case 2:
+ editor_paste(editor, input);
+ break;
+ }
+}
+
+static void
+show_menu(struct editor *editor, struct input *input, uint32_t time)
+{
+ int32_t x, y;
+ static const char *entries[] = {
+ "Cut", "Copy", "Paste"
+ };
+
+ input_get_position(input, &x, &y);
+ window_show_menu(editor->display, input, time, editor->window,
+ x + 10, y + 20, menu_func,
+ entries, ARRAY_LENGTH(entries));
+}
+
+static struct text_entry*
+text_entry_create(struct editor *editor, const char *text)
+{
+ struct text_entry *entry;
+
+ entry = xzalloc(sizeof *entry);
+
+ entry->widget = widget_add_widget(editor->widget, entry);
+ entry->window = editor->window;
+ entry->text = strdup(text);
+ entry->active = 0;
+ entry->panel_visible = false;
+ entry->cursor = strlen(text);
+ entry->anchor = entry->cursor;
+ entry->text_input =
+ zwp_text_input_manager_v1_create_text_input(editor->text_input_manager);
+ zwp_text_input_v1_add_listener(entry->text_input,
+ &text_input_listener, entry);
+
+ widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
+ widget_set_button_handler(entry->widget, text_entry_button_handler);
+ widget_set_motion_handler(entry->widget, text_entry_motion_handler);
+ widget_set_touch_down_handler(entry->widget, text_entry_touch_handler);
+
+ return entry;
+}
+
+static void
+text_entry_destroy(struct text_entry *entry)
+{
+ widget_destroy(entry->widget);
+ zwp_text_input_v1_destroy(entry->text_input);
+ g_clear_object(&entry->layout);
+ free(entry->text);
+ free(entry->preferred_language);
+ free(entry);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct editor *editor = data;
+ cairo_surface_t *surface;
+ struct rectangle allocation;
+ cairo_t *cr;
+
+ surface = window_get_surface(editor->window);
+ widget_get_allocation(editor->widget, &allocation);
+
+ cr = cairo_create(surface);
+ cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
+ cairo_clip(cr);
+
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ /* Draw background */
+ cairo_push_group(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 1, 1, 1, 1);
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill(cr);
+
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static void
+text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
+ int32_t width, int32_t height)
+{
+ widget_set_allocation(entry->widget, x, y, width, height);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct editor *editor = data;
+ struct rectangle allocation;
+
+ widget_get_allocation(editor->widget, &allocation);
+
+ text_entry_allocate(editor->entry,
+ allocation.x + 20, allocation.y + 20,
+ width - 40, height / 2 - 40);
+ text_entry_allocate(editor->editor,
+ allocation.x + 20, allocation.y + height / 2 + 20,
+ width - 40, height / 2 - 40);
+}
+
+static void
+text_entry_activate(struct text_entry *entry,
+ struct wl_seat *seat)
+{
+ struct wl_surface *surface = window_get_wl_surface(entry->window);
+
+ if (entry->click_to_show && entry->active) {
+ entry->panel_visible = !entry->panel_visible;
+
+ if (entry->panel_visible)
+ zwp_text_input_v1_show_input_panel(entry->text_input);
+ else
+ zwp_text_input_v1_hide_input_panel(entry->text_input);
+
+ return;
+ }
+
+ if (!entry->click_to_show)
+ zwp_text_input_v1_show_input_panel(entry->text_input);
+
+ zwp_text_input_v1_activate(entry->text_input,
+ seat,
+ surface);
+}
+
+static void
+text_entry_deactivate(struct text_entry *entry,
+ struct wl_seat *seat)
+{
+ zwp_text_input_v1_deactivate(entry->text_input,
+ seat);
+}
+
+static void
+text_entry_update_layout(struct text_entry *entry)
+{
+ char *text;
+ PangoAttrList *attr_list;
+
+ assert(entry->cursor <= (strlen(entry->text) +
+ (entry->preedit.text ? strlen(entry->preedit.text) : 0)));
+
+ if (entry->preedit.text) {
+ text = xmalloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
+ strncpy(text, entry->text, entry->cursor);
+ strcpy(text + entry->cursor, entry->preedit.text);
+ strcpy(text + entry->cursor + strlen(entry->preedit.text),
+ entry->text + entry->cursor);
+ } else {
+ text = strdup(entry->text);
+ }
+
+ if (entry->cursor != entry->anchor) {
+ int start_index = MIN(entry->cursor, entry->anchor);
+ int end_index = MAX(entry->cursor, entry->anchor);
+ PangoAttribute *attr;
+
+ attr_list = pango_attr_list_copy(entry->preedit.attr_list);
+
+ if (!attr_list)
+ attr_list = pango_attr_list_new();
+
+ attr = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
+ attr->start_index = start_index;
+ attr->end_index = end_index;
+ pango_attr_list_insert(attr_list, attr);
+
+ attr = pango_attr_foreground_new(65535, 65535, 65535);
+ attr->start_index = start_index;
+ attr->end_index = end_index;
+ pango_attr_list_insert(attr_list, attr);
+ } else {
+ attr_list = pango_attr_list_ref(entry->preedit.attr_list);
+ }
+
+ if (entry->preedit.text && !entry->preedit.attr_list) {
+ PangoAttribute *attr;
+
+ if (!attr_list)
+ attr_list = pango_attr_list_new();
+
+ attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+ attr->start_index = entry->cursor;
+ attr->end_index = entry->cursor + strlen(entry->preedit.text);
+ pango_attr_list_insert(attr_list, attr);
+ }
+
+ if (entry->layout) {
+ pango_layout_set_text(entry->layout, text, -1);
+ pango_layout_set_attributes(entry->layout, attr_list);
+ }
+
+ free(text);
+ pango_attr_list_unref(attr_list);
+}
+
+static void
+text_entry_update(struct text_entry *entry)
+{
+ struct rectangle cursor_rectangle;
+
+ zwp_text_input_v1_set_content_type(entry->text_input,
+ ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE,
+ entry->content_purpose);
+
+ zwp_text_input_v1_set_surrounding_text(entry->text_input,
+ entry->text,
+ entry->cursor,
+ entry->anchor);
+
+ if (entry->preferred_language)
+ zwp_text_input_v1_set_preferred_language(entry->text_input,
+ entry->preferred_language);
+
+ text_entry_get_cursor_rectangle(entry, &cursor_rectangle);
+ zwp_text_input_v1_set_cursor_rectangle(entry->text_input,
+ cursor_rectangle.x,
+ cursor_rectangle.y,
+ cursor_rectangle.width,
+ cursor_rectangle.height);
+
+ zwp_text_input_v1_commit_state(entry->text_input, ++entry->serial);
+}
+
+static void
+text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
+ int32_t cursor, int32_t anchor)
+{
+ char *new_text = xmalloc(strlen(entry->text) + strlen(text) + 1);
+
+ strncpy(new_text, entry->text, entry->cursor);
+ strcpy(new_text + entry->cursor, text);
+ strcpy(new_text + entry->cursor + strlen(text),
+ entry->text + entry->cursor);
+
+ free(entry->text);
+ entry->text = new_text;
+ if (anchor >= 0)
+ entry->anchor = entry->cursor + strlen(text) + anchor;
+ else
+ entry->anchor = entry->cursor + 1 + anchor;
+
+ if (cursor >= 0)
+ entry->cursor += strlen(text) + cursor;
+ else
+ entry->cursor += 1 + cursor;
+
+ text_entry_update_layout(entry);
+
+ widget_schedule_redraw(entry->widget);
+
+ text_entry_update(entry);
+}
+
+static void
+text_entry_reset_preedit(struct text_entry *entry)
+{
+ entry->preedit.cursor = 0;
+
+ free(entry->preedit.text);
+ entry->preedit.text = NULL;
+
+ free(entry->preedit.commit);
+ entry->preedit.commit = NULL;
+
+ pango_attr_list_unref(entry->preedit.attr_list);
+ entry->preedit.attr_list = NULL;
+}
+
+static void
+text_entry_commit_and_reset(struct text_entry *entry)
+{
+ char *commit = NULL;
+
+ if (entry->preedit.commit)
+ commit = strdup(entry->preedit.commit);
+
+ text_entry_reset_preedit(entry);
+ if (commit) {
+ text_entry_insert_at_cursor(entry, commit, 0, 0);
+ free(commit);
+ }
+
+ zwp_text_input_v1_reset(entry->text_input);
+ text_entry_update(entry);
+ entry->reset_serial = entry->serial;
+}
+
+static void
+text_entry_set_preedit(struct text_entry *entry,
+ const char *preedit_text,
+ int preedit_cursor)
+{
+ text_entry_reset_preedit(entry);
+
+ if (!preedit_text)
+ return;
+
+ entry->preedit.text = strdup(preedit_text);
+ entry->preedit.cursor = preedit_cursor;
+
+ text_entry_update_layout(entry);
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static uint32_t
+text_entry_try_invoke_preedit_action(struct text_entry *entry,
+ int32_t x, int32_t y,
+ uint32_t button,
+ enum wl_pointer_button_state state)
+{
+ int index, trailing;
+ uint32_t cursor;
+ const char *text;
+
+ if (!entry->preedit.text)
+ return 0;
+
+ pango_layout_xy_to_index(entry->layout,
+ x * PANGO_SCALE, y * PANGO_SCALE,
+ &index, &trailing);
+
+ text = pango_layout_get_text(entry->layout);
+ cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
+
+ if (cursor < entry->cursor ||
+ cursor > entry->cursor + strlen(entry->preedit.text)) {
+ return 0;
+ }
+
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED)
+ zwp_text_input_v1_invoke_action(entry->text_input,
+ button,
+ cursor - entry->cursor);
+
+ return 1;
+}
+
+static bool
+text_entry_has_preedit(struct text_entry *entry)
+{
+ return entry->preedit.text && (strlen(entry->preedit.text) > 0);
+}
+
+static void
+text_entry_set_cursor_position(struct text_entry *entry,
+ int32_t x, int32_t y,
+ bool move_anchor)
+{
+ int index, trailing;
+ const char *text;
+ uint32_t cursor;
+
+ pango_layout_xy_to_index(entry->layout,
+ x * PANGO_SCALE, y * PANGO_SCALE,
+ &index, &trailing);
+
+ text = pango_layout_get_text(entry->layout);
+
+ cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
+
+ if (move_anchor)
+ entry->anchor = cursor;
+
+ if (text_entry_has_preedit(entry)) {
+ text_entry_commit_and_reset(entry);
+
+ assert(!text_entry_has_preedit(entry));
+ }
+
+ if (entry->cursor == cursor)
+ return;
+
+ entry->cursor = cursor;
+
+ text_entry_update_layout(entry);
+
+ widget_schedule_redraw(entry->widget);
+
+ text_entry_update(entry);
+}
+
+static void
+text_entry_delete_text(struct text_entry *entry,
+ uint32_t index, uint32_t length)
+{
+ uint32_t l;
+
+ assert(index <= strlen(entry->text));
+ assert(index + length <= strlen(entry->text));
+ assert(index + length >= length);
+
+ l = strlen(entry->text + index + length);
+ memmove(entry->text + index,
+ entry->text + index + length,
+ l + 1);
+
+ if (entry->cursor > (index + length))
+ entry->cursor -= length;
+ else if (entry->cursor > index)
+ entry->cursor = index;
+
+ entry->anchor = entry->cursor;
+
+ text_entry_update_layout(entry);
+
+ widget_schedule_redraw(entry->widget);
+
+ text_entry_update(entry);
+}
+
+static void
+text_entry_delete_selected_text(struct text_entry *entry)
+{
+ uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
+ uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
+
+ if (entry->anchor == entry->cursor)
+ return;
+
+ text_entry_delete_text(entry, start_index, end_index - start_index);
+
+ entry->anchor = entry->cursor;
+}
+
+static void
+text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle)
+{
+ struct rectangle allocation;
+ PangoRectangle extents;
+ PangoRectangle cursor_pos;
+
+ widget_get_allocation(entry->widget, &allocation);
+
+ if (entry->preedit.text && entry->preedit.cursor < 0) {
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = 0;
+ rectangle->height = 0;
+ return;
+ }
+
+
+ pango_layout_get_extents(entry->layout, &extents, NULL);
+ pango_layout_get_cursor_pos(entry->layout,
+ entry->cursor + entry->preedit.cursor,
+ &cursor_pos, NULL);
+
+ rectangle->x = allocation.x + (allocation.height / 2) + PANGO_PIXELS(cursor_pos.x);
+ rectangle->y = allocation.y + 10 + PANGO_PIXELS(cursor_pos.y);
+ rectangle->width = PANGO_PIXELS(cursor_pos.width);
+ rectangle->height = PANGO_PIXELS(cursor_pos.height);
+}
+
+static void
+text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
+{
+ PangoRectangle extents;
+ PangoRectangle cursor_pos;
+
+ if (entry->preedit.text && entry->preedit.cursor < 0)
+ return;
+
+ pango_layout_get_extents(entry->layout, &extents, NULL);
+ pango_layout_get_cursor_pos(entry->layout,
+ entry->cursor + entry->preedit.cursor,
+ &cursor_pos, NULL);
+
+ cairo_set_line_width(cr, 1.0);
+ cairo_move_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y));
+ cairo_line_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y) + PANGO_PIXELS(cursor_pos.height));
+ cairo_stroke(cr);
+}
+
+static int
+text_offset_left(struct rectangle *allocation)
+{
+ return 10;
+}
+
+static int
+text_offset_top(struct rectangle *allocation)
+{
+ return allocation->height / 2;
+}
+
+static void
+text_entry_redraw_handler(struct widget *widget, void *data)
+{
+ struct text_entry *entry = data;
+ cairo_surface_t *surface;
+ struct rectangle allocation;
+ cairo_t *cr;
+
+ surface = window_get_surface(entry->window);
+ widget_get_allocation(entry->widget, &allocation);
+
+ cr = cairo_create(surface);
+ cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
+ cairo_clip(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ cairo_push_group(cr);
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ cairo_set_source_rgba(cr, 1, 1, 1, 1);
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+
+ if (entry->active) {
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
+ cairo_set_line_width (cr, 3);
+ cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
+ cairo_stroke(cr);
+ }
+
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+
+ cairo_translate(cr,
+ text_offset_left(&allocation),
+ text_offset_top(&allocation));
+
+ if (!entry->layout)
+ entry->layout = pango_cairo_create_layout(cr);
+ else
+ pango_cairo_update_layout(cr, entry->layout);
+
+ text_entry_update_layout(entry);
+
+ pango_cairo_show_layout(cr, entry->layout);
+
+ text_entry_draw_cursor(entry, cr);
+
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static int
+text_entry_motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct text_entry *entry = data;
+ struct rectangle allocation;
+ int tx, ty;
+
+ if (!entry->button_pressed) {
+ return CURSOR_IBEAM;
+ }
+
+ widget_get_allocation(entry->widget, &allocation);
+
+ tx = x - allocation.x - text_offset_left(&allocation);
+ ty = y - allocation.y - text_offset_top(&allocation);
+
+ text_entry_set_cursor_position(entry, tx, ty, false);
+
+ return CURSOR_IBEAM;
+}
+
+static void
+text_entry_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct text_entry *entry = data;
+ struct rectangle allocation;
+ struct editor *editor;
+ int32_t x, y;
+ uint32_t result;
+
+ widget_get_allocation(entry->widget, &allocation);
+ input_get_position(input, &x, &y);
+
+ x -= allocation.x + text_offset_left(&allocation);
+ y -= allocation.y + text_offset_top(&allocation);
+
+ editor = window_get_user_data(entry->window);
+
+ switch (button) {
+ case BTN_LEFT:
+ entry->button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ input_grab(input, entry->widget, button);
+ else
+ input_ungrab(input);
+ break;
+ case BTN_RIGHT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ show_menu(editor, input, time);
+ break;
+ }
+
+ if (text_entry_has_preedit(entry)) {
+ result = text_entry_try_invoke_preedit_action(entry, x, y, button, state);
+
+ if (result)
+ return;
+ }
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
+ button == BTN_LEFT) {
+ struct wl_seat *seat = input_get_seat(input);
+
+ text_entry_activate(entry, seat);
+ editor->active_entry = entry;
+
+ text_entry_set_cursor_position(entry, x, y, true);
+ }
+}
+
+static void
+text_entry_touch_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float tx, float ty, void *data)
+{
+ struct text_entry *entry = data;
+ struct wl_seat *seat = input_get_seat(input);
+ struct rectangle allocation;
+ struct editor *editor;
+ int32_t x, y;
+
+ widget_get_allocation(entry->widget, &allocation);
+
+ x = tx - (allocation.x + text_offset_left(&allocation));
+ y = ty - (allocation.y + text_offset_top(&allocation));
+
+ editor = window_get_user_data(entry->window);
+ text_entry_activate(entry, seat);
+ editor->active_entry = entry;
+
+ text_entry_set_cursor_position(entry, x, y, true);
+}
+
+static void
+editor_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct editor *editor = data;
+
+ if (button != BTN_LEFT) {
+ return;
+ }
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ struct wl_seat *seat = input_get_seat(input);
+
+ text_entry_deactivate(editor->entry, seat);
+ text_entry_deactivate(editor->editor, seat);
+ editor->active_entry = NULL;
+ }
+}
+
+static void
+editor_touch_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float tx, float ty, void *data)
+{
+ struct editor *editor = data;
+
+ struct wl_seat *seat = input_get_seat(input);
+
+ text_entry_deactivate(editor->entry, seat);
+ text_entry_deactivate(editor->editor, seat);
+ editor->active_entry = NULL;
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct editor *editor = data;
+
+ window_schedule_redraw(editor->window);
+}
+
+static int
+handle_bound_key(struct editor *editor,
+ struct input *input, uint32_t sym, uint32_t time)
+{
+ switch (sym) {
+ case XKB_KEY_X:
+ editor_copy_cut(editor, input, true);
+ return 1;
+ case XKB_KEY_C:
+ editor_copy_cut(editor, input, false);
+ return 1;
+ case XKB_KEY_V:
+ editor_paste(editor, input);
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void
+key_handler(struct window *window,
+ struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
+ void *data)
+{
+ struct editor *editor = data;
+ struct text_entry *entry;
+ const char *new_char;
+ char text[16];
+ uint32_t modifiers;
+
+ if (!editor->active_entry)
+ return;
+
+ entry = editor->active_entry;
+
+ if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
+ return;
+
+ modifiers = input_get_modifiers(input);
+ if ((modifiers & MOD_CONTROL_MASK) &&
+ (modifiers & MOD_SHIFT_MASK) &&
+ handle_bound_key(editor, input, sym, time))
+ return;
+
+ switch (sym) {
+ case XKB_KEY_BackSpace:
+ text_entry_commit_and_reset(entry);
+
+ new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
+ if (new_char != NULL)
+ text_entry_delete_text(entry,
+ new_char - entry->text,
+ (entry->text + entry->cursor) - new_char);
+ break;
+ case XKB_KEY_Delete:
+ text_entry_commit_and_reset(entry);
+
+ new_char = utf8_next_char(entry->text + entry->cursor);
+ if (new_char != NULL)
+ text_entry_delete_text(entry,
+ entry->cursor,
+ new_char - (entry->text + entry->cursor));
+ break;
+ case XKB_KEY_Left:
+ text_entry_commit_and_reset(entry);
+
+ new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
+ if (new_char != NULL) {
+ entry->cursor = new_char - entry->text;
+ if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
+ entry->anchor = entry->cursor;
+ widget_schedule_redraw(entry->widget);
+ }
+ break;
+ case XKB_KEY_Right:
+ text_entry_commit_and_reset(entry);
+
+ new_char = utf8_next_char(entry->text + entry->cursor);
+ if (new_char != NULL) {
+ entry->cursor = new_char - entry->text;
+ if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
+ entry->anchor = entry->cursor;
+ widget_schedule_redraw(entry->widget);
+ }
+ break;
+ case XKB_KEY_Up:
+ text_entry_commit_and_reset(entry);
+
+ move_up(entry->text, &entry->cursor);
+ if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
+ entry->anchor = entry->cursor;
+ widget_schedule_redraw(entry->widget);
+ break;
+ case XKB_KEY_Down:
+ text_entry_commit_and_reset(entry);
+
+ move_down(entry->text, &entry->cursor);
+ if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
+ entry->anchor = entry->cursor;
+ widget_schedule_redraw(entry->widget);
+ break;
+ case XKB_KEY_Escape:
+ break;
+ default:
+ if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0)
+ break;
+
+ text_entry_commit_and_reset(entry);
+
+ text_entry_insert_at_cursor(entry, text, 0, 0);
+ break;
+ }
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+global_handler(struct display *display, uint32_t name,
+ const char *interface, uint32_t version, void *data)
+{
+ struct editor *editor = data;
+
+ if (!strcmp(interface, "zwp_text_input_manager_v1")) {
+ editor->text_input_manager =
+ display_bind(display, name,
+ &zwp_text_input_manager_v1_interface, 1);
+ }
+}
+
+/** Display help for command line options, and exit */
+static bool opt_help = false;
+
+/** Require a distinct click to show the input panel (virtual keyboard) */
+static bool opt_click_to_show = false;
+
+/** Set a specific (RFC-3066) language. Used for the virtual keyboard, etc. */
+static const char *opt_preferred_language = NULL;
+
+/**
+ * \brief command line options for editor
+ */
+static const struct weston_option editor_options[] = {
+ { WESTON_OPTION_BOOLEAN, "help", 'h', &opt_help },
+ { WESTON_OPTION_BOOLEAN, "click-to-show", 'C', &opt_click_to_show },
+ { WESTON_OPTION_STRING, "preferred-language", 'L', &opt_preferred_language },
+};
+
+static void
+usage(const char *program_name, int exit_code)
+{
+ unsigned k;
+
+ fprintf(stderr, "Usage: %s [OPTIONS] [FILENAME]\n\n", program_name);
+ for (k = 0; k < ARRAY_LENGTH(editor_options); k++) {
+ const struct weston_option *p = &editor_options[k];
+ if (p->name) {
+ fprintf(stderr, " --%s", p->name);
+ if (p->type != WESTON_OPTION_BOOLEAN)
+ fprintf(stderr, "=VALUE");
+ fprintf(stderr, "\n");
+ }
+ if (p->short_name) {
+ fprintf(stderr, " -%c", p->short_name);
+ if (p->type != WESTON_OPTION_BOOLEAN)
+ fprintf(stderr, "VALUE");
+ fprintf(stderr, "\n");
+ }
+ }
+ exit(exit_code);
+}
+
+/* Load the contents of a file into a UTF-8 text buffer and return it.
+ *
+ * Caller is responsible for freeing the buffer when done.
+ * On error, returns NULL.
+ */
+static char *
+read_file(char *filename)
+{
+ char *buffer = NULL;
+ int buf_size, read_size;
+ FILE *fin;
+ int errsv;
+
+ fin = fopen(filename, "r");
+ if (fin == NULL)
+ goto error;
+
+ /* Determine required buffer size */
+ if (fseek(fin, 0, SEEK_END) != 0)
+ goto error;
+ buf_size = ftell(fin);
+ if (buf_size < 0)
+ goto error;
+ rewind(fin);
+
+ /* Create buffer and read in the text */
+ buffer = (char*) malloc(sizeof(char) * (buf_size + 1));
+ if (buffer == NULL)
+ goto error;
+ read_size = fread(buffer, sizeof(char), buf_size, fin);
+ fclose(fin);
+ if (buf_size != read_size)
+ goto error;
+ buffer[buf_size] = '\0';
+
+ return buffer;
+
+error:
+ errsv = errno;
+ if (fin)
+ fclose(fin);
+ free(buffer);
+ errno = errsv ? errsv : EINVAL;
+
+ return NULL;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct editor editor;
+ char *text_buffer = NULL;
+
+ parse_options(editor_options, ARRAY_LENGTH(editor_options),
+ &argc, argv);
+ if (opt_help)
+ usage(argv[0], EXIT_SUCCESS);
+
+ if (argc > 1) {
+ if (argv[1][0] == '-')
+ usage(argv[0], EXIT_FAILURE);
+
+ text_buffer = read_file(argv[1]);
+ if (text_buffer == NULL) {
+ fprintf(stderr, "could not read file '%s': %s\n",
+ argv[1], strerror(errno));
+ return -1;
+ }
+ }
+
+ memset(&editor, 0, sizeof editor);
+
+ editor.display = display_create(&argc, argv);
+ if (editor.display == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ free(text_buffer);
+ return -1;
+ }
+
+ display_set_user_data(editor.display, &editor);
+ display_set_global_handler(editor.display, global_handler);
+
+ if (editor.text_input_manager == NULL) {
+ fprintf(stderr, "No text input manager global\n");
+ display_destroy(editor.display);
+ free(text_buffer);
+ return -1;
+ }
+
+ editor.window = window_create(editor.display);
+ editor.widget = window_frame_create(editor.window, &editor);
+
+ if (text_buffer)
+ editor.entry = text_entry_create(&editor, text_buffer);
+ else
+ editor.entry = text_entry_create(&editor, "Entry");
+ editor.entry->click_to_show = opt_click_to_show;
+ if (opt_preferred_language)
+ editor.entry->preferred_language = strdup(opt_preferred_language);
+ editor.editor = text_entry_create(&editor, "Numeric");
+ editor.editor->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER;
+ editor.editor->click_to_show = opt_click_to_show;
+ editor.selection = NULL;
+ editor.selected_text = NULL;
+
+ window_set_title(editor.window, "Text Editor");
+ window_set_key_handler(editor.window, key_handler);
+ window_set_keyboard_focus_handler(editor.window,
+ keyboard_focus_handler);
+ window_set_user_data(editor.window, &editor);
+
+ widget_set_redraw_handler(editor.widget, redraw_handler);
+ widget_set_resize_handler(editor.widget, resize_handler);
+ widget_set_button_handler(editor.widget, editor_button_handler);
+ widget_set_touch_down_handler(editor.widget, editor_touch_handler);
+
+ window_schedule_resize(editor.window, 500, 400);
+
+ display_run(editor.display);
+
+ if (editor.selected_text)
+ free(editor.selected_text);
+ if (editor.selection)
+ wl_data_source_destroy(editor.selection);
+ text_entry_destroy(editor.entry);
+ text_entry_destroy(editor.editor);
+ widget_destroy(editor.widget);
+ window_destroy(editor.window);
+ display_destroy(editor.display);
+ free(text_buffer);
+
+ return 0;
+}
diff --git a/clients/eventdemo.c b/clients/eventdemo.c
new file mode 100644
index 0000000000000000000000000000000000000000..0a1a7ca91f7587087d56557092686c712243a527
--- /dev/null
+++ b/clients/eventdemo.c
@@ -0,0 +1,541 @@
+/*
+ * Copyright © 2011 Tim Wiederhake
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file eventdemo.c
+ * \brief Demonstrate the use of Wayland's toytoolkit.
+ *
+ * Heavily commented demo program that can report all events that are
+ * dispatched to the window. For other functionality, eg. opengl/egl,
+ * drag and drop, etc. have a look at the other demos.
+ * \author Tim Wiederhake
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "shared/helpers.h"
+#include "window.h"
+
+/** window title */
+static char *title = "EventDemo";
+
+/** window width */
+static int width = 500;
+
+/** window height */
+static int height = 400;
+
+/** set if window has no borders */
+static bool noborder = false;
+
+/** if non-zero, maximum window width */
+static int width_max = 0;
+
+/** if non-zero, maximum window height */
+static int height_max = 0;
+
+/** set to log redrawing */
+static bool log_redraw = false;
+
+/** set to log resizing */
+static bool log_resize = false;
+
+/** set to log keyboard focus */
+static bool log_focus = false;
+
+/** set to log key events */
+static bool log_key = false;
+
+/** set to log button events */
+static bool log_button = false;
+
+/** set to log axis events */
+static bool log_axis = false;
+
+/** set to log motion events */
+static bool log_motion = false;
+
+/**
+ * \struct eventdemo
+ * \brief Holds all data the program needs per window
+ *
+ * In this demo the struct holds the position of a
+ * red rectangle that is drawn in the window's area.
+ */
+struct eventdemo {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+
+ int x, y, w, h;
+
+ bool print_pointer_frame;
+};
+
+/**
+ * \brief CALLBACK function, Wayland requests the window to redraw.
+ * \param widget widget to be redrawn
+ * \param data user data associated to the window
+ *
+ * Draws a red rectangle as demonstration of per-window data.
+ */
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct eventdemo *e = data;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ struct rectangle rect;
+
+ if (log_redraw)
+ printf("redraw\n");
+
+ widget_get_allocation(e->widget, &rect);
+ surface = window_get_surface(e->window);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_fill(cr);
+
+ cairo_rectangle(cr, e->x, e->y, e->w, e->h);
+ cairo_set_source_rgba(cr, 1.0, 0, 0, 1);
+ cairo_fill(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+/**
+ * \brief CALLBACK function, Wayland requests the window to resize.
+ * \param widget widget to be resized
+ * \param width desired width
+ * \param height desired height
+ * \param data user data associated to the window
+ */
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct eventdemo *e = data;
+ if (log_resize)
+ printf("resize width: %d, height: %d\n", width, height);
+
+ /* if a maximum width is set, constrain to it */
+ if (width_max && width_max < width)
+ width = width_max;
+
+ /* if a maximum height is set, constrain to it */
+ if (height_max && height_max < height)
+ height = height_max;
+
+ /* set the new window dimensions */
+ widget_set_size(e->widget, width, height);
+}
+
+/**
+ * \brief CALLBACK function, Wayland informs about keyboard focus change
+ * \param window window
+ * \param device device that caused the focus change
+ * \param data user data associated to the window
+ */
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ int32_t x, y;
+ struct eventdemo *e = data;
+
+ if (log_focus) {
+ if (device) {
+ input_get_position(device, &x, &y);
+ printf("focus x: %d, y: %d\n", x, y);
+ } else {
+ printf("focus lost\n");
+ }
+ }
+
+ window_schedule_redraw(e->window);
+}
+
+/**
+ * \brief CALLBACK function, Wayland informs about key event
+ * \param window window
+ * \param input input
+ * \param time time
+ * \param key keycode
+ * \param unicode associated character
+ * \param state pressed or released
+ * \param data user data associated to the window
+ */
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t unicode, enum wl_keyboard_key_state state,
+ void *data)
+{
+ uint32_t modifiers = input_get_modifiers(input);
+
+ if (!log_key)
+ return;
+
+ printf("key key: %u, unicode: %u, state: %s, modifiers: 0x%x\n",
+ key, unicode,
+ (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? "pressed" :
+ "released",
+ modifiers);
+}
+
+/**
+ * \brief CALLBACK function, Wayland informs about button event
+ * \param widget widget
+ * \param input input device that caused the button event
+ * \param time time the event happened
+ * \param button button
+ * \param state pressed or released
+ * \param data user data associated to the window
+ */
+static void
+button_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state, void *data)
+{
+ struct eventdemo *e = data;
+ int32_t x, y;
+
+ if (!log_button)
+ return;
+
+ e->print_pointer_frame = true;
+
+ input_get_position(input, &x, &y);
+ printf("button time: %u, button: %u, state: %s, x: %d, y: %d\n",
+ time, button,
+ (state == WL_POINTER_BUTTON_STATE_PRESSED) ? "pressed" :
+ "released",
+ x, y);
+}
+
+/**
+ * \brief CALLBACK function, Wayland informs about axis event
+ * \param widget widget
+ * \param input input device that caused the axis event
+ * \param time time the event happened
+ * \param axis vertical or horizontal
+ * \param value amount of scrolling
+ * \param data user data associated to the widget
+ */
+static void
+axis_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t axis, wl_fixed_t value, void *data)
+{
+ struct eventdemo *e = data;
+
+ if (!log_axis)
+ return;
+
+ e->print_pointer_frame = true;
+
+ printf("axis time: %u, axis: %s, value: %f\n",
+ time,
+ axis == WL_POINTER_AXIS_VERTICAL_SCROLL ? "vertical" :
+ "horizontal",
+ wl_fixed_to_double(value));
+}
+
+static void
+pointer_frame_handler(struct widget *widget, struct input *input, void *data)
+{
+ struct eventdemo *e = data;
+
+ if (!e->print_pointer_frame)
+ return;
+
+ printf("pointer frame\n");
+ e->print_pointer_frame = false;
+}
+
+static void
+axis_source_handler(struct widget *widget, struct input *input,
+ uint32_t source, void *data)
+{
+ const char *axis_source;
+ struct eventdemo *e = data;
+
+ if (!log_axis)
+ return;
+
+ e->print_pointer_frame = true;
+
+ switch (source) {
+ case WL_POINTER_AXIS_SOURCE_WHEEL:
+ axis_source = "wheel";
+ break;
+ case WL_POINTER_AXIS_SOURCE_FINGER:
+ axis_source = "finger";
+ break;
+ case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
+ axis_source = "continuous";
+ break;
+ default:
+ axis_source = "";
+ break;
+ }
+
+ printf("axis source: %s\n", axis_source);
+}
+
+static void
+axis_stop_handler(struct widget *widget, struct input *input,
+ uint32_t time, uint32_t axis,
+ void *data)
+{
+ struct eventdemo *e = data;
+
+ if (!log_axis)
+ return;
+
+ e->print_pointer_frame = true;
+ printf("axis stop time: %u, axis: %s\n",
+ time,
+ axis == WL_POINTER_AXIS_VERTICAL_SCROLL ? "vertical" :
+ "horizontal");
+}
+
+static void
+axis_discrete_handler(struct widget *widget, struct input *input,
+ uint32_t axis, int32_t discrete, void *data)
+{
+ struct eventdemo *e = data;
+
+ if (!log_axis)
+ return;
+
+ e->print_pointer_frame = true;
+ printf("axis discrete axis: %u value: %d\n", axis, discrete);
+}
+
+/**
+ * \brief CALLBACK function, Waylands informs about pointer motion
+ * \param widget widget
+ * \param input input device that caused the motion event
+ * \param time time the event happened
+ * \param x absolute x position
+ * \param y absolute y position
+ * \param x x position relative to the window
+ * \param y y position relative to the window
+ * \param data user data associated to the window
+ *
+ * Demonstrates the use of different cursors
+ */
+static int
+motion_handler(struct widget *widget, struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct eventdemo *e = data;
+
+ if (log_motion) {
+ printf("motion time: %u, x: %f, y: %f\n", time, x, y);
+ e->print_pointer_frame = true;
+ }
+
+ if (x > e->x && x < e->x + e->w)
+ if (y > e->y && y < e->y + e->h)
+ return CURSOR_HAND1;
+
+ return CURSOR_LEFT_PTR;
+}
+
+/**
+ * \brief Create and initialise a new eventdemo window.
+ * The returned eventdemo instance should be destroyed using \c eventdemo_destroy().
+ * \param d associated display
+ */
+static struct eventdemo *
+eventdemo_create(struct display *d)
+{
+ struct eventdemo *e;
+
+ e = zalloc(sizeof (struct eventdemo));
+ if (e == NULL)
+ return NULL;
+
+ e->window = window_create(d);
+
+ if (noborder) {
+ /* Demonstrate how to create a borderless window.
+ * Move windows with META + left mouse button.
+ */
+ e->widget = window_add_widget(e->window, e);
+ } else {
+ e->widget = window_frame_create(e->window, e);
+ window_set_title(e->window, title);
+ }
+ e->display = d;
+
+ /* The eventdemo window draws a red rectangle as a demonstration
+ * of per-window data. The dimensions of that rectangle are set
+ * here.
+ */
+ e->x = width * 1.0 / 4.0;
+ e->w = width * 2.0 / 4.0;
+ e->y = height * 1.0 / 4.0;
+ e->h = height * 2.0 / 4.0;
+
+ /* Connect the user data to the window */
+ window_set_user_data(e->window, e);
+
+ /* Set the callback redraw handler for the window */
+ widget_set_redraw_handler(e->widget, redraw_handler);
+
+ /* Set the callback resize handler for the window */
+ widget_set_resize_handler(e->widget, resize_handler);
+
+ /* Set the callback focus handler for the window */
+ window_set_keyboard_focus_handler(e->window,
+ keyboard_focus_handler);
+
+ /* Set the callback key handler for the window */
+ window_set_key_handler(e->window, key_handler);
+
+ /* Set the callback button handler for the window */
+ widget_set_button_handler(e->widget, button_handler);
+
+ /* Set the callback motion handler for the window */
+ widget_set_motion_handler(e->widget, motion_handler);
+
+ /* Set the callback pointer frame handler for the window */
+ widget_set_pointer_frame_handler(e->widget, pointer_frame_handler);
+
+ /* Set the callback axis handler for the window */
+ widget_set_axis_handlers(e->widget,
+ axis_handler,
+ axis_source_handler,
+ axis_stop_handler,
+ axis_discrete_handler);
+
+ /* Initial drawing of the window */
+ window_schedule_resize(e->window, width, height);
+
+ return e;
+}
+/**
+ * \brief Destroy eventdemo instance previously created by \c eventdemo_create().
+ * \param eventdemo eventdemo instance to destroy
+ */
+static void eventdemo_destroy(struct eventdemo * eventdemo)
+{
+ widget_destroy(eventdemo->widget);
+ window_destroy(eventdemo->window);
+ free(eventdemo);
+}
+/**
+ * \brief command line options for eventdemo
+ */
+static const struct weston_option eventdemo_options[] = {
+ { WESTON_OPTION_STRING, "title", 0, &title },
+ { WESTON_OPTION_INTEGER, "width", 'w', &width },
+ { WESTON_OPTION_INTEGER, "height", 'h', &height },
+ { WESTON_OPTION_INTEGER, "max-width", 0, &width_max },
+ { WESTON_OPTION_INTEGER, "max-height", 0, &height_max },
+ { WESTON_OPTION_BOOLEAN, "no-border", 'b', &noborder },
+ { WESTON_OPTION_BOOLEAN, "log-redraw", 0, &log_redraw },
+ { WESTON_OPTION_BOOLEAN, "log-resize", 0, &log_resize },
+ { WESTON_OPTION_BOOLEAN, "log-focus", 0, &log_focus },
+ { WESTON_OPTION_BOOLEAN, "log-key", 0, &log_key },
+ { WESTON_OPTION_BOOLEAN, "log-button", 0, &log_button },
+ { WESTON_OPTION_BOOLEAN, "log-axis", 0, &log_axis },
+ { WESTON_OPTION_BOOLEAN, "log-motion", 0, &log_motion },
+};
+
+/**
+ * \brief Connects to the display, creates the window and hands over
+ * to the main loop.
+ */
+int
+main(int argc, char *argv[])
+{
+ struct display *d;
+ struct eventdemo *e;
+
+ if (parse_options(eventdemo_options,
+ ARRAY_LENGTH(eventdemo_options), &argc, argv) > 1) {
+ unsigned k;
+ printf("Usage: %s [OPTIONS]\n\n", argv[0]);
+ for (k = 0; k < ARRAY_LENGTH(eventdemo_options); k++) {
+ const struct weston_option* p = &eventdemo_options[k];
+ if (p->name) {
+ printf(" --%s", p->name);
+ if (p->type != WESTON_OPTION_BOOLEAN)
+ printf("=VALUE");
+ putchar('\n');
+ }
+ if (p->short_name) {
+ printf(" -%c", p->short_name);
+ if (p->type != WESTON_OPTION_BOOLEAN)
+ printf("VALUE");
+ putchar('\n');
+ }
+ }
+ return 1;
+ }
+
+ if (!log_redraw && !log_resize && !log_focus && !log_key &&
+ !log_button && !log_axis && !log_motion)
+ log_redraw = log_resize = log_focus = log_key =
+ log_button = log_axis = log_motion = true;
+
+ /* Connect to the display and have the arguments parsed */
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ /* Create new eventdemo window */
+ e = eventdemo_create(d);
+ if (e == NULL) {
+ fprintf(stderr, "failed to create eventdemo: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ display_run(d);
+
+ /* Release resources */
+ eventdemo_destroy(e);
+ display_destroy(d);
+
+ return 0;
+}
diff --git a/clients/flower.c b/clients/flower.c
new file mode 100644
index 0000000000000000000000000000000000000000..e3471ce7e0ec8c43b444e20c10ac4e645a7cd55f
--- /dev/null
+++ b/clients/flower.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include "window.h"
+
+struct flower {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ int width, height;
+};
+
+static void
+set_random_color(cairo_t *cr)
+{
+ cairo_set_source_rgba(cr,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 50) / 49.0,
+ 0.5 + (random() % 100) / 99.0);
+}
+
+
+static void
+draw_stuff(cairo_surface_t *surface, int width, int height)
+{
+ const int petal_count = 3 + random() % 5;
+ const double r1 = 60 + random() % 35;
+ const double r2 = 20 + random() % 40;
+ const double u = (10 + random() % 90) / 100.0;
+ const double v = (random() % 90) / 100.0;
+
+ cairo_t *cr;
+ int i;
+ double t, dt = 2 * M_PI / (petal_count * 2);
+ double x1, y1, x2, y2, x3, y3;
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0);
+ cairo_paint(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_translate(cr, width / 2, height / 2);
+ cairo_move_to(cr, cos(0) * r1, sin(0) * r1);
+ for (t = 0, i = 0; i < petal_count; i++, t += dt * 2) {
+ x1 = cos(t) * r1;
+ y1 = sin(t) * r1;
+ x2 = cos(t + dt) * r2;
+ y2 = sin(t + dt) * r2;
+ x3 = cos(t + 2 * dt) * r1;
+ y3 = sin(t + 2 * dt) * r1;
+
+ cairo_curve_to(cr,
+ x1 - y1 * u, y1 + x1 * u,
+ x2 + y2 * v, y2 - x2 * v,
+ x2, y2);
+
+ cairo_curve_to(cr,
+ x2 - y2 * v, y2 + x2 * v,
+ x3 + y3 * u, y3 - x3 * u,
+ x3, y3);
+ }
+
+ cairo_close_path(cr);
+ set_random_color(cr);
+ cairo_fill_preserve(cr);
+ set_random_color(cr);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct flower *flower = data;
+
+ /* Don't resize me */
+ widget_set_size(flower->widget, flower->width, flower->height);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct flower *flower = data;
+ cairo_surface_t *surface;
+
+ surface = window_get_surface(flower->window);
+ if (surface == NULL ||
+ cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "failed to create cairo egl surface\n");
+ return;
+ }
+
+ draw_stuff(surface, flower->width, flower->height);
+ cairo_surface_destroy(surface);
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state, void *data)
+{
+ struct flower *flower = data;
+
+ switch (button) {
+ case BTN_LEFT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ window_move(flower->window, input,
+ display_get_serial(flower->display));
+ break;
+ case BTN_MIDDLE:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ widget_schedule_redraw(widget);
+ break;
+ case BTN_RIGHT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ window_show_frame_menu(flower->window, input, time);
+ break;
+ }
+}
+
+static void
+touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct flower *flower = data;
+ window_move(flower->window, input, display_get_serial(flower->display));
+}
+
+int main(int argc, char *argv[])
+{
+ struct flower flower;
+ struct display *d;
+ struct timeval tv;
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ gettimeofday(&tv, NULL);
+ srandom(tv.tv_usec);
+
+ flower.width = 200;
+ flower.height = 200;
+ flower.display = d;
+ flower.window = window_create(d);
+ flower.widget = window_add_widget(flower.window, &flower);
+ window_set_title(flower.window, "Flower");
+
+ widget_set_resize_handler(flower.widget, resize_handler);
+ widget_set_redraw_handler(flower.widget, redraw_handler);
+ widget_set_button_handler(flower.widget, button_handler);
+ widget_set_default_cursor(flower.widget, CURSOR_HAND1);
+ widget_set_touch_down_handler(flower.widget, touch_down_handler);
+
+ window_schedule_resize(flower.window, flower.width, flower.height);
+
+ display_run(d);
+
+ widget_destroy(flower.widget);
+ window_destroy(flower.window);
+ display_destroy(d);
+
+ return 0;
+}
diff --git a/clients/fullscreen.c b/clients/fullscreen.c
new file mode 100644
index 0000000000000000000000000000000000000000..1b44ad535e7f318ac3a5f46e0246c19c8f10ee2c
--- /dev/null
+++ b/clients/fullscreen.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include "window.h"
+#include "fullscreen-shell-unstable-v1-client-protocol.h"
+#include
+
+struct fs_output {
+ struct wl_list link;
+ struct output *output;
+};
+
+struct fullscreen {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ struct zwp_fullscreen_shell_v1 *fshell;
+ enum zwp_fullscreen_shell_v1_present_method present_method;
+ int width, height;
+ int fullscreen;
+ float pointer_x, pointer_y;
+ int draw_cursor;
+
+ struct wl_list output_list;
+ struct fs_output *current_output;
+};
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct fullscreen *fullscreen = data;
+
+ fullscreen->fullscreen ^= 1;
+ window_set_fullscreen(window, fullscreen->fullscreen);
+}
+
+static void
+draw_string(cairo_t *cr,
+ const char *fmt, ...)
+{
+ char buffer[4096];
+ char *p, *end;
+ va_list argp;
+ cairo_text_extents_t text_extents;
+ cairo_font_extents_t font_extents;
+
+ cairo_save(cr);
+
+ cairo_select_font_face(cr, "sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 14);
+
+ cairo_font_extents (cr, &font_extents);
+
+ va_start(argp, fmt);
+
+ vsnprintf(buffer, sizeof(buffer), fmt, argp);
+
+ p = buffer;
+ while (*p) {
+ end = strchr(p, '\n');
+ if (end)
+ *end = 0;
+
+ cairo_show_text(cr, p);
+ cairo_text_extents (cr, p, &text_extents);
+ cairo_rel_move_to (cr, -text_extents.x_advance, font_extents.height);
+
+ if (end)
+ p = end + 1;
+ else
+ break;
+ }
+
+ va_end(argp);
+
+ cairo_restore(cr);
+
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct fullscreen *fullscreen = data;
+ struct rectangle allocation;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ int i;
+ double x, y, border;
+ const char *method_name[] = { "default", "center", "zoom", "zoom_crop", "stretch"};
+
+ surface = window_get_surface(fullscreen->window);
+ if (surface == NULL ||
+ cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "failed to create cairo egl surface\n");
+ return;
+ }
+
+ widget_get_allocation(fullscreen->widget, &allocation);
+
+ cr = widget_cairo_create(widget);
+
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ cairo_paint (cr);
+
+ cairo_set_source_rgb(cr, 0, 0, 1);
+ cairo_set_line_width (cr, 10);
+ cairo_rectangle(cr, 5, 5, allocation.width - 10, allocation.height - 10);
+ cairo_stroke (cr);
+
+ cairo_move_to(cr,
+ allocation.x + 15,
+ allocation.y + 25);
+ cairo_set_source_rgb(cr, 1, 1, 1);
+
+ if (fullscreen->fshell) {
+ draw_string(cr,
+ "Surface size: %d, %d\n"
+ "Scale: %d, transform: %d\n"
+ "Pointer: %f,%f\n"
+ "Output: %s, present method: %s\n"
+ "Keys: (s)cale, (t)ransform, si(z)e, (m)ethod,\n"
+ " (o)utput, modes(w)itch, (q)uit\n",
+ fullscreen->width, fullscreen->height,
+ window_get_buffer_scale (fullscreen->window),
+ window_get_buffer_transform (fullscreen->window),
+ fullscreen->pointer_x, fullscreen->pointer_y,
+ method_name[fullscreen->present_method],
+ fullscreen->current_output ? output_get_model(fullscreen->current_output->output): "null");
+ } else {
+ draw_string(cr,
+ "Surface size: %d, %d\n"
+ "Scale: %d, transform: %d\n"
+ "Pointer: %f,%f\n"
+ "Fullscreen: %d\n"
+ "Keys: (s)cale, (t)ransform, si(z)e, (f)ullscreen, (q)uit\n",
+ fullscreen->width, fullscreen->height,
+ window_get_buffer_scale (fullscreen->window),
+ window_get_buffer_transform (fullscreen->window),
+ fullscreen->pointer_x, fullscreen->pointer_y,
+ fullscreen->fullscreen);
+ }
+
+ y = 100;
+ i = 0;
+ while (y + 60 < fullscreen->height) {
+ border = (i++ % 2 == 0) ? 1 : 0.5;
+
+ x = 50;
+ cairo_set_line_width (cr, border);
+ while (x + 70 < fullscreen->width) {
+ if (window_has_focus(fullscreen->window) &&
+ fullscreen->pointer_x >= x && fullscreen->pointer_x < x + 50 &&
+ fullscreen->pointer_y >= y && fullscreen->pointer_y < y + 40) {
+ cairo_set_source_rgb(cr, 1, 0, 0);
+ cairo_rectangle(cr,
+ x, y,
+ 50, 40);
+ cairo_fill(cr);
+ }
+ cairo_set_source_rgb(cr, 0, 1, 0);
+ cairo_rectangle(cr,
+ x + border/2.0, y + border/2.0,
+ 50, 40);
+ cairo_stroke(cr);
+ x += 60;
+ }
+
+ y += 50;
+ }
+
+ if (window_has_focus(fullscreen->window) && fullscreen->draw_cursor) {
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_set_line_width (cr, 8);
+ cairo_move_to(cr,
+ fullscreen->pointer_x - 12,
+ fullscreen->pointer_y - 12);
+ cairo_line_to(cr,
+ fullscreen->pointer_x + 12,
+ fullscreen->pointer_y + 12);
+ cairo_stroke(cr);
+
+ cairo_move_to(cr,
+ fullscreen->pointer_x + 12,
+ fullscreen->pointer_y - 12);
+ cairo_line_to(cr,
+ fullscreen->pointer_x - 12,
+ fullscreen->pointer_y + 12);
+ cairo_stroke(cr);
+
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ cairo_set_line_width (cr, 4);
+ cairo_move_to(cr,
+ fullscreen->pointer_x - 10,
+ fullscreen->pointer_y - 10);
+ cairo_line_to(cr,
+ fullscreen->pointer_x + 10,
+ fullscreen->pointer_y + 10);
+ cairo_stroke(cr);
+
+ cairo_move_to(cr,
+ fullscreen->pointer_x + 10,
+ fullscreen->pointer_y - 10);
+ cairo_line_to(cr,
+ fullscreen->pointer_x - 10,
+ fullscreen->pointer_y + 10);
+ cairo_stroke(cr);
+ }
+
+ cairo_destroy(cr);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
+ void *data)
+{
+ struct fullscreen *fullscreen = data;
+ int transform, scale;
+ static int current_size = 0;
+ struct fs_output *fsout;
+ struct wl_output *wl_output;
+ int widths[] = { 640, 320, 800, 400 };
+ int heights[] = { 480, 240, 600, 300 };
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_t:
+ transform = window_get_buffer_transform (window);
+ transform = (transform + 1) % 8;
+ window_set_buffer_transform(window, transform);
+ window_schedule_redraw(window);
+ break;
+
+ case XKB_KEY_s:
+ scale = window_get_buffer_scale (window);
+ if (scale == 1)
+ scale = 2;
+ else
+ scale = 1;
+ window_set_buffer_scale(window, scale);
+ window_schedule_redraw(window);
+ break;
+
+ case XKB_KEY_z:
+ if (fullscreen->fullscreen)
+ break;
+
+ current_size = (current_size + 1) % 4;
+ fullscreen->width = widths[current_size];
+ fullscreen->height = heights[current_size];
+ window_schedule_resize(fullscreen->window,
+ fullscreen->width, fullscreen->height);
+ break;
+
+ case XKB_KEY_m:
+ if (!fullscreen->fshell)
+ break;
+
+ wl_output = NULL;
+ if (fullscreen->current_output)
+ wl_output = output_get_wl_output(fullscreen->current_output->output);
+ fullscreen->present_method = (fullscreen->present_method + 1) % 5;
+ zwp_fullscreen_shell_v1_present_surface(fullscreen->fshell,
+ window_get_wl_surface(fullscreen->window),
+ fullscreen->present_method,
+ wl_output);
+ window_schedule_redraw(window);
+ break;
+
+ case XKB_KEY_o:
+ if (!fullscreen->fshell)
+ break;
+
+ fsout = fullscreen->current_output;
+ wl_output = fsout ? output_get_wl_output(fsout->output) : NULL;
+
+ /* Clear the current presentation */
+ zwp_fullscreen_shell_v1_present_surface(fullscreen->fshell, NULL,
+ 0, wl_output);
+
+ if (fullscreen->current_output) {
+ if (fullscreen->current_output->link.next == &fullscreen->output_list)
+ fsout = NULL;
+ else
+ fsout = wl_container_of(fullscreen->current_output->link.next,
+ fsout, link);
+ } else {
+ fsout = wl_container_of(fullscreen->output_list.next,
+ fsout, link);
+ }
+
+ fullscreen->current_output = fsout;
+ wl_output = fsout ? output_get_wl_output(fsout->output) : NULL;
+ zwp_fullscreen_shell_v1_present_surface(fullscreen->fshell,
+ window_get_wl_surface(fullscreen->window),
+ fullscreen->present_method,
+ wl_output);
+ window_schedule_redraw(window);
+ break;
+
+ case XKB_KEY_w:
+ if (!fullscreen->fshell || !fullscreen->current_output)
+ break;
+
+ wl_output = NULL;
+ if (fullscreen->current_output)
+ wl_output = output_get_wl_output(fullscreen->current_output->output);
+ zwp_fullscreen_shell_mode_feedback_v1_destroy(
+ zwp_fullscreen_shell_v1_present_surface_for_mode(fullscreen->fshell,
+ window_get_wl_surface(fullscreen->window),
+ wl_output, 0));
+ window_schedule_redraw(window);
+ break;
+
+ case XKB_KEY_f:
+ if (fullscreen->fshell)
+ break;
+ fullscreen->fullscreen ^= 1;
+ window_set_fullscreen(window, fullscreen->fullscreen);
+ break;
+
+ case XKB_KEY_q:
+ exit (0);
+ break;
+ }
+}
+
+static int
+motion_handler(struct widget *widget,
+ struct input *input,
+ uint32_t time,
+ float x,
+ float y, void *data)
+{
+ struct fullscreen *fullscreen = data;
+
+ fullscreen->pointer_x = x;
+ fullscreen->pointer_y = y;
+
+ widget_schedule_redraw(widget);
+
+ return fullscreen->draw_cursor ? CURSOR_BLANK : CURSOR_LEFT_PTR;
+}
+
+static int
+enter_handler(struct widget *widget,
+ struct input *input,
+ float x, float y, void *data)
+{
+ struct fullscreen *fullscreen = data;
+
+ fullscreen->pointer_x = x;
+ fullscreen->pointer_y = y;
+
+ widget_schedule_redraw(widget);
+
+ return fullscreen->draw_cursor ? CURSOR_BLANK : CURSOR_LEFT_PTR;
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button, enum wl_pointer_button_state state, void *data)
+{
+ struct fullscreen *fullscreen = data;
+
+ switch (button) {
+ case BTN_LEFT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ window_move(fullscreen->window, input,
+ display_get_serial(fullscreen->display));
+ break;
+ case BTN_RIGHT:
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ window_show_frame_menu(fullscreen->window, input, time);
+ break;
+ }
+}
+
+static void
+touch_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ struct fullscreen *fullscreen = data;
+ window_move(fullscreen->window, input, display_get_serial(fullscreen->display));
+}
+
+static void
+fshell_capability_handler(void *data, struct zwp_fullscreen_shell_v1 *fshell,
+ uint32_t capability)
+{
+ struct fullscreen *fullscreen = data;
+
+ switch (capability) {
+ case ZWP_FULLSCREEN_SHELL_V1_CAPABILITY_CURSOR_PLANE:
+ fullscreen->draw_cursor = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+struct zwp_fullscreen_shell_v1_listener fullscreen_shell_listener = {
+ fshell_capability_handler
+};
+
+static void
+usage(int error_code)
+{
+ fprintf(stderr, "Usage: fullscreen [OPTIONS]\n\n"
+ " -w \tSet window width to \n"
+ " -h \tSet window height to \n"
+ " --help\tShow this help text\n\n");
+
+ exit(error_code);
+}
+
+static void
+output_handler(struct output *output, void *data)
+{
+ struct fullscreen *fullscreen = data;
+ struct fs_output *fsout;
+
+ /* If we've already seen this one, don't add it to the list */
+ wl_list_for_each(fsout, &fullscreen->output_list, link)
+ if (fsout->output == output)
+ return;
+
+ fsout = zalloc(sizeof *fsout);
+ if (fsout == NULL) {
+ fprintf(stderr, "out of memory in output_handler\n");
+ return;
+ }
+ fsout->output = output;
+ wl_list_insert(&fullscreen->output_list, &fsout->link);
+}
+
+static void
+global_handler(struct display *display, uint32_t id, const char *interface,
+ uint32_t version, void *data)
+{
+ struct fullscreen *fullscreen = data;
+
+ if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
+ fullscreen->fshell = display_bind(display, id,
+ &zwp_fullscreen_shell_v1_interface,
+ 1);
+ zwp_fullscreen_shell_v1_add_listener(fullscreen->fshell,
+ &fullscreen_shell_listener,
+ fullscreen);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct fullscreen fullscreen;
+ struct display *d;
+ int i;
+
+ fullscreen.width = 640;
+ fullscreen.height = 480;
+ fullscreen.fullscreen = 0;
+ fullscreen.present_method = ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT;
+ wl_list_init(&fullscreen.output_list);
+ fullscreen.current_output = NULL;
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-w") == 0) {
+ if (++i >= argc)
+ usage(EXIT_FAILURE);
+
+ fullscreen.width = atol(argv[i]);
+ } else if (strcmp(argv[i], "-h") == 0) {
+ if (++i >= argc)
+ usage(EXIT_FAILURE);
+
+ fullscreen.height = atol(argv[i]);
+ } else if (strcmp(argv[i], "--help") == 0)
+ usage(EXIT_SUCCESS);
+ else
+ usage(EXIT_FAILURE);
+ }
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ fullscreen.display = d;
+ fullscreen.fshell = NULL;
+ display_set_user_data(fullscreen.display, &fullscreen);
+ display_set_global_handler(fullscreen.display, global_handler);
+ display_set_output_configure_handler(fullscreen.display, output_handler);
+
+ if (fullscreen.fshell) {
+ fullscreen.window = window_create_custom(d);
+ zwp_fullscreen_shell_v1_present_surface(fullscreen.fshell,
+ window_get_wl_surface(fullscreen.window),
+ fullscreen.present_method,
+ NULL);
+ /* If we get the CURSOR_PLANE capability, we'll change this */
+ fullscreen.draw_cursor = 1;
+ } else {
+ fullscreen.window = window_create(d);
+ fullscreen.draw_cursor = 0;
+ }
+
+ fullscreen.widget =
+ window_add_widget(fullscreen.window, &fullscreen);
+
+ window_set_title(fullscreen.window, "Fullscreen");
+
+ widget_set_transparent(fullscreen.widget, 0);
+
+ widget_set_default_cursor(fullscreen.widget, CURSOR_LEFT_PTR);
+ widget_set_redraw_handler(fullscreen.widget, redraw_handler);
+ widget_set_button_handler(fullscreen.widget, button_handler);
+ widget_set_motion_handler(fullscreen.widget, motion_handler);
+ widget_set_enter_handler(fullscreen.widget, enter_handler);
+
+ widget_set_touch_down_handler(fullscreen.widget, touch_handler);
+
+ window_set_key_handler(fullscreen.window, key_handler);
+ window_set_fullscreen_handler(fullscreen.window, fullscreen_handler);
+
+ window_set_user_data(fullscreen.window, &fullscreen);
+ /* Hack to set minimum allocation so we can shrink later */
+ window_schedule_resize(fullscreen.window,
+ 1, 1);
+ window_schedule_resize(fullscreen.window,
+ fullscreen.width, fullscreen.height);
+
+ display_run(d);
+
+ widget_destroy(fullscreen.widget);
+ window_destroy(fullscreen.window);
+ display_destroy(d);
+
+ return 0;
+}
diff --git a/clients/gears.c b/clients/gears.c
new file mode 100644
index 0000000000000000000000000000000000000000..6090a850a55ee843bc259d9cef42217efc1c79a3
--- /dev/null
+++ b/clients/gears.c
@@ -0,0 +1,503 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include "window.h"
+
+struct gears {
+ struct window *window;
+ struct widget *widget;
+
+ struct display *d;
+
+ EGLDisplay display;
+ EGLDisplay config;
+ EGLContext context;
+ GLfloat angle;
+
+ struct {
+ GLfloat rotx;
+ GLfloat roty;
+ } view;
+
+ int button_down;
+ int last_x, last_y;
+
+ GLint gear_list[3];
+ int fullscreen;
+ int frames;
+ uint32_t last_fps;
+};
+
+struct gear_template {
+ GLfloat material[4];
+ GLfloat inner_radius;
+ GLfloat outer_radius;
+ GLfloat width;
+ GLint teeth;
+ GLfloat tooth_depth;
+};
+
+static const struct gear_template gear_templates[] = {
+ { { 0.8, 0.1, 0.0, 1.0 }, 1.0, 4.0, 1.0, 20, 0.7 },
+ { { 0.0, 0.8, 0.2, 1.0 }, 0.5, 2.0, 2.0, 10, 0.7 },
+ { { 0.2, 0.2, 1.0, 1.0 }, 1.3, 2.0, 0.5, 10, 0.7 },
+};
+
+static GLfloat light_pos[4] = {5.0, 5.0, 10.0, 0.0};
+
+static void die(const char *msg)
+{
+ fprintf(stderr, "%s", msg);
+ exit(EXIT_FAILURE);
+}
+
+static void
+make_gear(const struct gear_template *t)
+{
+ GLint i;
+ GLfloat r0, r1, r2;
+ GLfloat angle, da;
+ GLfloat u, v, len;
+
+ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->material);
+
+ r0 = t->inner_radius;
+ r1 = t->outer_radius - t->tooth_depth / 2.0;
+ r2 = t->outer_radius + t->tooth_depth / 2.0;
+
+ da = 2.0 * M_PI / t->teeth / 4.0;
+
+ glShadeModel(GL_FLAT);
+
+ glNormal3f(0.0, 0.0, 1.0);
+
+ /* draw front face */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
+ if (i < t->teeth) {
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
+ }
+ }
+ glEnd();
+
+ /* draw front sides of teeth */
+ glBegin(GL_QUADS);
+ da = 2.0 * M_PI / t->teeth / 4.0;
+ for (i = 0; i < t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
+ glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
+ glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
+ }
+ glEnd();
+
+ glNormal3f(0.0, 0.0, -1.0);
+
+ /* draw back face */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
+ if (i < t->teeth) {
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
+ }
+ }
+ glEnd();
+
+ /* draw back sides of teeth */
+ glBegin(GL_QUADS);
+ da = 2.0 * M_PI / t->teeth / 4.0;
+ for (i = 0; i < t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
+ glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
+ glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
+ }
+ glEnd();
+
+ /* draw outward faces of teeth */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i < t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
+ glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
+ u = r2 * cos(angle + da) - r1 * cos(angle);
+ v = r2 * sin(angle + da) - r1 * sin(angle);
+ len = sqrt(u * u + v * v);
+ u /= len;
+ v /= len;
+ glNormal3f(v, -u, 0.0);
+ glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
+ glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
+ glNormal3f(cos(angle), sin(angle), 0.0);
+ glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
+ glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
+ u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
+ v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
+ glNormal3f(v, -u, 0.0);
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
+ glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
+ glNormal3f(cos(angle), sin(angle), 0.0);
+ }
+
+ glVertex3f(r1 * cos(0), r1 * sin(0), t->width * 0.5);
+ glVertex3f(r1 * cos(0), r1 * sin(0), -t->width * 0.5);
+
+ glEnd();
+
+ glShadeModel(GL_SMOOTH);
+
+ /* draw inside radius cylinder */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= t->teeth; i++) {
+ angle = i * 2.0 * M_PI / t->teeth;
+ glNormal3f(-cos(angle), -sin(angle), 0.0);
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
+ glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
+ }
+ glEnd();
+}
+
+static void
+update_fps(struct gears *gears, uint32_t time)
+{
+ long diff_ms;
+ static bool first_call = true;
+
+ if (first_call) {
+ gears->last_fps = time;
+ first_call = false;
+ } else
+ gears->frames++;
+
+ diff_ms = time - gears->last_fps;
+
+ if (diff_ms > 5000) {
+ float seconds = diff_ms / 1000.0;
+ float fps = gears->frames / seconds;
+
+ printf("%d frames in %6.3f seconds = %6.3f FPS\n", gears->frames, seconds, fps);
+ fflush(stdout);
+
+ gears->frames = 0;
+ gears->last_fps = time;
+ }
+}
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct gears *gears = data;
+
+ update_fps(gears, time);
+
+ gears->angle = (GLfloat) (time % 8192) * 360 / 8192.0;
+
+ window_schedule_redraw(gears->window);
+
+ if (callback)
+ wl_callback_destroy(callback);
+}
+
+static const struct wl_callback_listener listener = {
+ frame_callback
+};
+
+static int
+motion_handler(struct widget *widget, struct input *input,
+ uint32_t time, float x, float y, void *data)
+{
+ struct gears *gears = data;
+ int offset_x, offset_y;
+ float step = 0.5;
+
+ if (gears->button_down) {
+ offset_x = x - gears->last_x;
+ offset_y = y - gears->last_y;
+ gears->last_x = x;
+ gears->last_y = y;
+ gears->view.roty += offset_x * step;
+ gears->view.rotx += offset_y * step;
+ if (gears->view.roty >= 360)
+ gears->view.roty = gears->view.roty - 360;
+ if (gears->view.roty <= 0)
+ gears->view.roty = gears->view.roty + 360;
+ if (gears->view.rotx >= 360)
+ gears->view.rotx = gears->view.rotx - 360;
+ if (gears->view.rotx <= 0)
+ gears->view.rotx = gears->view.rotx + 360;
+ }
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+button_handler(struct widget *widget, struct input *input,
+ uint32_t time, uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct gears *gears = data;
+
+ if (button == BTN_LEFT) {
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ gears->button_down = 1;
+ input_get_position(input,
+ &gears->last_x, &gears->last_y);
+ } else {
+ gears->button_down = 0;
+ }
+ }
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct rectangle window_allocation;
+ struct rectangle allocation;
+ struct wl_callback *callback;
+ struct gears *gears = data;
+
+ widget_get_allocation(gears->widget, &allocation);
+ window_get_allocation(gears->window, &window_allocation);
+
+ if (display_acquire_window_surface(gears->d,
+ gears->window,
+ gears->context) < 0) {
+ die("Unable to acquire window surface, "
+ "compiled without cairo-egl?\n");
+ }
+
+ glViewport(allocation.x,
+ window_allocation.height - allocation.height - allocation.y,
+ allocation.width, allocation.height);
+ glScissor(allocation.x,
+ window_allocation.height - allocation.height - allocation.y,
+ allocation.width, allocation.height);
+
+ glEnable(GL_SCISSOR_TEST);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix();
+
+ glTranslatef(0.0, 0.0, -50);
+
+ glRotatef(gears->view.rotx, 1.0, 0.0, 0.0);
+ glRotatef(gears->view.roty, 0.0, 1.0, 0.0);
+
+ glPushMatrix();
+ glTranslatef(-3.0, -2.0, 0.0);
+ glRotatef(gears->angle, 0.0, 0.0, 1.0);
+ glCallList(gears->gear_list[0]);
+ glPopMatrix();
+
+ glPushMatrix();
+ glTranslatef(3.1, -2.0, 0.0);
+ glRotatef(-2.0 * gears->angle - 9.0, 0.0, 0.0, 1.0);
+ glCallList(gears->gear_list[1]);
+ glPopMatrix();
+
+ glPushMatrix();
+ glTranslatef(-3.1, 4.2, 0.0);
+ glRotatef(-2.0 * gears->angle - 25.0, 0.0, 0.0, 1.0);
+ glCallList(gears->gear_list[2]);
+ glPopMatrix();
+
+ glPopMatrix();
+
+ glFlush();
+
+ display_release_window_surface(gears->d, gears->window);
+
+ callback = wl_surface_frame(window_get_wl_surface(gears->window));
+ wl_callback_add_listener(callback, &listener, gears);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct gears *gears = data;
+ int32_t size, big, small;
+
+ /* Constrain child size to be square and at least 300x300 */
+ if (width < height) {
+ small = width;
+ big = height;
+ } else {
+ small = height;
+ big = width;
+ }
+
+ if (gears->fullscreen)
+ size = small;
+ else
+ size = big;
+
+ widget_set_size(gears->widget, size, size);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ window_schedule_redraw(window);
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct gears *gears = data;
+
+ gears->fullscreen ^= 1;
+ window_set_fullscreen(window, gears->fullscreen);
+}
+
+static struct gears *
+gears_create(struct display *display)
+{
+ const int width = 450, height = 500;
+ struct gears *gears;
+ int i;
+
+ gears = zalloc(sizeof *gears);
+ gears->d = display;
+ gears->window = window_create(display);
+ gears->widget = window_frame_create(gears->window, gears);
+ window_set_title(gears->window, "Wayland Gears");
+
+ gears->display = display_get_egl_display(gears->d);
+ if (gears->display == NULL)
+ die("failed to create egl display\n");
+
+ eglBindAPI(EGL_OPENGL_API);
+
+ gears->config = display_get_argb_egl_config(gears->d);
+
+ gears->context = eglCreateContext(gears->display, gears->config,
+ EGL_NO_CONTEXT, NULL);
+ if (gears->context == NULL)
+ die("failed to create context\n");
+
+ if (!eglMakeCurrent(gears->display, NULL, NULL, gears->context))
+ die("failed to make context current\n");
+
+ for (i = 0; i < 3; i++) {
+ gears->gear_list[i] = glGenLists(1);
+ glNewList(gears->gear_list[i], GL_COMPILE);
+ make_gear(&gear_templates[i]);
+ glEndList();
+ }
+
+ gears->button_down = 0;
+ gears->last_x = 0;
+ gears->last_y = 0;
+
+ gears->view.rotx = 20.0;
+ gears->view.roty = 30.0;
+
+ printf("Warning: FPS count is limited by the wayland compositor or monitor refresh rate\n");
+
+ glEnable(GL_NORMALIZE);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 200.0);
+ glMatrixMode(GL_MODELVIEW);
+
+ glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_DEPTH_TEST);
+ glClearColor(0, 0, 0, 0.92);
+
+ window_set_user_data(gears->window, gears);
+ widget_set_resize_handler(gears->widget, resize_handler);
+ widget_set_redraw_handler(gears->widget, redraw_handler);
+ widget_set_button_handler(gears->widget, button_handler);
+ widget_set_motion_handler(gears->widget, motion_handler);
+ window_set_keyboard_focus_handler(gears->window,
+ keyboard_focus_handler);
+ window_set_fullscreen_handler(gears->window, fullscreen_handler);
+
+ window_schedule_resize(gears->window, width, height);
+
+ return gears;
+}
+
+static void
+gears_destroy(struct gears *gears)
+{
+ widget_destroy(gears->widget);
+ window_destroy(gears->window);
+ free(gears);
+}
+
+int main(int argc, char *argv[])
+{
+ struct display *d;
+ struct gears *gears;
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ gears = gears_create(d);
+ display_run(d);
+
+ gears_destroy(gears);
+ display_destroy(d);
+
+ return 0;
+}
diff --git a/clients/image.c b/clients/image.c
new file mode 100644
index 0000000000000000000000000000000000000000..0a8fb5b5b339bbd7fe2005d0f6b72b4b9c05c624
--- /dev/null
+++ b/clients/image.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "window.h"
+#include "shared/cairo-util.h"
+
+struct image {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+ char *filename;
+ cairo_surface_t *image;
+ int fullscreen;
+ int *image_counter;
+ int32_t width, height;
+
+ struct {
+ double x;
+ double y;
+ } pointer;
+ bool button_pressed;
+
+ bool initialized;
+ cairo_matrix_t matrix;
+};
+
+static double
+get_scale(struct image *image)
+{
+ assert(image->matrix.xy == 0.0 &&
+ image->matrix.yx == 0.0 &&
+ image->matrix.xx == image->matrix.yy);
+ return image->matrix.xx;
+}
+
+static void
+clamp_view(struct image *image)
+{
+ struct rectangle allocation;
+ double scale = get_scale(image);
+ double sw, sh;
+
+ sw = image->width * scale;
+ sh = image->height * scale;
+ widget_get_allocation(image->widget, &allocation);
+
+ if (sw < allocation.width) {
+ image->matrix.x0 =
+ (allocation.width - image->width * scale) / 2;
+ } else {
+ if (image->matrix.x0 > 0.0)
+ image->matrix.x0 = 0.0;
+ if (sw + image->matrix.x0 < allocation.width)
+ image->matrix.x0 = allocation.width - sw;
+ }
+
+ if (sh < allocation.height) {
+ image->matrix.y0 =
+ (allocation.height - image->height * scale) / 2;
+ } else {
+ if (image->matrix.y0 > 0.0)
+ image->matrix.y0 = 0.0;
+ if (sh + image->matrix.y0 < allocation.height)
+ image->matrix.y0 = allocation.height - sh;
+ }
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct image *image = data;
+ struct rectangle allocation;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ double width, height, doc_aspect, window_aspect, scale;
+ cairo_matrix_t matrix;
+ cairo_matrix_t translate;
+
+ surface = window_get_surface(image->window);
+ cr = cairo_create(surface);
+ widget_get_allocation(image->widget, &allocation);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_clip(cr);
+ cairo_push_group(cr);
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+ cairo_paint(cr);
+
+ if (!image->initialized) {
+ image->initialized = true;
+ width = cairo_image_surface_get_width(image->image);
+ height = cairo_image_surface_get_height(image->image);
+
+ doc_aspect = width / height;
+ window_aspect = (double) allocation.width / allocation.height;
+ if (doc_aspect < window_aspect)
+ scale = allocation.height / height;
+ else
+ scale = allocation.width / width;
+
+ image->width = width;
+ image->height = height;
+ cairo_matrix_init_scale(&image->matrix, scale, scale);
+
+ clamp_view(image);
+ }
+
+ matrix = image->matrix;
+ cairo_matrix_init_translate(&translate, allocation.x, allocation.y);
+ cairo_matrix_multiply(&matrix, &matrix, &translate);
+ cairo_set_matrix(cr, &matrix);
+
+ cairo_set_source_surface(cr, image->image, 0, 0);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_paint(cr);
+
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct image *image = data;
+
+ clamp_view(image);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct image *image = data;
+
+ window_schedule_redraw(image->window);
+}
+
+static int
+enter_handler(struct widget *widget,
+ struct input *input,
+ float x, float y, void *data)
+{
+ struct image *image = data;
+ struct rectangle allocation;
+
+ widget_get_allocation(image->widget, &allocation);
+ x -= allocation.x;
+ y -= allocation.y;
+
+ image->pointer.x = x;
+ image->pointer.y = y;
+
+ return 1;
+}
+
+static void
+move_viewport(struct image *image, double dx, double dy)
+{
+ double scale = get_scale(image);
+
+ if (!image->initialized)
+ return;
+
+ cairo_matrix_translate(&image->matrix, -dx/scale, -dy/scale);
+ clamp_view(image);
+
+ window_schedule_redraw(image->window);
+}
+
+static int
+motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct image *image = data;
+ struct rectangle allocation;
+
+ widget_get_allocation(image->widget, &allocation);
+ x -= allocation.x;
+ y -= allocation.y;
+
+ if (image->button_pressed)
+ move_viewport(image, image->pointer.x - x,
+ image->pointer.y - y);
+
+ image->pointer.x = x;
+ image->pointer.y = y;
+
+ return image->button_pressed ? CURSOR_DRAGGING : CURSOR_LEFT_PTR;
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state,
+ void *data)
+{
+ struct image *image = data;
+
+ if (button == BTN_LEFT) {
+ image->button_pressed =
+ state == WL_POINTER_BUTTON_STATE_PRESSED;
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ input_set_pointer_image(input, CURSOR_DRAGGING);
+ else
+ input_set_pointer_image(input, CURSOR_LEFT_PTR);
+ }
+}
+
+static void
+zoom(struct image *image, double scale)
+{
+ double x = image->pointer.x;
+ double y = image->pointer.y;
+ cairo_matrix_t scale_matrix;
+
+ if (!image->initialized)
+ return;
+
+ if (get_scale(image) * scale > 20.0 ||
+ get_scale(image) * scale < 0.02)
+ return;
+
+ cairo_matrix_init_identity(&scale_matrix);
+ cairo_matrix_translate(&scale_matrix, x, y);
+ cairo_matrix_scale(&scale_matrix, scale, scale);
+ cairo_matrix_translate(&scale_matrix, -x, -y);
+
+ cairo_matrix_multiply(&image->matrix, &image->matrix, &scale_matrix);
+ clamp_view(image);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
+ void *data)
+{
+ struct image *image = data;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_minus:
+ zoom(image, 0.8);
+ window_schedule_redraw(image->window);
+ break;
+ case XKB_KEY_equal:
+ case XKB_KEY_plus:
+ zoom(image, 1.2);
+ window_schedule_redraw(image->window);
+ break;
+ case XKB_KEY_1:
+ image->matrix.xx = 1.0;
+ image->matrix.xy = 0.0;
+ image->matrix.yx = 0.0;
+ image->matrix.yy = 1.0;
+ clamp_view(image);
+ window_schedule_redraw(image->window);
+ break;
+ }
+}
+
+static void
+axis_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t axis, wl_fixed_t value, void *data)
+{
+ struct image *image = data;
+
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
+ input_get_modifiers(input) == MOD_CONTROL_MASK) {
+ /* set zoom level to 2% per 10 axis units */
+ zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
+
+ window_schedule_redraw(image->window);
+ } else if (input_get_modifiers(input) == 0) {
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
+ move_viewport(image, 0, wl_fixed_to_double(value));
+ else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
+ move_viewport(image, wl_fixed_to_double(value), 0);
+ }
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct image *image = data;
+
+ image->fullscreen ^= 1;
+ window_set_fullscreen(window, image->fullscreen);
+}
+
+static void
+close_handler(void *data)
+{
+ struct image *image = data;
+
+ *image->image_counter -= 1;
+
+ if (*image->image_counter == 0)
+ display_exit(image->display);
+
+ widget_destroy(image->widget);
+ window_destroy(image->window);
+
+ free(image);
+}
+
+static struct image *
+image_create(struct display *display, const char *filename,
+ int *image_counter)
+{
+ struct image *image;
+ char *b, *copy, title[512];
+
+ image = zalloc(sizeof *image);
+ if (image == NULL)
+ return image;
+
+ copy = strdup(filename);
+ b = basename(copy);
+ snprintf(title, sizeof title, "Wayland Image - %s", b);
+ free(copy);
+
+ image->filename = strdup(filename);
+ image->image = load_cairo_surface(filename);
+
+ if (!image->image) {
+ free(image->filename);
+ free(image);
+ return NULL;
+ }
+
+ image->window = window_create(display);
+ image->widget = window_frame_create(image->window, image);
+ window_set_title(image->window, title);
+ image->display = display;
+ image->image_counter = image_counter;
+ *image_counter += 1;
+ image->initialized = false;
+
+ window_set_user_data(image->window, image);
+ widget_set_redraw_handler(image->widget, redraw_handler);
+ widget_set_resize_handler(image->widget, resize_handler);
+ window_set_keyboard_focus_handler(image->window,
+ keyboard_focus_handler);
+ window_set_fullscreen_handler(image->window, fullscreen_handler);
+ window_set_close_handler(image->window, close_handler);
+
+ widget_set_enter_handler(image->widget, enter_handler);
+ widget_set_motion_handler(image->widget, motion_handler);
+ widget_set_button_handler(image->widget, button_handler);
+ widget_set_axis_handler(image->widget, axis_handler);
+ window_set_key_handler(image->window, key_handler);
+ widget_schedule_resize(image->widget, 500, 400);
+
+ return image;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *d;
+ int i;
+ int image_counter = 0;
+
+ if (argc <= 1 || argv[1][0]=='-') {
+ printf("Usage: %s image...\n", argv[0]);
+ return 1;
+ }
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ for (i = 1; i < argc; i++)
+ image_create(d, argv[i], &image_counter);
+
+ if (image_counter > 0)
+ display_run(d);
+
+ display_destroy(d);
+
+ return 0;
+}
diff --git a/clients/ivi-shell-user-interface.c b/clients/ivi-shell-user-interface.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d2d1a20f075f28ab31a944bf7ed43c4cfda268c
--- /dev/null
+++ b/clients/ivi-shell-user-interface.c
@@ -0,0 +1,1338 @@
+/*
+ * Copyright (C) 2013 DENSO CORPORATION
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "shared/cairo-util.h"
+#include
+#include "shared/helpers.h"
+#include "shared/os-compatibility.h"
+#include "shared/xalloc.h"
+#include
+#include "shared/file-util.h"
+#include "ivi-application-client-protocol.h"
+#include "ivi-hmi-controller-client-protocol.h"
+
+/**
+ * A reference implementation how to use ivi-hmi-controller interface to
+ * interact with hmi-controller. This is launched from hmi-controller by using
+ * hmi_client_start and create a pthread.
+ *
+ * The basic flow is as followed,
+ * 1/ read configuration from weston.ini.
+ * 2/ draw png file to surface according to configuration of weston.ini
+ * 3/ set up UI by using ivi-hmi-controller protocol
+ * 4/ Enter event loop
+ * 5/ If a surface receives touch/pointer event, followings are invoked
+ * according to type of event and surface
+ * 5-1/ If a surface to launch ivi_application receive touch up, it execs
+ * ivi-application configured in weston.ini.
+ * 5-2/ If a surface to switch layout mode receive touch up, it sends a request,
+ * ivi_hmi_controller_switch_mode, to hmi-controller.
+ * 5-3/ If a surface to show workspace having launchers, it sends a request,
+ * ivi_hmi_controller_home, to hmi-controller.
+ * 5-4/ If touch down events happens in workspace,
+ * ivi_hmi_controller_workspace_control is sent to slide workspace.
+ * When control finished, event: ivi_hmi_controller_workspace_end_control
+ * is received.
+ */
+
+/*****************************************************************************
+ * structure, globals
+ ****************************************************************************/
+enum cursor_type {
+ CURSOR_BOTTOM_LEFT,
+ CURSOR_BOTTOM_RIGHT,
+ CURSOR_BOTTOM,
+ CURSOR_DRAGGING,
+ CURSOR_LEFT_PTR,
+ CURSOR_LEFT,
+ CURSOR_RIGHT,
+ CURSOR_TOP_LEFT,
+ CURSOR_TOP_RIGHT,
+ CURSOR_TOP,
+ CURSOR_IBEAM,
+ CURSOR_HAND1,
+ CURSOR_WATCH,
+
+ CURSOR_BLANK
+};
+struct wlContextCommon {
+ struct wl_display *wlDisplay;
+ struct wl_registry *wlRegistry;
+ struct wl_compositor *wlCompositor;
+ struct wl_shm *wlShm;
+ uint32_t formats;
+ struct wl_seat *wlSeat;
+ struct wl_pointer *wlPointer;
+ struct wl_touch *wlTouch;
+ struct ivi_application *iviApplication;
+ struct ivi_hmi_controller *hmiCtrl;
+ struct hmi_homescreen_setting *hmi_setting;
+ struct wl_list list_wlContextStruct;
+ struct wl_surface *enterSurface;
+ int32_t is_home_on;
+ struct wl_cursor_theme *cursor_theme;
+ struct wl_cursor **cursors;
+ struct wl_surface *pointer_surface;
+ enum cursor_type current_cursor;
+ uint32_t enter_serial;
+};
+
+struct wlContextStruct {
+ struct wlContextCommon *cmm;
+ struct wl_surface *wlSurface;
+ struct wl_buffer *wlBuffer;
+ cairo_surface_t *ctx_image;
+ void *data;
+ uint32_t id_surface;
+ struct wl_list link;
+};
+
+struct
+hmi_homescreen_srf {
+ uint32_t id;
+ char *filePath;
+ uint32_t color;
+};
+
+struct
+hmi_homescreen_workspace {
+ struct wl_array launcher_id_array;
+ struct wl_list link;
+};
+
+struct
+hmi_homescreen_launcher {
+ uint32_t icon_surface_id;
+ uint32_t workspace_id;
+ char *icon;
+ char *path;
+ struct wl_list link;
+};
+
+struct
+hmi_homescreen_setting {
+ struct hmi_homescreen_srf background;
+ struct hmi_homescreen_srf panel;
+ struct hmi_homescreen_srf tiling;
+ struct hmi_homescreen_srf sidebyside;
+ struct hmi_homescreen_srf fullscreen;
+ struct hmi_homescreen_srf random;
+ struct hmi_homescreen_srf home;
+ struct hmi_homescreen_srf workspace_background;
+
+ struct wl_list workspace_list;
+ struct wl_list launcher_list;
+
+ char *cursor_theme;
+ int32_t cursor_size;
+ uint32_t transition_duration;
+ uint32_t surface_id_offset;
+ int32_t screen_num;
+};
+
+/*****************************************************************************
+ * Event Handler
+ ****************************************************************************/
+
+static void
+shm_format(void *data, struct wl_shm *pWlShm, uint32_t format)
+{
+ struct wlContextCommon *pCtx = data;
+
+ pCtx->formats |= (1 << format);
+}
+
+static struct wl_shm_listener shm_listenter = {
+ shm_format
+};
+
+static int32_t
+getIdOfWlSurface(struct wlContextCommon *pCtx, struct wl_surface *wlSurface)
+{
+ struct wlContextStruct *pWlCtxSt = NULL;
+
+ if (NULL == pCtx || NULL == wlSurface )
+ return 0;
+
+ wl_list_for_each(pWlCtxSt, &pCtx->list_wlContextStruct, link) {
+ if (pWlCtxSt->wlSurface == wlSurface)
+ return pWlCtxSt->id_surface;
+ }
+
+ return -1;
+}
+
+static void
+set_pointer_image(struct wlContextCommon *pCtx, uint32_t index)
+{
+ struct wl_cursor *cursor = NULL;
+ struct wl_cursor_image *image = NULL;
+ struct wl_buffer *buffer = NULL;
+
+ if (!pCtx->wlPointer || !pCtx->cursors)
+ return;
+
+ if (CURSOR_BLANK == pCtx->current_cursor) {
+ wl_pointer_set_cursor(pCtx->wlPointer, pCtx->enter_serial,
+ NULL, 0, 0);
+ return;
+ }
+
+ cursor = pCtx->cursors[pCtx->current_cursor];
+ if (!cursor)
+ return;
+
+ if (cursor->image_count <= index) {
+ fprintf(stderr, "cursor index out of range\n");
+ return;
+ }
+
+ image = cursor->images[index];
+ buffer = wl_cursor_image_get_buffer(image);
+
+ if (!buffer)
+ return;
+
+ wl_pointer_set_cursor(pCtx->wlPointer, pCtx->enter_serial,
+ pCtx->pointer_surface,
+ image->hotspot_x, image->hotspot_y);
+
+ wl_surface_attach(pCtx->pointer_surface, buffer, 0, 0);
+
+ wl_surface_damage(pCtx->pointer_surface, 0, 0,
+ image->width, image->height);
+
+ wl_surface_commit(pCtx->pointer_surface);
+}
+
+static void
+PointerHandleEnter(void *data, struct wl_pointer *wlPointer, uint32_t serial,
+ struct wl_surface *wlSurface, wl_fixed_t sx, wl_fixed_t sy)
+{
+ struct wlContextCommon *pCtx = data;
+
+ pCtx->enter_serial = serial;
+ pCtx->enterSurface = wlSurface;
+ set_pointer_image(pCtx, 0);
+#ifdef _DEBUG
+ printf("ENTER PointerHandleEnter: x(%d), y(%d)\n", sx, sy);
+#endif
+}
+
+static void
+PointerHandleLeave(void *data, struct wl_pointer *wlPointer, uint32_t serial,
+ struct wl_surface *wlSurface)
+{
+ struct wlContextCommon *pCtx = data;
+
+ pCtx->enterSurface = NULL;
+
+#ifdef _DEBUG
+ printf("ENTER PointerHandleLeave: serial(%d)\n", serial);
+#endif
+}
+
+static void
+PointerHandleMotion(void *data, struct wl_pointer *wlPointer, uint32_t time,
+ wl_fixed_t sx, wl_fixed_t sy)
+{
+#ifdef _DEBUG
+ printf("ENTER PointerHandleMotion: x(%d), y(%d)\n", sx, sy);
+#endif
+}
+
+/**
+ * if a surface assigned as launcher receives touch-off event, invoking
+ * ivi-application which configured in weston.ini with path to binary.
+ */
+extern char **environ; /*defied by libc */
+
+static pid_t
+execute_process(char *path, char *argv[])
+{
+ pid_t pid = fork();
+ if (pid < 0)
+ fprintf(stderr, "Failed to fork\n");
+
+ if (pid)
+ return pid;
+
+ if (-1 == execve(path, argv, environ)) {
+ fprintf(stderr, "Failed to execve %s\n", path);
+ exit(1);
+ }
+
+ return pid;
+}
+
+static int32_t
+launcher_button(uint32_t surfaceId, struct wl_list *launcher_list)
+{
+ struct hmi_homescreen_launcher *launcher = NULL;
+
+ wl_list_for_each(launcher, launcher_list, link) {
+ char *argv[] = { NULL };
+
+ if (surfaceId != launcher->icon_surface_id)
+ continue;
+
+ execute_process(launcher->path, argv);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * is-method to identify a surface set as launcher in workspace or workspace
+ * itself. This is-method is used to decide whether request;
+ * ivi_hmi_controller_workspace_control is sent or not.
+ */
+static int32_t
+isWorkspaceSurface(uint32_t id, struct hmi_homescreen_setting *hmi_setting)
+{
+ struct hmi_homescreen_launcher *launcher = NULL;
+
+ if (id == hmi_setting->workspace_background.id)
+ return 1;
+
+ wl_list_for_each(launcher, &hmi_setting->launcher_list, link) {
+ if (id == launcher->icon_surface_id)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Decide which request is sent to hmi-controller
+ */
+static void
+touch_up(struct ivi_hmi_controller *hmi_ctrl, uint32_t id_surface,
+ int32_t *is_home_on, struct hmi_homescreen_setting *hmi_setting)
+{
+ if (launcher_button(id_surface, &hmi_setting->launcher_list)) {
+ *is_home_on = 0;
+ ivi_hmi_controller_home(hmi_ctrl, IVI_HMI_CONTROLLER_HOME_OFF);
+ } else if (id_surface == hmi_setting->tiling.id) {
+ ivi_hmi_controller_switch_mode(hmi_ctrl,
+ IVI_HMI_CONTROLLER_LAYOUT_MODE_TILING);
+ } else if (id_surface == hmi_setting->sidebyside.id) {
+ ivi_hmi_controller_switch_mode(hmi_ctrl,
+ IVI_HMI_CONTROLLER_LAYOUT_MODE_SIDE_BY_SIDE);
+ } else if (id_surface == hmi_setting->fullscreen.id) {
+ ivi_hmi_controller_switch_mode(hmi_ctrl,
+ IVI_HMI_CONTROLLER_LAYOUT_MODE_FULL_SCREEN);
+ } else if (id_surface == hmi_setting->random.id) {
+ ivi_hmi_controller_switch_mode(hmi_ctrl,
+ IVI_HMI_CONTROLLER_LAYOUT_MODE_RANDOM);
+ } else if (id_surface == hmi_setting->home.id) {
+ *is_home_on = !(*is_home_on);
+ if (*is_home_on) {
+ ivi_hmi_controller_home(hmi_ctrl,
+ IVI_HMI_CONTROLLER_HOME_ON);
+ } else {
+ ivi_hmi_controller_home(hmi_ctrl,
+ IVI_HMI_CONTROLLER_HOME_OFF);
+ }
+ }
+}
+
+/**
+ * Even handler of Pointer event. IVI system is usually manipulated by touch
+ * screen. However, some systems also have pointer device.
+ * Release is the same behavior as touch off
+ * Pressed is the same behavior as touch on
+ */
+static void
+PointerHandleButton(void *data, struct wl_pointer *wlPointer, uint32_t serial,
+ uint32_t time, uint32_t button, uint32_t state)
+{
+ struct wlContextCommon *pCtx = data;
+ struct ivi_hmi_controller *hmi_ctrl = pCtx->hmiCtrl;
+ const uint32_t id_surface = getIdOfWlSurface(pCtx, pCtx->enterSurface);
+
+ if (BTN_RIGHT == button)
+ return;
+
+ switch (state) {
+ case WL_POINTER_BUTTON_STATE_RELEASED:
+ touch_up(hmi_ctrl, id_surface, &pCtx->is_home_on,
+ pCtx->hmi_setting);
+ break;
+
+ case WL_POINTER_BUTTON_STATE_PRESSED:
+
+ if (isWorkspaceSurface(id_surface, pCtx->hmi_setting)) {
+ ivi_hmi_controller_workspace_control(hmi_ctrl,
+ pCtx->wlSeat,
+ serial);
+ }
+
+ break;
+ }
+#ifdef _DEBUG
+ printf("ENTER PointerHandleButton: button(%d), state(%d)\n",
+ button, state);
+#endif
+}
+
+static void
+PointerHandleAxis(void *data, struct wl_pointer *wlPointer, uint32_t time,
+ uint32_t axis, wl_fixed_t value)
+{
+#ifdef _DEBUG
+ printf("ENTER PointerHandleAxis: axis(%d), value(%d)\n", axis, value);
+#endif
+}
+
+static struct wl_pointer_listener pointer_listener = {
+ PointerHandleEnter,
+ PointerHandleLeave,
+ PointerHandleMotion,
+ PointerHandleButton,
+ PointerHandleAxis
+};
+
+/**
+ * Even handler of touch event
+ */
+static void
+TouchHandleDown(void *data, struct wl_touch *wlTouch, uint32_t serial,
+ uint32_t time, struct wl_surface *surface, int32_t id,
+ wl_fixed_t x_w, wl_fixed_t y_w)
+{
+ struct wlContextCommon *pCtx = data;
+ struct ivi_hmi_controller *hmi_ctrl = pCtx->hmiCtrl;
+ uint32_t id_surface = 0;
+
+ if (0 == id)
+ pCtx->enterSurface = surface;
+
+ id_surface = getIdOfWlSurface(pCtx, pCtx->enterSurface);
+
+ /**
+ * When touch down happens on surfaces of workspace, ask
+ * hmi-controller to start control workspace to select page of
+ * workspace. After sending seat to hmi-controller by
+ * ivi_hmi_controller_workspace_control,
+ * hmi-controller-homescreen doesn't receive any event till
+ * hmi-controller sends back it.
+ */
+ if (isWorkspaceSurface(id_surface, pCtx->hmi_setting)) {
+ ivi_hmi_controller_workspace_control(hmi_ctrl, pCtx->wlSeat,
+ serial);
+ }
+}
+
+static void
+TouchHandleUp(void *data, struct wl_touch *wlTouch, uint32_t serial,
+ uint32_t time, int32_t id)
+{
+ struct wlContextCommon *pCtx = data;
+ struct ivi_hmi_controller *hmi_ctrl = pCtx->hmiCtrl;
+
+ const uint32_t id_surface = getIdOfWlSurface(pCtx, pCtx->enterSurface);
+
+ /**
+ * triggering event according to touch-up happening on which surface.
+ */
+ if (id == 0){
+ touch_up(hmi_ctrl, id_surface, &pCtx->is_home_on,
+ pCtx->hmi_setting);
+ }
+}
+
+static void
+TouchHandleMotion(void *data, struct wl_touch *wlTouch, uint32_t time,
+ int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+}
+
+static void
+TouchHandleFrame(void *data, struct wl_touch *wlTouch)
+{
+}
+
+static void
+TouchHandleCancel(void *data, struct wl_touch *wlTouch)
+{
+}
+
+static struct wl_touch_listener touch_listener = {
+ TouchHandleDown,
+ TouchHandleUp,
+ TouchHandleMotion,
+ TouchHandleFrame,
+ TouchHandleCancel,
+};
+
+/**
+ * Handler of capabilities
+ */
+static void
+seat_handle_capabilities(void *data, struct wl_seat *seat, uint32_t caps)
+{
+ struct wlContextCommon *p_wlCtx = (struct wlContextCommon*)data;
+ struct wl_seat *wlSeat = p_wlCtx->wlSeat;
+ struct wl_pointer *wlPointer = p_wlCtx->wlPointer;
+ struct wl_touch *wlTouch = p_wlCtx->wlTouch;
+
+ if (p_wlCtx->hmi_setting->cursor_theme) {
+ if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wlPointer){
+ wlPointer = wl_seat_get_pointer(wlSeat);
+ wl_pointer_add_listener(wlPointer,
+ &pointer_listener, data);
+ } else
+ if (!(caps & WL_SEAT_CAPABILITY_POINTER) && wlPointer){
+ wl_pointer_destroy(wlPointer);
+ wlPointer = NULL;
+ }
+ p_wlCtx->wlPointer = wlPointer;
+ }
+
+ if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !wlTouch){
+ wlTouch = wl_seat_get_touch(wlSeat);
+ wl_touch_add_listener(wlTouch, &touch_listener, data);
+ } else
+ if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && wlTouch){
+ wl_touch_destroy(wlTouch);
+ wlTouch = NULL;
+ }
+ p_wlCtx->wlTouch = wlTouch;
+}
+
+static struct wl_seat_listener seat_Listener = {
+ seat_handle_capabilities,
+};
+
+/**
+ * Registration of event
+ * This event is received when hmi-controller server finished controlling
+ * workspace.
+ */
+static void
+ivi_hmi_controller_workspace_end_control(void *data,
+ struct ivi_hmi_controller *hmi_ctrl,
+ int32_t is_controlled)
+{
+ struct wlContextCommon *pCtx = data;
+ const uint32_t id_surface = getIdOfWlSurface(pCtx, pCtx->enterSurface);
+
+ if (is_controlled)
+ return;
+
+ /**
+ * During being controlled by hmi-controller, any input event is not
+ * notified. So when control ends with touch up, it invokes launcher
+ * if up event happens on a launcher surface.
+ *
+ */
+ if (launcher_button(id_surface, &pCtx->hmi_setting->launcher_list)) {
+ pCtx->is_home_on = 0;
+ ivi_hmi_controller_home(hmi_ctrl, IVI_HMI_CONTROLLER_HOME_OFF);
+ }
+}
+
+static const struct ivi_hmi_controller_listener hmi_controller_listener = {
+ ivi_hmi_controller_workspace_end_control
+};
+
+/**
+ * Registration of interfaces
+ */
+static void
+registry_handle_global(void *data, struct wl_registry *registry, uint32_t name,
+ const char *interface, uint32_t version)
+{
+ struct wlContextCommon *p_wlCtx = (struct wlContextCommon*)data;
+
+ if (!strcmp(interface, "wl_compositor")) {
+ p_wlCtx->wlCompositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (!strcmp(interface, "wl_shm")) {
+ p_wlCtx->wlShm =
+ wl_registry_bind(registry, name, &wl_shm_interface, 1);
+ wl_shm_add_listener(p_wlCtx->wlShm, &shm_listenter, p_wlCtx);
+ } else if (!strcmp(interface, "wl_seat")) {
+ /* XXX: should be handling multiple wl_seats */
+ if (p_wlCtx->wlSeat)
+ return;
+
+ p_wlCtx->wlSeat =
+ wl_registry_bind(registry, name, &wl_seat_interface, 1);
+ wl_seat_add_listener(p_wlCtx->wlSeat, &seat_Listener, data);
+ } else if (!strcmp(interface, "ivi_application")) {
+ p_wlCtx->iviApplication =
+ wl_registry_bind(registry, name,
+ &ivi_application_interface, 1);
+ } else if (!strcmp(interface, "ivi_hmi_controller")) {
+ p_wlCtx->hmiCtrl =
+ wl_registry_bind(registry, name,
+ &ivi_hmi_controller_interface, 1);
+
+ ivi_hmi_controller_add_listener(p_wlCtx->hmiCtrl,
+ &hmi_controller_listener, p_wlCtx);
+ } else if (!strcmp(interface, "wl_output")) {
+ p_wlCtx->hmi_setting->screen_num++;
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static void
+frame_listener_func(void *data, struct wl_callback *callback, uint32_t time)
+{
+ if (callback)
+ wl_callback_destroy(callback);
+}
+
+static const struct wl_callback_listener frame_listener = {
+ frame_listener_func
+};
+
+/*
+ * The following correspondences between file names and cursors was copied
+ * from: https://bugs.kde.org/attachment.cgi?id=67313
+ */
+static const char *bottom_left_corners[] = {
+ "bottom_left_corner",
+ "sw-resize",
+ "size_bdiag"
+};
+
+static const char *bottom_right_corners[] = {
+ "bottom_right_corner",
+ "se-resize",
+ "size_fdiag"
+};
+
+static const char *bottom_sides[] = {
+ "bottom_side",
+ "s-resize",
+ "size_ver"
+};
+
+static const char *grabbings[] = {
+ "grabbing",
+ "closedhand",
+ "208530c400c041818281048008011002"
+};
+
+static const char *left_ptrs[] = {
+ "left_ptr",
+ "default",
+ "top_left_arrow",
+ "left-arrow"
+};
+
+static const char *left_sides[] = {
+ "left_side",
+ "w-resize",
+ "size_hor"
+};
+
+static const char *right_sides[] = {
+ "right_side",
+ "e-resize",
+ "size_hor"
+};
+
+static const char *top_left_corners[] = {
+ "top_left_corner",
+ "nw-resize",
+ "size_fdiag"
+};
+
+static const char *top_right_corners[] = {
+ "top_right_corner",
+ "ne-resize",
+ "size_bdiag"
+};
+
+static const char *top_sides[] = {
+ "top_side",
+ "n-resize",
+ "size_ver"
+};
+
+static const char *xterms[] = {
+ "xterm",
+ "ibeam",
+ "text"
+};
+
+static const char *hand1s[] = {
+ "hand1",
+ "pointer",
+ "pointing_hand",
+ "e29285e634086352946a0e7090d73106"
+};
+
+static const char *watches[] = {
+ "watch",
+ "wait",
+ "0426c94ea35c87780ff01dc239897213"
+};
+
+struct cursor_alternatives {
+ const char **names;
+ size_t count;
+};
+
+static const struct cursor_alternatives cursors[] = {
+ { bottom_left_corners, ARRAY_LENGTH(bottom_left_corners) },
+ { bottom_right_corners, ARRAY_LENGTH(bottom_right_corners) },
+ { bottom_sides, ARRAY_LENGTH(bottom_sides) },
+ { grabbings, ARRAY_LENGTH(grabbings) },
+ { left_ptrs, ARRAY_LENGTH(left_ptrs) },
+ { left_sides, ARRAY_LENGTH(left_sides) },
+ { right_sides, ARRAY_LENGTH(right_sides) },
+ { top_left_corners, ARRAY_LENGTH(top_left_corners) },
+ { top_right_corners, ARRAY_LENGTH(top_right_corners) },
+ { top_sides, ARRAY_LENGTH(top_sides) },
+ { xterms, ARRAY_LENGTH(xterms) },
+ { hand1s, ARRAY_LENGTH(hand1s) },
+ { watches, ARRAY_LENGTH(watches) },
+};
+
+static void
+create_cursors(struct wlContextCommon *cmm)
+{
+ uint32_t i = 0;
+ uint32_t j = 0;
+ struct wl_cursor *cursor = NULL;
+ char *cursor_theme = cmm->hmi_setting->cursor_theme;
+ int32_t cursor_size = cmm->hmi_setting->cursor_size;
+
+ cmm->cursor_theme = wl_cursor_theme_load(cursor_theme, cursor_size,
+ cmm->wlShm);
+
+ cmm->cursors =
+ xzalloc(ARRAY_LENGTH(cursors) * sizeof(cmm->cursors[0]));
+
+ for (i = 0; i < ARRAY_LENGTH(cursors); i++) {
+ cursor = NULL;
+
+ for (j = 0; !cursor && j < cursors[i].count; ++j) {
+ cursor = wl_cursor_theme_get_cursor(
+ cmm->cursor_theme, cursors[i].names[j]);
+ }
+
+ if (!cursor) {
+ fprintf(stderr, "could not load cursor '%s'\n",
+ cursors[i].names[0]);
+ }
+
+ cmm->cursors[i] = cursor;
+ }
+}
+
+static void
+destroy_cursors(struct wlContextCommon *cmm)
+{
+ if (cmm->cursor_theme)
+ wl_cursor_theme_destroy(cmm->cursor_theme);
+
+ free(cmm->cursors);
+}
+
+/**
+ * Internal method to prepare parts of UI
+ */
+static void
+createShmBuffer(struct wlContextStruct *p_wlCtx)
+{
+ struct wl_shm_pool *pool;
+
+ int fd = -1;
+ int size = 0;
+ int width = 0;
+ int height = 0;
+ int stride = 0;
+
+ width = cairo_image_surface_get_width(p_wlCtx->ctx_image);
+ height = cairo_image_surface_get_height(p_wlCtx->ctx_image);
+ stride = cairo_image_surface_get_stride(p_wlCtx->ctx_image);
+
+ size = stride * height;
+
+ fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
+ size, strerror(errno));
+ return ;
+ }
+
+ p_wlCtx->data =
+ mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (MAP_FAILED == p_wlCtx->data) {
+ fprintf(stderr, "mmap failed: %s\n", strerror(errno));
+ close(fd);
+ return;
+ }
+
+ pool = wl_shm_create_pool(p_wlCtx->cmm->wlShm, fd, size);
+ p_wlCtx->wlBuffer = wl_shm_pool_create_buffer(pool, 0,
+ width,
+ height,
+ stride,
+ WL_SHM_FORMAT_ARGB8888);
+
+ if (NULL == p_wlCtx->wlBuffer) {
+ fprintf(stderr, "wl_shm_create_buffer failed: %s\n",
+ strerror(errno));
+ close(fd);
+ return;
+ }
+
+ wl_shm_pool_destroy(pool);
+ close(fd);
+}
+
+static void
+destroyWLContextCommon(struct wlContextCommon *p_wlCtx)
+{
+ destroy_cursors(p_wlCtx);
+
+ if (p_wlCtx->pointer_surface)
+ wl_surface_destroy(p_wlCtx->pointer_surface);
+
+ if (p_wlCtx->wlCompositor)
+ wl_compositor_destroy(p_wlCtx->wlCompositor);
+}
+
+static void
+destroyWLContextStruct(struct wlContextStruct *p_wlCtx)
+{
+ if (p_wlCtx->wlSurface)
+ wl_surface_destroy(p_wlCtx->wlSurface);
+
+ if (p_wlCtx->ctx_image) {
+ cairo_surface_destroy(p_wlCtx->ctx_image);
+ p_wlCtx->ctx_image = NULL;
+ }
+}
+
+static int
+createSurface(struct wlContextStruct *p_wlCtx)
+{
+ p_wlCtx->wlSurface =
+ wl_compositor_create_surface(p_wlCtx->cmm->wlCompositor);
+ if (NULL == p_wlCtx->wlSurface) {
+ printf("Error: wl_compositor_create_surface failed.\n");
+ destroyWLContextCommon(p_wlCtx->cmm);
+ abort();
+ }
+
+ return 0;
+}
+
+static void
+drawImage(struct wlContextStruct *p_wlCtx)
+{
+ struct wl_callback *callback;
+
+ int width = 0;
+ int height = 0;
+ int stride = 0;
+ void *data = NULL;
+
+ width = cairo_image_surface_get_width(p_wlCtx->ctx_image);
+ height = cairo_image_surface_get_height(p_wlCtx->ctx_image);
+ stride = cairo_image_surface_get_stride(p_wlCtx->ctx_image);
+ data = cairo_image_surface_get_data(p_wlCtx->ctx_image);
+
+ memcpy(p_wlCtx->data, data, stride * height);
+
+ wl_surface_attach(p_wlCtx->wlSurface, p_wlCtx->wlBuffer, 0, 0);
+ wl_surface_damage(p_wlCtx->wlSurface, 0, 0, width, height);
+
+ callback = wl_surface_frame(p_wlCtx->wlSurface);
+ wl_callback_add_listener(callback, &frame_listener, NULL);
+
+ wl_surface_commit(p_wlCtx->wlSurface);
+}
+
+static void
+create_ivisurface(struct wlContextStruct *p_wlCtx,
+ uint32_t id_surface,
+ cairo_surface_t *surface)
+{
+ struct ivi_surface *ivisurf = NULL;
+
+ p_wlCtx->ctx_image = surface;
+
+ p_wlCtx->id_surface = id_surface;
+ wl_list_init(&p_wlCtx->link);
+ wl_list_insert(&p_wlCtx->cmm->list_wlContextStruct, &p_wlCtx->link);
+
+ createSurface(p_wlCtx);
+ createShmBuffer(p_wlCtx);
+
+ ivisurf = ivi_application_surface_create(p_wlCtx->cmm->iviApplication,
+ id_surface,
+ p_wlCtx->wlSurface);
+ if (ivisurf == NULL) {
+ fprintf(stderr, "Failed to create ivi_client_surface\n");
+ return;
+ }
+
+ drawImage(p_wlCtx);
+}
+
+static void
+create_ivisurfaceFromFile(struct wlContextStruct *p_wlCtx,
+ uint32_t id_surface,
+ const char *imageFile)
+{
+ cairo_surface_t *surface = load_cairo_surface(imageFile);
+
+ if (NULL == surface) {
+ fprintf(stderr, "Failed to load_cairo_surface %s\n", imageFile);
+ return;
+ }
+
+ create_ivisurface(p_wlCtx, id_surface, surface);
+}
+
+static void
+set_hex_color(cairo_t *cr, uint32_t color)
+{
+ cairo_set_source_rgba(cr,
+ ((color >> 16) & 0xff) / 255.0,
+ ((color >> 8) & 0xff) / 255.0,
+ ((color >> 0) & 0xff) / 255.0,
+ ((color >> 24) & 0xff) / 255.0);
+}
+
+static void
+create_ivisurfaceFromColor(struct wlContextStruct *p_wlCtx,
+ uint32_t id_surface,
+ uint32_t width, uint32_t height,
+ uint32_t color)
+{
+ cairo_surface_t *surface = NULL;
+ cairo_t *cr = NULL;
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ width, height);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle(cr, 0, 0, width, height);
+ set_hex_color(cr, color);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+
+ create_ivisurface(p_wlCtx, id_surface, surface);
+}
+
+static void
+UI_ready(struct ivi_hmi_controller *controller)
+{
+ ivi_hmi_controller_UI_ready(controller);
+}
+
+/**
+ * Internal method to set up UI by using ivi-hmi-controller
+ */
+static void
+create_background(struct wlContextStruct *p_wlCtx, const uint32_t id_surface,
+ const char *imageFile)
+{
+ create_ivisurfaceFromFile(p_wlCtx, id_surface, imageFile);
+}
+
+static void
+create_panel(struct wlContextStruct *p_wlCtx, const uint32_t id_surface,
+ const char *imageFile)
+{
+ create_ivisurfaceFromFile(p_wlCtx, id_surface, imageFile);
+}
+
+static void
+create_button(struct wlContextStruct *p_wlCtx, const uint32_t id_surface,
+ const char *imageFile, uint32_t number)
+{
+ create_ivisurfaceFromFile(p_wlCtx, id_surface, imageFile);
+}
+
+static void
+create_home_button(struct wlContextStruct *p_wlCtx, const uint32_t id_surface,
+ const char *imageFile)
+{
+ create_ivisurfaceFromFile(p_wlCtx, id_surface, imageFile);
+}
+
+static void
+create_workspace_background(struct wlContextStruct *p_wlCtx,
+ struct hmi_homescreen_srf *srf)
+{
+ create_ivisurfaceFromColor(p_wlCtx, srf->id, 1, 1, srf->color);
+}
+
+static void
+create_launchers(struct wlContextCommon *cmm, struct wl_list *launcher_list)
+{
+ struct hmi_homescreen_launcher **launchers;
+ struct hmi_homescreen_launcher *launcher = NULL;
+
+ int launcher_count = wl_list_length(launcher_list);
+ int ii = 0;
+ int start = 0;
+
+ if (0 == launcher_count)
+ return;
+
+ launchers = xzalloc(launcher_count * sizeof(*launchers));
+
+ wl_list_for_each(launcher, launcher_list, link) {
+ launchers[ii] = launcher;
+ ii++;
+ }
+
+ for (ii = 0; ii < launcher_count; ii++) {
+ int jj = 0;
+
+ if (ii != launcher_count - 1 &&
+ launchers[ii]->workspace_id ==
+ launchers[ii + 1]->workspace_id)
+ continue;
+
+ for (jj = start; jj <= ii; jj++) {
+ struct wlContextStruct *p_wlCtx;
+
+ p_wlCtx = xzalloc(sizeof(*p_wlCtx));
+ p_wlCtx->cmm = cmm;
+ create_ivisurfaceFromFile(p_wlCtx,
+ launchers[jj]->icon_surface_id,
+ launchers[jj]->icon);
+ }
+
+ start = ii + 1;
+ }
+
+ free(launchers);
+}
+
+/**
+ * Internal method to read out weston.ini to get configuration
+ */
+static struct hmi_homescreen_setting *
+hmi_homescreen_setting_create(void)
+{
+ const char *config_file;
+ struct weston_config *config = NULL;
+ struct weston_config_section *shellSection = NULL;
+ struct hmi_homescreen_setting *setting = xzalloc(sizeof(*setting));
+ struct weston_config_section *section = NULL;
+ const char *name = NULL;
+ uint32_t workspace_layer_id;
+ uint32_t icon_surface_id = 0;
+ char *filename;
+
+ wl_list_init(&setting->workspace_list);
+ wl_list_init(&setting->launcher_list);
+
+ config_file = weston_config_get_name_from_env();
+ config = weston_config_parse(config_file);
+
+ shellSection =
+ weston_config_get_section(config, "ivi-shell", NULL, NULL);
+
+ weston_config_section_get_string(
+ shellSection, "cursor-theme", &setting->cursor_theme, NULL);
+
+ weston_config_section_get_int(
+ shellSection, "cursor-size", &setting->cursor_size, 32);
+
+ weston_config_section_get_uint(
+ shellSection, "workspace-layer-id", &workspace_layer_id, 3000);
+
+ filename = file_name_with_datadir("background.png");
+ weston_config_section_get_string(
+ shellSection, "background-image", &setting->background.filePath,
+ filename);
+ free(filename);
+
+ weston_config_section_get_uint(
+ shellSection, "background-id", &setting->background.id, 1001);
+
+ filename = file_name_with_datadir("panel.png");
+ weston_config_section_get_string(
+ shellSection, "panel-image", &setting->panel.filePath,
+ filename);
+ free(filename);
+
+ weston_config_section_get_uint(
+ shellSection, "panel-id", &setting->panel.id, 1002);
+
+ filename = file_name_with_datadir("tiling.png");
+ weston_config_section_get_string(
+ shellSection, "tiling-image", &setting->tiling.filePath,
+ filename);
+ free(filename);
+
+ weston_config_section_get_uint(
+ shellSection, "tiling-id", &setting->tiling.id, 1003);
+
+ filename = file_name_with_datadir("sidebyside.png");
+ weston_config_section_get_string(
+ shellSection, "sidebyside-image", &setting->sidebyside.filePath,
+ filename);
+ free(filename);
+
+ weston_config_section_get_uint(
+ shellSection, "sidebyside-id", &setting->sidebyside.id, 1004);
+
+ filename = file_name_with_datadir("fullscreen.png");
+ weston_config_section_get_string(
+ shellSection, "fullscreen-image", &setting->fullscreen.filePath,
+ filename);
+ free(filename);
+
+ weston_config_section_get_uint(
+ shellSection, "fullscreen-id", &setting->fullscreen.id, 1005);
+
+ filename = file_name_with_datadir("random.png");
+ weston_config_section_get_string(
+ shellSection, "random-image", &setting->random.filePath,
+ filename);
+ free(filename);
+
+ weston_config_section_get_uint(
+ shellSection, "random-id", &setting->random.id, 1006);
+
+ filename = file_name_with_datadir("home.png");
+ weston_config_section_get_string(
+ shellSection, "home-image", &setting->home.filePath,
+ filename);
+ free(filename);
+
+ weston_config_section_get_uint(
+ shellSection, "home-id", &setting->home.id, 1007);
+
+ weston_config_section_get_color(
+ shellSection, "workspace-background-color",
+ &setting->workspace_background.color, 0x99000000);
+
+ weston_config_section_get_uint(
+ shellSection, "workspace-background-id",
+ &setting->workspace_background.id, 2001);
+
+ weston_config_section_get_uint(
+ shellSection, "surface-id-offset", &setting->surface_id_offset, 10);
+
+ icon_surface_id = workspace_layer_id + 1;
+
+ while (weston_config_next_section(config, §ion, &name)) {
+ struct hmi_homescreen_launcher *launcher;
+
+ if (strcmp(name, "ivi-launcher") != 0)
+ continue;
+
+ launcher = xzalloc(sizeof(*launcher));
+ wl_list_init(&launcher->link);
+
+ weston_config_section_get_string(section, "icon",
+ &launcher->icon, NULL);
+ weston_config_section_get_string(section, "path",
+ &launcher->path, NULL);
+ weston_config_section_get_uint(section, "workspace-id",
+ &launcher->workspace_id, 0);
+ weston_config_section_get_uint(section, "icon-id",
+ &launcher->icon_surface_id,
+ icon_surface_id);
+ icon_surface_id++;
+
+ wl_list_insert(setting->launcher_list.prev, &launcher->link);
+ }
+
+ weston_config_destroy(config);
+ return setting;
+}
+
+/**
+ * Main thread
+ *
+ * The basic flow are as followed,
+ * 1/ read configuration from weston.ini by hmi_homescreen_setting_create
+ * 2/ draw png file to surface according to configuration of weston.ini and
+ * set up UI by using ivi-hmi-controller protocol by each create_* method
+ */
+int main(int argc, char **argv)
+{
+ struct wlContextCommon wlCtxCommon;
+ struct wlContextStruct *wlCtx_BackGround;
+ struct wlContextStruct *wlCtx_Panel;
+ struct wlContextStruct wlCtx_Button_1;
+ struct wlContextStruct wlCtx_Button_2;
+ struct wlContextStruct wlCtx_Button_3;
+ struct wlContextStruct wlCtx_Button_4;
+ struct wlContextStruct wlCtx_HomeButton;
+ struct wlContextStruct wlCtx_WorkSpaceBackGround;
+ struct wl_list launcher_wlCtxList;
+ int ret = 0;
+ struct hmi_homescreen_setting *hmi_setting;
+ struct wlContextStruct *pWlCtxSt = NULL;
+ int i = 0;
+
+ hmi_setting = hmi_homescreen_setting_create();
+
+ memset(&wlCtxCommon, 0x00, sizeof(wlCtxCommon));
+ memset(&wlCtx_Button_1, 0x00, sizeof(wlCtx_Button_1));
+ memset(&wlCtx_Button_2, 0x00, sizeof(wlCtx_Button_2));
+ memset(&wlCtx_Button_3, 0x00, sizeof(wlCtx_Button_3));
+ memset(&wlCtx_Button_4, 0x00, sizeof(wlCtx_Button_4));
+ memset(&wlCtx_HomeButton, 0x00, sizeof(wlCtx_HomeButton));
+ memset(&wlCtx_WorkSpaceBackGround, 0x00,
+ sizeof(wlCtx_WorkSpaceBackGround));
+ wl_list_init(&launcher_wlCtxList);
+ wl_list_init(&wlCtxCommon.list_wlContextStruct);
+
+ wlCtxCommon.hmi_setting = hmi_setting;
+
+ wlCtxCommon.wlDisplay = wl_display_connect(NULL);
+ if (NULL == wlCtxCommon.wlDisplay) {
+ printf("Error: wl_display_connect failed.\n");
+ return -1;
+ }
+
+ /* get wl_registry */
+ wlCtxCommon.formats = 0;
+ wlCtxCommon.wlRegistry = wl_display_get_registry(wlCtxCommon.wlDisplay);
+ wl_registry_add_listener(wlCtxCommon.wlRegistry,
+ ®istry_listener, &wlCtxCommon);
+ wl_display_roundtrip(wlCtxCommon.wlDisplay);
+
+ if (wlCtxCommon.wlShm == NULL) {
+ fprintf(stderr, "No wl_shm global\n");
+ exit(1);
+ }
+
+ wl_display_roundtrip(wlCtxCommon.wlDisplay);
+
+ if (!(wlCtxCommon.formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
+ fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
+ exit(1);
+ }
+
+ wlCtx_BackGround = xzalloc(hmi_setting->screen_num * sizeof(struct wlContextStruct));
+ wlCtx_Panel= xzalloc(hmi_setting->screen_num * sizeof(struct wlContextStruct));
+
+ if (wlCtxCommon.hmi_setting->cursor_theme) {
+ create_cursors(&wlCtxCommon);
+
+ wlCtxCommon.pointer_surface =
+ wl_compositor_create_surface(wlCtxCommon.wlCompositor);
+
+ wlCtxCommon.current_cursor = CURSOR_LEFT_PTR;
+ }
+
+ wlCtx_Button_1.cmm = &wlCtxCommon;
+ wlCtx_Button_2.cmm = &wlCtxCommon;
+ wlCtx_Button_3.cmm = &wlCtxCommon;
+ wlCtx_Button_4.cmm = &wlCtxCommon;
+ wlCtx_HomeButton.cmm = &wlCtxCommon;
+ wlCtx_WorkSpaceBackGround.cmm = &wlCtxCommon;
+
+ /* create desktop widgets */
+ for (i = 0; i < hmi_setting->screen_num; i++) {
+ wlCtx_BackGround[i].cmm = &wlCtxCommon;
+ create_background(&wlCtx_BackGround[i],
+ hmi_setting->background.id +
+ (i * hmi_setting->surface_id_offset),
+ hmi_setting->background.filePath);
+
+ wlCtx_Panel[i].cmm = &wlCtxCommon;
+ create_panel(&wlCtx_Panel[i],
+ hmi_setting->panel.id + (i * hmi_setting->surface_id_offset),
+ hmi_setting->panel.filePath);
+ }
+
+ create_button(&wlCtx_Button_1, hmi_setting->tiling.id,
+ hmi_setting->tiling.filePath, 0);
+
+ create_button(&wlCtx_Button_2, hmi_setting->sidebyside.id,
+ hmi_setting->sidebyside.filePath, 1);
+
+ create_button(&wlCtx_Button_3, hmi_setting->fullscreen.id,
+ hmi_setting->fullscreen.filePath, 2);
+
+ create_button(&wlCtx_Button_4, hmi_setting->random.id,
+ hmi_setting->random.filePath, 3);
+
+ create_workspace_background(&wlCtx_WorkSpaceBackGround,
+ &hmi_setting->workspace_background);
+
+ create_launchers(&wlCtxCommon, &hmi_setting->launcher_list);
+
+ create_home_button(&wlCtx_HomeButton, hmi_setting->home.id,
+ hmi_setting->home.filePath);
+
+ UI_ready(wlCtxCommon.hmiCtrl);
+
+ while (ret != -1)
+ ret = wl_display_dispatch(wlCtxCommon.wlDisplay);
+
+ wl_list_for_each(pWlCtxSt, &wlCtxCommon.list_wlContextStruct, link) {
+ destroyWLContextStruct(pWlCtxSt);
+ }
+
+ free(wlCtx_BackGround);
+ free(wlCtx_Panel);
+
+ destroyWLContextCommon(&wlCtxCommon);
+
+ return 0;
+}
diff --git a/clients/keyboard.c b/clients/keyboard.c
new file mode 100644
index 0000000000000000000000000000000000000000..e39d76dd2d1c669bfb18448cc1c08c1c50da9159
--- /dev/null
+++ b/clients/keyboard.c
@@ -0,0 +1,1041 @@
+/*
+ * Copyright © 2012 Openismus GmbH
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "window.h"
+#include "input-method-unstable-v1-client-protocol.h"
+#include "text-input-unstable-v1-client-protocol.h"
+#include "shared/xalloc.h"
+
+struct keyboard;
+
+struct virtual_keyboard {
+ struct zwp_input_panel_v1 *input_panel;
+ struct zwp_input_method_v1 *input_method;
+ struct zwp_input_method_context_v1 *context;
+ struct display *display;
+ struct output *output;
+ char *preedit_string;
+ uint32_t preedit_style;
+ struct {
+ xkb_mod_mask_t shift_mask;
+ } keysym;
+ uint32_t serial;
+ uint32_t content_hint;
+ uint32_t content_purpose;
+ char *preferred_language;
+ char *surrounding_text;
+ uint32_t surrounding_cursor;
+ struct keyboard *keyboard;
+ bool toplevel;
+};
+
+enum key_type {
+ keytype_default,
+ keytype_backspace,
+ keytype_enter,
+ keytype_space,
+ keytype_switch,
+ keytype_symbols,
+ keytype_tab,
+ keytype_arrow_up,
+ keytype_arrow_left,
+ keytype_arrow_right,
+ keytype_arrow_down,
+ keytype_style
+};
+
+struct key {
+ enum key_type key_type;
+
+ char *label;
+ char *uppercase;
+ char *symbol;
+
+ unsigned int width;
+};
+
+struct layout {
+ const struct key *keys;
+ uint32_t count;
+
+ uint32_t columns;
+ uint32_t rows;
+
+ const char *language;
+ uint32_t text_direction;
+};
+
+static const struct key normal_keys[] = {
+ { keytype_default, "q", "Q", "1", 1},
+ { keytype_default, "w", "W", "2", 1},
+ { keytype_default, "e", "E", "3", 1},
+ { keytype_default, "r", "R", "4", 1},
+ { keytype_default, "t", "T", "5", 1},
+ { keytype_default, "y", "Y", "6", 1},
+ { keytype_default, "u", "U", "7", 1},
+ { keytype_default, "i", "I", "8", 1},
+ { keytype_default, "o", "O", "9", 1},
+ { keytype_default, "p", "P", "0", 1},
+ { keytype_backspace, "<--", "<--", "<--", 2},
+
+ { keytype_tab, "->|", "->|", "->|", 1},
+ { keytype_default, "a", "A", "-", 1},
+ { keytype_default, "s", "S", "@", 1},
+ { keytype_default, "d", "D", "*", 1},
+ { keytype_default, "f", "F", "^", 1},
+ { keytype_default, "g", "G", ":", 1},
+ { keytype_default, "h", "H", ";", 1},
+ { keytype_default, "j", "J", "(", 1},
+ { keytype_default, "k", "K", ")", 1},
+ { keytype_default, "l", "L", "~", 1},
+ { keytype_enter, "Enter", "Enter", "Enter", 2},
+
+ { keytype_switch, "ABC", "abc", "ABC", 2},
+ { keytype_default, "z", "Z", "/", 1},
+ { keytype_default, "x", "X", "\'", 1},
+ { keytype_default, "c", "C", "\"", 1},
+ { keytype_default, "v", "V", "+", 1},
+ { keytype_default, "b", "B", "=", 1},
+ { keytype_default, "n", "N", "?", 1},
+ { keytype_default, "m", "M", "!", 1},
+ { keytype_default, ",", ",", "\\", 1},
+ { keytype_default, ".", ".", "|", 1},
+ { keytype_switch, "ABC", "abc", "ABC", 1},
+
+ { keytype_symbols, "?123", "?123", "abc", 1},
+ { keytype_space, "", "", "", 5},
+ { keytype_arrow_up, "/\\", "/\\", "/\\", 1},
+ { keytype_arrow_left, "<", "<", "<", 1},
+ { keytype_arrow_right, ">", ">", ">", 1},
+ { keytype_arrow_down, "\\/", "\\/", "\\/", 1},
+ { keytype_style, "", "", "", 2}
+};
+
+static const struct key numeric_keys[] = {
+ { keytype_default, "1", "1", "1", 1},
+ { keytype_default, "2", "2", "2", 1},
+ { keytype_default, "3", "3", "3", 1},
+ { keytype_default, "4", "4", "4", 1},
+ { keytype_default, "5", "5", "5", 1},
+ { keytype_default, "6", "6", "6", 1},
+ { keytype_default, "7", "7", "7", 1},
+ { keytype_default, "8", "8", "8", 1},
+ { keytype_default, "9", "9", "9", 1},
+ { keytype_default, "0", "0", "0", 1},
+ { keytype_backspace, "<--", "<--", "<--", 2},
+
+ { keytype_space, "", "", "", 4},
+ { keytype_enter, "Enter", "Enter", "Enter", 2},
+ { keytype_arrow_up, "/\\", "/\\", "/\\", 1},
+ { keytype_arrow_left, "<", "<", "<", 1},
+ { keytype_arrow_right, ">", ">", ">", 1},
+ { keytype_arrow_down, "\\/", "\\/", "\\/", 1},
+ { keytype_style, "", "", "", 2}
+};
+
+static const struct key arabic_keys[] = {
+ { keytype_default, "ض", "ﹶ", "۱", 1},
+ { keytype_default, "ص", "ﹰ", "۲", 1},
+ { keytype_default, "ث", "ﹸ", "۳", 1},
+ { keytype_default, "ق", "ﹲ", "۴", 1},
+ { keytype_default, "ف", "ﻹ", "۵", 1},
+ { keytype_default, "غ", "ﺇ", "۶", 1},
+ { keytype_default, "ع", "`", "۷", 1},
+ { keytype_default, "ه", "٪", "۸", 1},
+ { keytype_default, "خ", ">", "۹", 1},
+ { keytype_default, "ح", "<", "۰", 1},
+ { keytype_backspace, "-->", "-->", "-->", 2},
+
+ { keytype_tab, "->|", "->|", "->|", 1},
+ { keytype_default, "ش", "ﹺ", "ﹼ", 1},
+ { keytype_default, "س", "ﹴ", "!", 1},
+ { keytype_default, "ي", "[", "@", 1},
+ { keytype_default, "ب", "]", "#", 1},
+ { keytype_default, "ل", "ﻷ", "$", 1},
+ { keytype_default, "ا", "أ", "%", 1},
+ { keytype_default, "ت", "-", "^", 1},
+ { keytype_default, "ن", "x", "&", 1},
+ { keytype_default, "م", "/", "*", 1},
+ { keytype_default, "ك", ":", "_", 1},
+ { keytype_default, "د", "\"", "+", 1},
+ { keytype_enter, "Enter", "Enter", "Enter", 2},
+
+ { keytype_switch, "Shift", "Base", "Shift", 2},
+ { keytype_default, "ئ", "~", ")", 1},
+ { keytype_default, "ء", "°", "(", 1},
+ { keytype_default, "ؤ", "{", "\"", 1},
+ { keytype_default, "ر", "}", "\'", 1},
+ { keytype_default, "ى", "ﺁ", "؟", 1},
+ { keytype_default, "ة", "'", "!", 1},
+ { keytype_default, "و", ",", ";", 1},
+ { keytype_default, "ﺯ", ".", "\\", 1},
+ { keytype_default, "ظ", "؟", "=", 1},
+ { keytype_switch, "Shift", "Base", "Shift", 2},
+
+ { keytype_symbols, "؟٣٢١", "؟٣٢١", "Base", 1},
+ { keytype_default, "ﻻ", "ﻵ", "|", 1},
+ { keytype_default, ",", "،", "،", 1},
+ { keytype_space, "", "", "", 6},
+ { keytype_default, ".", "ذ", "]", 1},
+ { keytype_default, "ط", "ﺝ", "[", 1},
+ { keytype_style, "", "", "", 2}
+};
+
+
+static const struct layout normal_layout = {
+ normal_keys,
+ sizeof(normal_keys) / sizeof(*normal_keys),
+ 12,
+ 4,
+ "en",
+ ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR
+};
+
+static const struct layout numeric_layout = {
+ numeric_keys,
+ sizeof(numeric_keys) / sizeof(*numeric_keys),
+ 12,
+ 2,
+ "en",
+ ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR
+};
+
+static const struct layout arabic_layout = {
+ arabic_keys,
+ sizeof(arabic_keys) / sizeof(*arabic_keys),
+ 13,
+ 4,
+ "ar",
+ ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL
+};
+
+static const char *style_labels[] = {
+ "default",
+ "none",
+ "active",
+ "inactive",
+ "highlight",
+ "underline",
+ "selection",
+ "incorrect"
+};
+
+static const double key_width = 60;
+static const double key_height = 50;
+
+enum keyboard_state {
+ KEYBOARD_STATE_DEFAULT,
+ KEYBOARD_STATE_UPPERCASE,
+ KEYBOARD_STATE_SYMBOLS
+};
+
+struct keyboard {
+ struct virtual_keyboard *keyboard;
+ struct window *window;
+ struct widget *widget;
+
+ enum keyboard_state state;
+};
+
+static void __attribute__ ((format (printf, 1, 2)))
+dbg(const char *fmt, ...)
+{
+#ifdef DEBUG
+ va_list argp;
+
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
+#endif
+}
+
+static const char *
+label_from_key(struct keyboard *keyboard,
+ const struct key *key)
+{
+ if (key->key_type == keytype_style)
+ return style_labels[keyboard->keyboard->preedit_style];
+
+ switch(keyboard->state) {
+ case KEYBOARD_STATE_DEFAULT:
+ return key->label;
+ case KEYBOARD_STATE_UPPERCASE:
+ return key->uppercase;
+ case KEYBOARD_STATE_SYMBOLS:
+ return key->symbol;
+ }
+
+ return "";
+}
+
+static void
+draw_key(struct keyboard *keyboard,
+ const struct key *key,
+ cairo_t *cr,
+ unsigned int row,
+ unsigned int col)
+{
+ const char *label;
+ cairo_text_extents_t extents;
+
+ cairo_save(cr);
+ cairo_rectangle(cr,
+ col * key_width, row * key_height,
+ key->width * key_width, key_height);
+ cairo_clip(cr);
+
+ /* Paint frame */
+ cairo_rectangle(cr,
+ col * key_width, row * key_height,
+ key->width * key_width, key_height);
+ cairo_set_line_width(cr, 3);
+ cairo_stroke(cr);
+
+ /* Paint text */
+ label = label_from_key(keyboard, key);
+ cairo_text_extents(cr, label, &extents);
+
+ cairo_translate(cr,
+ col * key_width,
+ row * key_height);
+ cairo_translate(cr,
+ (key->width * key_width - extents.width) / 2,
+ (key_height - extents.y_bearing) / 2);
+ cairo_show_text(cr, label);
+
+ cairo_restore(cr);
+}
+
+static const struct layout *
+get_current_layout(struct virtual_keyboard *keyboard)
+{
+ switch (keyboard->content_purpose) {
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DIGITS:
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER:
+ return &numeric_layout;
+ default:
+ if (keyboard->preferred_language &&
+ strcmp(keyboard->preferred_language, "ar") == 0)
+ return &arabic_layout;
+ else
+ return &normal_layout;
+ }
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct keyboard *keyboard = data;
+ cairo_surface_t *surface;
+ struct rectangle allocation;
+ cairo_t *cr;
+ unsigned int i;
+ unsigned int row = 0, col = 0;
+ const struct layout *layout;
+
+ layout = get_current_layout(keyboard->keyboard);
+
+ surface = window_get_surface(keyboard->window);
+ widget_get_allocation(keyboard->widget, &allocation);
+
+ cr = cairo_create(surface);
+ cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
+ cairo_clip(cr);
+
+ cairo_select_font_face(cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size(cr, 16);
+
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 1, 1, 1, 0.75);
+ cairo_rectangle(cr, 0, 0, layout->columns * key_width, layout->rows * key_height);
+ cairo_paint(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+
+ for (i = 0; i < layout->count; ++i) {
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ draw_key(keyboard, &layout->keys[i], cr, row, col);
+ col += layout->keys[i].width;
+ if (col >= layout->columns) {
+ row += 1;
+ col = 0;
+ }
+ }
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ /* struct keyboard *keyboard = data; */
+}
+
+static char *
+insert_text(const char *text, uint32_t offset, const char *insert)
+{
+ int tlen = strlen(text), ilen = strlen(insert);
+ char *new_text = xmalloc(tlen + ilen + 1);
+
+ memcpy(new_text, text, offset);
+ memcpy(new_text + offset, insert, ilen);
+ memcpy(new_text + offset + ilen, text + offset, tlen - offset);
+ new_text[tlen + ilen] = '\0';
+
+ return new_text;
+}
+
+static void
+virtual_keyboard_commit_preedit(struct virtual_keyboard *keyboard)
+{
+ char *surrounding_text;
+
+ if (!keyboard->preedit_string ||
+ strlen(keyboard->preedit_string) == 0)
+ return;
+
+ zwp_input_method_context_v1_cursor_position(keyboard->context,
+ 0, 0);
+ zwp_input_method_context_v1_commit_string(keyboard->context,
+ keyboard->serial,
+ keyboard->preedit_string);
+
+ if (keyboard->surrounding_text) {
+ surrounding_text = insert_text(keyboard->surrounding_text,
+ keyboard->surrounding_cursor,
+ keyboard->preedit_string);
+ free(keyboard->surrounding_text);
+ keyboard->surrounding_text = surrounding_text;
+ keyboard->surrounding_cursor += strlen(keyboard->preedit_string);
+ } else {
+ keyboard->surrounding_text = strdup(keyboard->preedit_string);
+ keyboard->surrounding_cursor = strlen(keyboard->preedit_string);
+ }
+
+ free(keyboard->preedit_string);
+ keyboard->preedit_string = strdup("");
+}
+
+static void
+virtual_keyboard_send_preedit(struct virtual_keyboard *keyboard,
+ int32_t cursor)
+{
+ uint32_t index = strlen(keyboard->preedit_string);
+
+ if (keyboard->preedit_style)
+ zwp_input_method_context_v1_preedit_styling(keyboard->context,
+ 0,
+ strlen(keyboard->preedit_string),
+ keyboard->preedit_style);
+ if (cursor > 0)
+ index = cursor;
+ zwp_input_method_context_v1_preedit_cursor(keyboard->context,
+ index);
+ zwp_input_method_context_v1_preedit_string(keyboard->context,
+ keyboard->serial,
+ keyboard->preedit_string,
+ keyboard->preedit_string);
+}
+
+static const char *
+prev_utf8_char(const char *s, const char *p)
+{
+ for (--p; p >= s; --p) {
+ if ((*p & 0xc0) != 0x80)
+ return p;
+ }
+ return NULL;
+}
+
+static void
+delete_before_cursor(struct virtual_keyboard *keyboard)
+{
+ const char *start, *end;
+
+ if (!keyboard->surrounding_text) {
+ dbg("delete_before_cursor: No surrounding text available\n");
+ return;
+ }
+
+ start = prev_utf8_char(keyboard->surrounding_text,
+ keyboard->surrounding_text + keyboard->surrounding_cursor);
+ if (!start) {
+ dbg("delete_before_cursor: No previous character to delete\n");
+ return;
+ }
+
+ end = keyboard->surrounding_text + keyboard->surrounding_cursor;
+
+ zwp_input_method_context_v1_delete_surrounding_text(keyboard->context,
+ (start - keyboard->surrounding_text) - keyboard->surrounding_cursor,
+ end - start);
+ zwp_input_method_context_v1_commit_string(keyboard->context,
+ keyboard->serial,
+ "");
+
+ /* Update surrounding text */
+ keyboard->surrounding_cursor = start - keyboard->surrounding_text;
+ keyboard->surrounding_text[keyboard->surrounding_cursor] = '\0';
+ if (*end)
+ memmove(keyboard->surrounding_text + keyboard->surrounding_cursor, end, strlen(end));
+}
+
+static char *
+append(char *s1, const char *s2)
+{
+ int len1, len2;
+ char *s;
+
+ len1 = strlen(s1);
+ len2 = strlen(s2);
+ s = xrealloc(s1, len1 + len2 + 1);
+ memcpy(s + len1, s2, len2);
+ s[len1 + len2] = '\0';
+
+ return s;
+}
+
+static void
+keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *key, struct input *input, enum wl_pointer_button_state state)
+{
+ const char *label = NULL;
+
+ switch(keyboard->state) {
+ case KEYBOARD_STATE_DEFAULT :
+ label = key->label;
+ break;
+ case KEYBOARD_STATE_UPPERCASE :
+ label = key->uppercase;
+ break;
+ case KEYBOARD_STATE_SYMBOLS :
+ label = key->symbol;
+ break;
+ }
+
+ xkb_mod_mask_t mod_mask = keyboard->state == KEYBOARD_STATE_DEFAULT ? 0 : keyboard->keyboard->keysym.shift_mask;
+ uint32_t key_state = (state == WL_POINTER_BUTTON_STATE_PRESSED) ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED;
+
+ switch (key->key_type) {
+ case keytype_default:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+
+ keyboard->keyboard->preedit_string =
+ append(keyboard->keyboard->preedit_string,
+ label);
+ virtual_keyboard_send_preedit(keyboard->keyboard, -1);
+ break;
+ case keytype_backspace:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+
+ if (strlen(keyboard->keyboard->preedit_string) == 0) {
+ delete_before_cursor(keyboard->keyboard);
+ } else {
+ keyboard->keyboard->preedit_string[strlen(keyboard->keyboard->preedit_string) - 1] = '\0';
+ virtual_keyboard_send_preedit(keyboard->keyboard, -1);
+ }
+ break;
+ case keytype_enter:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Return, key_state, mod_mask);
+ break;
+ case keytype_space:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+ keyboard->keyboard->preedit_string =
+ append(keyboard->keyboard->preedit_string, " ");
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ break;
+ case keytype_switch:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+ switch(keyboard->state) {
+ case KEYBOARD_STATE_DEFAULT:
+ keyboard->state = KEYBOARD_STATE_UPPERCASE;
+ break;
+ case KEYBOARD_STATE_UPPERCASE:
+ keyboard->state = KEYBOARD_STATE_DEFAULT;
+ break;
+ case KEYBOARD_STATE_SYMBOLS:
+ keyboard->state = KEYBOARD_STATE_UPPERCASE;
+ break;
+ }
+ break;
+ case keytype_symbols:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+ switch(keyboard->state) {
+ case KEYBOARD_STATE_DEFAULT:
+ keyboard->state = KEYBOARD_STATE_SYMBOLS;
+ break;
+ case KEYBOARD_STATE_UPPERCASE:
+ keyboard->state = KEYBOARD_STATE_SYMBOLS;
+ break;
+ case KEYBOARD_STATE_SYMBOLS:
+ keyboard->state = KEYBOARD_STATE_DEFAULT;
+ break;
+ }
+ break;
+ case keytype_tab:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Tab, key_state, mod_mask);
+ break;
+ case keytype_arrow_up:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Up, key_state, mod_mask);
+ break;
+ case keytype_arrow_left:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Left, key_state, mod_mask);
+ break;
+ case keytype_arrow_right:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Right, key_state, mod_mask);
+ break;
+ case keytype_arrow_down:
+ virtual_keyboard_commit_preedit(keyboard->keyboard);
+ zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
+ display_get_serial(keyboard->keyboard->display),
+ time,
+ XKB_KEY_Down, key_state, mod_mask);
+ break;
+ case keytype_style:
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED)
+ break;
+ keyboard->keyboard->preedit_style = (keyboard->keyboard->preedit_style + 1) % 8; /* TODO */
+ virtual_keyboard_send_preedit(keyboard->keyboard, -1);
+ break;
+ }
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct keyboard *keyboard = data;
+ struct rectangle allocation;
+ int32_t x, y;
+ int row, col;
+ unsigned int i;
+ const struct layout *layout;
+
+ layout = get_current_layout(keyboard->keyboard);
+
+ if (button != BTN_LEFT) {
+ return;
+ }
+
+ input_get_position(input, &x, &y);
+
+ widget_get_allocation(keyboard->widget, &allocation);
+ x -= allocation.x;
+ y -= allocation.y;
+
+ row = y / key_height;
+ col = x / key_width + row * layout->columns;
+ for (i = 0; i < layout->count; ++i) {
+ col -= layout->keys[i].width;
+ if (col < 0) {
+ keyboard_handle_key(keyboard, time, &layout->keys[i], input, state);
+ break;
+ }
+ }
+
+ widget_schedule_redraw(widget);
+}
+
+static void
+touch_handler(struct input *input, uint32_t time,
+ float x, float y, uint32_t state, void *data)
+{
+ struct keyboard *keyboard = data;
+ struct rectangle allocation;
+ int row, col;
+ unsigned int i;
+ const struct layout *layout;
+
+ layout = get_current_layout(keyboard->keyboard);
+
+ widget_get_allocation(keyboard->widget, &allocation);
+
+ x -= allocation.x;
+ y -= allocation.y;
+
+ row = (int)y / key_height;
+ col = (int)x / key_width + row * layout->columns;
+ for (i = 0; i < layout->count; ++i) {
+ col -= layout->keys[i].width;
+ if (col < 0) {
+ keyboard_handle_key(keyboard, time,
+ &layout->keys[i], input, state);
+ break;
+ }
+ }
+
+ widget_schedule_redraw(keyboard->widget);
+}
+
+static void
+touch_down_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ float x, float y, void *data)
+{
+ touch_handler(input, time, x, y,
+ WL_POINTER_BUTTON_STATE_PRESSED, data);
+}
+
+static void
+touch_up_handler(struct widget *widget, struct input *input,
+ uint32_t serial, uint32_t time, int32_t id,
+ void *data)
+{
+ float x, y;
+
+ input_get_touch(input, id, &x, &y);
+
+ touch_handler(input, time, x, y,
+ WL_POINTER_BUTTON_STATE_RELEASED, data);
+}
+
+static void
+handle_surrounding_text(void *data,
+ struct zwp_input_method_context_v1 *context,
+ const char *text,
+ uint32_t cursor,
+ uint32_t anchor)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ free(keyboard->surrounding_text);
+ keyboard->surrounding_text = strdup(text);
+
+ keyboard->surrounding_cursor = cursor;
+}
+
+static void
+handle_reset(void *data,
+ struct zwp_input_method_context_v1 *context)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ dbg("Reset pre-edit buffer\n");
+
+ if (strlen(keyboard->preedit_string)) {
+ free(keyboard->preedit_string);
+ keyboard->preedit_string = strdup("");
+ }
+}
+
+static void
+handle_content_type(void *data,
+ struct zwp_input_method_context_v1 *context,
+ uint32_t hint,
+ uint32_t purpose)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ keyboard->content_hint = hint;
+ keyboard->content_purpose = purpose;
+}
+
+static void
+handle_invoke_action(void *data,
+ struct zwp_input_method_context_v1 *context,
+ uint32_t button,
+ uint32_t index)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ if (button != BTN_LEFT)
+ return;
+
+ virtual_keyboard_send_preedit(keyboard, index);
+}
+
+static void
+handle_commit_state(void *data,
+ struct zwp_input_method_context_v1 *context,
+ uint32_t serial)
+{
+ struct virtual_keyboard *keyboard = data;
+ const struct layout *layout;
+
+ keyboard->serial = serial;
+
+ layout = get_current_layout(keyboard);
+
+ if (keyboard->surrounding_text)
+ dbg("Surrounding text updated: %s\n", keyboard->surrounding_text);
+
+ window_schedule_resize(keyboard->keyboard->window,
+ layout->columns * key_width,
+ layout->rows * key_height);
+
+ zwp_input_method_context_v1_language(context,
+ keyboard->serial,
+ layout->language);
+ zwp_input_method_context_v1_text_direction(context,
+ keyboard->serial,
+ layout->text_direction);
+
+ widget_schedule_redraw(keyboard->keyboard->widget);
+}
+
+static void
+handle_preferred_language(void *data,
+ struct zwp_input_method_context_v1 *context,
+ const char *language)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ if (keyboard->preferred_language)
+ free(keyboard->preferred_language);
+
+ keyboard->preferred_language = NULL;
+
+ if (language)
+ keyboard->preferred_language = strdup(language);
+}
+
+static const struct zwp_input_method_context_v1_listener input_method_context_listener = {
+ handle_surrounding_text,
+ handle_reset,
+ handle_content_type,
+ handle_invoke_action,
+ handle_commit_state,
+ handle_preferred_language
+};
+
+static void
+input_method_activate(void *data,
+ struct zwp_input_method_v1 *input_method,
+ struct zwp_input_method_context_v1 *context)
+{
+ struct virtual_keyboard *keyboard = data;
+ struct wl_array modifiers_map;
+ const struct layout *layout;
+
+ keyboard->keyboard->state = KEYBOARD_STATE_DEFAULT;
+
+ if (keyboard->context)
+ zwp_input_method_context_v1_destroy(keyboard->context);
+
+ if (keyboard->preedit_string)
+ free(keyboard->preedit_string);
+
+ keyboard->preedit_string = strdup("");
+ keyboard->content_hint = 0;
+ keyboard->content_purpose = 0;
+ free(keyboard->preferred_language);
+ keyboard->preferred_language = NULL;
+ free(keyboard->surrounding_text);
+ keyboard->surrounding_text = NULL;
+
+ keyboard->serial = 0;
+
+ keyboard->context = context;
+ zwp_input_method_context_v1_add_listener(context,
+ &input_method_context_listener,
+ keyboard);
+
+ wl_array_init(&modifiers_map);
+ keysym_modifiers_add(&modifiers_map, "Shift");
+ keysym_modifiers_add(&modifiers_map, "Control");
+ keysym_modifiers_add(&modifiers_map, "Mod1");
+ zwp_input_method_context_v1_modifiers_map(context, &modifiers_map);
+ keyboard->keysym.shift_mask = keysym_modifiers_get_mask(&modifiers_map, "Shift");
+ wl_array_release(&modifiers_map);
+
+ layout = get_current_layout(keyboard);
+
+ window_schedule_resize(keyboard->keyboard->window,
+ layout->columns * key_width,
+ layout->rows * key_height);
+
+ zwp_input_method_context_v1_language(context,
+ keyboard->serial,
+ layout->language);
+ zwp_input_method_context_v1_text_direction(context,
+ keyboard->serial,
+ layout->text_direction);
+
+ widget_schedule_redraw(keyboard->keyboard->widget);
+}
+
+static void
+input_method_deactivate(void *data,
+ struct zwp_input_method_v1 *input_method,
+ struct zwp_input_method_context_v1 *context)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ if (!keyboard->context)
+ return;
+
+ zwp_input_method_context_v1_destroy(keyboard->context);
+ keyboard->context = NULL;
+}
+
+static const struct zwp_input_method_v1_listener input_method_listener = {
+ input_method_activate,
+ input_method_deactivate
+};
+
+static void
+global_handler(struct display *display, uint32_t name,
+ const char *interface, uint32_t version, void *data)
+{
+ struct virtual_keyboard *keyboard = data;
+
+ if (!strcmp(interface, "zwp_input_panel_v1")) {
+ keyboard->input_panel =
+ display_bind(display, name, &zwp_input_panel_v1_interface, 1);
+ } else if (!strcmp(interface, "zwp_input_method_v1")) {
+ keyboard->input_method =
+ display_bind(display, name,
+ &zwp_input_method_v1_interface, 1);
+ zwp_input_method_v1_add_listener(keyboard->input_method,
+ &input_method_listener,
+ keyboard);
+ }
+}
+
+static void
+set_toplevel(struct output *output, struct virtual_keyboard *virtual_keyboard)
+{
+ struct zwp_input_panel_surface_v1 *ips;
+ struct keyboard *keyboard = virtual_keyboard->keyboard;
+
+ ips = zwp_input_panel_v1_get_input_panel_surface(virtual_keyboard->input_panel,
+ window_get_wl_surface(keyboard->window));
+
+ zwp_input_panel_surface_v1_set_toplevel(ips,
+ output_get_wl_output(output),
+ ZWP_INPUT_PANEL_SURFACE_V1_POSITION_CENTER_BOTTOM);
+
+ virtual_keyboard->toplevel = true;
+}
+
+static void
+display_output_handler(struct output *output, void *data) {
+ struct virtual_keyboard *keyboard = data;
+
+ if (!keyboard->toplevel)
+ set_toplevel(output, keyboard);
+}
+
+static void
+keyboard_create(struct virtual_keyboard *virtual_keyboard)
+{
+ struct keyboard *keyboard;
+ const struct layout *layout;
+
+ layout = get_current_layout(virtual_keyboard);
+
+ keyboard = xzalloc(sizeof *keyboard);
+ keyboard->keyboard = virtual_keyboard;
+ keyboard->window = window_create_custom(virtual_keyboard->display);
+ keyboard->widget = window_add_widget(keyboard->window, keyboard);
+
+ virtual_keyboard->keyboard = keyboard;
+
+ window_set_title(keyboard->window, "Virtual keyboard");
+ window_set_user_data(keyboard->window, keyboard);
+
+ widget_set_redraw_handler(keyboard->widget, redraw_handler);
+ widget_set_resize_handler(keyboard->widget, resize_handler);
+ widget_set_button_handler(keyboard->widget, button_handler);
+ widget_set_touch_down_handler(keyboard->widget, touch_down_handler);
+ widget_set_touch_up_handler(keyboard->widget, touch_up_handler);
+
+ window_schedule_resize(keyboard->window,
+ layout->columns * key_width,
+ layout->rows * key_height);
+
+ display_set_output_configure_handler(virtual_keyboard->display,
+ display_output_handler);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct virtual_keyboard virtual_keyboard;
+
+ memset(&virtual_keyboard, 0, sizeof virtual_keyboard);
+
+ virtual_keyboard.display = display_create(&argc, argv);
+ if (virtual_keyboard.display == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ display_set_user_data(virtual_keyboard.display, &virtual_keyboard);
+ display_set_global_handler(virtual_keyboard.display, global_handler);
+
+ if (virtual_keyboard.input_panel == NULL) {
+ fprintf(stderr, "No input panel global\n");
+ return -1;
+ }
+
+ keyboard_create(&virtual_keyboard);
+
+ display_run(virtual_keyboard.display);
+
+ return 0;
+}
diff --git a/clients/meson.build b/clients/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..2c016b845490e772d79ccd74a6f0d804df827d25
--- /dev/null
+++ b/clients/meson.build
@@ -0,0 +1,373 @@
+if get_option('resize-pool')
+ config_h.set('USE_RESIZE_POOL', '1')
+endif
+
+srcs_toytoolkit = [
+ 'window.c',
+ xdg_shell_client_protocol_h,
+ xdg_shell_protocol_c,
+ text_cursor_position_client_protocol_h,
+ text_cursor_position_protocol_c,
+ relative_pointer_unstable_v1_client_protocol_h,
+ relative_pointer_unstable_v1_protocol_c,
+ pointer_constraints_unstable_v1_client_protocol_h,
+ pointer_constraints_unstable_v1_protocol_c,
+ ivi_application_client_protocol_h,
+ ivi_application_protocol_c,
+ viewporter_client_protocol_h,
+ viewporter_protocol_c,
+]
+deps_toytoolkit = [
+ dep_wayland_client,
+ dep_lib_cairo_shared,
+ dep_xkbcommon,
+ dependency('wayland-cursor'),
+ cc.find_library('util'),
+]
+lib_toytoolkit = static_library(
+ 'toytoolkit',
+ srcs_toytoolkit,
+ include_directories: common_inc,
+ dependencies: deps_toytoolkit,
+ install: false,
+)
+dep_toytoolkit = declare_dependency(
+ link_with: lib_toytoolkit,
+ dependencies: deps_toytoolkit,
+)
+
+simple_clients = [
+ {
+ 'name': 'damage',
+ 'sources': [
+ 'simple-damage.c',
+ viewporter_client_protocol_h,
+ viewporter_protocol_c,
+ xdg_shell_client_protocol_h,
+ xdg_shell_protocol_c,
+ fullscreen_shell_unstable_v1_client_protocol_h,
+ fullscreen_shell_unstable_v1_protocol_c,
+ ],
+ 'dep_objs': [ dep_wayland_client, dep_libshared ]
+ },
+ {
+ 'name': 'dmabuf-egl',
+ 'sources': [
+ 'simple-dmabuf-egl.c',
+ linux_dmabuf_unstable_v1_client_protocol_h,
+ linux_dmabuf_unstable_v1_protocol_c,
+ linux_explicit_synchronization_unstable_v1_client_protocol_h,
+ linux_explicit_synchronization_unstable_v1_protocol_c,
+ xdg_shell_client_protocol_h,
+ xdg_shell_protocol_c,
+ weston_direct_display_client_protocol_h,
+ weston_direct_display_protocol_c,
+ fullscreen_shell_unstable_v1_client_protocol_h,
+ fullscreen_shell_unstable_v1_protocol_c,
+ ],
+ 'dep_objs': [
+ dep_wayland_client,
+ dep_libdrm,
+ dep_libm
+ ],
+ 'deps': [ 'egl', 'glesv2', 'gbm' ],
+ 'options': [ 'renderer-gl' ]
+ },
+ {
+ 'name': 'dmabuf-v4l',
+ 'sources': [
+ 'simple-dmabuf-v4l.c',
+ linux_dmabuf_unstable_v1_client_protocol_h,
+ linux_dmabuf_unstable_v1_protocol_c,
+ xdg_shell_client_protocol_h,
+ xdg_shell_protocol_c,
+ weston_direct_display_client_protocol_h,
+ weston_direct_display_protocol_c,
+ fullscreen_shell_unstable_v1_client_protocol_h,
+ fullscreen_shell_unstable_v1_protocol_c,
+ ],
+ 'dep_objs': [ dep_wayland_client, dep_libdrm_headers ]
+ },
+ {
+ 'name': 'egl',
+ 'sources': [
+ 'simple-egl.c',
+ xdg_shell_client_protocol_h,
+ xdg_shell_protocol_c,
+ ivi_application_client_protocol_h,
+ ivi_application_protocol_c,
+ ],
+ 'dep_objs': [ dep_wayland_client, dep_libshared, dep_libm ],
+ 'deps': [ 'egl', 'wayland-egl', 'glesv2', 'wayland-cursor' ],
+ 'options': [ 'renderer-gl' ]
+ },
+ # weston-simple-im is handled specially separately due to install_dir and odd window.h usage
+ {
+ 'name': 'shm',
+ 'sources': [
+ 'simple-shm.c',
+ xdg_shell_client_protocol_h,
+ xdg_shell_protocol_c,
+ fullscreen_shell_unstable_v1_client_protocol_h,
+ fullscreen_shell_unstable_v1_protocol_c,
+ ivi_application_client_protocol_h,
+ ivi_application_protocol_c,
+ ],
+ 'dep_objs': [ dep_wayland_client, dep_libshared ]
+ },
+ {
+ 'name': 'touch',
+ 'sources': [
+ 'simple-touch.c',
+ ],
+ 'dep_objs': [ dep_wayland_client, dep_libshared ]
+ },
+]
+
+simple_clients_enabled = get_option('simple-clients')
+simple_build_all = simple_clients_enabled.contains('all')
+foreach t : simple_clients
+ if simple_build_all or simple_clients_enabled.contains(t.get('name'))
+ t_name = 'weston-simple-' + t.get('name')
+ t_deps = t.get('dep_objs', [])
+ foreach depname : t.get('deps', [])
+ dep = dependency(depname, required: false)
+ if not dep.found()
+ error('@0@ requires @1@ which was not found. If you rather not build this, drop "@2@" from simple-clients option.'.format(t_name, depname, t.get('name')))
+ endif
+ t_deps += dep
+ endforeach
+
+ foreach optname : t.get('options', [])
+ if not get_option(optname)
+ error('@0@ requires option @1@ which is not enabled. If you rather not build this, drop "@2@" from simple-clients option.'.format(t_name, optname, t.get('name')))
+ endif
+ endforeach
+
+ executable(
+ t_name, t.get('sources'),
+ include_directories: common_inc,
+ dependencies: t_deps,
+ install: true
+ )
+ endif
+endforeach
+
+if simple_build_all or simple_clients_enabled.contains('im')
+ executable(
+ 'weston-simple-im', [
+ 'simple-im.c',
+ input_method_unstable_v1_client_protocol_h,
+ input_method_unstable_v1_protocol_c,
+ ],
+ include_directories: common_inc,
+ dependencies: [
+ dep_libshared,
+ dep_wayland_client,
+ dep_xkbcommon,
+ dependency('wayland-cursor'),
+ dependency('cairo')
+ ],
+ install: true,
+ install_dir: dir_libexec
+ )
+endif
+
+tools_enabled = get_option('tools')
+tools_list = [
+ {
+ 'name': 'calibrator',
+ 'sources': [ 'calibrator.c' ],
+ 'deps': [ dep_toytoolkit, dep_matrix_c ],
+ },
+ {
+ 'name': 'debug',
+ 'sources': [
+ 'weston-debug.c',
+ weston_debug_client_protocol_h,
+ weston_debug_protocol_c,
+ ],
+ 'deps': [ dep_wayland_client ]
+ },
+ {
+ 'name': 'info',
+ 'sources': [
+ 'weston-info.c',
+ presentation_time_client_protocol_h,
+ presentation_time_protocol_c,
+ linux_dmabuf_unstable_v1_client_protocol_h,
+ linux_dmabuf_unstable_v1_protocol_c,
+ tablet_unstable_v2_client_protocol_h,
+ tablet_unstable_v2_protocol_c,
+ xdg_output_unstable_v1_client_protocol_h,
+ xdg_output_unstable_v1_protocol_c,
+ ],
+ 'deps': [ dep_wayland_client, dep_libshared ]
+ },
+ {
+ 'name': 'terminal',
+ 'sources': [ 'terminal.c' ],
+ 'deps': [ dep_toytoolkit ],
+ },
+ {
+ 'name': 'touch-calibrator',
+ 'sources': [
+ 'touch-calibrator.c',
+ weston_touch_calibration_client_protocol_h,
+ weston_touch_calibration_protocol_c,
+ ],
+ 'deps': [ dep_toytoolkit, dep_matrix_c ],
+ },
+]
+
+foreach t : tools_list
+ if tools_enabled.contains(t.get('name'))
+ executable(
+ 'weston-@0@'.format(t.get('name')),
+ t.get('sources'),
+ include_directories: common_inc,
+ dependencies: t.get('deps', []),
+ install: true
+ )
+ endif
+endforeach
+
+demo_clients = [
+ { 'basename': 'clickdot' },
+ {
+ 'basename': 'cliptest',
+ 'dep_objs': dep_vertex_clipping
+ },
+ { 'basename': 'confine' },
+ {
+ 'basename': 'content_protection',
+ 'add_sources': [
+ weston_content_protection_client_protocol_h,
+ weston_content_protection_protocol_c,
+ ]
+ },
+
+ { 'basename': 'dnd' },
+ {
+ 'basename': 'editor',
+ 'add_sources': [
+ text_input_unstable_v1_client_protocol_h,
+ text_input_unstable_v1_protocol_c,
+ ],
+ 'deps': [ 'pangocairo', 'gobject-2.0' ]
+ },
+ { 'basename': 'eventdemo' },
+ { 'basename': 'flower' },
+ {
+ 'basename': 'fullscreen',
+ 'add_sources': [
+ fullscreen_shell_unstable_v1_client_protocol_h,
+ fullscreen_shell_unstable_v1_protocol_c,
+ ]
+ },
+ { 'basename': 'image' },
+ { 'basename': 'multi-resource' },
+ {
+ 'basename': 'presentation-shm',
+ 'add_sources': [
+ presentation_time_client_protocol_h,
+ presentation_time_protocol_c,
+ xdg_shell_client_protocol_h,
+ xdg_shell_protocol_c,
+ ]
+ },
+ { 'basename': 'resizor' },
+ {
+ 'basename': 'scaler',
+ 'add_sources': [
+ viewporter_client_protocol_h,
+ viewporter_protocol_c,
+ ]
+ },
+ { 'basename': 'smoke' },
+ { 'basename': 'stacking' },
+ {
+ 'basename': 'subsurfaces',
+ 'deps': [ 'egl', 'glesv2', 'wayland-egl' ]
+ },
+ { 'basename': 'transformed' },
+]
+
+if get_option('demo-clients')
+ foreach t : demo_clients
+ t_name = 'weston-' + t.get('basename')
+ t_srcs = [ t.get('basename') + '.c' ] + t.get('add_sources', [])
+ t_deps = [ dep_toytoolkit, t.get('dep_objs', []) ]
+ foreach depname : t.get('deps', [])
+ dep = dependency(depname, required: false)
+ if not dep.found()
+ error('@0@ requires \'@1@\' which was not found. If you rather not build this, set \'-Ddemo-clients=false\'.'.format(t_name, depname))
+ endif
+ t_deps += dep
+ endforeach
+
+ executable(
+ t_name, t_srcs,
+ include_directories: common_inc,
+ dependencies: t_deps,
+ install: true
+ )
+ endforeach
+endif
+
+if get_option('shell-desktop')
+ exe_keyboard = executable(
+ 'weston-keyboard',
+ 'keyboard.c',
+ text_input_unstable_v1_client_protocol_h,
+ text_input_unstable_v1_protocol_c,
+ input_method_unstable_v1_client_protocol_h,
+ input_method_unstable_v1_protocol_c,
+ include_directories: common_inc,
+ dependencies: dep_toytoolkit,
+ install_dir: get_option('libexecdir'),
+ install: true
+ )
+ env_modmap += 'weston-keyboard=@0@;'.format(exe_keyboard.full_path())
+
+ exe_shooter = executable(
+ 'weston-screenshooter',
+ 'screenshot.c',
+ weston_screenshooter_client_protocol_h,
+ weston_screenshooter_protocol_c,
+ include_directories: common_inc,
+ dependencies: dep_toytoolkit,
+ install_dir: get_option('bindir'),
+ install: true
+ )
+ env_modmap += 'weston-screenshooter=@0@;'.format(exe_shooter.full_path())
+
+ exe_shell_desktop = executable(
+ 'weston-desktop-shell',
+ 'desktop-shell.c',
+ weston_desktop_shell_client_protocol_h,
+ weston_desktop_shell_protocol_c,
+ include_directories: common_inc,
+ dependencies: dep_toytoolkit,
+ install_dir: get_option('libexecdir'),
+ install: true
+ )
+ env_modmap += 'weston-desktop-shell=@0@;'.format(exe_shell_desktop.full_path())
+endif
+
+
+if get_option('shell-ivi')
+ exe_shell_ivi_ui = executable(
+ 'weston-ivi-shell-user-interface',
+ 'ivi-shell-user-interface.c',
+ ivi_hmi_controller_client_protocol_h,
+ ivi_hmi_controller_protocol_c,
+ ivi_application_client_protocol_h,
+ ivi_application_protocol_c,
+ include_directories: common_inc,
+ dependencies: dep_toytoolkit,
+ install: true,
+ install_dir: get_option('libexecdir')
+ )
+ env_modmap += 'weston-ivi-shell-user-interface=@0@;'.format(exe_shell_ivi_ui.full_path())
+endif
diff --git a/clients/multi-resource.c b/clients/multi-resource.c
new file mode 100644
index 0000000000000000000000000000000000000000..b86503db962376f3296ef2fe53c8148c27c6be68
--- /dev/null
+++ b/clients/multi-resource.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ * Copyright © 2010, 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include "shared/os-compatibility.h"
+#include "shared/xalloc.h"
+#include
+
+struct device {
+ enum { KEYBOARD, POINTER } type;
+
+ int start_time;
+ int end_time;
+ struct wl_list link;
+
+ union {
+ struct wl_keyboard *keyboard;
+ struct wl_pointer *pointer;
+ } p;
+};
+
+struct display {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_shell *shell;
+ struct wl_seat *seat;
+ struct wl_shm *shm;
+ uint32_t formats;
+ struct wl_list devices;
+};
+
+struct window {
+ struct display *display;
+ int width, height;
+ struct wl_surface *surface;
+ struct wl_shell_surface *shell_surface;
+};
+
+static void
+buffer_release(void *data, struct wl_buffer *buffer)
+{
+ wl_buffer_destroy(buffer);
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+ buffer_release
+};
+
+static int
+attach_buffer(struct window *window, int width, int height)
+{
+ struct wl_shm_pool *pool;
+ struct wl_buffer *buffer;
+ int fd, size, stride;
+
+ stride = width * 4;
+ size = stride * height;
+
+ fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
+ size, strerror(errno));
+ return -1;
+ }
+
+ pool = wl_shm_create_pool(window->display->shm, fd, size);
+ buffer = wl_shm_pool_create_buffer(pool, 0,
+ width, height,
+ stride,
+ WL_SHM_FORMAT_XRGB8888);
+ wl_surface_attach(window->surface, buffer, 0, 0);
+ wl_buffer_add_listener(buffer, &buffer_listener, buffer);
+ wl_shm_pool_destroy(pool);
+ close(fd);
+
+ return 0;
+}
+
+static void
+handle_ping(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t serial)
+{
+ wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void
+handle_configure(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+}
+
+static void
+handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+ handle_ping,
+ handle_configure,
+ handle_popup_done
+};
+
+static struct window *
+create_window(struct display *display, int width, int height)
+{
+ struct window *window;
+
+ window = xzalloc(sizeof *window);
+ window->display = display;
+ window->width = width;
+ window->height = height;
+ window->surface = wl_compositor_create_surface(display->compositor);
+ window->shell_surface = wl_shell_get_shell_surface(display->shell,
+ window->surface);
+
+ if (window->shell_surface)
+ wl_shell_surface_add_listener(window->shell_surface,
+ &shell_surface_listener, window);
+
+ wl_shell_surface_set_title(window->shell_surface, "simple-shm");
+
+ wl_shell_surface_set_toplevel(window->shell_surface);
+
+ wl_surface_damage(window->surface, 0, 0, width, height);
+ attach_buffer(window, width, height);
+ wl_surface_commit(window->surface);
+
+ return window;
+}
+
+static void
+destroy_window(struct window *window)
+{
+ wl_shell_surface_destroy(window->shell_surface);
+ wl_surface_destroy(window->surface);
+ free(window);
+}
+
+static void
+shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+ struct display *d = data;
+
+ d->formats |= (1 << format);
+}
+
+struct wl_shm_listener shm_listener = {
+ shm_format
+};
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t id, const char *interface, uint32_t version)
+{
+ struct display *d = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ d->compositor =
+ wl_registry_bind(registry,
+ id, &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ d->shell = wl_registry_bind(registry,
+ id, &wl_shell_interface, 1);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ d->shm = wl_registry_bind(registry,
+ id, &wl_shm_interface, 1);
+ wl_shm_add_listener(d->shm, &shm_listener, d);
+ } else if (strcmp(interface, "wl_seat") == 0 &&
+ d->seat == NULL) {
+ d->seat = wl_registry_bind(registry,
+ id, &wl_seat_interface, 3);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static struct display *
+create_display(void)
+{
+ struct display *display;
+
+ display = xzalloc(sizeof *display);
+ display->display = wl_display_connect(NULL);
+ assert(display->display);
+
+ display->formats = 0;
+ display->registry = wl_display_get_registry(display->display);
+ wl_registry_add_listener(display->registry,
+ ®istry_listener, display);
+ wl_display_roundtrip(display->display);
+ if (display->shm == NULL) {
+ fprintf(stderr, "No wl_shm global\n");
+ exit(1);
+ }
+
+ wl_display_roundtrip(display->display);
+
+ if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
+ fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
+ exit(1);
+ }
+
+ wl_list_init(&display->devices);
+
+ return display;
+}
+
+static void
+pointer_handle_enter(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t sx_w, wl_fixed_t sy_w)
+{
+}
+
+static void
+pointer_handle_leave(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void
+pointer_handle_motion(void *data, struct wl_pointer *pointer,
+ uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
+{
+}
+
+static void
+pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+}
+
+static void
+pointer_handle_axis(void *data, struct wl_pointer *pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ pointer_handle_enter,
+ pointer_handle_leave,
+ pointer_handle_motion,
+ pointer_handle_button,
+ pointer_handle_axis,
+};
+
+static void
+keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
+ uint32_t format, int fd, uint32_t size)
+{
+ /* Just so we don’t leak the keymap fd */
+ close(fd);
+}
+
+static void
+keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface,
+ struct wl_array *keys)
+{
+}
+
+static void
+keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void
+keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key,
+ uint32_t state_w)
+{
+}
+
+static void
+keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group)
+{
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ keyboard_handle_keymap,
+ keyboard_handle_enter,
+ keyboard_handle_leave,
+ keyboard_handle_key,
+ keyboard_handle_modifiers,
+};
+
+static void
+start_device(struct display *display, struct device *device)
+{
+ if (display->seat == NULL)
+ return;
+
+ switch (device->type) {
+ case KEYBOARD:
+ if (device->p.keyboard == NULL) {
+ device->p.keyboard =
+ wl_seat_get_keyboard(display->seat);
+ wl_keyboard_add_listener(device->p.keyboard,
+ &keyboard_listener,
+ NULL);
+ }
+ break;
+ case POINTER:
+ if (device->p.pointer == NULL) {
+ device->p.pointer =
+ wl_seat_get_pointer(display->seat);
+ wl_pointer_add_listener(device->p.pointer,
+ &pointer_listener,
+ NULL);
+ }
+ break;
+ }
+}
+
+static void
+destroy_device(struct device *device)
+{
+ switch (device->type) {
+ case KEYBOARD:
+ if (device->p.keyboard)
+ wl_keyboard_release(device->p.keyboard);
+ break;
+ case POINTER:
+ if (device->p.pointer)
+ wl_pointer_release(device->p.pointer);
+ break;
+ }
+
+ wl_list_remove(&device->link);
+ free(device);
+}
+
+static void
+destroy_devices(struct display *display)
+{
+ struct device *device, *tmp;
+
+ wl_list_for_each_safe(device, tmp, &display->devices, link)
+ destroy_device(device);
+}
+
+static void
+destroy_display(struct display *display)
+{
+ destroy_devices(display);
+
+ if (display->shm)
+ wl_shm_destroy(display->shm);
+
+ if (display->shell)
+ wl_shell_destroy(display->shell);
+
+ if (display->seat)
+ wl_seat_destroy(display->seat);
+
+ if (display->compositor)
+ wl_compositor_destroy(display->compositor);
+
+ wl_registry_destroy(display->registry);
+ wl_display_flush(display->display);
+ wl_display_disconnect(display->display);
+ free(display);
+}
+
+static int running = 1;
+
+static void
+signal_int(int signum)
+{
+ running = 0;
+}
+
+static int
+create_device(struct display *display, const char *time_desc, int type)
+{
+ int start_time;
+ int end_time = -1;
+ char *tail;
+ struct device *device;
+
+ if (time_desc == NULL) {
+ fprintf(stderr, "missing time description\n");
+ return -1;
+ }
+
+ errno = 0;
+ start_time = strtoul(time_desc, &tail, 10);
+ if (errno || tail == time_desc)
+ goto error;
+
+ if (*tail == ':') {
+ time_desc = tail + 1;
+ end_time = strtoul(time_desc, &tail, 10);
+ if (errno || tail == time_desc || *tail != '\0')
+ goto error;
+ } else if (*tail != '\0') {
+ goto error;
+ }
+
+ device = xzalloc(sizeof *device);
+ device->type = type;
+ device->start_time = start_time;
+ device->end_time = end_time;
+ wl_list_insert(&display->devices, &device->link);
+
+ return 0;
+
+error:
+ fprintf(stderr, "invalid time description\n");
+ return -1;
+}
+
+static struct timespec begin_time;
+
+static void
+reset_timer(void)
+{
+ clock_gettime(CLOCK_MONOTONIC, &begin_time);
+}
+
+static double
+read_timer(void)
+{
+ struct timespec t;
+
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return (double)(t.tv_sec - begin_time.tv_sec) +
+ 1e-9 * (t.tv_nsec - begin_time.tv_nsec);
+}
+
+static void
+main_loop(struct display *display)
+{
+ reset_timer();
+
+ while (running) {
+ struct device *device, *tmp;
+ struct pollfd fds[1];
+ double sleep_time = DBL_MAX;
+ double now;
+
+ if (wl_display_dispatch_pending(display->display) == -1)
+ break;
+ if (wl_display_flush(display->display) == -1)
+ break;
+
+ now = read_timer();
+
+ wl_list_for_each(device, &display->devices, link) {
+ double next_time = device->start_time - now;
+ if (next_time < 0.0) {
+ sleep_time = 0.0;
+ break;
+ } else if (next_time < sleep_time) {
+ sleep_time = next_time;
+ }
+ next_time = device->end_time - now;
+ if (next_time < 0.0) {
+ sleep_time = 0.0;
+ break;
+ } else if (next_time < sleep_time) {
+ sleep_time = next_time;
+ }
+ }
+
+ fds[0].fd = wl_display_get_fd(display->display);
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+
+ poll(fds,
+ sizeof fds / sizeof fds[0],
+ sleep_time == DBL_MAX ? -1 : ceil(sleep_time * 1000.0));
+
+ if (fds[0].revents &&
+ wl_display_dispatch(display->display) == -1)
+ break;
+
+ now = read_timer();
+
+ wl_list_for_each_safe(device, tmp, &display->devices, link) {
+ if (device->start_time <= now)
+ start_device(display, device);
+ if (device->end_time >= 0 && device->end_time <= now)
+ destroy_device(device);
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sigaction sigint;
+ struct display *display;
+ struct window *window;
+ int i;
+
+ display = create_display();
+ window = create_window(display, 250, 250);
+
+ for (i = 1; i < argc; i++) {
+ if (!strncmp(argv[i], "-p", 2)) {
+ char *arg;
+ if (argv[i][2]) {
+ arg = argv[i] + 2;
+ } else {
+ arg = argv[i + 1];
+ i++;
+ }
+ if (create_device(display, arg, POINTER) == -1)
+ return 1;
+ } else if (!strncmp(argv[i], "-k", 2)) {
+ char *arg;
+ if (argv[i][2]) {
+ arg = argv[i] + 2;
+ } else {
+ arg = argv[i + 1];
+ i++;
+ }
+ if (create_device(display, arg, KEYBOARD) == -1)
+ return 1;
+ } else {
+ fprintf(stderr, "unknown argument %s\n", argv[i]);
+ return 1;
+ }
+ }
+
+ sigint.sa_handler = signal_int;
+ sigemptyset(&sigint.sa_mask);
+ sigint.sa_flags = SA_RESETHAND;
+ sigaction(SIGINT, &sigint, NULL);
+
+ main_loop(display);
+
+ fprintf(stderr, "multi-resource exiting\n");
+ destroy_window(window);
+ destroy_display(display);
+
+ return 0;
+}
diff --git a/clients/nested-client.c b/clients/nested-client.c
new file mode 100644
index 0000000000000000000000000000000000000000..a9e034efbb629fddfed0a5268b928801cef5373f
--- /dev/null
+++ b/clients/nested-client.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include "shared/platform.h"
+
+struct window;
+struct seat;
+
+struct nested_client {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+ EGLConfig egl_config;
+ EGLSurface egl_surface;
+ struct program *color_program;
+
+ GLuint vert, frag, program;
+ GLuint rotation;
+ GLuint pos;
+ GLuint col;
+
+ struct wl_surface *surface;
+ struct wl_egl_window *native;
+ int width, height;
+};
+
+#define POS 0
+#define COL 1
+
+static GLuint
+create_shader(const char *source, GLenum shader_type)
+{
+ GLuint shader;
+ GLint status;
+
+ shader = glCreateShader(shader_type);
+ if (shader == 0)
+ return 0;
+
+ glShaderSource(shader, 1, (const char **) &source, NULL);
+ glCompileShader(shader);
+
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (!status) {
+ char log[1000];
+ GLsizei len;
+ glGetShaderInfoLog(shader, 1000, &len, log);
+ fprintf(stderr, "Error: compiling %s: %.*s\n",
+ shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
+ len, log);
+ return 0;
+ }
+
+ return shader;
+}
+
+static void
+create_program(struct nested_client *client,
+ const char *vert, const char *frag)
+{
+ GLint status;
+
+ client->vert = create_shader(vert, GL_VERTEX_SHADER);
+ client->frag = create_shader(frag, GL_FRAGMENT_SHADER);
+
+ client->program = glCreateProgram();
+ glAttachShader(client->program, client->frag);
+ glAttachShader(client->program, client->vert);
+ glBindAttribLocation(client->program, POS, "pos");
+ glBindAttribLocation(client->program, COL, "color");
+ glLinkProgram(client->program);
+
+ glGetProgramiv(client->program, GL_LINK_STATUS, &status);
+ if (!status) {
+ char log[1000];
+ GLsizei len;
+ glGetProgramInfoLog(client->program, 1000, &len, log);
+ fprintf(stderr, "Error: linking:\n%.*s\n", len, log);
+ exit(1);
+ }
+
+ client->rotation =
+ glGetUniformLocation(client->program, "rotation");
+}
+
+static const char vertex_shader_text[] =
+ "uniform mat4 rotation;\n"
+ "attribute vec4 pos;\n"
+ "attribute vec4 color;\n"
+ "varying vec4 v_color;\n"
+ "void main() {\n"
+ " gl_Position = rotation * pos;\n"
+ " v_color = color;\n"
+ "}\n";
+
+static const char color_fragment_shader_text[] =
+ "precision mediump float;\n"
+ "varying vec4 v_color;\n"
+ "void main() {\n"
+ " gl_FragColor = v_color;\n"
+ "}\n";
+
+static void
+render_triangle(struct nested_client *client, uint32_t time)
+{
+ static const GLfloat verts[3][2] = {
+ { -0.5, -0.5 },
+ { 0.5, -0.5 },
+ { 0, 0.5 }
+ };
+ static const GLfloat colors[3][3] = {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 }
+ };
+ GLfloat angle;
+ GLfloat rotation[4][4] = {
+ { 1, 0, 0, 0 },
+ { 0, 1, 0, 0 },
+ { 0, 0, 1, 0 },
+ { 0, 0, 0, 1 }
+ };
+ static const int32_t speed_div = 5;
+ static uint32_t start_time = 0;
+
+ if (client->program == 0)
+ create_program(client, vertex_shader_text,
+ color_fragment_shader_text);
+
+ if (start_time == 0)
+ start_time = time;
+
+ angle = ((time - start_time) / speed_div) % 360 * M_PI / 180.0;
+ rotation[0][0] = cos(angle);
+ rotation[0][2] = sin(angle);
+ rotation[2][0] = -sin(angle);
+ rotation[2][2] = cos(angle);
+
+ glClearColor(0.4, 0.4, 0.4, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glUseProgram(client->program);
+
+ glViewport(0, 0, client->width, client->height);
+
+ glUniformMatrix4fv(client->rotation, 1, GL_FALSE,
+ (GLfloat *) rotation);
+
+ glVertexAttribPointer(POS, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glVertexAttribPointer(COL, 3, GL_FLOAT, GL_FALSE, 0, colors);
+ glEnableVertexAttribArray(POS);
+ glEnableVertexAttribArray(COL);
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ glDisableVertexAttribArray(POS);
+ glDisableVertexAttribArray(COL);
+
+ glFlush();
+}
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time);
+
+static const struct wl_callback_listener frame_listener = {
+ frame_callback
+};
+
+static void
+frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct nested_client *client = data;
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ callback = wl_surface_frame(client->surface);
+ wl_callback_add_listener(callback, &frame_listener, client);
+
+ render_triangle(client, time);
+
+ eglSwapBuffers(client->egl_display, client->egl_surface);
+}
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version)
+{
+ struct nested_client *client = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ client->compositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static struct nested_client *
+nested_client_create(void)
+{
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ static const EGLint config_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 1,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ EGLint major, minor, n;
+ EGLBoolean ret;
+
+ struct nested_client *client;
+
+ client = malloc(sizeof *client);
+ if (client == NULL)
+ return NULL;
+
+ client->width = 250;
+ client->height = 250;
+
+ client->display = wl_display_connect(NULL);
+
+ client->registry = wl_display_get_registry(client->display);
+ wl_registry_add_listener(client->registry,
+ ®istry_listener, client);
+
+ /* get globals */
+ wl_display_roundtrip(client->display);
+
+ client->egl_display =
+ weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
+ client->display, NULL);
+ if (client->egl_display == NULL)
+ return NULL;
+
+ ret = eglInitialize(client->egl_display, &major, &minor);
+ if (!ret)
+ return NULL;
+ ret = eglBindAPI(EGL_OPENGL_ES_API);
+ if (!ret)
+ return NULL;
+
+ ret = eglChooseConfig(client->egl_display, config_attribs,
+ &client->egl_config, 1, &n);
+ if (!ret || n != 1)
+ return NULL;
+
+ client->egl_context = eglCreateContext(client->egl_display,
+ client->egl_config,
+ EGL_NO_CONTEXT,
+ context_attribs);
+ if (!client->egl_context)
+ return NULL;
+
+ client->surface = wl_compositor_create_surface(client->compositor);
+
+ client->native = wl_egl_window_create(client->surface,
+ client->width, client->height);
+
+ client->egl_surface = weston_platform_create_egl_surface(client->egl_display,
+ client->egl_config,
+ client->native, NULL);
+
+ eglMakeCurrent(client->egl_display, client->egl_surface,
+ client->egl_surface, client->egl_context);
+
+ wl_egl_window_resize(client->native,
+ client->width, client->height, 0, 0);
+
+ frame_callback(client, NULL, 0);
+
+ return client;
+}
+
+static void
+nested_client_destroy(struct nested_client *client)
+{
+ eglMakeCurrent(client->egl_display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+
+ weston_platform_destroy_egl_surface(client->egl_display,
+ client->egl_surface);
+ wl_egl_window_destroy(client->native);
+
+ wl_surface_destroy(client->surface);
+
+ if (client->compositor)
+ wl_compositor_destroy(client->compositor);
+
+ wl_registry_destroy(client->registry);
+ eglTerminate(client->egl_display);
+ eglReleaseThread();
+ wl_display_flush(client->display);
+ wl_display_disconnect(client->display);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct nested_client *client;
+ int ret = 0;
+
+ if (getenv("WAYLAND_SOCKET") == NULL) {
+ fprintf(stderr,
+ "must be run by nested, don't run standalone\n");
+ return EXIT_FAILURE;
+ }
+
+ client = nested_client_create();
+ if (client == NULL)
+ return EXIT_FAILURE;
+
+ while (ret != -1)
+ ret = wl_display_dispatch(client->display);
+
+ nested_client_destroy(client);
+
+ return 0;
+}
diff --git a/clients/nested.c b/clients/nested.c
new file mode 100644
index 0000000000000000000000000000000000000000..4bbaa27ea21fc7a1d96936e582f028e39f36eeba
--- /dev/null
+++ b/clients/nested.c
@@ -0,0 +1,1137 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#define WL_HIDE_DEPRECATED
+#include
+
+#include "shared/helpers.h"
+#include "shared/xalloc.h"
+#include "window.h"
+
+#include "shared/weston-egl-ext.h"
+
+
+static bool option_blit;
+
+struct nested {
+ struct display *display;
+ struct window *window;
+ struct widget *widget;
+ struct wl_display *child_display;
+ struct task child_task;
+
+ EGLDisplay egl_display;
+ struct program *texture_program;
+
+ struct wl_list surface_list;
+
+ const struct nested_renderer *renderer;
+};
+
+struct nested_region {
+ struct wl_resource *resource;
+ pixman_region32_t region;
+};
+
+struct nested_buffer_reference {
+ struct nested_buffer *buffer;
+ struct wl_listener destroy_listener;
+};
+
+struct nested_buffer {
+ struct wl_resource *resource;
+ struct wl_signal destroy_signal;
+ struct wl_listener destroy_listener;
+ uint32_t busy_count;
+
+ /* A buffer in the parent compositor representing the same
+ * data. This is created on-demand when the subsurface
+ * renderer is used */
+ struct wl_buffer *parent_buffer;
+ /* This reference is used to mark when the parent buffer has
+ * been attached to the subsurface. It will be unrefenced when
+ * we receive a buffer release event. That way we won't inform
+ * the client that the buffer is free until the parent
+ * compositor is also finished with it */
+ struct nested_buffer_reference parent_ref;
+};
+
+struct nested_surface {
+ struct wl_resource *resource;
+ struct nested *nested;
+ EGLImageKHR *image;
+ struct wl_list link;
+
+ struct wl_list frame_callback_list;
+
+ struct {
+ /* wl_surface.attach */
+ int newly_attached;
+ struct nested_buffer *buffer;
+ struct wl_listener buffer_destroy_listener;
+
+ /* wl_surface.frame */
+ struct wl_list frame_callback_list;
+
+ /* wl_surface.damage */
+ pixman_region32_t damage;
+ } pending;
+
+ void *renderer_data;
+};
+
+/* Data used for the blit renderer */
+struct nested_blit_surface {
+ struct nested_buffer_reference buffer_ref;
+ GLuint texture;
+ cairo_surface_t *cairo_surface;
+};
+
+/* Data used for the subsurface renderer */
+struct nested_ss_surface {
+ struct widget *widget;
+ struct wl_surface *surface;
+ struct wl_subsurface *subsurface;
+ struct wl_callback *frame_callback;
+};
+
+struct nested_frame_callback {
+ struct wl_resource *resource;
+ struct wl_list link;
+};
+
+struct nested_renderer {
+ void (* surface_init)(struct nested_surface *surface);
+ void (* surface_fini)(struct nested_surface *surface);
+ void (* render_clients)(struct nested *nested, cairo_t *cr);
+ void (* surface_attach)(struct nested_surface *surface,
+ struct nested_buffer *buffer);
+};
+
+static const struct weston_option nested_options[] = {
+ { WESTON_OPTION_BOOLEAN, "blit", 'b', &option_blit },
+};
+
+static const struct nested_renderer nested_blit_renderer;
+static const struct nested_renderer nested_ss_renderer;
+
+static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
+static PFNEGLCREATEIMAGEKHRPROC create_image;
+static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
+static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
+static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
+static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
+static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_wayland_buffer_from_image;
+
+static void
+nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
+{
+ struct nested_buffer *buffer =
+ container_of(listener, struct nested_buffer, destroy_listener);
+
+ wl_signal_emit(&buffer->destroy_signal, buffer);
+
+ if (buffer->parent_buffer)
+ wl_buffer_destroy(buffer->parent_buffer);
+
+ free(buffer);
+}
+
+static struct nested_buffer *
+nested_buffer_from_resource(struct wl_resource *resource)
+{
+ struct nested_buffer *buffer;
+ struct wl_listener *listener;
+
+ listener =
+ wl_resource_get_destroy_listener(resource,
+ nested_buffer_destroy_handler);
+
+ if (listener)
+ return container_of(listener, struct nested_buffer,
+ destroy_listener);
+
+ buffer = zalloc(sizeof *buffer);
+ if (buffer == NULL)
+ return NULL;
+
+ buffer->resource = resource;
+ wl_signal_init(&buffer->destroy_signal);
+ buffer->destroy_listener.notify = nested_buffer_destroy_handler;
+ wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
+
+ return buffer;
+}
+
+static void
+nested_buffer_reference_handle_destroy(struct wl_listener *listener,
+ void *data)
+{
+ struct nested_buffer_reference *ref =
+ container_of(listener, struct nested_buffer_reference,
+ destroy_listener);
+
+ assert((struct nested_buffer *)data == ref->buffer);
+ ref->buffer = NULL;
+}
+
+static void
+nested_buffer_reference(struct nested_buffer_reference *ref,
+ struct nested_buffer *buffer)
+{
+ if (buffer == ref->buffer)
+ return;
+
+ if (ref->buffer) {
+ ref->buffer->busy_count--;
+ if (ref->buffer->busy_count == 0) {
+ assert(wl_resource_get_client(ref->buffer->resource));
+ wl_buffer_send_release(ref->buffer->resource);
+ }
+ wl_list_remove(&ref->destroy_listener.link);
+ }
+
+ if (buffer) {
+ buffer->busy_count++;
+ wl_signal_add(&buffer->destroy_signal,
+ &ref->destroy_listener);
+
+ ref->destroy_listener.notify =
+ nested_buffer_reference_handle_destroy;
+ }
+
+ ref->buffer = buffer;
+}
+
+static void
+flush_surface_frame_callback_list(struct nested_surface *surface,
+ uint32_t time)
+{
+ struct nested_frame_callback *nc, *next;
+
+ wl_list_for_each_safe(nc, next, &surface->frame_callback_list, link) {
+ wl_callback_send_done(nc->resource, time);
+ wl_resource_destroy(nc->resource);
+ }
+ wl_list_init(&surface->frame_callback_list);
+
+ /* FIXME: toytoolkit need a pre-block handler where we can
+ * call this. */
+ wl_display_flush_clients(surface->nested->child_display);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct nested *nested = data;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ struct rectangle allocation;
+
+ widget_get_allocation(nested->widget, &allocation);
+
+ surface = window_get_surface(nested->window);
+
+ cr = cairo_create(surface);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle(cr,
+ allocation.x,
+ allocation.y,
+ allocation.width,
+ allocation.height);
+ cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+ cairo_fill(cr);
+
+ nested->renderer->render_clients(nested, cr);
+
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct nested *nested = data;
+
+ window_schedule_redraw(nested->window);
+}
+
+static void
+handle_child_data(struct task *task, uint32_t events)
+{
+ struct nested *nested = container_of(task, struct nested, child_task);
+ struct wl_event_loop *loop;
+
+ loop = wl_display_get_event_loop(nested->child_display);
+
+ wl_event_loop_dispatch(loop, -1);
+ wl_display_flush_clients(nested->child_display);
+}
+
+struct nested_client {
+ struct wl_client *client;
+ pid_t pid;
+};
+
+static struct nested_client *
+launch_client(struct nested *nested, const char *path)
+{
+ int sv[2];
+ pid_t pid;
+ struct nested_client *client;
+
+ client = malloc(sizeof *client);
+ if (client == NULL)
+ return NULL;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
+ fprintf(stderr, "launch_client: "
+ "socketpair failed while launching '%s': %s\n",
+ path, strerror(errno));
+ free(client);
+ return NULL;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ close(sv[0]);
+ close(sv[1]);
+ free(client);
+ fprintf(stderr, "launch_client: "
+ "fork failed while launching '%s': %s\n", path,
+ strerror(errno));
+ return NULL;
+ }
+
+ if (pid == 0) {
+ int clientfd;
+ char s[32];
+
+ /* SOCK_CLOEXEC closes both ends, so we dup the fd to
+ * get a non-CLOEXEC fd to pass through exec. */
+ clientfd = dup(sv[1]);
+ if (clientfd == -1) {
+ fprintf(stderr, "compositor: dup failed: %s\n",
+ strerror(errno));
+ exit(-1);
+ }
+
+ snprintf(s, sizeof s, "%d", clientfd);
+ setenv("WAYLAND_SOCKET", s, 1);
+
+ execl(path, path, NULL);
+
+ fprintf(stderr, "compositor: executing '%s' failed: %s\n",
+ path, strerror(errno));
+ exit(-1);
+ }
+
+ close(sv[1]);
+
+ client->client = wl_client_create(nested->child_display, sv[0]);
+ if (!client->client) {
+ close(sv[0]);
+ free(client);
+ fprintf(stderr, "launch_client: "
+ "wl_client_create failed while launching '%s'.\n",
+ path);
+ return NULL;
+ }
+
+ client->pid = pid;
+
+ return client;
+}
+
+static void
+destroy_surface(struct wl_resource *resource)
+{
+ struct nested_surface *surface = wl_resource_get_user_data(resource);
+ struct nested *nested = surface->nested;
+ struct nested_frame_callback *cb, *next;
+
+ wl_list_for_each_safe(cb, next,
+ &surface->frame_callback_list, link)
+ wl_resource_destroy(cb->resource);
+
+ wl_list_for_each_safe(cb, next,
+ &surface->pending.frame_callback_list, link)
+ wl_resource_destroy(cb->resource);
+
+ pixman_region32_fini(&surface->pending.damage);
+
+ nested->renderer->surface_fini(surface);
+
+ wl_list_remove(&surface->link);
+
+ free(surface);
+}
+
+static void
+surface_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+surface_attach(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
+{
+ struct nested_surface *surface = wl_resource_get_user_data(resource);
+ struct nested *nested = surface->nested;
+ struct nested_buffer *buffer = NULL;
+
+ if (buffer_resource) {
+ int format;
+
+ if (!query_buffer(nested->egl_display, (void *) buffer_resource,
+ EGL_TEXTURE_FORMAT, &format)) {
+ wl_resource_post_error(buffer_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "attaching non-egl wl_buffer");
+ return;
+ }
+
+ switch (format) {
+ case EGL_TEXTURE_RGB:
+ case EGL_TEXTURE_RGBA:
+ break;
+ default:
+ wl_resource_post_error(buffer_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "invalid format");
+ return;
+ }
+
+ buffer = nested_buffer_from_resource(buffer_resource);
+ if (buffer == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ }
+
+ if (surface->pending.buffer)
+ wl_list_remove(&surface->pending.buffer_destroy_listener.link);
+
+ surface->pending.buffer = buffer;
+ surface->pending.newly_attached = 1;
+ if (buffer) {
+ wl_signal_add(&buffer->destroy_signal,
+ &surface->pending.buffer_destroy_listener);
+ }
+}
+
+static void
+nested_surface_attach(struct nested_surface *surface,
+ struct nested_buffer *buffer)
+{
+ struct nested *nested = surface->nested;
+
+ if (surface->image != EGL_NO_IMAGE_KHR)
+ destroy_image(nested->egl_display, surface->image);
+
+ surface->image = create_image(nested->egl_display, NULL,
+ EGL_WAYLAND_BUFFER_WL, buffer->resource,
+ NULL);
+ if (surface->image == EGL_NO_IMAGE_KHR) {
+ fprintf(stderr, "failed to create img\n");
+ return;
+ }
+
+ nested->renderer->surface_attach(surface, buffer);
+}
+
+static void
+surface_damage(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct nested_surface *surface = wl_resource_get_user_data(resource);
+
+ pixman_region32_union_rect(&surface->pending.damage,
+ &surface->pending.damage,
+ x, y, width, height);
+}
+
+static void
+destroy_frame_callback(struct wl_resource *resource)
+{
+ struct nested_frame_callback *callback = wl_resource_get_user_data(resource);
+
+ wl_list_remove(&callback->link);
+ free(callback);
+}
+
+static void
+surface_frame(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct nested_frame_callback *callback;
+ struct nested_surface *surface = wl_resource_get_user_data(resource);
+
+ callback = malloc(sizeof *callback);
+ if (callback == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ callback->resource = wl_resource_create(client,
+ &wl_callback_interface, 1, id);
+ wl_resource_set_implementation(callback->resource, NULL, callback,
+ destroy_frame_callback);
+
+ wl_list_insert(surface->pending.frame_callback_list.prev,
+ &callback->link);
+}
+
+static void
+surface_set_opaque_region(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region_resource)
+{
+ fprintf(stderr, "surface_set_opaque_region\n");
+}
+
+static void
+surface_set_input_region(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region_resource)
+{
+ fprintf(stderr, "surface_set_input_region\n");
+}
+
+static void
+surface_commit(struct wl_client *client, struct wl_resource *resource)
+{
+ struct nested_surface *surface = wl_resource_get_user_data(resource);
+ struct nested *nested = surface->nested;
+
+ /* wl_surface.attach */
+ if (surface->pending.newly_attached)
+ nested_surface_attach(surface, surface->pending.buffer);
+
+ if (surface->pending.buffer) {
+ wl_list_remove(&surface->pending.buffer_destroy_listener.link);
+ surface->pending.buffer = NULL;
+ }
+ surface->pending.newly_attached = 0;
+
+ /* wl_surface.damage */
+ pixman_region32_clear(&surface->pending.damage);
+
+ /* wl_surface.frame */
+ wl_list_insert_list(&surface->frame_callback_list,
+ &surface->pending.frame_callback_list);
+ wl_list_init(&surface->pending.frame_callback_list);
+
+ /* FIXME: For the subsurface renderer we don't need to
+ * actually redraw the window. However we do want to cause a
+ * commit because the subsurface is synchronized. Ideally we
+ * would just queue the commit */
+ window_schedule_redraw(nested->window);
+}
+
+static void
+surface_set_buffer_transform(struct wl_client *client,
+ struct wl_resource *resource, int transform)
+{
+ fprintf(stderr, "surface_set_buffer_transform\n");
+}
+
+static const struct wl_surface_interface surface_interface = {
+ surface_destroy,
+ surface_attach,
+ surface_damage,
+ surface_frame,
+ surface_set_opaque_region,
+ surface_set_input_region,
+ surface_commit,
+ surface_set_buffer_transform
+};
+
+static void
+surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
+{
+ struct nested_surface *surface =
+ container_of(listener, struct nested_surface,
+ pending.buffer_destroy_listener);
+
+ surface->pending.buffer = NULL;
+}
+
+static void
+compositor_create_surface(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct nested *nested = wl_resource_get_user_data(resource);
+ struct nested_surface *surface;
+
+ surface = zalloc(sizeof *surface);
+ if (surface == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ surface->nested = nested;
+
+ wl_list_init(&surface->frame_callback_list);
+
+ wl_list_init(&surface->pending.frame_callback_list);
+ surface->pending.buffer_destroy_listener.notify =
+ surface_handle_pending_buffer_destroy;
+ pixman_region32_init(&surface->pending.damage);
+
+ display_acquire_window_surface(nested->display,
+ nested->window, NULL);
+
+ nested->renderer->surface_init(surface);
+
+ display_release_window_surface(nested->display, nested->window);
+
+ surface->resource =
+ wl_resource_create(client, &wl_surface_interface, 1, id);
+
+ wl_resource_set_implementation(surface->resource,
+ &surface_interface, surface,
+ destroy_surface);
+
+ wl_list_insert(nested->surface_list.prev, &surface->link);
+}
+
+static void
+destroy_region(struct wl_resource *resource)
+{
+ struct nested_region *region = wl_resource_get_user_data(resource);
+
+ pixman_region32_fini(®ion->region);
+ free(region);
+}
+
+static void
+region_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+region_add(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct nested_region *region = wl_resource_get_user_data(resource);
+
+ pixman_region32_union_rect(®ion->region, ®ion->region,
+ x, y, width, height);
+}
+
+static void
+region_subtract(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct nested_region *region = wl_resource_get_user_data(resource);
+ pixman_region32_t rect;
+
+ pixman_region32_init_rect(&rect, x, y, width, height);
+ pixman_region32_subtract(®ion->region, ®ion->region, &rect);
+ pixman_region32_fini(&rect);
+}
+
+static const struct wl_region_interface region_interface = {
+ region_destroy,
+ region_add,
+ region_subtract
+};
+
+static void
+compositor_create_region(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct nested_region *region;
+
+ region = malloc(sizeof *region);
+ if (region == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ pixman_region32_init(®ion->region);
+
+ region->resource =
+ wl_resource_create(client, &wl_region_interface, 1, id);
+ wl_resource_set_implementation(region->resource, ®ion_interface,
+ region, destroy_region);
+}
+
+static const struct wl_compositor_interface compositor_interface = {
+ compositor_create_surface,
+ compositor_create_region
+};
+
+static void
+compositor_bind(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct nested *nested = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wl_compositor_interface,
+ MIN(version, 3), id);
+ wl_resource_set_implementation(resource, &compositor_interface,
+ nested, NULL);
+}
+
+static int
+nested_init_compositor(struct nested *nested)
+{
+ const char *extensions;
+ struct wl_event_loop *loop;
+ int use_ss_renderer = 0;
+ int fd, ret;
+
+ wl_list_init(&nested->surface_list);
+ nested->child_display = wl_display_create();
+ loop = wl_display_get_event_loop(nested->child_display);
+ fd = wl_event_loop_get_fd(loop);
+ nested->child_task.run = handle_child_data;
+ display_watch_fd(nested->display, fd,
+ EPOLLIN, &nested->child_task);
+
+ if (!wl_global_create(nested->child_display,
+ &wl_compositor_interface, 1,
+ nested, compositor_bind))
+ return -1;
+
+ wl_display_init_shm(nested->child_display);
+
+ nested->egl_display = display_get_egl_display(nested->display);
+ extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS);
+ if (!weston_check_egl_extension(extensions, "EGL_WL_bind_wayland_display")) {
+ fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n");
+ return -1;
+ }
+
+ bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
+ unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
+ create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
+ destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
+ query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
+ image_target_texture_2d =
+ (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
+
+ ret = bind_display(nested->egl_display, nested->child_display);
+ if (!ret) {
+ fprintf(stderr, "failed to bind wl_display\n");
+ return -1;
+ }
+
+ if (display_has_subcompositor(nested->display)) {
+ const char *func = "eglCreateWaylandBufferFromImageWL";
+ const char *ext = "EGL_WL_create_wayland_buffer_from_image";
+
+ if (weston_check_egl_extension(extensions, ext)) {
+ create_wayland_buffer_from_image =
+ (void *) eglGetProcAddress(func);
+ use_ss_renderer = 1;
+ }
+ }
+
+ if (option_blit)
+ use_ss_renderer = 0;
+
+ if (use_ss_renderer) {
+ printf("Using subsurfaces to render client surfaces\n");
+ nested->renderer = &nested_ss_renderer;
+ } else {
+ printf("Using local compositing with blits to "
+ "render client surfaces\n");
+ nested->renderer = &nested_blit_renderer;
+ }
+
+ return 0;
+}
+
+static struct nested *
+nested_create(struct display *display)
+{
+ struct nested *nested;
+
+ nested = zalloc(sizeof *nested);
+ if (nested == NULL)
+ return nested;
+
+ nested->window = window_create(display);
+ nested->widget = window_frame_create(nested->window, nested);
+ window_set_title(nested->window, "Wayland Nested");
+ nested->display = display;
+
+ window_set_user_data(nested->window, nested);
+ widget_set_redraw_handler(nested->widget, redraw_handler);
+ window_set_keyboard_focus_handler(nested->window,
+ keyboard_focus_handler);
+
+ nested_init_compositor(nested);
+
+ widget_schedule_resize(nested->widget, 400, 400);
+
+ return nested;
+}
+
+static void
+nested_destroy(struct nested *nested)
+{
+ widget_destroy(nested->widget);
+ window_destroy(nested->window);
+ free(nested);
+}
+
+/*** blit renderer ***/
+
+static void
+blit_surface_init(struct nested_surface *surface)
+{
+ struct nested_blit_surface *blit_surface =
+ xzalloc(sizeof *blit_surface);
+
+ glGenTextures(1, &blit_surface->texture);
+ glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
+ 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_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ surface->renderer_data = blit_surface;
+}
+
+static void
+blit_surface_fini(struct nested_surface *surface)
+{
+ struct nested_blit_surface *blit_surface = surface->renderer_data;
+
+ nested_buffer_reference(&blit_surface->buffer_ref, NULL);
+
+ glDeleteTextures(1, &blit_surface->texture);
+
+ free(blit_surface);
+}
+
+static void
+blit_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct nested *nested = data;
+ struct nested_surface *surface;
+
+ wl_list_for_each(surface, &nested->surface_list, link)
+ flush_surface_frame_callback_list(surface, time);
+
+ if (callback)
+ wl_callback_destroy(callback);
+}
+
+static const struct wl_callback_listener blit_frame_listener = {
+ blit_frame_callback
+};
+
+static void
+blit_render_clients(struct nested *nested,
+ cairo_t *cr)
+{
+ struct nested_surface *s;
+ struct rectangle allocation;
+ struct wl_callback *callback;
+
+ widget_get_allocation(nested->widget, &allocation);
+
+ wl_list_for_each(s, &nested->surface_list, link) {
+ struct nested_blit_surface *blit_surface = s->renderer_data;
+
+ display_acquire_window_surface(nested->display,
+ nested->window, NULL);
+
+ glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
+ image_target_texture_2d(GL_TEXTURE_2D, s->image);
+
+ display_release_window_surface(nested->display,
+ nested->window);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_surface(cr, blit_surface->cairo_surface,
+ allocation.x + 10,
+ allocation.y + 10);
+ cairo_rectangle(cr, allocation.x + 10,
+ allocation.y + 10,
+ allocation.width - 10,
+ allocation.height - 10);
+
+ cairo_fill(cr);
+ }
+
+ callback = wl_surface_frame(window_get_wl_surface(nested->window));
+ wl_callback_add_listener(callback, &blit_frame_listener, nested);
+}
+
+static void
+blit_surface_attach(struct nested_surface *surface,
+ struct nested_buffer *buffer)
+{
+ struct nested *nested = surface->nested;
+ struct nested_blit_surface *blit_surface = surface->renderer_data;
+ EGLint width, height;
+ cairo_device_t *device;
+
+ nested_buffer_reference(&blit_surface->buffer_ref, buffer);
+
+ if (blit_surface->cairo_surface)
+ cairo_surface_destroy(blit_surface->cairo_surface);
+
+ query_buffer(nested->egl_display, (void *) buffer->resource,
+ EGL_WIDTH, &width);
+ query_buffer(nested->egl_display, (void *) buffer->resource,
+ EGL_HEIGHT, &height);
+
+ device = display_get_cairo_device(nested->display);
+ blit_surface->cairo_surface =
+ cairo_gl_surface_create_for_texture(device,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ blit_surface->texture,
+ width, height);
+}
+
+static const struct nested_renderer
+nested_blit_renderer = {
+ .surface_init = blit_surface_init,
+ .surface_fini = blit_surface_fini,
+ .render_clients = blit_render_clients,
+ .surface_attach = blit_surface_attach
+};
+
+/*** subsurface renderer ***/
+
+static void
+ss_surface_init(struct nested_surface *surface)
+{
+ struct nested *nested = surface->nested;
+ struct wl_compositor *compositor =
+ display_get_compositor(nested->display);
+ struct nested_ss_surface *ss_surface =
+ xzalloc(sizeof *ss_surface);
+ struct rectangle allocation;
+ struct wl_region *region;
+
+ ss_surface->widget =
+ window_add_subsurface(nested->window,
+ nested,
+ SUBSURFACE_SYNCHRONIZED);
+
+ widget_set_use_cairo(ss_surface->widget, 0);
+
+ ss_surface->surface = widget_get_wl_surface(ss_surface->widget);
+ ss_surface->subsurface = widget_get_wl_subsurface(ss_surface->widget);
+
+ /* The toy toolkit gets confused about the pointer position
+ * when it gets motion events for a subsurface so we'll just
+ * disable input on it */
+ region = wl_compositor_create_region(compositor);
+ wl_surface_set_input_region(ss_surface->surface, region);
+ wl_region_destroy(region);
+
+ widget_get_allocation(nested->widget, &allocation);
+ wl_subsurface_set_position(ss_surface->subsurface,
+ allocation.x + 10,
+ allocation.y + 10);
+
+ surface->renderer_data = ss_surface;
+}
+
+static void
+ss_surface_fini(struct nested_surface *surface)
+{
+ struct nested_ss_surface *ss_surface = surface->renderer_data;
+
+ widget_destroy(ss_surface->widget);
+
+ if (ss_surface->frame_callback)
+ wl_callback_destroy(ss_surface->frame_callback);
+
+ free(ss_surface);
+}
+
+static void
+ss_render_clients(struct nested *nested,
+ cairo_t *cr)
+{
+ /* The clients are composited by the parent compositor so we
+ * don't need to do anything here */
+}
+
+static void
+ss_buffer_release(void *data, struct wl_buffer *wl_buffer)
+{
+ struct nested_buffer *buffer = data;
+
+ nested_buffer_reference(&buffer->parent_ref, NULL);
+}
+
+static struct wl_buffer_listener ss_buffer_listener = {
+ ss_buffer_release
+};
+
+static void
+ss_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct nested_surface *surface = data;
+ struct nested_ss_surface *ss_surface = surface->renderer_data;
+
+ flush_surface_frame_callback_list(surface, time);
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ ss_surface->frame_callback = NULL;
+}
+
+static const struct wl_callback_listener ss_frame_listener = {
+ ss_frame_callback
+};
+
+static void
+ss_surface_attach(struct nested_surface *surface,
+ struct nested_buffer *buffer)
+{
+ struct nested *nested = surface->nested;
+ struct nested_ss_surface *ss_surface = surface->renderer_data;
+ struct wl_buffer *parent_buffer;
+ const pixman_box32_t *rects;
+ int n_rects, i;
+
+ if (buffer) {
+ /* Create a representation of the buffer in the parent
+ * compositor if we haven't already */
+ if (buffer->parent_buffer == NULL) {
+ EGLDisplay *edpy = nested->egl_display;
+ EGLImageKHR image = surface->image;
+
+ buffer->parent_buffer =
+ create_wayland_buffer_from_image(edpy, image);
+
+ wl_buffer_add_listener(buffer->parent_buffer,
+ &ss_buffer_listener,
+ buffer);
+ }
+
+ parent_buffer = buffer->parent_buffer;
+
+ /* We'll take a reference to the buffer while the parent
+ * compositor is using it so that we won't report the release
+ * event until the parent has also finished with it */
+ nested_buffer_reference(&buffer->parent_ref, buffer);
+ } else {
+ parent_buffer = NULL;
+ }
+
+ wl_surface_attach(ss_surface->surface, parent_buffer, 0, 0);
+
+ rects = pixman_region32_rectangles(&surface->pending.damage, &n_rects);
+
+ for (i = 0; i < n_rects; i++) {
+ const pixman_box32_t *rect = rects + i;
+ wl_surface_damage(ss_surface->surface,
+ rect->x1,
+ rect->y1,
+ rect->x2 - rect->x1,
+ rect->y2 - rect->y1);
+ }
+
+ if (ss_surface->frame_callback)
+ wl_callback_destroy(ss_surface->frame_callback);
+
+ ss_surface->frame_callback = wl_surface_frame(ss_surface->surface);
+ wl_callback_add_listener(ss_surface->frame_callback,
+ &ss_frame_listener,
+ surface);
+
+ wl_surface_commit(ss_surface->surface);
+}
+
+static const struct nested_renderer
+nested_ss_renderer = {
+ .surface_init = ss_surface_init,
+ .surface_fini = ss_surface_fini,
+ .render_clients = ss_render_clients,
+ .surface_attach = ss_surface_attach
+};
+
+int
+main(int argc, char *argv[])
+{
+ struct display *display;
+ struct nested *nested;
+
+ if (parse_options(nested_options,
+ ARRAY_LENGTH(nested_options), &argc, argv) > 1) {
+ printf("Usage: %s [OPTIONS]\n --blit or -b\n", argv[0]);
+ exit(1);
+ }
+
+ display = display_create(&argc, argv);
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ nested = nested_create(display);
+
+ launch_client(nested, "weston-nested-client");
+
+ display_run(display);
+
+ nested_destroy(nested);
+ display_destroy(display);
+
+ return 0;
+}
diff --git a/clients/presentation-shm.c b/clients/presentation-shm.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5d73a2a854ed81f3cf950b133ab387baad7ab16
--- /dev/null
+++ b/clients/presentation-shm.c
@@ -0,0 +1,956 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2014 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include "shared/helpers.h"
+#include
+#include "shared/timespec-util.h"
+#include "shared/os-compatibility.h"
+#include "presentation-time-client-protocol.h"
+#include "xdg-shell-client-protocol.h"
+
+enum run_mode {
+ RUN_MODE_FEEDBACK,
+ RUN_MODE_FEEDBACK_IDLE,
+ RUN_MODE_PRESENT,
+};
+
+static const char * const run_mode_name[] = {
+ [RUN_MODE_FEEDBACK] = "feedback",
+ [RUN_MODE_FEEDBACK_IDLE] = "feedback-idle",
+ [RUN_MODE_PRESENT] = "low-lat present",
+};
+
+struct output {
+ struct wl_output *output;
+ uint32_t name;
+ struct wl_list link;
+};
+
+struct display {
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct xdg_wm_base *wm_base;
+
+ struct wl_shm *shm;
+ uint32_t formats;
+
+ struct wp_presentation *presentation;
+ clockid_t clk_id;
+
+ struct wl_list output_list; /* struct output::link */
+};
+
+struct feedback {
+ struct window *window;
+ unsigned frame_no;
+ struct wp_presentation_feedback *feedback;
+ struct timespec commit;
+ struct timespec target;
+ uint32_t frame_stamp;
+ struct wl_list link;
+ struct timespec present;
+};
+
+struct buffer {
+ struct wl_buffer *buffer;
+ void *shm_data;
+ int busy;
+};
+
+struct window {
+ struct display *display;
+ int width, height;
+ enum run_mode mode;
+ struct wl_surface *surface;
+ struct xdg_surface *xdg_surface;
+ struct xdg_toplevel *xdg_toplevel;
+ uint32_t configure_serial;
+
+ struct buffer *buffers;
+ int num_buffers;
+ int next;
+ int refresh_nsec;
+ int commit_delay_msecs;
+
+ struct wl_callback *callback;
+ struct wl_list feedback_list;
+
+ struct feedback *received_feedback;
+};
+
+#define NSEC_PER_SEC 1000000000
+
+static void
+buffer_release(void *data, struct wl_buffer *buffer)
+{
+ struct buffer *mybuf = data;
+
+ mybuf->busy = 0;
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+ buffer_release
+};
+
+static int
+create_shm_buffers(struct display *display, struct buffer **buffers,
+ int num_buffers, int width, int height, uint32_t format)
+{
+ struct buffer *bufs;
+ struct wl_shm_pool *pool;
+ int fd, size, stride, offset;
+ void *data;
+ int i;
+
+ stride = width * 4;
+ size = stride * height * num_buffers;
+
+ fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
+ size, strerror(errno));
+ return -1;
+ }
+
+ data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED) {
+ fprintf(stderr, "mmap failed: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ pool = wl_shm_create_pool(display->shm, fd, size);
+ offset = 0;
+
+ bufs = calloc(num_buffers, sizeof(*bufs));
+ assert(bufs);
+
+ for (i = 0; i < num_buffers; i++) {
+ bufs[i].buffer = wl_shm_pool_create_buffer(pool, offset,
+ width, height,
+ stride, format);
+ assert(bufs[i].buffer);
+ wl_buffer_add_listener(bufs[i].buffer,
+ &buffer_listener, &bufs[i]);
+
+ bufs[i].shm_data = (char *)data + offset;
+ offset += stride * height;
+ }
+
+ wl_shm_pool_destroy(pool);
+ close(fd);
+
+ *buffers = bufs;
+
+ return 0;
+}
+
+static void
+xdg_wm_base_handle_ping(void *data, struct xdg_wm_base *xdg_wm_base,
+ uint32_t serial)
+{
+ xdg_wm_base_pong(xdg_wm_base, serial);
+}
+
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+ .ping = xdg_wm_base_handle_ping,
+};
+
+static void
+xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface,
+ uint32_t serial)
+{
+ struct window *window = data;
+
+ window->configure_serial = serial;
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+ .configure = xdg_surface_handle_configure,
+};
+
+
+static void
+xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel,
+ int32_t width, int32_t height,
+ struct wl_array *states)
+{
+ /* noop */
+}
+
+static void
+xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)
+{
+ fprintf(stderr, "presentation-shm exiting\n");
+ exit(0);
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+ .configure = xdg_toplevel_handle_configure,
+ .close = xdg_toplevel_handle_close,
+};
+
+static struct window *
+create_window(struct display *display, int width, int height,
+ enum run_mode mode, int commit_delay_msecs)
+{
+ struct window *window;
+ char title[128];
+ int ret;
+
+ snprintf(title, sizeof(title),
+ "presentation-shm: %s [Delay %i msecs]", run_mode_name[mode],
+ commit_delay_msecs);
+
+ window = zalloc(sizeof *window);
+ if (!window)
+ return NULL;
+
+ window->commit_delay_msecs = commit_delay_msecs;
+ window->mode = mode;
+ window->callback = NULL;
+ wl_list_init(&window->feedback_list);
+ window->display = display;
+ window->width = width;
+ window->height = height;
+ window->surface = wl_compositor_create_surface(display->compositor);
+ window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base,
+ window->surface);
+
+ if (!window->xdg_surface)
+ return NULL;
+
+ window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
+
+ if (!window->xdg_toplevel)
+ return NULL;
+
+ xdg_wm_base_add_listener(display->wm_base, &xdg_wm_base_listener,
+ NULL);
+ xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener,
+ window);
+ xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener,
+ window);
+
+ xdg_toplevel_set_title(window->xdg_toplevel, title);
+ xdg_toplevel_set_min_size(window->xdg_toplevel, width, height);
+ xdg_toplevel_set_max_size(window->xdg_toplevel, width, height);
+
+ wl_surface_commit(window->surface);
+ wl_display_roundtrip(window->display->display);
+
+ window->num_buffers = 60;
+ window->refresh_nsec = NSEC_PER_SEC / 60; /* 60 Hz guess */
+ window->next = 0;
+ ret = create_shm_buffers(window->display,
+ &window->buffers, window->num_buffers,
+ window->width, window->height,
+ WL_SHM_FORMAT_XRGB8888);
+ assert(ret == 0);
+
+ return window;
+}
+
+static void
+destroy_feedback(struct feedback *feedback)
+{
+ if (feedback->feedback)
+ wp_presentation_feedback_destroy(feedback->feedback);
+
+ wl_list_remove(&feedback->link);
+ free(feedback);
+}
+
+static void
+destroy_window(struct window *window)
+{
+ int i;
+
+ while (!wl_list_empty(&window->feedback_list)) {
+ struct feedback *f;
+
+ f = wl_container_of(window->feedback_list.next, f, link);
+ printf("clean up feedback %u\n", f->frame_no);
+ destroy_feedback(f);
+ }
+
+ if (window->callback)
+ wl_callback_destroy(window->callback);
+
+ xdg_surface_destroy(window->xdg_surface);
+ wl_surface_destroy(window->surface);
+
+ for (i = 0; i < window->num_buffers; i++)
+ wl_buffer_destroy(window->buffers[i].buffer);
+ /* munmap(window->buffers[0].shm_data, size); */
+ free(window->buffers);
+
+ free(window);
+}
+
+static struct buffer *
+window_next_buffer(struct window *window)
+{
+ struct buffer *buf = &window->buffers[window->next];
+
+ window->next = (window->next + 1) % window->num_buffers;
+
+ return buf;
+}
+
+static void
+paint_pixels(void *image, int width, int height, uint32_t phase)
+{
+ const int halfh = height / 2;
+ const int halfw = width / 2;
+ uint32_t *pixel = image;
+ int y, or;
+ double ang = M_PI * 2.0 / 1000000.0 * phase;
+ double s = sin(ang);
+ double c = cos(ang);
+
+ /* squared radii thresholds */
+ or = (halfw < halfh ? halfw : halfh) - 16;
+ or *= or;
+
+ for (y = 0; y < height; y++) {
+ int x;
+ int oy = y - halfh;
+ int y2 = oy * oy;
+
+ for (x = 0; x < width; x++) {
+ int ox = x - halfw;
+ uint32_t v = 0xff000000;
+ double rx, ry;
+
+ if (ox * ox + y2 > or) {
+ if (ox * oy > 0)
+ *pixel++ = 0xff000000;
+ else
+ *pixel++ = 0xffffffff;
+ continue;
+ }
+
+ rx = c * ox + s * oy;
+ ry = -s * ox + c * oy;
+
+ if (rx < 0.0)
+ v |= 0x00ff0000;
+ if (ry < 0.0)
+ v |= 0x0000ff00;
+ if ((rx < 0.0) == (ry < 0.0))
+ v |= 0x000000ff;
+
+ *pixel++ = v;
+ }
+ }
+}
+
+static void
+feedback_sync_output(void *data,
+ struct wp_presentation_feedback *presentation_feedback,
+ struct wl_output *output)
+{
+ /* not interested */
+}
+
+static char *
+pflags_to_str(uint32_t flags, char *str, unsigned len)
+{
+ static const struct {
+ uint32_t flag;
+ char sym;
+ } desc[] = {
+ { WP_PRESENTATION_FEEDBACK_KIND_VSYNC, 's' },
+ { WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK, 'c' },
+ { WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION, 'e' },
+ { WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY, 'z' },
+ };
+ unsigned i;
+
+ *str = '\0';
+ if (len < ARRAY_LENGTH(desc) + 1)
+ return str;
+
+ for (i = 0; i < ARRAY_LENGTH(desc); i++)
+ str[i] = flags & desc[i].flag ? desc[i].sym : '_';
+ str[ARRAY_LENGTH(desc)] = '\0';
+
+ return str;
+}
+
+static uint32_t
+timespec_to_ms(const struct timespec *ts)
+{
+ return (uint32_t)ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
+}
+
+static int
+timespec_diff_to_usec(const struct timespec *a, const struct timespec *b)
+{
+ time_t secs = a->tv_sec - b->tv_sec;
+ long nsec = a->tv_nsec - b->tv_nsec;
+
+ return secs * 1000000 + nsec / 1000;
+}
+
+static void
+feedback_presented(void *data,
+ struct wp_presentation_feedback *presentation_feedback,
+ uint32_t tv_sec_hi,
+ uint32_t tv_sec_lo,
+ uint32_t tv_nsec,
+ uint32_t refresh_nsec,
+ uint32_t seq_hi,
+ uint32_t seq_lo,
+ uint32_t flags)
+{
+ struct feedback *feedback = data;
+ struct window *window = feedback->window;
+ struct feedback *prev_feedback = window->received_feedback;
+ uint64_t seq = ((uint64_t)seq_hi << 32) + seq_lo;
+ const struct timespec *prevpresent;
+ uint32_t commit, present;
+ uint32_t f2c, c2p, f2p;
+ int p2p, t2p;
+ char flagstr[10];
+
+ timespec_from_proto(&feedback->present, tv_sec_hi, tv_sec_lo, tv_nsec);
+ commit = timespec_to_ms(&feedback->commit);
+ present = timespec_to_ms(&feedback->present);
+
+ if (prev_feedback)
+ prevpresent = &prev_feedback->present;
+ else
+ prevpresent = &feedback->present;
+
+ f2c = commit - feedback->frame_stamp;
+ c2p = present - commit;
+ f2p = present - feedback->frame_stamp;
+ p2p = timespec_diff_to_usec(&feedback->present, prevpresent);
+ t2p = timespec_diff_to_usec(&feedback->present, &feedback->target);
+
+ switch (window->mode) {
+ case RUN_MODE_PRESENT:
+ printf("%6u: c2p %4u ms, p2p %5d us, t2p %6d us, [%s] "
+ "seq %" PRIu64 "\n", feedback->frame_no, c2p,
+ p2p, t2p,
+ pflags_to_str(flags, flagstr, sizeof(flagstr)), seq);
+ break;
+ case RUN_MODE_FEEDBACK:
+ case RUN_MODE_FEEDBACK_IDLE:
+ printf("%6u: f2c %2u ms, c2p %2u ms, f2p %2u ms, p2p %5d us, "
+ "t2p %6d, [%s], seq %" PRIu64 "\n", feedback->frame_no,
+ f2c, c2p, f2p, p2p, t2p,
+ pflags_to_str(flags, flagstr, sizeof(flagstr)), seq);
+ }
+
+ if (window->received_feedback)
+ destroy_feedback(window->received_feedback);
+ window->received_feedback = feedback;
+}
+
+static void
+feedback_discarded(void *data,
+ struct wp_presentation_feedback *presentation_feedback)
+{
+ struct feedback *feedback = data;
+
+ printf("discarded %u\n", feedback->frame_no);
+
+ destroy_feedback(feedback);
+}
+
+static const struct wp_presentation_feedback_listener feedback_listener = {
+ feedback_sync_output,
+ feedback_presented,
+ feedback_discarded
+};
+
+static void
+window_emulate_rendering(struct window *window)
+{
+ struct timespec delay;
+ int ret;
+
+ if (window->commit_delay_msecs <= 0)
+ return;
+
+ delay.tv_sec = window->commit_delay_msecs / 1000;
+ delay.tv_nsec = (window->commit_delay_msecs % 1000) * 1000000;
+
+ ret = nanosleep(&delay, NULL);
+ if (ret)
+ printf("nanosleep failed: %s\n", strerror(errno));
+}
+
+static void
+window_create_feedback(struct window *window, uint32_t frame_stamp)
+{
+ static unsigned seq;
+ struct wp_presentation *pres = window->display->presentation;
+ struct feedback *feedback;
+
+ seq++;
+
+ if (!pres)
+ return;
+
+ feedback = zalloc(sizeof *feedback);
+ if (!feedback)
+ return;
+
+ feedback->window = window;
+ feedback->feedback = wp_presentation_feedback(pres, window->surface);
+ wp_presentation_feedback_add_listener(feedback->feedback,
+ &feedback_listener, feedback);
+
+ feedback->frame_no = seq;
+
+ clock_gettime(window->display->clk_id, &feedback->commit);
+ feedback->frame_stamp = frame_stamp;
+ feedback->target = feedback->commit;
+
+ wl_list_insert(&window->feedback_list, &feedback->link);
+}
+
+static void
+window_commit_next(struct window *window)
+{
+ struct buffer *buffer;
+
+ buffer = window_next_buffer(window);
+ assert(buffer);
+
+ if (window->configure_serial) {
+ xdg_surface_ack_configure(window->xdg_surface,
+ window->configure_serial);
+ window->configure_serial = 0;
+ }
+
+ wl_surface_attach(window->surface, buffer->buffer, 0, 0);
+ wl_surface_damage(window->surface, 0, 0, window->width, window->height);
+ wl_surface_commit(window->surface);
+ buffer->busy = 1;
+}
+
+static const struct wl_callback_listener frame_listener_mode_feedback;
+
+static void
+redraw_mode_feedback(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct window *window = data;
+
+ if (callback && window->mode == RUN_MODE_FEEDBACK_IDLE)
+ sleep(1);
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ window_emulate_rendering(window);
+
+ window->callback = wl_surface_frame(window->surface);
+ wl_callback_add_listener(window->callback,
+ &frame_listener_mode_feedback, window);
+
+ window_create_feedback(window, time);
+ window_commit_next(window);
+}
+
+static const struct wl_callback_listener frame_listener_mode_feedback = {
+ redraw_mode_feedback
+};
+
+static const struct wp_presentation_feedback_listener feedkick_listener;
+
+static void
+window_feedkick(struct window *window)
+{
+ struct wp_presentation *pres = window->display->presentation;
+ struct wp_presentation_feedback *fback;
+
+ fback = wp_presentation_feedback(pres, window->surface);
+ wp_presentation_feedback_add_listener(fback, &feedkick_listener,
+ window);
+}
+
+static void
+feedkick_presented(void *data,
+ struct wp_presentation_feedback *presentation_feedback,
+ uint32_t tv_sec_hi,
+ uint32_t tv_sec_lo,
+ uint32_t tv_nsec,
+ uint32_t refresh_nsec,
+ uint32_t seq_hi,
+ uint32_t seq_lo,
+ uint32_t flags)
+{
+ struct window *window = data;
+
+ wp_presentation_feedback_destroy(presentation_feedback);
+ window->refresh_nsec = refresh_nsec;
+
+ switch (window->mode) {
+ case RUN_MODE_PRESENT:
+ window_emulate_rendering(window);
+ window_create_feedback(window, 0);
+ window_feedkick(window);
+ window_commit_next(window);
+ break;
+ case RUN_MODE_FEEDBACK:
+ case RUN_MODE_FEEDBACK_IDLE:
+ assert(0 && "bad mode");
+ }
+}
+
+static void
+feedkick_discarded(void *data,
+ struct wp_presentation_feedback *presentation_feedback)
+{
+ struct window *window = data;
+
+ wp_presentation_feedback_destroy(presentation_feedback);
+
+ switch (window->mode) {
+ case RUN_MODE_PRESENT:
+ window_emulate_rendering(window);
+ window_create_feedback(window, 0);
+ window_feedkick(window);
+ window_commit_next(window);
+ break;
+ case RUN_MODE_FEEDBACK:
+ case RUN_MODE_FEEDBACK_IDLE:
+ assert(0 && "bad mode");
+ }
+}
+
+static const struct wp_presentation_feedback_listener feedkick_listener = {
+ feedback_sync_output,
+ feedkick_presented,
+ feedkick_discarded
+};
+
+static void
+firstdraw_mode_burst(struct window *window)
+{
+ window_emulate_rendering(window);
+
+ switch (window->mode) {
+ case RUN_MODE_PRESENT:
+ window_create_feedback(window, 0);
+ break;
+ case RUN_MODE_FEEDBACK:
+ case RUN_MODE_FEEDBACK_IDLE:
+ assert(0 && "bad mode");
+ }
+
+ window_feedkick(window);
+ window_commit_next(window);
+}
+
+static void
+window_prerender(struct window *window)
+{
+ int i;
+ int timefactor = 1000000 / window->num_buffers;
+
+ for (i = 0; i < window->num_buffers; i++) {
+ struct buffer *buf = &window->buffers[i];
+
+ if (buf->busy)
+ fprintf(stderr, "wl_buffer id %u) busy\n",
+ wl_proxy_get_id(
+ (struct wl_proxy *)buf->buffer));
+
+ paint_pixels(buf->shm_data, window->width, window->height,
+ i * timefactor);
+ }
+}
+
+static void
+output_destroy(struct output *o)
+{
+ wl_output_destroy(o->output);
+ wl_list_remove(&o->link);
+ free(o);
+}
+
+static void
+display_add_output(struct display *d, uint32_t name, uint32_t version)
+{
+ struct output *o;
+
+ o = zalloc(sizeof(*o));
+ assert(o);
+
+ o->output = wl_registry_bind(d->registry, name,
+ &wl_output_interface, 1);
+ o->name = name;
+ wl_list_insert(&d->output_list, &o->link);
+}
+
+static void
+presentation_clock_id(void *data, struct wp_presentation *presentation,
+ uint32_t clk_id)
+{
+ struct display *d = data;
+
+ d->clk_id = clk_id;
+}
+
+static const struct wp_presentation_listener presentation_listener = {
+ presentation_clock_id
+};
+
+static void
+shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+ struct display *d = data;
+
+ d->formats |= (1 << format);
+}
+
+static const struct wl_shm_listener shm_listener = {
+ shm_format
+};
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version)
+{
+ struct display *d = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ d->compositor =
+ wl_registry_bind(registry,
+ name, &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "xdg_wm_base") == 0) {
+ d->wm_base =
+ wl_registry_bind(registry, name,
+ &xdg_wm_base_interface, version);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ d->shm = wl_registry_bind(registry,
+ name, &wl_shm_interface, 1);
+ wl_shm_add_listener(d->shm, &shm_listener, d);
+ } else if (strcmp(interface, "wl_output") == 0) {
+ display_add_output(d, name, version);
+ } else if (strcmp(interface, wp_presentation_interface.name) == 0) {
+ d->presentation =
+ wl_registry_bind(registry,
+ name, &wp_presentation_interface, 1);
+ wp_presentation_add_listener(d->presentation,
+ &presentation_listener, d);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+ struct display *d = data;
+ struct output *output, *otmp;
+
+ wl_list_for_each_safe(output, otmp, &d->output_list, link) {
+ if (output->name != name)
+ continue;
+
+ output_destroy(output);
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static struct display *
+create_display(void)
+{
+ struct display *display;
+
+ display = malloc(sizeof *display);
+ if (display == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ display->display = wl_display_connect(NULL);
+ assert(display->display);
+
+ display->formats = 0;
+ display->clk_id = -1;
+ wl_list_init(&display->output_list);
+ display->registry = wl_display_get_registry(display->display);
+ wl_registry_add_listener(display->registry,
+ ®istry_listener, display);
+ wl_display_roundtrip(display->display);
+ if (display->shm == NULL) {
+ fprintf(stderr, "No wl_shm global\n");
+ exit(1);
+ }
+
+ wl_display_roundtrip(display->display);
+
+ if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
+ fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
+ exit(1);
+ }
+
+ wl_display_get_fd(display->display);
+
+ return display;
+}
+
+static void
+destroy_display(struct display *display)
+{
+ while (!wl_list_empty(&display->output_list)) {
+ struct output *o;
+
+ o = wl_container_of(display->output_list.next, o, link);
+ output_destroy(o);
+ }
+
+ if (display->shm)
+ wl_shm_destroy(display->shm);
+
+ if (display->wm_base)
+ xdg_wm_base_destroy(display->wm_base);
+
+ if (display->compositor)
+ wl_compositor_destroy(display->compositor);
+
+ wl_registry_destroy(display->registry);
+ wl_display_flush(display->display);
+ wl_display_disconnect(display->display);
+ free(display);
+}
+
+static int running = 1;
+
+static void
+signal_int(int signum)
+{
+ running = 0;
+}
+
+static void
+usage(const char *prog, int exit_code)
+{
+ fprintf(stderr, "Usage: %s [mode] [options]\n"
+ "where 'mode' is one of\n"
+ " -f\t\trun in feedback mode (default)\n"
+ " -i\t\trun in feedback-idle mode; sleep 1s between frames\n"
+ " -p\t\trun in low-latency presentation mode\n"
+ "and 'options' may include\n"
+ " -d msecs\temulate the time used for rendering by a delay \n"
+ "\t\tof the given milliseconds before commit\n\n",
+ prog);
+
+ fprintf(stderr, "Printed timing statistics, depending on mode:\n"
+ " commit sequence number\n"
+ " f2c: time from frame callback timestamp to commit\n"
+ " c2p: time from commit to presentation\n"
+ " f2p: time from frame callback timestamp to presentation\n"
+ " p2p: time from previous presentation to this one\n"
+ " t2p: time from target timestamp to presentation\n"
+ " seq: MSC\n");
+
+
+ exit(exit_code);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sigaction sigint;
+ struct display *display;
+ struct window *window;
+ int ret = 0;
+ enum run_mode mode = RUN_MODE_FEEDBACK;
+ int i;
+ int commit_delay_msecs = 0;
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp("-f", argv[i]) == 0)
+ mode = RUN_MODE_FEEDBACK;
+ else if (strcmp("-i", argv[i]) == 0)
+ mode = RUN_MODE_FEEDBACK_IDLE;
+ else if (strcmp("-p", argv[i]) == 0)
+ mode = RUN_MODE_PRESENT;
+ else if ((strcmp("-d", argv[i]) == 0) && (i + 1 < argc)) {
+ i++;
+ commit_delay_msecs = atoi(argv[i]);
+ }
+ else
+ usage(argv[0], EXIT_FAILURE);
+ }
+
+ display = create_display();
+ window = create_window(display, 250, 250, mode, commit_delay_msecs);
+ if (!window)
+ return 1;
+
+ sigint.sa_handler = signal_int;
+ sigemptyset(&sigint.sa_mask);
+ sigint.sa_flags = SA_RESETHAND;
+ sigaction(SIGINT, &sigint, NULL);
+
+ window_prerender(window);
+
+ switch (mode) {
+ case RUN_MODE_FEEDBACK:
+ case RUN_MODE_FEEDBACK_IDLE:
+ redraw_mode_feedback(window, NULL, 0);
+ break;
+ case RUN_MODE_PRESENT:
+ firstdraw_mode_burst(window);
+ break;
+ }
+
+ while (running && ret != -1)
+ ret = wl_display_dispatch(display->display);
+
+ fprintf(stderr, "presentation-shm exiting\n");
+ destroy_window(window);
+ destroy_display(display);
+
+ return 0;
+}
diff --git a/clients/resizor.c b/clients/resizor.c
new file mode 100644
index 0000000000000000000000000000000000000000..cfc5d419ea611f906e01f2d7e9e1ec668895bded
--- /dev/null
+++ b/clients/resizor.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include